Merge branch 'update-http-parser'
This commit is contained in:
commit
eb21e6f8f7
|
@ -1581,6 +1581,10 @@ int construct_push_component(BlockAllocator &balloc, StringRef &scheme,
|
||||||
int rv;
|
int rv;
|
||||||
StringRef rel, relq;
|
StringRef rel, relq;
|
||||||
|
|
||||||
|
if (uri.size() == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
http_parser_url u{};
|
http_parser_url u{};
|
||||||
|
|
||||||
rv = http_parser_parse_url(uri.c_str(), uri.size(), 0, &u);
|
rv = http_parser_parse_url(uri.c_str(), uri.size(), 0, &u);
|
||||||
|
|
|
@ -995,12 +995,8 @@ void test_http2_construct_push_component(void) {
|
||||||
|
|
||||||
uri = StringRef{};
|
uri = StringRef{};
|
||||||
|
|
||||||
CU_ASSERT(0 == http2::construct_push_component(balloc, scheme, authority,
|
CU_ASSERT(-1 == http2::construct_push_component(balloc, scheme, authority,
|
||||||
path, base, uri));
|
path, base, uri));
|
||||||
CU_ASSERT("" == scheme);
|
|
||||||
CU_ASSERT("" == authority);
|
|
||||||
CU_ASSERT("/" == path);
|
|
||||||
|
|
||||||
scheme = StringRef{};
|
scheme = StringRef{};
|
||||||
authority = StringRef{};
|
authority = StringRef{};
|
||||||
path = StringRef{};
|
path = StringRef{};
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
Contributors must agree to the Contributor License Agreement before patches
|
|
||||||
can be accepted.
|
|
||||||
|
|
||||||
http://spreadsheets2.google.com/viewform?hl=en&formkey=dDJXOGUwbzlYaWM4cHN1MERwQS1CSnc6MQ
|
|
|
@ -1,8 +1,4 @@
|
||||||
http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
|
Copyright Joyent, Inc. and other Node contributors.
|
||||||
Igor Sysoev.
|
|
||||||
|
|
||||||
Additional changes are licensed under the same terms as NGINX and
|
|
||||||
copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to
|
of this software and associated documentation files (the "Software"), to
|
||||||
|
|
|
@ -72,9 +72,9 @@ if (parser->upgrade) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
HTTP needs to know where the end of the stream is. For example, sometimes
|
`http_parser` needs to know where the end of the stream is. For example, sometimes
|
||||||
servers send responses without Content-Length and expect the client to
|
servers send responses without Content-Length and expect the client to
|
||||||
consume input (for the body) until EOF. To tell http_parser about EOF, give
|
consume input (for the body) until EOF. To tell `http_parser` about EOF, give
|
||||||
`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors
|
`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors
|
||||||
can still be encountered during an EOF, so one must still be prepared
|
can still be encountered during an EOF, so one must still be prepared
|
||||||
to receive them.
|
to receive them.
|
||||||
|
@ -93,7 +93,7 @@ the on_body callback.
|
||||||
The Special Problem of Upgrade
|
The Special Problem of Upgrade
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
HTTP supports upgrading the connection to a different protocol. An
|
`http_parser` supports upgrading the connection to a different protocol. An
|
||||||
increasingly common example of this is the WebSocket protocol which sends
|
increasingly common example of this is the WebSocket protocol which sends
|
||||||
a request like
|
a request like
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ parse a request, and then give a response over that socket. By instantiation
|
||||||
of a thread-local struct containing relevant data (e.g. accepted socket,
|
of a thread-local struct containing relevant data (e.g. accepted socket,
|
||||||
allocated memory for callbacks to write into, etc), a parser's callbacks are
|
allocated memory for callbacks to write into, etc), a parser's callbacks are
|
||||||
able to communicate data between the scope of the thread and the scope of the
|
able to communicate data between the scope of the thread and the scope of the
|
||||||
callback in a threadsafe manner. This allows http-parser to be used in
|
callback in a threadsafe manner. This allows `http_parser` to be used in
|
||||||
multi-threaded contexts.
|
multi-threaded contexts.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
@ -202,7 +202,7 @@ void http_parser_thread(socket_t sock) {
|
||||||
|
|
||||||
In case you parse HTTP message in chunks (i.e. `read()` request line
|
In case you parse HTTP message in chunks (i.e. `read()` request line
|
||||||
from socket, parse, read half headers, parse, etc) your data callbacks
|
from socket, parse, read half headers, parse, etc) your data callbacks
|
||||||
may be called more than once. Http-parser guarantees that data pointer is only
|
may be called more than once. `http_parser` guarantees that data pointer is only
|
||||||
valid for the lifetime of callback. You can also `read()` into a heap allocated
|
valid for the lifetime of callback. You can also `read()` into a heap allocated
|
||||||
buffer to avoid copying memory around if this fits your application.
|
buffer to avoid copying memory around if this fits your application.
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,14 @@
|
||||||
*/
|
*/
|
||||||
#include "http_parser.h"
|
#include "http_parser.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
/* 8 gb */
|
||||||
|
static const int64_t kBytes = 8LL << 30;
|
||||||
|
|
||||||
static const char data[] =
|
static const char data[] =
|
||||||
"POST /joyent/http-parser HTTP/1.1\r\n"
|
"POST /joyent/http-parser HTTP/1.1\r\n"
|
||||||
"Host: github.com\r\n"
|
"Host: github.com\r\n"
|
||||||
|
@ -38,7 +42,7 @@ static const char data[] =
|
||||||
"Referer: https://github.com/joyent/http-parser\r\n"
|
"Referer: https://github.com/joyent/http-parser\r\n"
|
||||||
"Connection: keep-alive\r\n"
|
"Connection: keep-alive\r\n"
|
||||||
"Transfer-Encoding: chunked\r\n"
|
"Transfer-Encoding: chunked\r\n"
|
||||||
"Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n\r\n";
|
"Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n";
|
||||||
static const size_t data_len = sizeof(data) - 1;
|
static const size_t data_len = sizeof(data) - 1;
|
||||||
|
|
||||||
static int on_info(http_parser* p) {
|
static int on_info(http_parser* p) {
|
||||||
|
@ -67,13 +71,13 @@ int bench(int iter_count, int silent) {
|
||||||
int err;
|
int err;
|
||||||
struct timeval start;
|
struct timeval start;
|
||||||
struct timeval end;
|
struct timeval end;
|
||||||
float rps;
|
|
||||||
|
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
err = gettimeofday(&start, NULL);
|
err = gettimeofday(&start, NULL);
|
||||||
assert(err == 0);
|
assert(err == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "req_len=%d\n", (int) data_len);
|
||||||
for (i = 0; i < iter_count; i++) {
|
for (i = 0; i < iter_count; i++) {
|
||||||
size_t parsed;
|
size_t parsed;
|
||||||
http_parser_init(&parser, HTTP_REQUEST);
|
http_parser_init(&parser, HTTP_REQUEST);
|
||||||
|
@ -83,17 +87,27 @@ int bench(int iter_count, int silent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
|
double elapsed;
|
||||||
|
double bw;
|
||||||
|
double total;
|
||||||
|
|
||||||
err = gettimeofday(&end, NULL);
|
err = gettimeofday(&end, NULL);
|
||||||
assert(err == 0);
|
assert(err == 0);
|
||||||
|
|
||||||
fprintf(stdout, "Benchmark result:\n");
|
fprintf(stdout, "Benchmark result:\n");
|
||||||
|
|
||||||
rps = (float) (end.tv_sec - start.tv_sec) +
|
elapsed = (double) (end.tv_sec - start.tv_sec) +
|
||||||
(end.tv_usec - start.tv_usec) * 1e-6f;
|
(end.tv_usec - start.tv_usec) * 1e-6f;
|
||||||
fprintf(stdout, "Took %f seconds to run\n", rps);
|
|
||||||
|
total = (double) iter_count * data_len;
|
||||||
|
bw = (double) total / elapsed;
|
||||||
|
|
||||||
|
fprintf(stdout, "%.2f mb | %.2f mb/s | %.2f req/sec | %.2f s\n",
|
||||||
|
(double) total / (1024 * 1024),
|
||||||
|
bw / (1024 * 1024),
|
||||||
|
(double) iter_count / elapsed,
|
||||||
|
elapsed);
|
||||||
|
|
||||||
rps = (float) iter_count / rps;
|
|
||||||
fprintf(stdout, "%f req/sec\n", rps);
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,11 +115,14 @@ int bench(int iter_count, int silent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
int64_t iterations;
|
||||||
|
|
||||||
|
iterations = kBytes / (int64_t) data_len;
|
||||||
if (argc == 2 && strcmp(argv[1], "infinite") == 0) {
|
if (argc == 2 && strcmp(argv[1], "infinite") == 0) {
|
||||||
for (;;)
|
for (;;)
|
||||||
bench(5000000, 1);
|
bench(iterations, 1);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return bench(5000000, 0);
|
return bench(iterations, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
|
/* Copyright Joyent, Inc. and other Node contributors.
|
||||||
*
|
|
||||||
* Additional changes are licensed under the same terms as NGINX and
|
|
||||||
* copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
|
/* Copyright Joyent, Inc. and other Node contributors.
|
||||||
*
|
|
||||||
* Additional changes are licensed under the same terms as NGINX and
|
|
||||||
* copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
@ -25,10 +22,11 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
|
static uint32_t max_header_size = HTTP_MAX_HEADER_SIZE;
|
||||||
|
|
||||||
#ifndef ULLONG_MAX
|
#ifndef ULLONG_MAX
|
||||||
# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
|
# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
|
||||||
#endif
|
#endif
|
||||||
|
@ -53,6 +51,7 @@
|
||||||
|
|
||||||
#define SET_ERRNO(e) \
|
#define SET_ERRNO(e) \
|
||||||
do { \
|
do { \
|
||||||
|
parser->nread = nread; \
|
||||||
parser->http_errno = (e); \
|
parser->http_errno = (e); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
@ -60,6 +59,7 @@ do { \
|
||||||
#define UPDATE_STATE(V) p_state = (enum state) (V);
|
#define UPDATE_STATE(V) p_state = (enum state) (V);
|
||||||
#define RETURN(V) \
|
#define RETURN(V) \
|
||||||
do { \
|
do { \
|
||||||
|
parser->nread = nread; \
|
||||||
parser->state = CURRENT_STATE(); \
|
parser->state = CURRENT_STATE(); \
|
||||||
return (V); \
|
return (V); \
|
||||||
} while (0);
|
} while (0);
|
||||||
|
@ -141,20 +141,20 @@ do { \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* Don't allow the total size of the HTTP headers (including the status
|
/* Don't allow the total size of the HTTP headers (including the status
|
||||||
* line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
|
* line) to exceed max_header_size. This check is here to protect
|
||||||
* embedders against denial-of-service attacks where the attacker feeds
|
* embedders against denial-of-service attacks where the attacker feeds
|
||||||
* us a never-ending header that the embedder keeps buffering.
|
* us a never-ending header that the embedder keeps buffering.
|
||||||
*
|
*
|
||||||
* This check is arguably the responsibility of embedders but we're doing
|
* This check is arguably the responsibility of embedders but we're doing
|
||||||
* it on the embedder's behalf because most won't bother and this way we
|
* it on the embedder's behalf because most won't bother and this way we
|
||||||
* make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
|
* make the web a little safer. max_header_size is still far bigger
|
||||||
* than any reasonable request or response so this should never affect
|
* than any reasonable request or response so this should never affect
|
||||||
* day-to-day operation.
|
* day-to-day operation.
|
||||||
*/
|
*/
|
||||||
#define COUNT_HEADER_SIZE(V) \
|
#define COUNT_HEADER_SIZE(V) \
|
||||||
do { \
|
do { \
|
||||||
parser->nread += (V); \
|
nread += (uint32_t)(V); \
|
||||||
if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \
|
if (UNLIKELY(nread > max_header_size)) { \
|
||||||
SET_ERRNO(HPE_HEADER_OVERFLOW); \
|
SET_ERRNO(HPE_HEADER_OVERFLOW); \
|
||||||
goto error; \
|
goto error; \
|
||||||
} \
|
} \
|
||||||
|
@ -196,7 +196,7 @@ static const char tokens[256] = {
|
||||||
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
|
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
|
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
|
||||||
0, '!', 0, '#', '$', '%', '&', '\'',
|
' ', '!', 0, '#', '$', '%', '&', '\'',
|
||||||
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
|
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
|
||||||
0, 0, '*', '+', 0, '-', '.', 0,
|
0, 0, '*', '+', 0, '-', '.', 0,
|
||||||
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
|
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
|
||||||
|
@ -286,10 +286,10 @@ enum state
|
||||||
, s_res_HT
|
, s_res_HT
|
||||||
, s_res_HTT
|
, s_res_HTT
|
||||||
, s_res_HTTP
|
, s_res_HTTP
|
||||||
, s_res_first_http_major
|
|
||||||
, s_res_http_major
|
, s_res_http_major
|
||||||
, s_res_first_http_minor
|
, s_res_http_dot
|
||||||
, s_res_http_minor
|
, s_res_http_minor
|
||||||
|
, s_res_http_end
|
||||||
, s_res_first_status_code
|
, s_res_first_status_code
|
||||||
, s_res_status_code
|
, s_res_status_code
|
||||||
, s_res_status_start
|
, s_res_status_start
|
||||||
|
@ -316,10 +316,12 @@ enum state
|
||||||
, s_req_http_HT
|
, s_req_http_HT
|
||||||
, s_req_http_HTT
|
, s_req_http_HTT
|
||||||
, s_req_http_HTTP
|
, s_req_http_HTTP
|
||||||
, s_req_first_http_major
|
, s_req_http_I
|
||||||
|
, s_req_http_IC
|
||||||
, s_req_http_major
|
, s_req_http_major
|
||||||
, s_req_first_http_minor
|
, s_req_http_dot
|
||||||
, s_req_http_minor
|
, s_req_http_minor
|
||||||
|
, s_req_http_end
|
||||||
, s_req_line_almost_done
|
, s_req_line_almost_done
|
||||||
|
|
||||||
, s_header_field_start
|
, s_header_field_start
|
||||||
|
@ -374,6 +376,8 @@ enum header_states
|
||||||
|
|
||||||
, h_connection
|
, h_connection
|
||||||
, h_content_length
|
, h_content_length
|
||||||
|
, h_content_length_num
|
||||||
|
, h_content_length_ws
|
||||||
, h_transfer_encoding
|
, h_transfer_encoding
|
||||||
, h_upgrade
|
, h_upgrade
|
||||||
|
|
||||||
|
@ -421,14 +425,14 @@ enum http_host_state
|
||||||
(c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
|
(c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
|
||||||
(c) == '$' || (c) == ',')
|
(c) == '$' || (c) == ',')
|
||||||
|
|
||||||
#define STRICT_TOKEN(c) (tokens[(unsigned char)c])
|
#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c])
|
||||||
|
|
||||||
#if HTTP_PARSER_STRICT
|
#if HTTP_PARSER_STRICT
|
||||||
#define TOKEN(c) (tokens[(unsigned char)c])
|
#define TOKEN(c) STRICT_TOKEN(c)
|
||||||
#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
|
#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
|
||||||
#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
|
#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
|
||||||
#else
|
#else
|
||||||
#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c])
|
#define TOKEN(c) tokens[(unsigned char)c]
|
||||||
#define IS_URL_CHAR(c) \
|
#define IS_URL_CHAR(c) \
|
||||||
(BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
|
(BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
|
||||||
#define IS_HOST_CHAR(c) \
|
#define IS_HOST_CHAR(c) \
|
||||||
|
@ -542,7 +546,7 @@ parse_url_char(enum state s, const char ch)
|
||||||
return s_dead;
|
return s_dead;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FALLTHROUGH */
|
/* fall through */
|
||||||
case s_req_server_start:
|
case s_req_server_start:
|
||||||
case s_req_server:
|
case s_req_server:
|
||||||
if (ch == '/') {
|
if (ch == '/') {
|
||||||
|
@ -646,6 +650,7 @@ size_t http_parser_execute (http_parser *parser,
|
||||||
const char *status_mark = 0;
|
const char *status_mark = 0;
|
||||||
enum state p_state = (enum state) parser->state;
|
enum state p_state = (enum state) parser->state;
|
||||||
const unsigned int lenient = parser->lenient_http_headers;
|
const unsigned int lenient = parser->lenient_http_headers;
|
||||||
|
uint32_t nread = parser->nread;
|
||||||
|
|
||||||
/* We're in an error state. Don't bother doing anything. */
|
/* We're in an error state. Don't bother doing anything. */
|
||||||
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
|
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
|
||||||
|
@ -757,21 +762,16 @@ reexecute:
|
||||||
|
|
||||||
case s_start_res:
|
case s_start_res:
|
||||||
{
|
{
|
||||||
|
if (ch == CR || ch == LF)
|
||||||
|
break;
|
||||||
parser->flags = 0;
|
parser->flags = 0;
|
||||||
parser->content_length = ULLONG_MAX;
|
parser->content_length = ULLONG_MAX;
|
||||||
|
|
||||||
switch (ch) {
|
if (ch == 'H') {
|
||||||
case 'H':
|
UPDATE_STATE(s_res_H);
|
||||||
UPDATE_STATE(s_res_H);
|
} else {
|
||||||
break;
|
SET_ERRNO(HPE_INVALID_CONSTANT);
|
||||||
|
goto error;
|
||||||
case CR:
|
|
||||||
case LF:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
SET_ERRNO(HPE_INVALID_CONSTANT);
|
|
||||||
goto error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CALLBACK_NOTIFY(message_begin);
|
CALLBACK_NOTIFY(message_begin);
|
||||||
|
@ -795,75 +795,48 @@ reexecute:
|
||||||
|
|
||||||
case s_res_HTTP:
|
case s_res_HTTP:
|
||||||
STRICT_CHECK(ch != '/');
|
STRICT_CHECK(ch != '/');
|
||||||
UPDATE_STATE(s_res_first_http_major);
|
UPDATE_STATE(s_res_http_major);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case s_res_first_http_major:
|
case s_res_http_major:
|
||||||
if (UNLIKELY(ch < '0' || ch > '9')) {
|
if (UNLIKELY(!IS_NUM(ch))) {
|
||||||
SET_ERRNO(HPE_INVALID_VERSION);
|
SET_ERRNO(HPE_INVALID_VERSION);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
parser->http_major = ch - '0';
|
parser->http_major = ch - '0';
|
||||||
UPDATE_STATE(s_res_http_major);
|
UPDATE_STATE(s_res_http_dot);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* major HTTP version or dot */
|
case s_res_http_dot:
|
||||||
case s_res_http_major:
|
|
||||||
{
|
{
|
||||||
if (ch == '.') {
|
if (UNLIKELY(ch != '.')) {
|
||||||
UPDATE_STATE(s_res_first_http_minor);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IS_NUM(ch)) {
|
|
||||||
SET_ERRNO(HPE_INVALID_VERSION);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
parser->http_major *= 10;
|
|
||||||
parser->http_major += ch - '0';
|
|
||||||
|
|
||||||
if (UNLIKELY(parser->http_major > 999)) {
|
|
||||||
SET_ERRNO(HPE_INVALID_VERSION);
|
SET_ERRNO(HPE_INVALID_VERSION);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UPDATE_STATE(s_res_http_minor);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* first digit of minor HTTP version */
|
case s_res_http_minor:
|
||||||
case s_res_first_http_minor:
|
|
||||||
if (UNLIKELY(!IS_NUM(ch))) {
|
if (UNLIKELY(!IS_NUM(ch))) {
|
||||||
SET_ERRNO(HPE_INVALID_VERSION);
|
SET_ERRNO(HPE_INVALID_VERSION);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
parser->http_minor = ch - '0';
|
parser->http_minor = ch - '0';
|
||||||
UPDATE_STATE(s_res_http_minor);
|
UPDATE_STATE(s_res_http_end);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* minor HTTP version or end of request line */
|
case s_res_http_end:
|
||||||
case s_res_http_minor:
|
|
||||||
{
|
{
|
||||||
if (ch == ' ') {
|
if (UNLIKELY(ch != ' ')) {
|
||||||
UPDATE_STATE(s_res_first_status_code);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UNLIKELY(!IS_NUM(ch))) {
|
|
||||||
SET_ERRNO(HPE_INVALID_VERSION);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
parser->http_minor *= 10;
|
|
||||||
parser->http_minor += ch - '0';
|
|
||||||
|
|
||||||
if (UNLIKELY(parser->http_minor > 999)) {
|
|
||||||
SET_ERRNO(HPE_INVALID_VERSION);
|
SET_ERRNO(HPE_INVALID_VERSION);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UPDATE_STATE(s_res_first_status_code);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -890,10 +863,9 @@ reexecute:
|
||||||
UPDATE_STATE(s_res_status_start);
|
UPDATE_STATE(s_res_status_start);
|
||||||
break;
|
break;
|
||||||
case CR:
|
case CR:
|
||||||
UPDATE_STATE(s_res_line_almost_done);
|
|
||||||
break;
|
|
||||||
case LF:
|
case LF:
|
||||||
UPDATE_STATE(s_header_field_start);
|
UPDATE_STATE(s_res_status_start);
|
||||||
|
REEXECUTE();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
SET_ERRNO(HPE_INVALID_STATUS);
|
SET_ERRNO(HPE_INVALID_STATUS);
|
||||||
|
@ -915,19 +887,13 @@ reexecute:
|
||||||
|
|
||||||
case s_res_status_start:
|
case s_res_status_start:
|
||||||
{
|
{
|
||||||
if (ch == CR) {
|
|
||||||
UPDATE_STATE(s_res_line_almost_done);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch == LF) {
|
|
||||||
UPDATE_STATE(s_header_field_start);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
MARK(status);
|
MARK(status);
|
||||||
UPDATE_STATE(s_res_status);
|
UPDATE_STATE(s_res_status);
|
||||||
parser->index = 0;
|
parser->index = 0;
|
||||||
|
|
||||||
|
if (ch == CR || ch == LF)
|
||||||
|
REEXECUTE();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -980,7 +946,7 @@ reexecute:
|
||||||
/* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
|
/* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
|
||||||
break;
|
break;
|
||||||
case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
|
case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
|
||||||
case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
|
case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */ break;
|
||||||
case 'T': parser->method = HTTP_TRACE; break;
|
case 'T': parser->method = HTTP_TRACE; break;
|
||||||
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
|
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
|
||||||
default:
|
default:
|
||||||
|
@ -1007,7 +973,7 @@ reexecute:
|
||||||
UPDATE_STATE(s_req_spaces_before_url);
|
UPDATE_STATE(s_req_spaces_before_url);
|
||||||
} else if (ch == matcher[parser->index]) {
|
} else if (ch == matcher[parser->index]) {
|
||||||
; /* nada */
|
; /* nada */
|
||||||
} else if (IS_ALPHA(ch)) {
|
} else if ((ch >= 'A' && ch <= 'Z') || ch == '-') {
|
||||||
|
|
||||||
switch (parser->method << 16 | parser->index << 8 | ch) {
|
switch (parser->method << 16 | parser->index << 8 | ch) {
|
||||||
#define XX(meth, pos, ch, new_meth) \
|
#define XX(meth, pos, ch, new_meth) \
|
||||||
|
@ -1016,31 +982,28 @@ reexecute:
|
||||||
|
|
||||||
XX(POST, 1, 'U', PUT)
|
XX(POST, 1, 'U', PUT)
|
||||||
XX(POST, 1, 'A', PATCH)
|
XX(POST, 1, 'A', PATCH)
|
||||||
|
XX(POST, 1, 'R', PROPFIND)
|
||||||
|
XX(PUT, 2, 'R', PURGE)
|
||||||
XX(CONNECT, 1, 'H', CHECKOUT)
|
XX(CONNECT, 1, 'H', CHECKOUT)
|
||||||
XX(CONNECT, 2, 'P', COPY)
|
XX(CONNECT, 2, 'P', COPY)
|
||||||
XX(MKCOL, 1, 'O', MOVE)
|
XX(MKCOL, 1, 'O', MOVE)
|
||||||
XX(MKCOL, 1, 'E', MERGE)
|
XX(MKCOL, 1, 'E', MERGE)
|
||||||
|
XX(MKCOL, 1, '-', MSEARCH)
|
||||||
XX(MKCOL, 2, 'A', MKACTIVITY)
|
XX(MKCOL, 2, 'A', MKACTIVITY)
|
||||||
XX(MKCOL, 3, 'A', MKCALENDAR)
|
XX(MKCOL, 3, 'A', MKCALENDAR)
|
||||||
XX(SUBSCRIBE, 1, 'E', SEARCH)
|
XX(SUBSCRIBE, 1, 'E', SEARCH)
|
||||||
|
XX(SUBSCRIBE, 1, 'O', SOURCE)
|
||||||
XX(REPORT, 2, 'B', REBIND)
|
XX(REPORT, 2, 'B', REBIND)
|
||||||
XX(POST, 1, 'R', PROPFIND)
|
|
||||||
XX(PROPFIND, 4, 'P', PROPPATCH)
|
XX(PROPFIND, 4, 'P', PROPPATCH)
|
||||||
XX(PUT, 2, 'R', PURGE)
|
|
||||||
XX(LOCK, 1, 'I', LINK)
|
XX(LOCK, 1, 'I', LINK)
|
||||||
XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
|
XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
|
||||||
XX(UNLOCK, 2, 'B', UNBIND)
|
XX(UNLOCK, 2, 'B', UNBIND)
|
||||||
XX(UNLOCK, 3, 'I', UNLINK)
|
XX(UNLOCK, 3, 'I', UNLINK)
|
||||||
#undef XX
|
#undef XX
|
||||||
|
|
||||||
default:
|
default:
|
||||||
SET_ERRNO(HPE_INVALID_METHOD);
|
SET_ERRNO(HPE_INVALID_METHOD);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
} else if (ch == '-' &&
|
|
||||||
parser->index == 1 &&
|
|
||||||
parser->method == HTTP_MKCOL) {
|
|
||||||
parser->method = HTTP_MSEARCH;
|
|
||||||
} else {
|
} else {
|
||||||
SET_ERRNO(HPE_INVALID_METHOD);
|
SET_ERRNO(HPE_INVALID_METHOD);
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -1125,11 +1088,17 @@ reexecute:
|
||||||
|
|
||||||
case s_req_http_start:
|
case s_req_http_start:
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
|
case ' ':
|
||||||
|
break;
|
||||||
case 'H':
|
case 'H':
|
||||||
UPDATE_STATE(s_req_http_H);
|
UPDATE_STATE(s_req_http_H);
|
||||||
break;
|
break;
|
||||||
case ' ':
|
case 'I':
|
||||||
break;
|
if (parser->method == HTTP_SOURCE) {
|
||||||
|
UPDATE_STATE(s_req_http_I);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
default:
|
default:
|
||||||
SET_ERRNO(HPE_INVALID_CONSTANT);
|
SET_ERRNO(HPE_INVALID_CONSTANT);
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -1151,59 +1120,53 @@ reexecute:
|
||||||
UPDATE_STATE(s_req_http_HTTP);
|
UPDATE_STATE(s_req_http_HTTP);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case s_req_http_HTTP:
|
case s_req_http_I:
|
||||||
STRICT_CHECK(ch != '/');
|
STRICT_CHECK(ch != 'C');
|
||||||
UPDATE_STATE(s_req_first_http_major);
|
UPDATE_STATE(s_req_http_IC);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* first digit of major HTTP version */
|
case s_req_http_IC:
|
||||||
case s_req_first_http_major:
|
STRICT_CHECK(ch != 'E');
|
||||||
if (UNLIKELY(ch < '1' || ch > '9')) {
|
UPDATE_STATE(s_req_http_HTTP); /* Treat "ICE" as "HTTP". */
|
||||||
SET_ERRNO(HPE_INVALID_VERSION);
|
break;
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
parser->http_major = ch - '0';
|
case s_req_http_HTTP:
|
||||||
|
STRICT_CHECK(ch != '/');
|
||||||
UPDATE_STATE(s_req_http_major);
|
UPDATE_STATE(s_req_http_major);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* major HTTP version or dot */
|
|
||||||
case s_req_http_major:
|
case s_req_http_major:
|
||||||
{
|
|
||||||
if (ch == '.') {
|
|
||||||
UPDATE_STATE(s_req_first_http_minor);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UNLIKELY(!IS_NUM(ch))) {
|
if (UNLIKELY(!IS_NUM(ch))) {
|
||||||
SET_ERRNO(HPE_INVALID_VERSION);
|
SET_ERRNO(HPE_INVALID_VERSION);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
parser->http_major *= 10;
|
parser->http_major = ch - '0';
|
||||||
parser->http_major += ch - '0';
|
UPDATE_STATE(s_req_http_dot);
|
||||||
|
break;
|
||||||
|
|
||||||
if (UNLIKELY(parser->http_major > 999)) {
|
case s_req_http_dot:
|
||||||
|
{
|
||||||
|
if (UNLIKELY(ch != '.')) {
|
||||||
SET_ERRNO(HPE_INVALID_VERSION);
|
SET_ERRNO(HPE_INVALID_VERSION);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UPDATE_STATE(s_req_http_minor);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* first digit of minor HTTP version */
|
case s_req_http_minor:
|
||||||
case s_req_first_http_minor:
|
|
||||||
if (UNLIKELY(!IS_NUM(ch))) {
|
if (UNLIKELY(!IS_NUM(ch))) {
|
||||||
SET_ERRNO(HPE_INVALID_VERSION);
|
SET_ERRNO(HPE_INVALID_VERSION);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
parser->http_minor = ch - '0';
|
parser->http_minor = ch - '0';
|
||||||
UPDATE_STATE(s_req_http_minor);
|
UPDATE_STATE(s_req_http_end);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* minor HTTP version or end of request line */
|
case s_req_http_end:
|
||||||
case s_req_http_minor:
|
|
||||||
{
|
{
|
||||||
if (ch == CR) {
|
if (ch == CR) {
|
||||||
UPDATE_STATE(s_req_line_almost_done);
|
UPDATE_STATE(s_req_line_almost_done);
|
||||||
|
@ -1215,21 +1178,8 @@ reexecute:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX allow spaces after digit? */
|
SET_ERRNO(HPE_INVALID_VERSION);
|
||||||
|
goto error;
|
||||||
if (UNLIKELY(!IS_NUM(ch))) {
|
|
||||||
SET_ERRNO(HPE_INVALID_VERSION);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
parser->http_minor *= 10;
|
|
||||||
parser->http_minor += ch - '0';
|
|
||||||
|
|
||||||
if (UNLIKELY(parser->http_minor > 999)) {
|
|
||||||
SET_ERRNO(HPE_INVALID_VERSION);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1306,8 +1256,14 @@ reexecute:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
switch (parser->header_state) {
|
switch (parser->header_state) {
|
||||||
case h_general:
|
case h_general: {
|
||||||
|
size_t limit = data + len - p;
|
||||||
|
limit = MIN(limit, max_header_size);
|
||||||
|
while (p+1 < data + limit && TOKEN(p[1])) {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case h_C:
|
case h_C:
|
||||||
parser->index++;
|
parser->index++;
|
||||||
|
@ -1407,13 +1363,14 @@ reexecute:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
COUNT_HEADER_SIZE(p - start);
|
|
||||||
|
|
||||||
if (p == data + len) {
|
if (p == data + len) {
|
||||||
--p;
|
--p;
|
||||||
|
COUNT_HEADER_SIZE(p - start);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
COUNT_HEADER_SIZE(p - start);
|
||||||
|
|
||||||
if (ch == ':') {
|
if (ch == ':') {
|
||||||
UPDATE_STATE(s_header_value_discard_ws);
|
UPDATE_STATE(s_header_value_discard_ws);
|
||||||
CALLBACK_DATA(header_field);
|
CALLBACK_DATA(header_field);
|
||||||
|
@ -1437,7 +1394,7 @@ reexecute:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FALLTHROUGH */
|
/* fall through */
|
||||||
|
|
||||||
case s_header_value_start:
|
case s_header_value_start:
|
||||||
{
|
{
|
||||||
|
@ -1476,6 +1433,12 @@ reexecute:
|
||||||
|
|
||||||
parser->flags |= F_CONTENTLENGTH;
|
parser->flags |= F_CONTENTLENGTH;
|
||||||
parser->content_length = ch - '0';
|
parser->content_length = ch - '0';
|
||||||
|
parser->header_state = h_content_length_num;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* when obsolete line folding is encountered for content length
|
||||||
|
* continue to the s_header_value state */
|
||||||
|
case h_content_length_ws:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case h_connection:
|
case h_connection:
|
||||||
|
@ -1538,7 +1501,7 @@ reexecute:
|
||||||
const char* p_lf;
|
const char* p_lf;
|
||||||
size_t limit = data + len - p;
|
size_t limit = data + len - p;
|
||||||
|
|
||||||
limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
|
limit = MIN(limit, max_header_size);
|
||||||
|
|
||||||
p_cr = (const char*) memchr(p, CR, limit);
|
p_cr = (const char*) memchr(p, CR, limit);
|
||||||
p_lf = (const char*) memchr(p, LF, limit);
|
p_lf = (const char*) memchr(p, LF, limit);
|
||||||
|
@ -1553,7 +1516,6 @@ reexecute:
|
||||||
p = data + len;
|
p = data + len;
|
||||||
}
|
}
|
||||||
--p;
|
--p;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1563,10 +1525,18 @@ reexecute:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case h_content_length:
|
case h_content_length:
|
||||||
|
if (ch == ' ') break;
|
||||||
|
h_state = h_content_length_num;
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
case h_content_length_num:
|
||||||
{
|
{
|
||||||
uint64_t t;
|
uint64_t t;
|
||||||
|
|
||||||
if (ch == ' ') break;
|
if (ch == ' ') {
|
||||||
|
h_state = h_content_length_ws;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (UNLIKELY(!IS_NUM(ch))) {
|
if (UNLIKELY(!IS_NUM(ch))) {
|
||||||
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
|
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
|
||||||
|
@ -1589,6 +1559,12 @@ reexecute:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case h_content_length_ws:
|
||||||
|
if (ch == ' ') break;
|
||||||
|
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
|
||||||
|
parser->header_state = h_state;
|
||||||
|
goto error;
|
||||||
|
|
||||||
/* Transfer-Encoding: chunked */
|
/* Transfer-Encoding: chunked */
|
||||||
case h_matching_transfer_encoding_chunked:
|
case h_matching_transfer_encoding_chunked:
|
||||||
parser->index++;
|
parser->index++;
|
||||||
|
@ -1687,10 +1663,10 @@ reexecute:
|
||||||
}
|
}
|
||||||
parser->header_state = h_state;
|
parser->header_state = h_state;
|
||||||
|
|
||||||
COUNT_HEADER_SIZE(p - start);
|
|
||||||
|
|
||||||
if (p == data + len)
|
if (p == data + len)
|
||||||
--p;
|
--p;
|
||||||
|
|
||||||
|
COUNT_HEADER_SIZE(p - start);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1708,6 +1684,10 @@ reexecute:
|
||||||
case s_header_value_lws:
|
case s_header_value_lws:
|
||||||
{
|
{
|
||||||
if (ch == ' ' || ch == '\t') {
|
if (ch == ' ' || ch == '\t') {
|
||||||
|
if (parser->header_state == h_content_length_num) {
|
||||||
|
/* treat obsolete line folding as space */
|
||||||
|
parser->header_state = h_content_length_ws;
|
||||||
|
}
|
||||||
UPDATE_STATE(s_header_value_start);
|
UPDATE_STATE(s_header_value_start);
|
||||||
REEXECUTE();
|
REEXECUTE();
|
||||||
}
|
}
|
||||||
|
@ -1760,6 +1740,11 @@ reexecute:
|
||||||
case h_transfer_encoding_chunked:
|
case h_transfer_encoding_chunked:
|
||||||
parser->flags |= F_CHUNKED;
|
parser->flags |= F_CHUNKED;
|
||||||
break;
|
break;
|
||||||
|
case h_content_length:
|
||||||
|
/* do not allow empty content length */
|
||||||
|
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
|
||||||
|
goto error;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1794,10 +1779,17 @@ reexecute:
|
||||||
UPDATE_STATE(s_headers_done);
|
UPDATE_STATE(s_headers_done);
|
||||||
|
|
||||||
/* Set this here so that on_headers_complete() callbacks can see it */
|
/* Set this here so that on_headers_complete() callbacks can see it */
|
||||||
parser->upgrade =
|
if ((parser->flags & F_UPGRADE) &&
|
||||||
((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) ==
|
(parser->flags & F_CONNECTION_UPGRADE)) {
|
||||||
(F_UPGRADE | F_CONNECTION_UPGRADE) ||
|
/* For responses, "Upgrade: foo" and "Connection: upgrade" are
|
||||||
parser->method == HTTP_CONNECT);
|
* mandatory only when it is a 101 Switching Protocols response,
|
||||||
|
* otherwise it is purely informational, to announce support.
|
||||||
|
*/
|
||||||
|
parser->upgrade =
|
||||||
|
(parser->type == HTTP_REQUEST || parser->status_code == 101);
|
||||||
|
} else {
|
||||||
|
parser->upgrade = (parser->method == HTTP_CONNECT);
|
||||||
|
}
|
||||||
|
|
||||||
/* Here we call the headers_complete callback. This is somewhat
|
/* Here we call the headers_complete callback. This is somewhat
|
||||||
* different than other callbacks because if the user returns 1, we
|
* different than other callbacks because if the user returns 1, we
|
||||||
|
@ -1816,6 +1808,7 @@ reexecute:
|
||||||
case 2:
|
case 2:
|
||||||
parser->upgrade = 1;
|
parser->upgrade = 1;
|
||||||
|
|
||||||
|
/* fall through */
|
||||||
case 1:
|
case 1:
|
||||||
parser->flags |= F_SKIPBODY;
|
parser->flags |= F_SKIPBODY;
|
||||||
break;
|
break;
|
||||||
|
@ -1839,6 +1832,7 @@ reexecute:
|
||||||
STRICT_CHECK(ch != LF);
|
STRICT_CHECK(ch != LF);
|
||||||
|
|
||||||
parser->nread = 0;
|
parser->nread = 0;
|
||||||
|
nread = 0;
|
||||||
|
|
||||||
hasBody = parser->flags & F_CHUNKED ||
|
hasBody = parser->flags & F_CHUNKED ||
|
||||||
(parser->content_length > 0 && parser->content_length != ULLONG_MAX);
|
(parser->content_length > 0 && parser->content_length != ULLONG_MAX);
|
||||||
|
@ -1933,7 +1927,7 @@ reexecute:
|
||||||
|
|
||||||
case s_chunk_size_start:
|
case s_chunk_size_start:
|
||||||
{
|
{
|
||||||
assert(parser->nread == 1);
|
assert(nread == 1);
|
||||||
assert(parser->flags & F_CHUNKED);
|
assert(parser->flags & F_CHUNKED);
|
||||||
|
|
||||||
unhex_val = unhex[(unsigned char)ch];
|
unhex_val = unhex[(unsigned char)ch];
|
||||||
|
@ -2001,6 +1995,7 @@ reexecute:
|
||||||
STRICT_CHECK(ch != LF);
|
STRICT_CHECK(ch != LF);
|
||||||
|
|
||||||
parser->nread = 0;
|
parser->nread = 0;
|
||||||
|
nread = 0;
|
||||||
|
|
||||||
if (parser->content_length == 0) {
|
if (parser->content_length == 0) {
|
||||||
parser->flags |= F_TRAILING;
|
parser->flags |= F_TRAILING;
|
||||||
|
@ -2047,6 +2042,7 @@ reexecute:
|
||||||
assert(parser->flags & F_CHUNKED);
|
assert(parser->flags & F_CHUNKED);
|
||||||
STRICT_CHECK(ch != LF);
|
STRICT_CHECK(ch != LF);
|
||||||
parser->nread = 0;
|
parser->nread = 0;
|
||||||
|
nread = 0;
|
||||||
UPDATE_STATE(s_chunk_size_start);
|
UPDATE_STATE(s_chunk_size_start);
|
||||||
CALLBACK_NOTIFY(chunk_complete);
|
CALLBACK_NOTIFY(chunk_complete);
|
||||||
break;
|
break;
|
||||||
|
@ -2058,7 +2054,7 @@ reexecute:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Run callbacks for any marks that we have leftover after we ran our of
|
/* Run callbacks for any marks that we have leftover after we ran out of
|
||||||
* bytes. There should be at most one of these set, so it's OK to invoke
|
* bytes. There should be at most one of these set, so it's OK to invoke
|
||||||
* them in series (unset marks will not result in callbacks).
|
* them in series (unset marks will not result in callbacks).
|
||||||
*
|
*
|
||||||
|
@ -2140,6 +2136,16 @@ http_method_str (enum http_method m)
|
||||||
return ELEM_AT(method_strings, m, "<unknown>");
|
return ELEM_AT(method_strings, m, "<unknown>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
http_status_str (enum http_status s)
|
||||||
|
{
|
||||||
|
switch (s) {
|
||||||
|
#define XX(num, name, string) case HTTP_STATUS_##name: return #string;
|
||||||
|
HTTP_STATUS_MAP(XX)
|
||||||
|
#undef XX
|
||||||
|
default: return "<unknown>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
http_parser_init (http_parser *parser, enum http_parser_type t)
|
http_parser_init (http_parser *parser, enum http_parser_type t)
|
||||||
|
@ -2200,7 +2206,7 @@ http_parse_host_char(enum http_host_state s, const char ch) {
|
||||||
return s_http_host;
|
return s_http_host;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FALLTHROUGH */
|
/* fall through */
|
||||||
case s_http_host_v6_end:
|
case s_http_host_v6_end:
|
||||||
if (ch == ':') {
|
if (ch == ':') {
|
||||||
return s_http_host_port_start;
|
return s_http_host_port_start;
|
||||||
|
@ -2213,7 +2219,7 @@ http_parse_host_char(enum http_host_state s, const char ch) {
|
||||||
return s_http_host_v6_end;
|
return s_http_host_v6_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FALLTHROUGH */
|
/* fall through */
|
||||||
case s_http_host_v6_start:
|
case s_http_host_v6_start:
|
||||||
if (IS_HEX(ch) || ch == ':' || ch == '.') {
|
if (IS_HEX(ch) || ch == ':' || ch == '.') {
|
||||||
return s_http_host_v6;
|
return s_http_host_v6;
|
||||||
|
@ -2229,7 +2235,7 @@ http_parse_host_char(enum http_host_state s, const char ch) {
|
||||||
return s_http_host_v6_end;
|
return s_http_host_v6_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FALLTHROUGH */
|
/* fall through */
|
||||||
case s_http_host_v6_zone_start:
|
case s_http_host_v6_zone_start:
|
||||||
/* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
|
/* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
|
||||||
if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
|
if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
|
||||||
|
@ -2275,14 +2281,14 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
|
||||||
switch(new_s) {
|
switch(new_s) {
|
||||||
case s_http_host:
|
case s_http_host:
|
||||||
if (s != s_http_host) {
|
if (s != s_http_host) {
|
||||||
u->field_data[UF_HOST].off = p - buf;
|
u->field_data[UF_HOST].off = (uint16_t)(p - buf);
|
||||||
}
|
}
|
||||||
u->field_data[UF_HOST].len++;
|
u->field_data[UF_HOST].len++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case s_http_host_v6:
|
case s_http_host_v6:
|
||||||
if (s != s_http_host_v6) {
|
if (s != s_http_host_v6) {
|
||||||
u->field_data[UF_HOST].off = p - buf;
|
u->field_data[UF_HOST].off = (uint16_t)(p - buf);
|
||||||
}
|
}
|
||||||
u->field_data[UF_HOST].len++;
|
u->field_data[UF_HOST].len++;
|
||||||
break;
|
break;
|
||||||
|
@ -2294,7 +2300,7 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
|
||||||
|
|
||||||
case s_http_host_port:
|
case s_http_host_port:
|
||||||
if (s != s_http_host_port) {
|
if (s != s_http_host_port) {
|
||||||
u->field_data[UF_PORT].off = p - buf;
|
u->field_data[UF_PORT].off = (uint16_t)(p - buf);
|
||||||
u->field_data[UF_PORT].len = 0;
|
u->field_data[UF_PORT].len = 0;
|
||||||
u->field_set |= (1 << UF_PORT);
|
u->field_set |= (1 << UF_PORT);
|
||||||
}
|
}
|
||||||
|
@ -2303,7 +2309,7 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
|
||||||
|
|
||||||
case s_http_userinfo:
|
case s_http_userinfo:
|
||||||
if (s != s_http_userinfo) {
|
if (s != s_http_userinfo) {
|
||||||
u->field_data[UF_USERINFO].off = p - buf ;
|
u->field_data[UF_USERINFO].off = (uint16_t)(p - buf);
|
||||||
u->field_data[UF_USERINFO].len = 0;
|
u->field_data[UF_USERINFO].len = 0;
|
||||||
u->field_set |= (1 << UF_USERINFO);
|
u->field_set |= (1 << UF_USERINFO);
|
||||||
}
|
}
|
||||||
|
@ -2348,6 +2354,10 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
|
||||||
enum http_parser_url_fields uf, old_uf;
|
enum http_parser_url_fields uf, old_uf;
|
||||||
int found_at = 0;
|
int found_at = 0;
|
||||||
|
|
||||||
|
if (buflen == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
u->port = u->field_set = 0;
|
u->port = u->field_set = 0;
|
||||||
s = is_connect ? s_req_server_start : s_req_spaces_before_url;
|
s = is_connect ? s_req_server_start : s_req_spaces_before_url;
|
||||||
old_uf = UF_MAX;
|
old_uf = UF_MAX;
|
||||||
|
@ -2375,7 +2385,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
|
||||||
case s_req_server_with_at:
|
case s_req_server_with_at:
|
||||||
found_at = 1;
|
found_at = 1;
|
||||||
|
|
||||||
/* FALLTROUGH */
|
/* fall through */
|
||||||
case s_req_server:
|
case s_req_server:
|
||||||
uf = UF_HOST;
|
uf = UF_HOST;
|
||||||
break;
|
break;
|
||||||
|
@ -2403,7 +2413,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
u->field_data[uf].off = p - buf;
|
u->field_data[uf].off = (uint16_t)(p - buf);
|
||||||
u->field_data[uf].len = 1;
|
u->field_data[uf].len = 1;
|
||||||
|
|
||||||
u->field_set |= (1 << uf);
|
u->field_set |= (1 << uf);
|
||||||
|
@ -2429,12 +2439,27 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (u->field_set & (1 << UF_PORT)) {
|
if (u->field_set & (1 << UF_PORT)) {
|
||||||
/* Don't bother with endp; we've already validated the string */
|
uint16_t off;
|
||||||
unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
|
uint16_t len;
|
||||||
|
const char* p;
|
||||||
|
const char* end;
|
||||||
|
unsigned long v;
|
||||||
|
|
||||||
/* Ports have a max value of 2^16 */
|
off = u->field_data[UF_PORT].off;
|
||||||
if (v > 0xffff) {
|
len = u->field_data[UF_PORT].len;
|
||||||
return 1;
|
end = buf + off + len;
|
||||||
|
|
||||||
|
/* NOTE: The characters are already validated and are in the [0-9] range */
|
||||||
|
assert(off + len <= buflen && "Port number overflow");
|
||||||
|
v = 0;
|
||||||
|
for (p = buf + off; p < end; p++) {
|
||||||
|
v *= 10;
|
||||||
|
v += *p - '0';
|
||||||
|
|
||||||
|
/* Ports have a max value of 2^16 */
|
||||||
|
if (v > 0xffff) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u->port = (uint16_t) v;
|
u->port = (uint16_t) v;
|
||||||
|
@ -2451,6 +2476,7 @@ http_parser_pause(http_parser *parser, int paused) {
|
||||||
*/
|
*/
|
||||||
if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
|
if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
|
||||||
HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
|
HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
|
||||||
|
uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */
|
||||||
SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
|
SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
|
||||||
} else {
|
} else {
|
||||||
assert(0 && "Attempting to pause parser in error state");
|
assert(0 && "Attempting to pause parser in error state");
|
||||||
|
@ -2468,3 +2494,8 @@ http_parser_version(void) {
|
||||||
HTTP_PARSER_VERSION_MINOR * 0x00100 |
|
HTTP_PARSER_VERSION_MINOR * 0x00100 |
|
||||||
HTTP_PARSER_VERSION_PATCH * 0x00001;
|
HTTP_PARSER_VERSION_PATCH * 0x00001;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
http_parser_set_max_header_size(uint32_t size) {
|
||||||
|
max_header_size = size;
|
||||||
|
}
|
||||||
|
|
|
@ -26,14 +26,13 @@ extern "C" {
|
||||||
|
|
||||||
/* Also update SONAME in the Makefile whenever you change these. */
|
/* Also update SONAME in the Makefile whenever you change these. */
|
||||||
#define HTTP_PARSER_VERSION_MAJOR 2
|
#define HTTP_PARSER_VERSION_MAJOR 2
|
||||||
#define HTTP_PARSER_VERSION_MINOR 7
|
#define HTTP_PARSER_VERSION_MINOR 9
|
||||||
#define HTTP_PARSER_VERSION_PATCH 1
|
#define HTTP_PARSER_VERSION_PATCH 0
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <stddef.h>
|
||||||
#if defined(_WIN32) && !defined(__MINGW32__) && \
|
#if defined(_WIN32) && !defined(__MINGW32__) && \
|
||||||
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
|
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
|
||||||
#include <BaseTsd.h>
|
#include <BaseTsd.h>
|
||||||
#include <stddef.h>
|
|
||||||
typedef __int8 int8_t;
|
typedef __int8 int8_t;
|
||||||
typedef unsigned __int8 uint8_t;
|
typedef unsigned __int8 uint8_t;
|
||||||
typedef __int16 int16_t;
|
typedef __int16 int16_t;
|
||||||
|
@ -90,6 +89,76 @@ typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
|
||||||
typedef int (*http_cb) (http_parser*);
|
typedef int (*http_cb) (http_parser*);
|
||||||
|
|
||||||
|
|
||||||
|
/* Status Codes */
|
||||||
|
#define HTTP_STATUS_MAP(XX) \
|
||||||
|
XX(100, CONTINUE, Continue) \
|
||||||
|
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
|
||||||
|
XX(102, PROCESSING, Processing) \
|
||||||
|
XX(200, OK, OK) \
|
||||||
|
XX(201, CREATED, Created) \
|
||||||
|
XX(202, ACCEPTED, Accepted) \
|
||||||
|
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
|
||||||
|
XX(204, NO_CONTENT, No Content) \
|
||||||
|
XX(205, RESET_CONTENT, Reset Content) \
|
||||||
|
XX(206, PARTIAL_CONTENT, Partial Content) \
|
||||||
|
XX(207, MULTI_STATUS, Multi-Status) \
|
||||||
|
XX(208, ALREADY_REPORTED, Already Reported) \
|
||||||
|
XX(226, IM_USED, IM Used) \
|
||||||
|
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
|
||||||
|
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
|
||||||
|
XX(302, FOUND, Found) \
|
||||||
|
XX(303, SEE_OTHER, See Other) \
|
||||||
|
XX(304, NOT_MODIFIED, Not Modified) \
|
||||||
|
XX(305, USE_PROXY, Use Proxy) \
|
||||||
|
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
|
||||||
|
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
|
||||||
|
XX(400, BAD_REQUEST, Bad Request) \
|
||||||
|
XX(401, UNAUTHORIZED, Unauthorized) \
|
||||||
|
XX(402, PAYMENT_REQUIRED, Payment Required) \
|
||||||
|
XX(403, FORBIDDEN, Forbidden) \
|
||||||
|
XX(404, NOT_FOUND, Not Found) \
|
||||||
|
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
|
||||||
|
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
|
||||||
|
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
|
||||||
|
XX(408, REQUEST_TIMEOUT, Request Timeout) \
|
||||||
|
XX(409, CONFLICT, Conflict) \
|
||||||
|
XX(410, GONE, Gone) \
|
||||||
|
XX(411, LENGTH_REQUIRED, Length Required) \
|
||||||
|
XX(412, PRECONDITION_FAILED, Precondition Failed) \
|
||||||
|
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
|
||||||
|
XX(414, URI_TOO_LONG, URI Too Long) \
|
||||||
|
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
|
||||||
|
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
|
||||||
|
XX(417, EXPECTATION_FAILED, Expectation Failed) \
|
||||||
|
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
|
||||||
|
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
|
||||||
|
XX(423, LOCKED, Locked) \
|
||||||
|
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
|
||||||
|
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
|
||||||
|
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
|
||||||
|
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
|
||||||
|
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
|
||||||
|
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
|
||||||
|
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
|
||||||
|
XX(501, NOT_IMPLEMENTED, Not Implemented) \
|
||||||
|
XX(502, BAD_GATEWAY, Bad Gateway) \
|
||||||
|
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
|
||||||
|
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
|
||||||
|
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
|
||||||
|
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
|
||||||
|
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
|
||||||
|
XX(508, LOOP_DETECTED, Loop Detected) \
|
||||||
|
XX(510, NOT_EXTENDED, Not Extended) \
|
||||||
|
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
|
||||||
|
|
||||||
|
enum http_status
|
||||||
|
{
|
||||||
|
#define XX(num, name, string) HTTP_STATUS_##name = num,
|
||||||
|
HTTP_STATUS_MAP(XX)
|
||||||
|
#undef XX
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Request Methods */
|
/* Request Methods */
|
||||||
#define HTTP_METHOD_MAP(XX) \
|
#define HTTP_METHOD_MAP(XX) \
|
||||||
XX(0, DELETE, DELETE) \
|
XX(0, DELETE, DELETE) \
|
||||||
|
@ -132,6 +201,8 @@ typedef int (*http_cb) (http_parser*);
|
||||||
/* RFC-2068, section 19.6.1.2 */ \
|
/* RFC-2068, section 19.6.1.2 */ \
|
||||||
XX(31, LINK, LINK) \
|
XX(31, LINK, LINK) \
|
||||||
XX(32, UNLINK, UNLINK) \
|
XX(32, UNLINK, UNLINK) \
|
||||||
|
/* icecast */ \
|
||||||
|
XX(33, SOURCE, SOURCE) \
|
||||||
|
|
||||||
enum http_method
|
enum http_method
|
||||||
{
|
{
|
||||||
|
@ -336,6 +407,9 @@ int http_should_keep_alive(const http_parser *parser);
|
||||||
/* Returns a string version of the HTTP method. */
|
/* Returns a string version of the HTTP method. */
|
||||||
const char *http_method_str(enum http_method m);
|
const char *http_method_str(enum http_method m);
|
||||||
|
|
||||||
|
/* Returns a string version of the HTTP status code. */
|
||||||
|
const char *http_status_str(enum http_status s);
|
||||||
|
|
||||||
/* Return a string name of the given error */
|
/* Return a string name of the given error */
|
||||||
const char *http_errno_name(enum http_errno err);
|
const char *http_errno_name(enum http_errno err);
|
||||||
|
|
||||||
|
@ -356,6 +430,9 @@ void http_parser_pause(http_parser *parser, int paused);
|
||||||
/* Checks if this is the final chunk of the body. */
|
/* Checks if this is the final chunk of the body. */
|
||||||
int http_body_is_final(const http_parser *parser);
|
int http_body_is_final(const http_parser *parser);
|
||||||
|
|
||||||
|
/* Change the maximum header size provided at compile time. */
|
||||||
|
void http_parser_set_max_header_size(uint32_t size);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -27,9 +27,7 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
# undef strlcat
|
|
||||||
# undef strlncpy
|
# undef strlncpy
|
||||||
# undef strlcpy
|
|
||||||
#endif /* defined(__APPLE__) */
|
#endif /* defined(__APPLE__) */
|
||||||
|
|
||||||
#undef TRUE
|
#undef TRUE
|
||||||
|
@ -43,7 +41,9 @@
|
||||||
|
|
||||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
static http_parser *parser;
|
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
|
||||||
|
|
||||||
|
static http_parser parser;
|
||||||
|
|
||||||
struct message {
|
struct message {
|
||||||
const char *name; // for debugging purposes
|
const char *name; // for debugging purposes
|
||||||
|
@ -78,6 +78,7 @@ struct message {
|
||||||
int message_begin_cb_called;
|
int message_begin_cb_called;
|
||||||
int headers_complete_cb_called;
|
int headers_complete_cb_called;
|
||||||
int message_complete_cb_called;
|
int message_complete_cb_called;
|
||||||
|
int status_cb_called;
|
||||||
int message_complete_on_eof;
|
int message_complete_on_eof;
|
||||||
int body_is_final;
|
int body_is_final;
|
||||||
};
|
};
|
||||||
|
@ -152,10 +153,10 @@ const struct message requests[] =
|
||||||
,.body= ""
|
,.body= ""
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DUMBFUCK 2
|
#define DUMBLUCK 2
|
||||||
, {.name= "dumbfuck"
|
, {.name= "dumbluck"
|
||||||
,.type= HTTP_REQUEST
|
,.type= HTTP_REQUEST
|
||||||
,.raw= "GET /dumbfuck HTTP/1.1\r\n"
|
,.raw= "GET /dumbluck HTTP/1.1\r\n"
|
||||||
"aaaaaaaaaaaaa:++++++++++\r\n"
|
"aaaaaaaaaaaaa:++++++++++\r\n"
|
||||||
"\r\n"
|
"\r\n"
|
||||||
,.should_keep_alive= TRUE
|
,.should_keep_alive= TRUE
|
||||||
|
@ -165,8 +166,8 @@ const struct message requests[] =
|
||||||
,.method= HTTP_GET
|
,.method= HTTP_GET
|
||||||
,.query_string= ""
|
,.query_string= ""
|
||||||
,.fragment= ""
|
,.fragment= ""
|
||||||
,.request_path= "/dumbfuck"
|
,.request_path= "/dumbluck"
|
||||||
,.request_url= "/dumbfuck"
|
,.request_url= "/dumbluck"
|
||||||
,.num_headers= 1
|
,.num_headers= 1
|
||||||
,.headers=
|
,.headers=
|
||||||
{ { "aaaaaaaaaaaaa", "++++++++++" }
|
{ { "aaaaaaaaaaaaa", "++++++++++" }
|
||||||
|
@ -370,13 +371,13 @@ const struct message requests[] =
|
||||||
,.chunk_lengths= { 5, 6 }
|
,.chunk_lengths= { 5, 6 }
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
|
#define CHUNKED_W_NONSENSE_AFTER_LENGTH 11
|
||||||
, {.name= "with bullshit after the length"
|
, {.name= "with nonsense after the length"
|
||||||
,.type= HTTP_REQUEST
|
,.type= HTTP_REQUEST
|
||||||
,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
|
,.raw= "POST /chunked_w_nonsense_after_length HTTP/1.1\r\n"
|
||||||
"Transfer-Encoding: chunked\r\n"
|
"Transfer-Encoding: chunked\r\n"
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n"
|
"5; ilovew3;whattheluck=aretheseparametersfor\r\nhello\r\n"
|
||||||
"6; blahblah; blah\r\n world\r\n"
|
"6; blahblah; blah\r\n world\r\n"
|
||||||
"0\r\n"
|
"0\r\n"
|
||||||
"\r\n"
|
"\r\n"
|
||||||
|
@ -387,8 +388,8 @@ const struct message requests[] =
|
||||||
,.method= HTTP_POST
|
,.method= HTTP_POST
|
||||||
,.query_string= ""
|
,.query_string= ""
|
||||||
,.fragment= ""
|
,.fragment= ""
|
||||||
,.request_path= "/chunked_w_bullshit_after_length"
|
,.request_path= "/chunked_w_nonsense_after_length"
|
||||||
,.request_url= "/chunked_w_bullshit_after_length"
|
,.request_url= "/chunked_w_nonsense_after_length"
|
||||||
,.num_headers= 1
|
,.num_headers= 1
|
||||||
,.headers=
|
,.headers=
|
||||||
{ { "Transfer-Encoding", "chunked" }
|
{ { "Transfer-Encoding", "chunked" }
|
||||||
|
@ -1131,7 +1132,7 @@ const struct message requests[] =
|
||||||
}
|
}
|
||||||
|
|
||||||
#define UNLINK_REQUEST 41
|
#define UNLINK_REQUEST 41
|
||||||
, {.name = "link request"
|
, {.name = "unlink request"
|
||||||
,.type= HTTP_REQUEST
|
,.type= HTTP_REQUEST
|
||||||
,.raw= "UNLINK /images/my_dog.jpg HTTP/1.1\r\n"
|
,.raw= "UNLINK /images/my_dog.jpg HTTP/1.1\r\n"
|
||||||
"Host: example.com\r\n"
|
"Host: example.com\r\n"
|
||||||
|
@ -1153,7 +1154,45 @@ const struct message requests[] =
|
||||||
,.body= ""
|
,.body= ""
|
||||||
}
|
}
|
||||||
|
|
||||||
, {.name= NULL } /* sentinel */
|
#define SOURCE_REQUEST 42
|
||||||
|
, {.name = "source request"
|
||||||
|
,.type= HTTP_REQUEST
|
||||||
|
,.raw= "SOURCE /music/sweet/music HTTP/1.1\r\n"
|
||||||
|
"Host: example.com\r\n"
|
||||||
|
"\r\n"
|
||||||
|
,.should_keep_alive= TRUE
|
||||||
|
,.message_complete_on_eof= FALSE
|
||||||
|
,.http_major= 1
|
||||||
|
,.http_minor= 1
|
||||||
|
,.method= HTTP_SOURCE
|
||||||
|
,.request_path= "/music/sweet/music"
|
||||||
|
,.request_url= "/music/sweet/music"
|
||||||
|
,.query_string= ""
|
||||||
|
,.fragment= ""
|
||||||
|
,.num_headers= 1
|
||||||
|
,.headers= { { "Host", "example.com" } }
|
||||||
|
,.body= ""
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SOURCE_ICE_REQUEST 42
|
||||||
|
, {.name = "source request"
|
||||||
|
,.type= HTTP_REQUEST
|
||||||
|
,.raw= "SOURCE /music/sweet/music ICE/1.0\r\n"
|
||||||
|
"Host: example.com\r\n"
|
||||||
|
"\r\n"
|
||||||
|
,.should_keep_alive= FALSE
|
||||||
|
,.message_complete_on_eof= FALSE
|
||||||
|
,.http_major= 1
|
||||||
|
,.http_minor= 0
|
||||||
|
,.method= HTTP_SOURCE
|
||||||
|
,.request_path= "/music/sweet/music"
|
||||||
|
,.request_url= "/music/sweet/music"
|
||||||
|
,.query_string= ""
|
||||||
|
,.fragment= ""
|
||||||
|
,.num_headers= 1
|
||||||
|
,.headers= { { "Host", "example.com" } }
|
||||||
|
,.body= ""
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* * R E S P O N S E S * */
|
/* * R E S P O N S E S * */
|
||||||
|
@ -1771,7 +1810,166 @@ const struct message responses[] =
|
||||||
,.chunk_lengths= { 2 }
|
,.chunk_lengths= { 2 }
|
||||||
}
|
}
|
||||||
|
|
||||||
, {.name= NULL } /* sentinel */
|
#define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER 22
|
||||||
|
, {.name= "HTTP 101 response with Upgrade header"
|
||||||
|
,.type= HTTP_RESPONSE
|
||||||
|
,.raw= "HTTP/1.1 101 Switching Protocols\r\n"
|
||||||
|
"Connection: upgrade\r\n"
|
||||||
|
"Upgrade: h2c\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"proto"
|
||||||
|
,.should_keep_alive= TRUE
|
||||||
|
,.message_complete_on_eof= FALSE
|
||||||
|
,.http_major= 1
|
||||||
|
,.http_minor= 1
|
||||||
|
,.status_code= 101
|
||||||
|
,.response_status= "Switching Protocols"
|
||||||
|
,.upgrade= "proto"
|
||||||
|
,.num_headers= 2
|
||||||
|
,.headers=
|
||||||
|
{ { "Connection", "upgrade" }
|
||||||
|
, { "Upgrade", "h2c" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER_AND_CONTENT_LENGTH 23
|
||||||
|
, {.name= "HTTP 101 response with Upgrade and Content-Length header"
|
||||||
|
,.type= HTTP_RESPONSE
|
||||||
|
,.raw= "HTTP/1.1 101 Switching Protocols\r\n"
|
||||||
|
"Connection: upgrade\r\n"
|
||||||
|
"Upgrade: h2c\r\n"
|
||||||
|
"Content-Length: 4\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"body"
|
||||||
|
"proto"
|
||||||
|
,.should_keep_alive= TRUE
|
||||||
|
,.message_complete_on_eof= FALSE
|
||||||
|
,.http_major= 1
|
||||||
|
,.http_minor= 1
|
||||||
|
,.status_code= 101
|
||||||
|
,.response_status= "Switching Protocols"
|
||||||
|
,.body= "body"
|
||||||
|
,.upgrade= "proto"
|
||||||
|
,.num_headers= 3
|
||||||
|
,.headers=
|
||||||
|
{ { "Connection", "upgrade" }
|
||||||
|
, { "Upgrade", "h2c" }
|
||||||
|
, { "Content-Length", "4" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER_AND_TRANSFER_ENCODING 24
|
||||||
|
, {.name= "HTTP 101 response with Upgrade and Transfer-Encoding header"
|
||||||
|
,.type= HTTP_RESPONSE
|
||||||
|
,.raw= "HTTP/1.1 101 Switching Protocols\r\n"
|
||||||
|
"Connection: upgrade\r\n"
|
||||||
|
"Upgrade: h2c\r\n"
|
||||||
|
"Transfer-Encoding: chunked\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"2\r\n"
|
||||||
|
"bo\r\n"
|
||||||
|
"2\r\n"
|
||||||
|
"dy\r\n"
|
||||||
|
"0\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"proto"
|
||||||
|
,.should_keep_alive= TRUE
|
||||||
|
,.message_complete_on_eof= FALSE
|
||||||
|
,.http_major= 1
|
||||||
|
,.http_minor= 1
|
||||||
|
,.status_code= 101
|
||||||
|
,.response_status= "Switching Protocols"
|
||||||
|
,.body= "body"
|
||||||
|
,.upgrade= "proto"
|
||||||
|
,.num_headers= 3
|
||||||
|
,.headers=
|
||||||
|
{ { "Connection", "upgrade" }
|
||||||
|
, { "Upgrade", "h2c" }
|
||||||
|
, { "Transfer-Encoding", "chunked" }
|
||||||
|
}
|
||||||
|
,.num_chunks_complete= 3
|
||||||
|
,.chunk_lengths= { 2, 2 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER 25
|
||||||
|
, {.name= "HTTP 200 response with Upgrade header"
|
||||||
|
,.type= HTTP_RESPONSE
|
||||||
|
,.raw= "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Connection: upgrade\r\n"
|
||||||
|
"Upgrade: h2c\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"body"
|
||||||
|
,.should_keep_alive= FALSE
|
||||||
|
,.message_complete_on_eof= TRUE
|
||||||
|
,.http_major= 1
|
||||||
|
,.http_minor= 1
|
||||||
|
,.status_code= 200
|
||||||
|
,.response_status= "OK"
|
||||||
|
,.body= "body"
|
||||||
|
,.upgrade= NULL
|
||||||
|
,.num_headers= 2
|
||||||
|
,.headers=
|
||||||
|
{ { "Connection", "upgrade" }
|
||||||
|
, { "Upgrade", "h2c" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER_AND_CONTENT_LENGTH 26
|
||||||
|
, {.name= "HTTP 200 response with Upgrade and Content-Length header"
|
||||||
|
,.type= HTTP_RESPONSE
|
||||||
|
,.raw= "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Connection: upgrade\r\n"
|
||||||
|
"Upgrade: h2c\r\n"
|
||||||
|
"Content-Length: 4\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"body"
|
||||||
|
,.should_keep_alive= TRUE
|
||||||
|
,.message_complete_on_eof= FALSE
|
||||||
|
,.http_major= 1
|
||||||
|
,.http_minor= 1
|
||||||
|
,.status_code= 200
|
||||||
|
,.response_status= "OK"
|
||||||
|
,.num_headers= 3
|
||||||
|
,.body= "body"
|
||||||
|
,.upgrade= NULL
|
||||||
|
,.headers=
|
||||||
|
{ { "Connection", "upgrade" }
|
||||||
|
, { "Upgrade", "h2c" }
|
||||||
|
, { "Content-Length", "4" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER_AND_TRANSFER_ENCODING 27
|
||||||
|
, {.name= "HTTP 200 response with Upgrade and Transfer-Encoding header"
|
||||||
|
,.type= HTTP_RESPONSE
|
||||||
|
,.raw= "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Connection: upgrade\r\n"
|
||||||
|
"Upgrade: h2c\r\n"
|
||||||
|
"Transfer-Encoding: chunked\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"2\r\n"
|
||||||
|
"bo\r\n"
|
||||||
|
"2\r\n"
|
||||||
|
"dy\r\n"
|
||||||
|
"0\r\n"
|
||||||
|
"\r\n"
|
||||||
|
,.should_keep_alive= TRUE
|
||||||
|
,.message_complete_on_eof= FALSE
|
||||||
|
,.http_major= 1
|
||||||
|
,.http_minor= 1
|
||||||
|
,.status_code= 200
|
||||||
|
,.response_status= "OK"
|
||||||
|
,.num_headers= 3
|
||||||
|
,.body= "body"
|
||||||
|
,.upgrade= NULL
|
||||||
|
,.headers=
|
||||||
|
{ { "Connection", "upgrade" }
|
||||||
|
, { "Upgrade", "h2c" }
|
||||||
|
, { "Transfer-Encoding", "chunked" }
|
||||||
|
}
|
||||||
|
,.num_chunks_complete= 3
|
||||||
|
,.chunk_lengths= { 2, 2 }
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* strnlen() is a POSIX.2008 addition. Can't rely on it being available so
|
/* strnlen() is a POSIX.2008 addition. Can't rely on it being available so
|
||||||
|
@ -1811,12 +2009,6 @@ strlncat(char *dst, size_t len, const char *src, size_t n)
|
||||||
return slen + dlen;
|
return slen + dlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
|
||||||
strlcat(char *dst, const char *src, size_t len)
|
|
||||||
{
|
|
||||||
return strlncat(dst, len, src, (size_t) -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
strlncpy(char *dst, size_t len, const char *src, size_t n)
|
strlncpy(char *dst, size_t len, const char *src, size_t n)
|
||||||
{
|
{
|
||||||
|
@ -1835,16 +2027,10 @@ strlncpy(char *dst, size_t len, const char *src, size_t n)
|
||||||
return slen;
|
return slen;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
|
||||||
strlcpy(char *dst, const char *src, size_t len)
|
|
||||||
{
|
|
||||||
return strlncpy(dst, len, src, (size_t) -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
request_url_cb (http_parser *p, const char *buf, size_t len)
|
request_url_cb (http_parser *p, const char *buf, size_t len)
|
||||||
{
|
{
|
||||||
assert(p == parser);
|
assert(p == &parser);
|
||||||
strlncat(messages[num_messages].request_url,
|
strlncat(messages[num_messages].request_url,
|
||||||
sizeof(messages[num_messages].request_url),
|
sizeof(messages[num_messages].request_url),
|
||||||
buf,
|
buf,
|
||||||
|
@ -1855,7 +2041,7 @@ request_url_cb (http_parser *p, const char *buf, size_t len)
|
||||||
int
|
int
|
||||||
header_field_cb (http_parser *p, const char *buf, size_t len)
|
header_field_cb (http_parser *p, const char *buf, size_t len)
|
||||||
{
|
{
|
||||||
assert(p == parser);
|
assert(p == &parser);
|
||||||
struct message *m = &messages[num_messages];
|
struct message *m = &messages[num_messages];
|
||||||
|
|
||||||
if (m->last_header_element != FIELD)
|
if (m->last_header_element != FIELD)
|
||||||
|
@ -1874,7 +2060,7 @@ header_field_cb (http_parser *p, const char *buf, size_t len)
|
||||||
int
|
int
|
||||||
header_value_cb (http_parser *p, const char *buf, size_t len)
|
header_value_cb (http_parser *p, const char *buf, size_t len)
|
||||||
{
|
{
|
||||||
assert(p == parser);
|
assert(p == &parser);
|
||||||
struct message *m = &messages[num_messages];
|
struct message *m = &messages[num_messages];
|
||||||
|
|
||||||
strlncat(m->headers[m->num_headers-1][1],
|
strlncat(m->headers[m->num_headers-1][1],
|
||||||
|
@ -1903,7 +2089,7 @@ check_body_is_final (const http_parser *p)
|
||||||
int
|
int
|
||||||
body_cb (http_parser *p, const char *buf, size_t len)
|
body_cb (http_parser *p, const char *buf, size_t len)
|
||||||
{
|
{
|
||||||
assert(p == parser);
|
assert(p == &parser);
|
||||||
strlncat(messages[num_messages].body,
|
strlncat(messages[num_messages].body,
|
||||||
sizeof(messages[num_messages].body),
|
sizeof(messages[num_messages].body),
|
||||||
buf,
|
buf,
|
||||||
|
@ -1917,7 +2103,7 @@ body_cb (http_parser *p, const char *buf, size_t len)
|
||||||
int
|
int
|
||||||
count_body_cb (http_parser *p, const char *buf, size_t len)
|
count_body_cb (http_parser *p, const char *buf, size_t len)
|
||||||
{
|
{
|
||||||
assert(p == parser);
|
assert(p == &parser);
|
||||||
assert(buf);
|
assert(buf);
|
||||||
messages[num_messages].body_size += len;
|
messages[num_messages].body_size += len;
|
||||||
check_body_is_final(p);
|
check_body_is_final(p);
|
||||||
|
@ -1927,7 +2113,8 @@ count_body_cb (http_parser *p, const char *buf, size_t len)
|
||||||
int
|
int
|
||||||
message_begin_cb (http_parser *p)
|
message_begin_cb (http_parser *p)
|
||||||
{
|
{
|
||||||
assert(p == parser);
|
assert(p == &parser);
|
||||||
|
assert(!messages[num_messages].message_begin_cb_called);
|
||||||
messages[num_messages].message_begin_cb_called = TRUE;
|
messages[num_messages].message_begin_cb_called = TRUE;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1935,21 +2122,22 @@ message_begin_cb (http_parser *p)
|
||||||
int
|
int
|
||||||
headers_complete_cb (http_parser *p)
|
headers_complete_cb (http_parser *p)
|
||||||
{
|
{
|
||||||
assert(p == parser);
|
assert(p == &parser);
|
||||||
messages[num_messages].method = parser->method;
|
messages[num_messages].method = parser.method;
|
||||||
messages[num_messages].status_code = parser->status_code;
|
messages[num_messages].status_code = parser.status_code;
|
||||||
messages[num_messages].http_major = parser->http_major;
|
messages[num_messages].http_major = parser.http_major;
|
||||||
messages[num_messages].http_minor = parser->http_minor;
|
messages[num_messages].http_minor = parser.http_minor;
|
||||||
messages[num_messages].headers_complete_cb_called = TRUE;
|
messages[num_messages].headers_complete_cb_called = TRUE;
|
||||||
messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
|
messages[num_messages].should_keep_alive = http_should_keep_alive(&parser);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
message_complete_cb (http_parser *p)
|
message_complete_cb (http_parser *p)
|
||||||
{
|
{
|
||||||
assert(p == parser);
|
assert(p == &parser);
|
||||||
if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser))
|
if (messages[num_messages].should_keep_alive !=
|
||||||
|
http_should_keep_alive(&parser))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
|
fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
|
||||||
"value in both on_message_complete and on_headers_complete "
|
"value in both on_message_complete and on_headers_complete "
|
||||||
|
@ -1980,7 +2168,10 @@ message_complete_cb (http_parser *p)
|
||||||
int
|
int
|
||||||
response_status_cb (http_parser *p, const char *buf, size_t len)
|
response_status_cb (http_parser *p, const char *buf, size_t len)
|
||||||
{
|
{
|
||||||
assert(p == parser);
|
assert(p == &parser);
|
||||||
|
|
||||||
|
messages[num_messages].status_cb_called = TRUE;
|
||||||
|
|
||||||
strlncat(messages[num_messages].response_status,
|
strlncat(messages[num_messages].response_status,
|
||||||
sizeof(messages[num_messages].response_status),
|
sizeof(messages[num_messages].response_status),
|
||||||
buf,
|
buf,
|
||||||
|
@ -1991,7 +2182,7 @@ response_status_cb (http_parser *p, const char *buf, size_t len)
|
||||||
int
|
int
|
||||||
chunk_header_cb (http_parser *p)
|
chunk_header_cb (http_parser *p)
|
||||||
{
|
{
|
||||||
assert(p == parser);
|
assert(p == &parser);
|
||||||
int chunk_idx = messages[num_messages].num_chunks;
|
int chunk_idx = messages[num_messages].num_chunks;
|
||||||
messages[num_messages].num_chunks++;
|
messages[num_messages].num_chunks++;
|
||||||
if (chunk_idx < MAX_CHUNKS) {
|
if (chunk_idx < MAX_CHUNKS) {
|
||||||
|
@ -2004,7 +2195,7 @@ chunk_header_cb (http_parser *p)
|
||||||
int
|
int
|
||||||
chunk_complete_cb (http_parser *p)
|
chunk_complete_cb (http_parser *p)
|
||||||
{
|
{
|
||||||
assert(p == parser);
|
assert(p == &parser);
|
||||||
|
|
||||||
/* Here we want to verify that each chunk_header_cb is matched by a
|
/* Here we want to verify that each chunk_header_cb is matched by a
|
||||||
* chunk_complete_cb, so not only should the total number of calls to
|
* chunk_complete_cb, so not only should the total number of calls to
|
||||||
|
@ -2209,7 +2400,7 @@ connect_headers_complete_cb (http_parser *p)
|
||||||
int
|
int
|
||||||
connect_message_complete_cb (http_parser *p)
|
connect_message_complete_cb (http_parser *p)
|
||||||
{
|
{
|
||||||
messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
|
messages[num_messages].should_keep_alive = http_should_keep_alive(&parser);
|
||||||
return message_complete_cb(p);
|
return message_complete_cb(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2282,30 +2473,15 @@ void
|
||||||
parser_init (enum http_parser_type type)
|
parser_init (enum http_parser_type type)
|
||||||
{
|
{
|
||||||
num_messages = 0;
|
num_messages = 0;
|
||||||
|
http_parser_init(&parser, type);
|
||||||
assert(parser == NULL);
|
|
||||||
|
|
||||||
parser = malloc(sizeof(http_parser));
|
|
||||||
|
|
||||||
http_parser_init(parser, type);
|
|
||||||
|
|
||||||
memset(&messages, 0, sizeof messages);
|
memset(&messages, 0, sizeof messages);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
parser_free ()
|
|
||||||
{
|
|
||||||
assert(parser);
|
|
||||||
free(parser);
|
|
||||||
parser = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t parse (const char *buf, size_t len)
|
size_t parse (const char *buf, size_t len)
|
||||||
{
|
{
|
||||||
size_t nparsed;
|
size_t nparsed;
|
||||||
currently_parsing_eof = (len == 0);
|
currently_parsing_eof = (len == 0);
|
||||||
nparsed = http_parser_execute(parser, &settings, buf, len);
|
nparsed = http_parser_execute(&parser, &settings, buf, len);
|
||||||
return nparsed;
|
return nparsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2313,7 +2489,7 @@ size_t parse_count_body (const char *buf, size_t len)
|
||||||
{
|
{
|
||||||
size_t nparsed;
|
size_t nparsed;
|
||||||
currently_parsing_eof = (len == 0);
|
currently_parsing_eof = (len == 0);
|
||||||
nparsed = http_parser_execute(parser, &settings_count_body, buf, len);
|
nparsed = http_parser_execute(&parser, &settings_count_body, buf, len);
|
||||||
return nparsed;
|
return nparsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2324,7 +2500,7 @@ size_t parse_pause (const char *buf, size_t len)
|
||||||
|
|
||||||
currently_parsing_eof = (len == 0);
|
currently_parsing_eof = (len == 0);
|
||||||
current_pause_parser = &s;
|
current_pause_parser = &s;
|
||||||
nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
|
nparsed = http_parser_execute(&parser, current_pause_parser, buf, len);
|
||||||
return nparsed;
|
return nparsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2332,7 +2508,7 @@ size_t parse_connect (const char *buf, size_t len)
|
||||||
{
|
{
|
||||||
size_t nparsed;
|
size_t nparsed;
|
||||||
currently_parsing_eof = (len == 0);
|
currently_parsing_eof = (len == 0);
|
||||||
nparsed = http_parser_execute(parser, &settings_connect, buf, len);
|
nparsed = http_parser_execute(&parser, &settings_connect, buf, len);
|
||||||
return nparsed;
|
return nparsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2405,6 +2581,7 @@ message_eq (int index, int connect, const struct message *expected)
|
||||||
} else {
|
} else {
|
||||||
MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
|
MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
|
||||||
MESSAGE_CHECK_STR_EQ(expected, m, response_status);
|
MESSAGE_CHECK_STR_EQ(expected, m, response_status);
|
||||||
|
assert(m->status_cb_called);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!connect) {
|
if (!connect) {
|
||||||
|
@ -2476,7 +2653,9 @@ message_eq (int index, int connect, const struct message *expected)
|
||||||
if (!r) return 0;
|
if (!r) return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
|
if (!connect) {
|
||||||
|
MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -2549,7 +2728,7 @@ static void
|
||||||
print_error (const char *raw, size_t error_location)
|
print_error (const char *raw, size_t error_location)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "\n*** %s ***\n\n",
|
fprintf(stderr, "\n*** %s ***\n\n",
|
||||||
http_errno_description(HTTP_PARSER_ERRNO(parser)));
|
http_errno_description(HTTP_PARSER_ERRNO(&parser)));
|
||||||
|
|
||||||
int this_line = 0, char_len = 0;
|
int this_line = 0, char_len = 0;
|
||||||
size_t i, j, len = strlen(raw), error_location_line = 0;
|
size_t i, j, len = strlen(raw), error_location_line = 0;
|
||||||
|
@ -3092,6 +3271,24 @@ const struct url_test url_tests[] =
|
||||||
,.rv=1 /* s_dead */
|
,.rv=1 /* s_dead */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
, {.name="empty url"
|
||||||
|
,.url=""
|
||||||
|
,.is_connect=0
|
||||||
|
,.rv=1
|
||||||
|
}
|
||||||
|
|
||||||
|
, {.name="NULL url"
|
||||||
|
,.url=NULL
|
||||||
|
,.is_connect=0
|
||||||
|
,.rv=1
|
||||||
|
}
|
||||||
|
|
||||||
|
, {.name="full of spaces url"
|
||||||
|
,.url=" "
|
||||||
|
,.is_connect=0
|
||||||
|
,.rv=1
|
||||||
|
}
|
||||||
|
|
||||||
#if HTTP_PARSER_STRICT
|
#if HTTP_PARSER_STRICT
|
||||||
|
|
||||||
, {.name="tab in URL"
|
, {.name="tab in URL"
|
||||||
|
@ -3176,7 +3373,7 @@ test_parse_url (void)
|
||||||
memset(&u, 0, sizeof(u));
|
memset(&u, 0, sizeof(u));
|
||||||
|
|
||||||
rv = http_parser_parse_url(test->url,
|
rv = http_parser_parse_url(test->url,
|
||||||
strlen(test->url),
|
test->url ? strlen(test->url) : 0,
|
||||||
test->is_connect,
|
test->is_connect,
|
||||||
&u);
|
&u);
|
||||||
|
|
||||||
|
@ -3216,6 +3413,14 @@ test_method_str (void)
|
||||||
assert(0 == strcmp("<unknown>", http_method_str(1337)));
|
assert(0 == strcmp("<unknown>", http_method_str(1337)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_status_str (void)
|
||||||
|
{
|
||||||
|
assert(0 == strcmp("OK", http_status_str(HTTP_STATUS_OK)));
|
||||||
|
assert(0 == strcmp("Not Found", http_status_str(HTTP_STATUS_NOT_FOUND)));
|
||||||
|
assert(0 == strcmp("<unknown>", http_status_str(1337)));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
test_message (const struct message *message)
|
test_message (const struct message *message)
|
||||||
{
|
{
|
||||||
|
@ -3230,9 +3435,18 @@ test_message (const struct message *message)
|
||||||
size_t msg2len = raw_len - msg1len;
|
size_t msg2len = raw_len - msg1len;
|
||||||
|
|
||||||
if (msg1len) {
|
if (msg1len) {
|
||||||
|
assert(num_messages == 0);
|
||||||
|
messages[0].headers_complete_cb_called = FALSE;
|
||||||
|
|
||||||
read = parse(msg1, msg1len);
|
read = parse(msg1, msg1len);
|
||||||
|
|
||||||
if (message->upgrade && parser->upgrade && num_messages > 0) {
|
if (!messages[0].headers_complete_cb_called && parser.nread != read) {
|
||||||
|
assert(parser.nread == read);
|
||||||
|
print_error(msg1, read);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message->upgrade && parser.upgrade && num_messages > 0) {
|
||||||
messages[num_messages - 1].upgrade = msg1 + read;
|
messages[num_messages - 1].upgrade = msg1 + read;
|
||||||
goto test;
|
goto test;
|
||||||
}
|
}
|
||||||
|
@ -3246,7 +3460,7 @@ test_message (const struct message *message)
|
||||||
|
|
||||||
read = parse(msg2, msg2len);
|
read = parse(msg2, msg2len);
|
||||||
|
|
||||||
if (message->upgrade && parser->upgrade) {
|
if (message->upgrade && parser.upgrade) {
|
||||||
messages[num_messages - 1].upgrade = msg2 + read;
|
messages[num_messages - 1].upgrade = msg2 + read;
|
||||||
goto test;
|
goto test;
|
||||||
}
|
}
|
||||||
|
@ -3271,8 +3485,6 @@ test_message (const struct message *message)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!message_eq(0, 0, message)) abort();
|
if(!message_eq(0, 0, message)) abort();
|
||||||
|
|
||||||
parser_free();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3308,23 +3520,21 @@ test_message_count_body (const struct message *message)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!message_eq(0, 0, message)) abort();
|
if(!message_eq(0, 0, message)) abort();
|
||||||
|
|
||||||
parser_free();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
test_simple (const char *buf, enum http_errno err_expected)
|
test_simple_type (const char *buf,
|
||||||
|
enum http_errno err_expected,
|
||||||
|
enum http_parser_type type)
|
||||||
{
|
{
|
||||||
parser_init(HTTP_REQUEST);
|
parser_init(type);
|
||||||
|
|
||||||
enum http_errno err;
|
enum http_errno err;
|
||||||
|
|
||||||
parse(buf, strlen(buf));
|
parse(buf, strlen(buf));
|
||||||
err = HTTP_PARSER_ERRNO(parser);
|
err = HTTP_PARSER_ERRNO(&parser);
|
||||||
parse(NULL, 0);
|
parse(NULL, 0);
|
||||||
|
|
||||||
parser_free();
|
|
||||||
|
|
||||||
/* In strict mode, allow us to pass with an unexpected HPE_STRICT as
|
/* In strict mode, allow us to pass with an unexpected HPE_STRICT as
|
||||||
* long as the caller isn't expecting success.
|
* long as the caller isn't expecting success.
|
||||||
*/
|
*/
|
||||||
|
@ -3339,6 +3549,12 @@ test_simple (const char *buf, enum http_errno err_expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_simple (const char *buf, enum http_errno err_expected)
|
||||||
|
{
|
||||||
|
test_simple_type(buf, err_expected, HTTP_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
test_invalid_header_content (int req, const char* str)
|
test_invalid_header_content (int req, const char* str)
|
||||||
{
|
{
|
||||||
|
@ -3488,6 +3704,30 @@ test_header_cr_no_lf_error (int req)
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_no_overflow_parse_url (void)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
struct http_parser_url u;
|
||||||
|
|
||||||
|
http_parser_url_init(&u);
|
||||||
|
rv = http_parser_parse_url("http://example.com:8001", 22, 0, &u);
|
||||||
|
|
||||||
|
if (rv != 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"\n*** test_no_overflow_parse_url invalid return value=%d\n",
|
||||||
|
rv);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u.port != 800) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"\n*** test_no_overflow_parse_url invalid port number=%d\n",
|
||||||
|
u.port);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
test_header_overflow_error (int req)
|
test_header_overflow_error (int req)
|
||||||
{
|
{
|
||||||
|
@ -3634,7 +3874,7 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
|
||||||
|
|
||||||
read = parse(total, strlen(total));
|
read = parse(total, strlen(total));
|
||||||
|
|
||||||
if (parser->upgrade) {
|
if (parser.upgrade) {
|
||||||
upgrade_message_fix(total, read, 3, r1, r2, r3);
|
upgrade_message_fix(total, read, 3, r1, r2, r3);
|
||||||
goto test;
|
goto test;
|
||||||
}
|
}
|
||||||
|
@ -3661,8 +3901,6 @@ test:
|
||||||
if (!message_eq(0, 0, r1)) abort();
|
if (!message_eq(0, 0, r1)) abort();
|
||||||
if (message_count > 1 && !message_eq(1, 0, r2)) abort();
|
if (message_count > 1 && !message_eq(1, 0, r2)) abort();
|
||||||
if (message_count > 2 && !message_eq(2, 0, r3)) abort();
|
if (message_count > 2 && !message_eq(2, 0, r3)) abort();
|
||||||
|
|
||||||
parser_free();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SCAN through every possible breaking to make sure the
|
/* SCAN through every possible breaking to make sure the
|
||||||
|
@ -3716,9 +3954,17 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
|
||||||
strlncpy(buf3, sizeof(buf1), total+j, buf3_len);
|
strlncpy(buf3, sizeof(buf1), total+j, buf3_len);
|
||||||
buf3[buf3_len] = 0;
|
buf3[buf3_len] = 0;
|
||||||
|
|
||||||
|
assert(num_messages == 0);
|
||||||
|
messages[0].headers_complete_cb_called = FALSE;
|
||||||
|
|
||||||
read = parse(buf1, buf1_len);
|
read = parse(buf1, buf1_len);
|
||||||
|
|
||||||
if (parser->upgrade) goto test;
|
if (!messages[0].headers_complete_cb_called && parser.nread != read) {
|
||||||
|
print_error(buf1, read);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser.upgrade) goto test;
|
||||||
|
|
||||||
if (read != buf1_len) {
|
if (read != buf1_len) {
|
||||||
print_error(buf1, read);
|
print_error(buf1, read);
|
||||||
|
@ -3727,7 +3973,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
|
||||||
|
|
||||||
read += parse(buf2, buf2_len);
|
read += parse(buf2, buf2_len);
|
||||||
|
|
||||||
if (parser->upgrade) goto test;
|
if (parser.upgrade) goto test;
|
||||||
|
|
||||||
if (read != buf1_len + buf2_len) {
|
if (read != buf1_len + buf2_len) {
|
||||||
print_error(buf2, read);
|
print_error(buf2, read);
|
||||||
|
@ -3736,7 +3982,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
|
||||||
|
|
||||||
read += parse(buf3, buf3_len);
|
read += parse(buf3, buf3_len);
|
||||||
|
|
||||||
if (parser->upgrade) goto test;
|
if (parser.upgrade) goto test;
|
||||||
|
|
||||||
if (read != buf1_len + buf2_len + buf3_len) {
|
if (read != buf1_len + buf2_len + buf3_len) {
|
||||||
print_error(buf3, read);
|
print_error(buf3, read);
|
||||||
|
@ -3746,7 +3992,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
|
||||||
parse(NULL, 0);
|
parse(NULL, 0);
|
||||||
|
|
||||||
test:
|
test:
|
||||||
if (parser->upgrade) {
|
if (parser.upgrade) {
|
||||||
upgrade_message_fix(total, read, 3, r1, r2, r3);
|
upgrade_message_fix(total, read, 3, r1, r2, r3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3770,8 +4016,6 @@ test:
|
||||||
fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
|
fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
parser_free();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3835,7 +4079,7 @@ test_message_pause (const struct message *msg)
|
||||||
// completion callback.
|
// completion callback.
|
||||||
if (messages[0].message_complete_cb_called &&
|
if (messages[0].message_complete_cb_called &&
|
||||||
msg->upgrade &&
|
msg->upgrade &&
|
||||||
parser->upgrade) {
|
parser.upgrade) {
|
||||||
messages[0].upgrade = buf + nread;
|
messages[0].upgrade = buf + nread;
|
||||||
goto test;
|
goto test;
|
||||||
}
|
}
|
||||||
|
@ -3843,17 +4087,16 @@ test_message_pause (const struct message *msg)
|
||||||
if (nread < buflen) {
|
if (nread < buflen) {
|
||||||
|
|
||||||
// Not much do to if we failed a strict-mode check
|
// Not much do to if we failed a strict-mode check
|
||||||
if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
|
if (HTTP_PARSER_ERRNO(&parser) == HPE_STRICT) {
|
||||||
parser_free();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
|
assert (HTTP_PARSER_ERRNO(&parser) == HPE_PAUSED);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf += nread;
|
buf += nread;
|
||||||
buflen -= nread;
|
buflen -= nread;
|
||||||
http_parser_pause(parser, 0);
|
http_parser_pause(&parser, 0);
|
||||||
} while (buflen > 0);
|
} while (buflen > 0);
|
||||||
|
|
||||||
nread = parse_pause(NULL, 0);
|
nread = parse_pause(NULL, 0);
|
||||||
|
@ -3866,8 +4109,6 @@ test:
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!message_eq(0, 0, msg)) abort();
|
if(!message_eq(0, 0, msg)) abort();
|
||||||
|
|
||||||
parser_free();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Verify that body and next message won't be parsed in responses to CONNECT */
|
/* Verify that body and next message won't be parsed in responses to CONNECT */
|
||||||
|
@ -3887,17 +4128,12 @@ test_message_connect (const struct message *msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!message_eq(0, 1, msg)) abort();
|
if(!message_eq(0, 1, msg)) abort();
|
||||||
|
|
||||||
parser_free();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main (void)
|
main (void)
|
||||||
{
|
{
|
||||||
parser = NULL;
|
unsigned i, j, k;
|
||||||
int i, j, k;
|
|
||||||
int request_count;
|
|
||||||
int response_count;
|
|
||||||
unsigned long version;
|
unsigned long version;
|
||||||
unsigned major;
|
unsigned major;
|
||||||
unsigned minor;
|
unsigned minor;
|
||||||
|
@ -3911,18 +4147,17 @@ main (void)
|
||||||
|
|
||||||
printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser));
|
printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser));
|
||||||
|
|
||||||
for (request_count = 0; requests[request_count].name; request_count++);
|
|
||||||
for (response_count = 0; responses[response_count].name; response_count++);
|
|
||||||
|
|
||||||
//// API
|
//// API
|
||||||
test_preserve_data();
|
test_preserve_data();
|
||||||
test_parse_url();
|
test_parse_url();
|
||||||
test_method_str();
|
test_method_str();
|
||||||
|
test_status_str();
|
||||||
|
|
||||||
//// NREAD
|
//// NREAD
|
||||||
test_header_nread_value();
|
test_header_nread_value();
|
||||||
|
|
||||||
//// OVERFLOW CONDITIONS
|
//// OVERFLOW CONDITIONS
|
||||||
|
test_no_overflow_parse_url();
|
||||||
|
|
||||||
test_header_overflow_error(HTTP_REQUEST);
|
test_header_overflow_error(HTTP_REQUEST);
|
||||||
test_no_overflow_long_body(HTTP_REQUEST, 1000);
|
test_no_overflow_long_body(HTTP_REQUEST, 1000);
|
||||||
|
@ -3947,25 +4182,74 @@ main (void)
|
||||||
test_invalid_header_field_token_error(HTTP_RESPONSE);
|
test_invalid_header_field_token_error(HTTP_RESPONSE);
|
||||||
test_invalid_header_field_content_error(HTTP_RESPONSE);
|
test_invalid_header_field_content_error(HTTP_RESPONSE);
|
||||||
|
|
||||||
|
test_simple_type(
|
||||||
|
"POST / HTTP/1.1\r\n"
|
||||||
|
"Content-Length:\r\n" // empty
|
||||||
|
"\r\n",
|
||||||
|
HPE_INVALID_CONTENT_LENGTH,
|
||||||
|
HTTP_REQUEST);
|
||||||
|
|
||||||
|
test_simple_type(
|
||||||
|
"POST / HTTP/1.1\r\n"
|
||||||
|
"Content-Length: 42 \r\n" // Note the surrounding whitespace.
|
||||||
|
"\r\n",
|
||||||
|
HPE_OK,
|
||||||
|
HTTP_REQUEST);
|
||||||
|
|
||||||
|
test_simple_type(
|
||||||
|
"POST / HTTP/1.1\r\n"
|
||||||
|
"Content-Length: 4 2\r\n"
|
||||||
|
"\r\n",
|
||||||
|
HPE_INVALID_CONTENT_LENGTH,
|
||||||
|
HTTP_REQUEST);
|
||||||
|
|
||||||
|
test_simple_type(
|
||||||
|
"POST / HTTP/1.1\r\n"
|
||||||
|
"Content-Length: 13 37\r\n"
|
||||||
|
"\r\n",
|
||||||
|
HPE_INVALID_CONTENT_LENGTH,
|
||||||
|
HTTP_REQUEST);
|
||||||
|
|
||||||
|
test_simple_type(
|
||||||
|
"POST / HTTP/1.1\r\n"
|
||||||
|
"Content-Length: 42\r\n"
|
||||||
|
" Hello world!\r\n",
|
||||||
|
HPE_INVALID_CONTENT_LENGTH,
|
||||||
|
HTTP_REQUEST);
|
||||||
|
|
||||||
|
test_simple_type(
|
||||||
|
"POST / HTTP/1.1\r\n"
|
||||||
|
"Content-Length: 42\r\n"
|
||||||
|
" \r\n",
|
||||||
|
HPE_OK,
|
||||||
|
HTTP_REQUEST);
|
||||||
|
|
||||||
//// RESPONSES
|
//// RESPONSES
|
||||||
|
|
||||||
for (i = 0; i < response_count; i++) {
|
test_simple_type("HTP/1.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
|
||||||
|
test_simple_type("HTTP/01.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
|
||||||
|
test_simple_type("HTTP/11.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
|
||||||
|
test_simple_type("HTTP/1.01 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
|
||||||
|
test_simple_type("HTTP/1.1\t200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
|
||||||
|
test_simple_type("\rHTTP/1.1\t200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(responses); i++) {
|
||||||
test_message(&responses[i]);
|
test_message(&responses[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < response_count; i++) {
|
for (i = 0; i < ARRAY_SIZE(responses); i++) {
|
||||||
test_message_pause(&responses[i]);
|
test_message_pause(&responses[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < response_count; i++) {
|
for (i = 0; i < ARRAY_SIZE(responses); i++) {
|
||||||
test_message_connect(&responses[i]);
|
test_message_connect(&responses[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < response_count; i++) {
|
for (i = 0; i < ARRAY_SIZE(responses); i++) {
|
||||||
if (!responses[i].should_keep_alive) continue;
|
if (!responses[i].should_keep_alive) continue;
|
||||||
for (j = 0; j < response_count; j++) {
|
for (j = 0; j < ARRAY_SIZE(responses); j++) {
|
||||||
if (!responses[j].should_keep_alive) continue;
|
if (!responses[j].should_keep_alive) continue;
|
||||||
for (k = 0; k < response_count; k++) {
|
for (k = 0; k < ARRAY_SIZE(responses); k++) {
|
||||||
test_multiple3(&responses[i], &responses[j], &responses[k]);
|
test_multiple3(&responses[i], &responses[j], &responses[k]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4025,7 +4309,12 @@ main (void)
|
||||||
|
|
||||||
/// REQUESTS
|
/// REQUESTS
|
||||||
|
|
||||||
|
test_simple("GET / IHTTP/1.0\r\n\r\n", HPE_INVALID_CONSTANT);
|
||||||
|
test_simple("GET / ICE/1.0\r\n\r\n", HPE_INVALID_CONSTANT);
|
||||||
test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
|
test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
|
||||||
|
test_simple("GET / HTTP/01.1\r\n\r\n", HPE_INVALID_VERSION);
|
||||||
|
test_simple("GET / HTTP/11.1\r\n\r\n", HPE_INVALID_VERSION);
|
||||||
|
test_simple("GET / HTTP/1.01\r\n\r\n", HPE_INVALID_VERSION);
|
||||||
|
|
||||||
// Extended characters - see nodejs/test/parallel/test-http-headers-obstext.js
|
// Extended characters - see nodejs/test/parallel/test-http-headers-obstext.js
|
||||||
test_simple("GET / HTTP/1.1\r\n"
|
test_simple("GET / HTTP/1.1\r\n"
|
||||||
|
@ -4109,9 +4398,9 @@ main (void)
|
||||||
"\r\n",
|
"\r\n",
|
||||||
HPE_INVALID_HEADER_TOKEN);
|
HPE_INVALID_HEADER_TOKEN);
|
||||||
|
|
||||||
const char *dumbfuck2 =
|
const char *dumbluck2 =
|
||||||
"GET / HTTP/1.1\r\n"
|
"GET / HTTP/1.1\r\n"
|
||||||
"X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n"
|
"X-SSL-Nonsense: -----BEGIN CERTIFICATE-----\r\n"
|
||||||
"\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n"
|
"\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n"
|
||||||
"\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n"
|
"\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n"
|
||||||
"\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n"
|
"\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n"
|
||||||
|
@ -4144,7 +4433,7 @@ main (void)
|
||||||
"\tRA==\r\n"
|
"\tRA==\r\n"
|
||||||
"\t-----END CERTIFICATE-----\r\n"
|
"\t-----END CERTIFICATE-----\r\n"
|
||||||
"\r\n";
|
"\r\n";
|
||||||
test_simple(dumbfuck2, HPE_OK);
|
test_simple(dumbluck2, HPE_OK);
|
||||||
|
|
||||||
const char *corrupted_connection =
|
const char *corrupted_connection =
|
||||||
"GET / HTTP/1.1\r\n"
|
"GET / HTTP/1.1\r\n"
|
||||||
|
@ -4178,19 +4467,19 @@ main (void)
|
||||||
|
|
||||||
|
|
||||||
/* check to make sure our predefined requests are okay */
|
/* check to make sure our predefined requests are okay */
|
||||||
for (i = 0; requests[i].name; i++) {
|
for (i = 0; i < ARRAY_SIZE(requests); i++) {
|
||||||
test_message(&requests[i]);
|
test_message(&requests[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < request_count; i++) {
|
for (i = 0; i < ARRAY_SIZE(requests); i++) {
|
||||||
test_message_pause(&requests[i]);
|
test_message_pause(&requests[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < request_count; i++) {
|
for (i = 0; i < ARRAY_SIZE(requests); i++) {
|
||||||
if (!requests[i].should_keep_alive) continue;
|
if (!requests[i].should_keep_alive) continue;
|
||||||
for (j = 0; j < request_count; j++) {
|
for (j = 0; j < ARRAY_SIZE(requests); j++) {
|
||||||
if (!requests[j].should_keep_alive) continue;
|
if (!requests[j].should_keep_alive) continue;
|
||||||
for (k = 0; k < request_count; k++) {
|
for (k = 0; k < ARRAY_SIZE(requests); k++) {
|
||||||
test_multiple3(&requests[i], &requests[j], &requests[k]);
|
test_multiple3(&requests[i], &requests[j], &requests[k]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4211,7 +4500,7 @@ main (void)
|
||||||
printf("request scan 3/4 ");
|
printf("request scan 3/4 ");
|
||||||
test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END]
|
test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END]
|
||||||
, &requests[CHUNKED_W_TRAILING_HEADERS]
|
, &requests[CHUNKED_W_TRAILING_HEADERS]
|
||||||
, &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH]
|
, &requests[CHUNKED_W_NONSENSE_AFTER_LENGTH]
|
||||||
);
|
);
|
||||||
|
|
||||||
printf("request scan 4/4 ");
|
printf("request scan 4/4 ");
|
||||||
|
|
Loading…
Reference in New Issue