diff --git a/deps/http_parser/Makefile b/deps/http_parser/Makefile index 6cf63bd35ea1..23be2884ea81 100644 --- a/deps/http_parser/Makefile +++ b/deps/http_parser/Makefile @@ -23,8 +23,8 @@ HELPER ?= BINEXT ?= SOLIBNAME = libhttp_parser SOMAJOR = 2 -SOMINOR = 8 -SOREV = 0 +SOMINOR = 9 +SOREV = 1 ifeq (darwin,$(PLATFORM)) SOEXT ?= dylib SONAME ?= $(SOLIBNAME).$(SOMAJOR).$(SOMINOR).$(SOEXT) diff --git a/deps/http_parser/README.md b/deps/http_parser/README.md index 9c4c9999c266..b265d71715f7 100644 --- a/deps/http_parser/README.md +++ b/deps/http_parser/README.md @@ -148,7 +148,7 @@ callback in a threadsafe manner. This allows `http_parser` to be used in multi-threaded contexts. Example: -``` +```c typedef struct { socket_t sock; void* buffer; @@ -184,7 +184,7 @@ void http_parser_thread(socket_t sock) { parser supplied to callback functions */ parser->data = my_data; - http_parser_settings settings; / * set up callbacks */ + http_parser_settings settings; /* set up callbacks */ settings.on_url = my_url_callback; /* execute parser */ diff --git a/deps/http_parser/bench.c b/deps/http_parser/bench.c index 5b452fa1cdb6..678f5556c598 100644 --- a/deps/http_parser/bench.c +++ b/deps/http_parser/bench.c @@ -20,10 +20,14 @@ */ #include "http_parser.h" #include +#include #include #include #include +/* 8 gb */ +static const int64_t kBytes = 8LL << 30; + static const char data[] = "POST /joyent/http-parser HTTP/1.1\r\n" "Host: github.com\r\n" @@ -38,7 +42,7 @@ static const char data[] = "Referer: https://github.com/joyent/http-parser\r\n" "Connection: keep-alive\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 int on_info(http_parser* p) { @@ -67,13 +71,13 @@ int bench(int iter_count, int silent) { int err; struct timeval start; struct timeval end; - float rps; if (!silent) { err = gettimeofday(&start, NULL); assert(err == 0); } + fprintf(stderr, "req_len=%d\n", (int) data_len); for (i = 0; i < iter_count; i++) { size_t parsed; http_parser_init(&parser, HTTP_REQUEST); @@ -83,17 +87,27 @@ int bench(int iter_count, int silent) { } if (!silent) { + double elapsed; + double bw; + double total; + err = gettimeofday(&end, NULL); assert(err == 0); fprintf(stdout, "Benchmark result:\n"); - rps = (float) (end.tv_sec - start.tv_sec) + - (end.tv_usec - start.tv_usec) * 1e-6f; - fprintf(stdout, "Took %f seconds to run\n", rps); + elapsed = (double) (end.tv_sec - start.tv_sec) + + (end.tv_usec - start.tv_usec) * 1e-6f; + + 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); } @@ -101,11 +115,14 @@ int bench(int iter_count, int silent) { } int main(int argc, char** argv) { + int64_t iterations; + + iterations = kBytes / (int64_t) data_len; if (argc == 2 && strcmp(argv[1], "infinite") == 0) { for (;;) - bench(5000000, 1); + bench(iterations, 1); return 0; } else { - return bench(5000000, 0); + return bench(iterations, 0); } } diff --git a/deps/http_parser/http_parser.c b/deps/http_parser/http_parser.c index 46764bced094..2ea228eb0fdc 100644 --- a/deps/http_parser/http_parser.c +++ b/deps/http_parser/http_parser.c @@ -51,6 +51,7 @@ static uint32_t max_header_size = HTTP_MAX_HEADER_SIZE; #define SET_ERRNO(e) \ do { \ + parser->nread = nread; \ parser->http_errno = (e); \ } while(0) @@ -58,6 +59,7 @@ do { \ #define UPDATE_STATE(V) p_state = (enum state) (V); #define RETURN(V) \ do { \ + parser->nread = nread; \ parser->state = CURRENT_STATE(); \ return (V); \ } while (0); @@ -151,8 +153,8 @@ do { \ */ #define COUNT_HEADER_SIZE(V) \ do { \ - parser->nread += (V); \ - if (UNLIKELY(parser->nread > max_header_size)) { \ + nread += (uint32_t)(V); \ + if (UNLIKELY(nread > max_header_size)) { \ SET_ERRNO(HPE_HEADER_OVERFLOW); \ goto error; \ } \ @@ -194,7 +196,7 @@ static const char tokens[256] = { /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 0, 0, 0, 0, 0, 0, 0, 0, /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ - 0, '!', 0, '#', '$', '%', '&', '\'', + ' ', '!', 0, '#', '$', '%', '&', '\'', /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 0, 0, '*', '+', 0, '-', '.', 0, /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ @@ -314,6 +316,8 @@ enum state , s_req_http_HT , s_req_http_HTT , s_req_http_HTTP + , s_req_http_I + , s_req_http_IC , s_req_http_major , s_req_http_dot , s_req_http_minor @@ -421,14 +425,14 @@ enum http_host_state (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 -#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_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') #else -#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) +#define TOKEN(c) tokens[(unsigned char)c] #define IS_URL_CHAR(c) \ (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) #define IS_HOST_CHAR(c) \ @@ -542,7 +546,7 @@ parse_url_char(enum state s, const char ch) return s_dead; } - /* FALLTHROUGH */ + /* fall through */ case s_req_server_start: case s_req_server: if (ch == '/') { @@ -646,6 +650,7 @@ size_t http_parser_execute (http_parser *parser, const char *status_mark = 0; enum state p_state = (enum state) parser->state; const unsigned int lenient = parser->lenient_http_headers; + uint32_t nread = parser->nread; /* We're in an error state. Don't bother doing anything. */ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { @@ -757,21 +762,16 @@ size_t http_parser_execute (http_parser *parser, case s_start_res: { + if (ch == CR || ch == LF) + break; parser->flags = 0; parser->content_length = ULLONG_MAX; - switch (ch) { - case 'H': - UPDATE_STATE(s_res_H); - break; - - case CR: - case LF: - break; - - default: - SET_ERRNO(HPE_INVALID_CONSTANT); - goto error; + if (ch == 'H') { + UPDATE_STATE(s_res_H); + } else { + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; } CALLBACK_NOTIFY(message_begin); @@ -1088,11 +1088,17 @@ size_t http_parser_execute (http_parser *parser, case s_req_http_start: switch (ch) { + case ' ': + break; case 'H': UPDATE_STATE(s_req_http_H); break; - case ' ': - break; + case 'I': + if (parser->method == HTTP_SOURCE) { + UPDATE_STATE(s_req_http_I); + break; + } + /* fall through */ default: SET_ERRNO(HPE_INVALID_CONSTANT); goto error; @@ -1114,6 +1120,16 @@ size_t http_parser_execute (http_parser *parser, UPDATE_STATE(s_req_http_HTTP); break; + case s_req_http_I: + STRICT_CHECK(ch != 'C'); + UPDATE_STATE(s_req_http_IC); + break; + + case s_req_http_IC: + STRICT_CHECK(ch != 'E'); + UPDATE_STATE(s_req_http_HTTP); /* Treat "ICE" as "HTTP". */ + break; + case s_req_http_HTTP: STRICT_CHECK(ch != '/'); UPDATE_STATE(s_req_http_major); @@ -1240,8 +1256,14 @@ size_t http_parser_execute (http_parser *parser, break; 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; + } case h_C: parser->index++; @@ -1341,13 +1363,14 @@ size_t http_parser_execute (http_parser *parser, } } - COUNT_HEADER_SIZE(p - start); - if (p == data + len) { --p; + COUNT_HEADER_SIZE(p - start); break; } + COUNT_HEADER_SIZE(p - start); + if (ch == ':') { UPDATE_STATE(s_header_value_discard_ws); CALLBACK_DATA(header_field); @@ -1371,7 +1394,7 @@ size_t http_parser_execute (http_parser *parser, break; } - /* FALLTHROUGH */ + /* fall through */ case s_header_value_start: { @@ -1413,6 +1436,11 @@ size_t http_parser_execute (http_parser *parser, 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; + case h_connection: /* looking for 'Connection: keep-alive' */ if (c == 'k') { @@ -1468,29 +1496,24 @@ size_t http_parser_execute (http_parser *parser, switch (h_state) { case h_general: - { - const char* p_cr; - const char* p_lf; - size_t limit = data + len - p; - - limit = MIN(limit, max_header_size); - - p_cr = (const char*) memchr(p, CR, limit); - p_lf = (const char*) memchr(p, LF, limit); - if (p_cr != NULL) { - if (p_lf != NULL && p_cr >= p_lf) - p = p_lf; - else - p = p_cr; - } else if (UNLIKELY(p_lf != NULL)) { - p = p_lf; - } else { - p = data + len; + { + const char* limit = p + MIN(data + len - p, max_header_size); + + for (; p != limit; p++) { + ch = *p; + if (ch == CR || ch == LF) { + --p; + break; + } + if (!lenient && !IS_HEADER_CHAR(ch)) { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + } + if (p == data + len) + --p; + break; } - --p; - - break; - } case h_connection: case h_transfer_encoding: @@ -1500,7 +1523,7 @@ size_t http_parser_execute (http_parser *parser, case h_content_length: if (ch == ' ') break; h_state = h_content_length_num; - /* FALLTHROUGH */ + /* fall through */ case h_content_length_num: { @@ -1636,10 +1659,10 @@ size_t http_parser_execute (http_parser *parser, } parser->header_state = h_state; - COUNT_HEADER_SIZE(p - start); - if (p == data + len) --p; + + COUNT_HEADER_SIZE(p - start); break; } @@ -1657,6 +1680,10 @@ size_t http_parser_execute (http_parser *parser, case s_header_value_lws: { 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); REEXECUTE(); } @@ -1709,6 +1736,11 @@ size_t http_parser_execute (http_parser *parser, case h_transfer_encoding_chunked: parser->flags |= F_CHUNKED; break; + case h_content_length: + /* do not allow empty content length */ + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + break; default: break; } @@ -1772,7 +1804,7 @@ size_t http_parser_execute (http_parser *parser, case 2: parser->upgrade = 1; - /* FALLTHROUGH */ + /* fall through */ case 1: parser->flags |= F_SKIPBODY; break; @@ -1796,6 +1828,7 @@ size_t http_parser_execute (http_parser *parser, STRICT_CHECK(ch != LF); parser->nread = 0; + nread = 0; hasBody = parser->flags & F_CHUNKED || (parser->content_length > 0 && parser->content_length != ULLONG_MAX); @@ -1890,7 +1923,7 @@ size_t http_parser_execute (http_parser *parser, case s_chunk_size_start: { - assert(parser->nread == 1); + assert(nread == 1); assert(parser->flags & F_CHUNKED); unhex_val = unhex[(unsigned char)ch]; @@ -1958,6 +1991,7 @@ size_t http_parser_execute (http_parser *parser, STRICT_CHECK(ch != LF); parser->nread = 0; + nread = 0; if (parser->content_length == 0) { parser->flags |= F_TRAILING; @@ -2004,6 +2038,7 @@ size_t http_parser_execute (http_parser *parser, assert(parser->flags & F_CHUNKED); STRICT_CHECK(ch != LF); parser->nread = 0; + nread = 0; UPDATE_STATE(s_chunk_size_start); CALLBACK_NOTIFY(chunk_complete); break; @@ -2015,7 +2050,7 @@ size_t http_parser_execute (http_parser *parser, } } - /* 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 * them in series (unset marks will not result in callbacks). * @@ -2097,6 +2132,16 @@ http_method_str (enum http_method m) return ELEM_AT(method_strings, m, ""); } +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 ""; + } +} void http_parser_init (http_parser *parser, enum http_parser_type t) @@ -2157,7 +2202,7 @@ http_parse_host_char(enum http_host_state s, const char ch) { return s_http_host; } - /* FALLTHROUGH */ + /* fall through */ case s_http_host_v6_end: if (ch == ':') { return s_http_host_port_start; @@ -2170,7 +2215,7 @@ http_parse_host_char(enum http_host_state s, const char ch) { return s_http_host_v6_end; } - /* FALLTHROUGH */ + /* fall through */ case s_http_host_v6_start: if (IS_HEX(ch) || ch == ':' || ch == '.') { return s_http_host_v6; @@ -2186,7 +2231,7 @@ http_parse_host_char(enum http_host_state s, const char ch) { return s_http_host_v6_end; } - /* FALLTHROUGH */ + /* fall through */ case s_http_host_v6_zone_start: /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */ if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' || @@ -2211,12 +2256,13 @@ http_parse_host_char(enum http_host_state s, const char ch) { static int http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { - assert(u->field_set & (1 << UF_HOST)); enum http_host_state s; const char *p; size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; + assert(u->field_set & (1 << UF_HOST)); + u->field_data[UF_HOST].len = 0; s = found_at ? s_http_userinfo_start : s_http_host_start; @@ -2231,14 +2277,14 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { switch(new_s) { case 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++; break; case 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++; break; @@ -2250,7 +2296,7 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { case 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_set |= (1 << UF_PORT); } @@ -2259,7 +2305,7 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { case 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_set |= (1 << UF_USERINFO); } @@ -2304,6 +2350,10 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect, enum http_parser_url_fields uf, old_uf; int found_at = 0; + if (buflen == 0) { + return 1; + } + u->port = u->field_set = 0; s = is_connect ? s_req_server_start : s_req_spaces_before_url; old_uf = UF_MAX; @@ -2331,7 +2381,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect, case s_req_server_with_at: found_at = 1; - /* FALLTHROUGH */ + /* fall through */ case s_req_server: uf = UF_HOST; break; @@ -2359,7 +2409,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect, 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_set |= (1 << uf); @@ -2422,6 +2472,7 @@ http_parser_pause(http_parser *parser, int paused) { */ if (HTTP_PARSER_ERRNO(parser) == HPE_OK || HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { + uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */ SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); } else { assert(0 && "Attempting to pause parser in error state"); diff --git a/deps/http_parser/http_parser.gyp b/deps/http_parser/http_parser.gyp index 4364f73d1f45..ef34ecaeaeab 100644 --- a/deps/http_parser/http_parser.gyp +++ b/deps/http_parser/http_parser.gyp @@ -56,7 +56,7 @@ 'defines': [ 'HTTP_PARSER_STRICT=0' ], 'include_dirs': [ '.' ], }, - 'defines': [ 'HTTP_MAX_HEADER_SIZE=8192', 'HTTP_PARSER_STRICT=0' ], + 'defines': [ 'HTTP_PARSER_STRICT=0' ], 'sources': [ './http_parser.c', ], 'conditions': [ ['OS=="win"', { @@ -79,7 +79,7 @@ 'defines': [ 'HTTP_PARSER_STRICT=1' ], 'include_dirs': [ '.' ], }, - 'defines': [ 'HTTP_MAX_HEADER_SIZE=8192', 'HTTP_PARSER_STRICT=1' ], + 'defines': [ 'HTTP_PARSER_STRICT=1' ], 'sources': [ './http_parser.c', ], 'conditions': [ ['OS=="win"', { diff --git a/deps/http_parser/http_parser.h b/deps/http_parser/http_parser.h index ea7bafef2c31..471250bc798c 100644 --- a/deps/http_parser/http_parser.h +++ b/deps/http_parser/http_parser.h @@ -26,8 +26,8 @@ extern "C" { /* Also update SONAME in the Makefile whenever you change these. */ #define HTTP_PARSER_VERSION_MAJOR 2 -#define HTTP_PARSER_VERSION_MINOR 8 -#define HTTP_PARSER_VERSION_PATCH 0 +#define HTTP_PARSER_VERSION_MINOR 9 +#define HTTP_PARSER_VERSION_PATCH 1 #include #if defined(_WIN32) && !defined(__MINGW32__) && \ @@ -407,6 +407,9 @@ int http_should_keep_alive(const http_parser *parser); /* Returns a string version of the HTTP method. */ 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 */ const char *http_errno_name(enum http_errno err); diff --git a/deps/http_parser/test.c b/deps/http_parser/test.c index cb445cea8607..0140a18b7a2c 100644 --- a/deps/http_parser/test.c +++ b/deps/http_parser/test.c @@ -27,9 +27,7 @@ #include #if defined(__APPLE__) -# undef strlcat # undef strlncpy -# undef strlcpy #endif /* defined(__APPLE__) */ #undef TRUE @@ -43,7 +41,9 @@ #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 { const char *name; // for debugging purposes @@ -153,10 +153,10 @@ const struct message requests[] = ,.body= "" } -#define DUMBFUCK 2 -, {.name= "dumbfuck" +#define DUMBLUCK 2 +, {.name= "dumbluck" ,.type= HTTP_REQUEST - ,.raw= "GET /dumbfuck HTTP/1.1\r\n" + ,.raw= "GET /dumbluck HTTP/1.1\r\n" "aaaaaaaaaaaaa:++++++++++\r\n" "\r\n" ,.should_keep_alive= TRUE @@ -166,8 +166,8 @@ const struct message requests[] = ,.method= HTTP_GET ,.query_string= "" ,.fragment= "" - ,.request_path= "/dumbfuck" - ,.request_url= "/dumbfuck" + ,.request_path= "/dumbluck" + ,.request_url= "/dumbluck" ,.num_headers= 1 ,.headers= { { "aaaaaaaaaaaaa", "++++++++++" } @@ -371,13 +371,13 @@ const struct message requests[] = ,.chunk_lengths= { 5, 6 } } -#define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11 -, {.name= "with bullshit after the length" +#define CHUNKED_W_NONSENSE_AFTER_LENGTH 11 +, {.name= "with nonsense after the length" ,.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" "\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" "0\r\n" "\r\n" @@ -388,8 +388,8 @@ const struct message requests[] = ,.method= HTTP_POST ,.query_string= "" ,.fragment= "" - ,.request_path= "/chunked_w_bullshit_after_length" - ,.request_url= "/chunked_w_bullshit_after_length" + ,.request_path= "/chunked_w_nonsense_after_length" + ,.request_url= "/chunked_w_nonsense_after_length" ,.num_headers= 1 ,.headers= { { "Transfer-Encoding", "chunked" } @@ -1174,7 +1174,25 @@ const struct message requests[] = ,.body= "" } -, {.name= NULL } /* sentinel */ +#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 * */ @@ -1952,8 +1970,6 @@ const struct message responses[] = ,.num_chunks_complete= 3 ,.chunk_lengths= { 2, 2 } } - -, {.name= NULL } /* sentinel */ }; /* strnlen() is a POSIX.2008 addition. Can't rely on it being available so @@ -1993,12 +2009,6 @@ strlncat(char *dst, size_t len, const char *src, size_t n) return slen + dlen; } -size_t -strlcat(char *dst, const char *src, size_t len) -{ - return strlncat(dst, len, src, (size_t) -1); -} - size_t strlncpy(char *dst, size_t len, const char *src, size_t n) { @@ -2017,16 +2027,10 @@ strlncpy(char *dst, size_t len, const char *src, size_t n) return slen; } -size_t -strlcpy(char *dst, const char *src, size_t len) -{ - return strlncpy(dst, len, src, (size_t) -1); -} - int request_url_cb (http_parser *p, const char *buf, size_t len) { - assert(p == parser); + assert(p == &parser); strlncat(messages[num_messages].request_url, sizeof(messages[num_messages].request_url), buf, @@ -2037,7 +2041,7 @@ request_url_cb (http_parser *p, const char *buf, size_t len) int header_field_cb (http_parser *p, const char *buf, size_t len) { - assert(p == parser); + assert(p == &parser); struct message *m = &messages[num_messages]; if (m->last_header_element != FIELD) @@ -2056,7 +2060,7 @@ header_field_cb (http_parser *p, const char *buf, size_t len) int header_value_cb (http_parser *p, const char *buf, size_t len) { - assert(p == parser); + assert(p == &parser); struct message *m = &messages[num_messages]; strlncat(m->headers[m->num_headers-1][1], @@ -2085,7 +2089,7 @@ check_body_is_final (const http_parser *p) int body_cb (http_parser *p, const char *buf, size_t len) { - assert(p == parser); + assert(p == &parser); strlncat(messages[num_messages].body, sizeof(messages[num_messages].body), buf, @@ -2099,7 +2103,7 @@ body_cb (http_parser *p, const char *buf, size_t len) int count_body_cb (http_parser *p, const char *buf, size_t len) { - assert(p == parser); + assert(p == &parser); assert(buf); messages[num_messages].body_size += len; check_body_is_final(p); @@ -2109,7 +2113,8 @@ count_body_cb (http_parser *p, const char *buf, size_t len) int 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; return 0; } @@ -2117,21 +2122,22 @@ message_begin_cb (http_parser *p) int headers_complete_cb (http_parser *p) { - assert(p == parser); - messages[num_messages].method = parser->method; - messages[num_messages].status_code = parser->status_code; - messages[num_messages].http_major = parser->http_major; - messages[num_messages].http_minor = parser->http_minor; + assert(p == &parser); + messages[num_messages].method = parser.method; + messages[num_messages].status_code = parser.status_code; + messages[num_messages].http_major = parser.http_major; + messages[num_messages].http_minor = parser.http_minor; 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; } int message_complete_cb (http_parser *p) { - assert(p == parser); - if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser)) + assert(p == &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 " "value in both on_message_complete and on_headers_complete " @@ -2162,7 +2168,7 @@ message_complete_cb (http_parser *p) int 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; @@ -2176,7 +2182,7 @@ response_status_cb (http_parser *p, const char *buf, size_t len) int chunk_header_cb (http_parser *p) { - assert(p == parser); + assert(p == &parser); int chunk_idx = messages[num_messages].num_chunks; messages[num_messages].num_chunks++; if (chunk_idx < MAX_CHUNKS) { @@ -2189,7 +2195,7 @@ chunk_header_cb (http_parser *p) int 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 * chunk_complete_cb, so not only should the total number of calls to @@ -2394,7 +2400,7 @@ connect_headers_complete_cb (http_parser *p) int 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); } @@ -2467,30 +2473,15 @@ void parser_init (enum http_parser_type type) { num_messages = 0; - - assert(parser == NULL); - - parser = malloc(sizeof(http_parser)); - - http_parser_init(parser, type); - + http_parser_init(&parser, type); 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 nparsed; currently_parsing_eof = (len == 0); - nparsed = http_parser_execute(parser, &settings, buf, len); + nparsed = http_parser_execute(&parser, &settings, buf, len); return nparsed; } @@ -2498,7 +2489,7 @@ size_t parse_count_body (const char *buf, size_t len) { size_t nparsed; 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; } @@ -2509,7 +2500,7 @@ size_t parse_pause (const char *buf, size_t len) currently_parsing_eof = (len == 0); 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; } @@ -2517,7 +2508,7 @@ size_t parse_connect (const char *buf, size_t len) { size_t nparsed; 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; } @@ -2737,7 +2728,7 @@ static void print_error (const char *raw, size_t error_location) { 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; size_t i, j, len = strlen(raw), error_location_line = 0; @@ -3280,6 +3271,24 @@ const struct url_test url_tests[] = ,.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 , {.name="tab in URL" @@ -3364,7 +3373,7 @@ test_parse_url (void) memset(&u, 0, sizeof(u)); rv = http_parser_parse_url(test->url, - strlen(test->url), + test->url ? strlen(test->url) : 0, test->is_connect, &u); @@ -3404,6 +3413,14 @@ test_method_str (void) assert(0 == strcmp("", 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("", http_status_str(1337))); +} + void test_message (const struct message *message) { @@ -3418,9 +3435,18 @@ test_message (const struct message *message) size_t msg2len = raw_len - msg1len; if (msg1len) { + assert(num_messages == 0); + messages[0].headers_complete_cb_called = FALSE; + 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; goto test; } @@ -3434,7 +3460,7 @@ test_message (const struct message *message) read = parse(msg2, msg2len); - if (message->upgrade && parser->upgrade) { + if (message->upgrade && parser.upgrade) { messages[num_messages - 1].upgrade = msg2 + read; goto test; } @@ -3459,8 +3485,6 @@ test_message (const struct message *message) } if(!message_eq(0, 0, message)) abort(); - - parser_free(); } } @@ -3496,8 +3520,6 @@ test_message_count_body (const struct message *message) } if(!message_eq(0, 0, message)) abort(); - - parser_free(); } void @@ -3510,11 +3532,9 @@ test_simple_type (const char *buf, enum http_errno err; parse(buf, strlen(buf)); - err = HTTP_PARSER_ERRNO(parser); + err = HTTP_PARSER_ERRNO(&parser); parse(NULL, 0); - parser_free(); - /* In strict mode, allow us to pass with an unexpected HPE_STRICT as * long as the caller isn't expecting success. */ @@ -3854,7 +3874,7 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct read = parse(total, strlen(total)); - if (parser->upgrade) { + if (parser.upgrade) { upgrade_message_fix(total, read, 3, r1, r2, r3); goto test; } @@ -3881,8 +3901,6 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct if (!message_eq(0, 0, r1)) abort(); if (message_count > 1 && !message_eq(1, 0, r2)) abort(); if (message_count > 2 && !message_eq(2, 0, r3)) abort(); - - parser_free(); } /* SCAN through every possible breaking to make sure the @@ -3936,9 +3954,17 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess strlncpy(buf3, sizeof(buf1), total+j, buf3_len); buf3[buf3_len] = 0; + assert(num_messages == 0); + messages[0].headers_complete_cb_called = FALSE; + 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) { print_error(buf1, read); @@ -3947,7 +3973,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess read += parse(buf2, buf2_len); - if (parser->upgrade) goto test; + if (parser.upgrade) goto test; if (read != buf1_len + buf2_len) { print_error(buf2, read); @@ -3956,7 +3982,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess read += parse(buf3, buf3_len); - if (parser->upgrade) goto test; + if (parser.upgrade) goto test; if (read != buf1_len + buf2_len + buf3_len) { print_error(buf3, read); @@ -3966,7 +3992,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess parse(NULL, 0); test: - if (parser->upgrade) { + if (parser.upgrade) { upgrade_message_fix(total, read, 3, r1, r2, r3); } @@ -3990,8 +4016,6 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n"); goto error; } - - parser_free(); } } } @@ -4055,7 +4079,7 @@ test_message_pause (const struct message *msg) // completion callback. if (messages[0].message_complete_cb_called && msg->upgrade && - parser->upgrade) { + parser.upgrade) { messages[0].upgrade = buf + nread; goto test; } @@ -4063,17 +4087,16 @@ test_message_pause (const struct message *msg) if (nread < buflen) { // Not much do to if we failed a strict-mode check - if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) { - parser_free(); + if (HTTP_PARSER_ERRNO(&parser) == HPE_STRICT) { return; } - assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED); + assert (HTTP_PARSER_ERRNO(&parser) == HPE_PAUSED); } buf += nread; buflen -= nread; - http_parser_pause(parser, 0); + http_parser_pause(&parser, 0); } while (buflen > 0); nread = parse_pause(NULL, 0); @@ -4086,8 +4109,6 @@ test_message_pause (const struct message *msg) } if(!message_eq(0, 0, msg)) abort(); - - parser_free(); } /* Verify that body and next message won't be parsed in responses to CONNECT */ @@ -4107,17 +4128,12 @@ test_message_connect (const struct message *msg) } if(!message_eq(0, 1, msg)) abort(); - - parser_free(); } int main (void) { - parser = NULL; - int i, j, k; - int request_count; - int response_count; + unsigned i, j, k; unsigned long version; unsigned major; unsigned minor; @@ -4131,13 +4147,11 @@ main (void) 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 test_preserve_data(); test_parse_url(); test_method_str(); + test_status_str(); //// NREAD test_header_nread_value(); @@ -4168,6 +4182,13 @@ main (void) test_invalid_header_field_token_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. @@ -4189,6 +4210,20 @@ main (void) 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 test_simple_type("HTP/1.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE); @@ -4196,24 +4231,25 @@ main (void) 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 < response_count; i++) { + for (i = 0; i < ARRAY_SIZE(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]); } - for (i = 0; i < response_count; i++) { + for (i = 0; i < ARRAY_SIZE(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; - for (j = 0; j < response_count; j++) { + for (j = 0; j < ARRAY_SIZE(responses); j++) { 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]); } } @@ -4273,11 +4309,16 @@ main (void) /// 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 / 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); + test_simple("GET / HTTP/1.0\r\nHello: w\1rld\r\n\r\n", HPE_INVALID_HEADER_TOKEN); + test_simple("GET / HTTP/1.0\r\nHello: woooo\2rld\r\n\r\n", HPE_INVALID_HEADER_TOKEN); + // Extended characters - see nodejs/test/parallel/test-http-headers-obstext.js test_simple("GET / HTTP/1.1\r\n" "Test: Düsseldorf\r\n", @@ -4360,9 +4401,9 @@ main (void) "\r\n", HPE_INVALID_HEADER_TOKEN); - const char *dumbfuck2 = + const char *dumbluck2 = "GET / HTTP/1.1\r\n" - "X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n" + "X-SSL-Nonsense: -----BEGIN CERTIFICATE-----\r\n" "\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n" "\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n" "\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n" @@ -4395,7 +4436,7 @@ main (void) "\tRA==\r\n" "\t-----END CERTIFICATE-----\r\n" "\r\n"; - test_simple(dumbfuck2, HPE_OK); + test_simple(dumbluck2, HPE_OK); const char *corrupted_connection = "GET / HTTP/1.1\r\n" @@ -4429,19 +4470,19 @@ main (void) /* 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]); } - for (i = 0; i < request_count; i++) { + for (i = 0; i < ARRAY_SIZE(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; - for (j = 0; j < request_count; j++) { + for (j = 0; j < ARRAY_SIZE(requests); j++) { 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]); } } @@ -4462,7 +4503,7 @@ main (void) printf("request scan 3/4 "); test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END] , &requests[CHUNKED_W_TRAILING_HEADERS] - , &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH] + , &requests[CHUNKED_W_NONSENSE_AFTER_LENGTH] ); printf("request scan 4/4 ");