From 3b5b5ce25412c8aa98e49dc48d89cd2c6afcbe99 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Fri, 16 May 2014 00:23:03 +0900 Subject: [PATCH] Update http-parser to 8d9e5db981b623fffc93657abacdc80270cbee58 --- src/nghttp.cc | 4 +- src/shrpx_http2_session.cc | 2 +- src/shrpx_http_downstream_connection.cc | 2 +- src/shrpx_https_upstream.cc | 2 +- third-party/http-parser/AUTHORS | 9 + third-party/http-parser/README.md | 2 +- third-party/http-parser/contrib/parsertrace.c | 12 +- third-party/http-parser/http_parser.c | 110 ++++++++---- third-party/http-parser/http_parser.h | 37 +++-- third-party/http-parser/test.c | 157 ++++++++++++++---- 10 files changed, 253 insertions(+), 84 deletions(-) diff --git a/src/nghttp.cc b/src/nghttp.cc index 0e17d197..98388a73 100644 --- a/src/nghttp.cc +++ b/src/nghttp.cc @@ -972,7 +972,7 @@ int htp_msg_begincb(http_parser *htp) } // namespace namespace { -int htp_status_completecb(http_parser *htp) +int htp_statuscb(http_parser *htp, const char *at, size_t length) { auto client = static_cast(htp->data); client->upgrade_response_status_code = htp->status_code; @@ -993,7 +993,7 @@ namespace { http_parser_settings htp_hooks = { htp_msg_begincb, // http_cb on_message_begin; nullptr, // http_data_cb on_url; - htp_status_completecb, // http_cb on_status_complete; + htp_statuscb, // http_data_cb on_status; nullptr, // http_data_cb on_header_field; nullptr, // http_data_cb on_header_value; nullptr, // http_cb on_headers_complete; diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index 1382839b..b0e6a878 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -529,7 +529,7 @@ namespace { http_parser_settings htp_hooks = { nullptr, // http_cb on_message_begin; nullptr, // http_data_cb on_url; - nullptr, // http_cb on_status_complete; + nullptr, // http_data_cb on_status; nullptr, // http_data_cb on_header_field; nullptr, // http_data_cb on_header_value; htp_hdrs_completecb, // http_cb on_headers_complete; diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index a343df69..deb92757 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -490,7 +490,7 @@ namespace { http_parser_settings htp_hooks = { nullptr, // http_cb on_message_begin; nullptr, // http_data_cb on_url; - nullptr, // http_cb on_status_complete; + nullptr, // http_data_cb on_status; htp_hdr_keycb, // http_data_cb on_header_field; htp_hdr_valcb, // http_data_cb on_header_value; htp_hdrs_completecb, // http_cb on_headers_complete; diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index 028bf19d..403beffc 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -248,7 +248,7 @@ namespace { http_parser_settings htp_hooks = { htp_msg_begin, // http_cb on_message_begin; htp_uricb, // http_data_cb on_url; - nullptr, // http_cb on_status_complete; + nullptr, // http_data_cb on_status; htp_hdr_keycb, // http_data_cb on_header_field; htp_hdr_valcb, // http_data_cb on_header_value; htp_hdrs_completecb, // http_cb on_headers_complete; diff --git a/third-party/http-parser/AUTHORS b/third-party/http-parser/AUTHORS index 92ee45ca..51b53b12 100644 --- a/third-party/http-parser/AUTHORS +++ b/third-party/http-parser/AUTHORS @@ -39,3 +39,12 @@ BogDan Vatra Peter Faiman Corey Richardson Tóth Tamás +Patrik Stutz +Cam Swords +Chris Dickinson +Uli Köhler +Charlie Somerville +Fedor Indutny +runner +Alexis Campailla +David Wragg diff --git a/third-party/http-parser/README.md b/third-party/http-parser/README.md index 0bf5d359..a054dbe1 100644 --- a/third-party/http-parser/README.md +++ b/third-party/http-parser/README.md @@ -61,7 +61,7 @@ if (recved < 0) { } /* Start up / continue the parser. - * Note we pass recved==0 to signal that EOF has been recieved. + * Note we pass recved==0 to signal that EOF has been received. */ nparsed = http_parser_execute(parser, &settings, buf, recved); diff --git a/third-party/http-parser/contrib/parsertrace.c b/third-party/http-parser/contrib/parsertrace.c index c9bc71ec..e7153680 100644 --- a/third-party/http-parser/contrib/parsertrace.c +++ b/third-party/http-parser/contrib/parsertrace.c @@ -111,14 +111,14 @@ int main(int argc, char* argv[]) { FILE* file = fopen(filename, "r"); if (file == NULL) { perror("fopen"); - return EXIT_FAILURE; + goto fail; } fseek(file, 0, SEEK_END); long file_length = ftell(file); if (file_length == -1) { perror("ftell"); - return EXIT_FAILURE; + goto fail; } fseek(file, 0, SEEK_SET); @@ -126,7 +126,7 @@ int main(int argc, char* argv[]) { if (fread(data, 1, file_length, file) != (size_t)file_length) { fprintf(stderr, "couldn't read entire file\n"); free(data); - return EXIT_FAILURE; + goto fail; } http_parser_settings settings; @@ -149,8 +149,12 @@ int main(int argc, char* argv[]) { "Error: %s (%s)\n", http_errno_description(HTTP_PARSER_ERRNO(&parser)), http_errno_name(HTTP_PARSER_ERRNO(&parser))); - return EXIT_FAILURE; + goto fail; } return EXIT_SUCCESS; + +fail: + fclose(file); + return EXIT_FAILURE; } diff --git a/third-party/http-parser/http_parser.c b/third-party/http-parser/http_parser.c index 20cb67b3..df1b696b 100644 --- a/third-party/http-parser/http_parser.c +++ b/third-party/http-parser/http_parser.c @@ -248,6 +248,7 @@ enum state , s_res_http_minor , s_res_first_status_code , s_res_status_code + , s_res_status_start , s_res_status , s_res_line_almost_done @@ -279,6 +280,9 @@ enum state , s_header_field_start , s_header_field + , s_header_value_discard_ws + , s_header_value_discard_ws_almost_done + , s_header_value_discard_lws , s_header_value_start , s_header_value , s_header_value_lws @@ -581,6 +585,7 @@ size_t http_parser_execute (http_parser *parser, const char *header_value_mark = 0; const char *url_mark = 0; const char *body_mark = 0; + const char *status_mark = 0; /* We're in an error state. Don't bother doing anything. */ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { @@ -627,6 +632,9 @@ size_t http_parser_execute (http_parser *parser, case s_req_fragment: url_mark = data; break; + case s_res_status: + status_mark = data; + break; } for (p=data; p != data + len; p++) { @@ -645,7 +653,7 @@ size_t http_parser_execute (http_parser *parser, * than any reasonable request or response so this should never affect * day-to-day operation. */ - if (parser->nread > HTTP_MAX_HEADER_SIZE) { + if (parser->nread > (HTTP_MAX_HEADER_SIZE)) { SET_ERRNO(HPE_HEADER_OVERFLOW); goto error; } @@ -833,7 +841,7 @@ size_t http_parser_execute (http_parser *parser, if (!IS_NUM(ch)) { switch (ch) { case ' ': - parser->state = s_res_status; + parser->state = s_res_status_start; break; case CR: parser->state = s_res_line_almost_done; @@ -859,9 +867,8 @@ size_t http_parser_execute (http_parser *parser, break; } - case s_res_status: - /* the human readable status. e.g. "NOT FOUND" - * we are not humans so just ignore this */ + case s_res_status_start: + { if (ch == CR) { parser->state = s_res_line_almost_done; break; @@ -871,12 +878,31 @@ size_t http_parser_execute (http_parser *parser, parser->state = s_header_field_start; break; } + + MARK(status); + parser->state = s_res_status; + parser->index = 0; + break; + } + + case s_res_status: + if (ch == CR) { + parser->state = s_res_line_almost_done; + CALLBACK_DATA(status); + break; + } + + if (ch == LF) { + parser->state = s_header_field_start; + CALLBACK_DATA(status); + break; + } + break; case s_res_line_almost_done: STRICT_CHECK(ch != LF); parser->state = s_header_field_start; - CALLBACK_NOTIFY(status_complete); break; case s_start_req: @@ -1357,7 +1383,7 @@ size_t http_parser_execute (http_parser *parser, } if (ch == ':') { - parser->state = s_header_value_start; + parser->state = s_header_value_discard_ws; CALLBACK_DATA(header_field); break; } @@ -1378,28 +1404,28 @@ size_t http_parser_execute (http_parser *parser, goto error; } - case s_header_value_start: - { + case s_header_value_discard_ws: if (ch == ' ' || ch == '\t') break; - MARK(header_value); - - parser->state = s_header_value; - parser->index = 0; - if (ch == CR) { - parser->header_state = h_general; - parser->state = s_header_almost_done; - CALLBACK_DATA(header_value); + parser->state = s_header_value_discard_ws_almost_done; break; } if (ch == LF) { - parser->state = s_header_field_start; - CALLBACK_DATA(header_value); + parser->state = s_header_value_discard_lws; break; } + /* FALLTHROUGH */ + + case s_header_value_start: + { + MARK(header_value); + + parser->state = s_header_value; + parser->index = 0; + c = LOWER(ch); switch (parser->header_state) { @@ -1486,8 +1512,8 @@ size_t http_parser_execute (http_parser *parser, t *= 10; t += ch - '0'; - /* Overflow? */ - if (t < parser->content_length || t == ULLONG_MAX) { + /* Overflow? Test against a conservative limit for simplicity. */ + if ((ULLONG_MAX - 10) / 10 < parser->content_length) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); goto error; } @@ -1547,7 +1573,17 @@ size_t http_parser_execute (http_parser *parser, STRICT_CHECK(ch != LF); parser->state = s_header_value_lws; + break; + } + case s_header_value_lws: + { + if (ch == ' ' || ch == '\t') { + parser->state = s_header_value_start; + goto reexecute_byte; + } + + /* finished the header */ switch (parser->header_state) { case h_connection_keep_alive: parser->flags |= F_CONNECTION_KEEP_ALIVE; @@ -1562,19 +1598,29 @@ size_t http_parser_execute (http_parser *parser, break; } + parser->state = s_header_field_start; + goto reexecute_byte; + } + + case s_header_value_discard_ws_almost_done: + { + STRICT_CHECK(ch != LF); + parser->state = s_header_value_discard_lws; break; } - case s_header_value_lws: + case s_header_value_discard_lws: { - if (ch == ' ' || ch == '\t') - parser->state = s_header_value_start; - else - { + if (ch == ' ' || ch == '\t') { + parser->state = s_header_value_discard_ws; + break; + } else { + /* header value was empty */ + MARK(header_value); parser->state = s_header_field_start; + CALLBACK_DATA_NOADVANCE(header_value); goto reexecute_byte; } - break; } case s_headers_almost_done: @@ -1759,8 +1805,8 @@ size_t http_parser_execute (http_parser *parser, t *= 16; t += unhex_val; - /* Overflow? */ - if (t < parser->content_length || t == ULLONG_MAX) { + /* Overflow? Test against a conservative limit for simplicity. */ + if ((ULLONG_MAX - 16) / 16 < parser->content_length) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); goto error; } @@ -1854,12 +1900,14 @@ size_t http_parser_execute (http_parser *parser, assert(((header_field_mark ? 1 : 0) + (header_value_mark ? 1 : 0) + (url_mark ? 1 : 0) + - (body_mark ? 1 : 0)) <= 1); + (body_mark ? 1 : 0) + + (status_mark ? 1 : 0)) <= 1); CALLBACK_DATA_NOADVANCE(header_field); CALLBACK_DATA_NOADVANCE(header_value); CALLBACK_DATA_NOADVANCE(url); CALLBACK_DATA_NOADVANCE(body); + CALLBACK_DATA_NOADVANCE(status); return len; @@ -2094,7 +2142,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect, u->port = u->field_set = 0; s = is_connect ? s_req_server_start : s_req_spaces_before_url; - old_uf = UF_MAX; + uf = old_uf = UF_MAX; for (p = buf; p < buf + buflen; p++) { s = parse_url_char(s, *p); diff --git a/third-party/http-parser/http_parser.h b/third-party/http-parser/http_parser.h index 98c0905b..d150f26d 100644 --- a/third-party/http-parser/http_parser.h +++ b/third-party/http-parser/http_parser.h @@ -26,7 +26,7 @@ extern "C" { /* Also update SONAME in the Makefile whenever you change these. */ #define HTTP_PARSER_VERSION_MAJOR 2 -#define HTTP_PARSER_VERSION_MINOR 1 +#define HTTP_PARSER_VERSION_MINOR 3 #define HTTP_PARSER_VERSION_PATCH 0 #include @@ -52,9 +52,16 @@ typedef unsigned __int64 uint64_t; # define HTTP_PARSER_STRICT 1 #endif -/* Maximium header size allowed */ -#define HTTP_MAX_HEADER_SIZE (80*1024) - +/* Maximium header size allowed. If the macro is not defined + * before including this header then the default is used. To + * change the maximum header size, define the macro in the build + * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove + * the effective limit on the size of the header, define the macro + * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) + */ +#ifndef HTTP_MAX_HEADER_SIZE +# define HTTP_MAX_HEADER_SIZE (80*1024) +#endif typedef struct http_parser http_parser; typedef struct http_parser_settings http_parser_settings; @@ -143,13 +150,13 @@ enum flags \ /* Callback-related errors */ \ XX(CB_message_begin, "the on_message_begin callback failed") \ - XX(CB_status_complete, "the on_status_complete callback failed") \ XX(CB_url, "the on_url callback failed") \ XX(CB_header_field, "the on_header_field callback failed") \ XX(CB_header_value, "the on_header_value callback failed") \ XX(CB_headers_complete, "the on_headers_complete callback failed") \ XX(CB_body, "the on_body callback failed") \ XX(CB_message_complete, "the on_message_complete callback failed") \ + XX(CB_status, "the on_status callback failed") \ \ /* Parsing-related errors */ \ XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ @@ -193,11 +200,11 @@ enum http_errno { struct http_parser { /** PRIVATE **/ - unsigned char type : 2; /* enum http_parser_type */ - unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */ - unsigned char state; /* enum state from http_parser.c */ - unsigned char header_state; /* enum header_state from http_parser.c */ - unsigned char index; /* index into current matcher */ + unsigned int type : 2; /* enum http_parser_type */ + unsigned int flags : 6; /* F_* values from 'flags' enum; semi-public */ + unsigned int state : 8; /* enum state from http_parser.c */ + unsigned int header_state : 8; /* enum header_state from http_parser.c */ + unsigned int index : 8; /* index into current matcher */ uint32_t nread; /* # bytes read in various scenarios */ uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ @@ -205,16 +212,16 @@ struct http_parser { /** READ-ONLY **/ unsigned short http_major; unsigned short http_minor; - unsigned short status_code; /* responses only */ - unsigned char method; /* requests only */ - unsigned char http_errno : 7; + unsigned int status_code : 16; /* responses only */ + unsigned int method : 8; /* requests only */ + unsigned int http_errno : 7; /* 1 = Upgrade header was present and the parser has exited because of that. * 0 = No upgrade header present. * Should be checked when http_parser_execute() returns in addition to * error checking. */ - unsigned char upgrade : 1; + unsigned int upgrade : 1; /** PUBLIC **/ void *data; /* A pointer to get hook to the "connection" or "socket" object */ @@ -224,7 +231,7 @@ struct http_parser { struct http_parser_settings { http_cb on_message_begin; http_data_cb on_url; - http_cb on_status_complete; + http_data_cb on_status; http_data_cb on_header_field; http_data_cb on_header_value; http_cb on_headers_complete; diff --git a/third-party/http-parser/test.c b/third-party/http-parser/test.c index 656bc9f8..9799dc6d 100644 --- a/third-party/http-parser/test.c +++ b/third-party/http-parser/test.c @@ -26,6 +26,12 @@ #include #include +#if defined(__APPLE__) +# undef strlcat +# undef strlncpy +# undef strlcpy +#endif /* defined(__APPLE__) */ + #undef TRUE #define TRUE 1 #undef FALSE @@ -44,6 +50,7 @@ struct message { enum http_parser_type type; enum http_method method; int status_code; + char response_status[MAX_ELEMENT_SIZE]; char request_path[MAX_ELEMENT_SIZE]; char request_url[MAX_ELEMENT_SIZE]; char fragment[MAX_ELEMENT_SIZE]; @@ -601,8 +608,14 @@ const struct message requests[] = " mno \r\n" "\t \tqrs\r\n" "Line2: \t line2\t\r\n" + "Line3:\r\n" + " line3\r\n" + "Line4: \r\n" + " \r\n" + "Connection:\r\n" + " close\r\n" "\r\n" - ,.should_keep_alive= TRUE + ,.should_keep_alive= FALSE ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 @@ -611,9 +624,12 @@ const struct message requests[] = ,.fragment= "" ,.request_path= "/" ,.request_url= "/" - ,.num_headers= 2 - ,.headers= { { "Line1", "abcdefghijklmno qrs" } + ,.num_headers= 5 + ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" } , { "Line2", "line2\t" } + , { "Line3", "line3" } + , { "Line4", "" } + , { "Connection", "close" }, } ,.body= "" } @@ -897,6 +913,43 @@ const struct message requests[] = ,.body= "" } +#define LINE_FOLDING_IN_HEADER_WITH_LF 34 +, {.name= "line folding in header value" + ,.type= HTTP_REQUEST + ,.raw= "GET / HTTP/1.1\n" + "Line1: abc\n" + "\tdef\n" + " ghi\n" + "\t\tjkl\n" + " mno \n" + "\t \tqrs\n" + "Line2: \t line2\t\n" + "Line3:\n" + " line3\n" + "Line4: \n" + " \n" + "Connection:\n" + " close\n" + "\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/" + ,.request_url= "/" + ,.num_headers= 5 + ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" } + , { "Line2", "line2\t" } + , { "Line3", "line3" } + , { "Line4", "" } + , { "Connection", "close" }, + } + ,.body= "" + } + , {.name= NULL } /* sentinel */ }; @@ -927,6 +980,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 301 + ,.response_status= "Moved Permanently" ,.num_headers= 8 ,.headers= { { "Location", "http://www.google.com/" } @@ -975,6 +1029,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 5 ,.headers= { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" } @@ -1003,6 +1058,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 404 + ,.response_status= "Not Found" ,.num_headers= 0 ,.headers= {} ,.body_size= 0 @@ -1018,6 +1074,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 301 + ,.response_status= "" ,.num_headers= 0 ,.headers= {} ,.body= "" @@ -1043,6 +1100,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 2 ,.headers= { {"Content-Type", "text/plain" } @@ -1068,6 +1126,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 2 ,.headers= { {"Content-Type", "text/html; charset=utf-8" } @@ -1091,6 +1150,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 4 ,.headers= { {"Content-Type", "text/html; charset=UTF-8" } @@ -1116,6 +1176,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 4 ,.headers= { {"Server", "DCLK-AdSvr" } @@ -1148,6 +1209,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 0 ,.status_code= 301 + ,.response_status= "Moved Permanently" ,.num_headers= 9 ,.headers= { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" } @@ -1186,6 +1248,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 11 ,.headers= { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" } @@ -1217,6 +1280,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 500 + ,.response_status= "Oriëntatieprobleem" ,.num_headers= 3 ,.headers= { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" } @@ -1237,6 +1301,7 @@ const struct message responses[] = ,.http_major= 0 ,.http_minor= 9 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 0 ,.headers= {} @@ -1259,6 +1324,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 1 ,.headers= { { "Content-Type", "text/plain" } @@ -1277,6 +1343,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 0 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 1 ,.headers= { { "Connection", "keep-alive" } @@ -1296,6 +1363,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 0 ,.status_code= 204 + ,.response_status= "No content" ,.num_headers= 1 ,.headers= { { "Connection", "keep-alive" } @@ -1314,6 +1382,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 0 ,.headers={} ,.body_size= 0 @@ -1330,6 +1399,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 204 + ,.response_status= "No content" ,.num_headers= 0 ,.headers={} ,.body_size= 0 @@ -1347,6 +1417,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 204 + ,.response_status= "No content" ,.num_headers= 1 ,.headers= { { "Connection", "close" } @@ -1368,6 +1439,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 1 ,.headers= { { "Transfer-Encoding", "chunked" } @@ -1396,6 +1468,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 7 ,.headers= { { "Server", "Microsoft-IIS/6.0" } @@ -1433,6 +1506,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 301 + ,.response_status= "MovedPermanently" ,.num_headers= 9 ,.headers= { { "Date", "Wed, 15 May 2013 17:06:33 GMT" } , { "Server", "Server" } @@ -1447,6 +1521,22 @@ const struct message responses[] = ,.body= "\n" } +#define EMPTY_REASON_PHRASE_AFTER_SPACE 20 +, {.name= "empty reason phrase after space" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 \r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "" + ,.num_headers= 0 + ,.headers= {} + ,.body= "" + } + , {.name= NULL } /* sentinel */ }; @@ -1528,13 +1618,6 @@ request_url_cb (http_parser *p, const char *buf, size_t len) return 0; } -int -status_complete_cb (http_parser *p) { - assert(p == parser); - p->data++; - return 0; -} - int header_field_cb (http_parser *p, const char *buf, size_t len) { @@ -1660,6 +1743,17 @@ message_complete_cb (http_parser *p) return 0; } +int +response_status_cb (http_parser *p, const char *buf, size_t len) +{ + assert(p == parser); + strlncat(messages[num_messages].response_status, + sizeof(messages[num_messages].response_status), + buf, + len); + return 0; +} + /* These dontcall_* callbacks exist so that we can verify that when we're * paused, no additional callbacks are invoked */ int @@ -1720,11 +1814,20 @@ dontcall_message_complete_cb (http_parser *p) abort(); } +int +dontcall_response_status_cb (http_parser *p, const char *buf, size_t len) +{ + if (p || buf || len) { } // gcc + fprintf(stderr, "\n\n*** on_status() called on paused parser ***\n\n"); + abort(); +} + static http_parser_settings settings_dontcall = {.on_message_begin = dontcall_message_begin_cb ,.on_header_field = dontcall_header_field_cb ,.on_header_value = dontcall_header_value_cb ,.on_url = dontcall_request_url_cb + ,.on_status = dontcall_response_status_cb ,.on_body = dontcall_body_cb ,.on_headers_complete = dontcall_headers_complete_cb ,.on_message_complete = dontcall_message_complete_cb @@ -1790,11 +1893,20 @@ pause_message_complete_cb (http_parser *p) return message_complete_cb(p); } +int +pause_response_status_cb (http_parser *p, const char *buf, size_t len) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return response_status_cb(p, buf, len); +} + static http_parser_settings settings_pause = {.on_message_begin = pause_message_begin_cb ,.on_header_field = pause_header_field_cb ,.on_header_value = pause_header_value_cb ,.on_url = pause_request_url_cb + ,.on_status = pause_response_status_cb ,.on_body = pause_body_cb ,.on_headers_complete = pause_headers_complete_cb ,.on_message_complete = pause_message_complete_cb @@ -1805,6 +1917,7 @@ static http_parser_settings settings = ,.on_header_field = header_field_cb ,.on_header_value = header_value_cb ,.on_url = request_url_cb + ,.on_status = response_status_cb ,.on_body = body_cb ,.on_headers_complete = headers_complete_cb ,.on_message_complete = message_complete_cb @@ -1815,6 +1928,7 @@ static http_parser_settings settings_count_body = ,.on_header_field = header_field_cb ,.on_header_value = header_value_cb ,.on_url = request_url_cb + ,.on_status = response_status_cb ,.on_body = count_body_cb ,.on_headers_complete = headers_complete_cb ,.on_message_complete = message_complete_cb @@ -1825,6 +1939,7 @@ static http_parser_settings settings_null = ,.on_header_field = 0 ,.on_header_value = 0 ,.on_url = 0 + ,.on_status = 0 ,.on_body = 0 ,.on_headers_complete = 0 ,.on_message_complete = 0 @@ -1948,6 +2063,7 @@ message_eq (int index, const struct message *expected) MESSAGE_CHECK_NUM_EQ(expected, m, method); } else { MESSAGE_CHECK_NUM_EQ(expected, m, status_code); + MESSAGE_CHECK_STR_EQ(expected, m, response_status); } MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive); @@ -2868,7 +2984,7 @@ test_header_content_length_overflow_error (void) "HTTP/1.1 200 OK\r\n" \ "Content-Length: " #size "\r\n" \ "\r\n" - const char a[] = X(18446744073709551614); /* 2^64-2 */ + const char a[] = X(1844674407370955160); /* 2^64 / 10 - 1 */ const char b[] = X(18446744073709551615); /* 2^64-1 */ const char c[] = X(18446744073709551616); /* 2^64 */ #undef X @@ -2886,7 +3002,7 @@ test_chunk_content_length_overflow_error (void) "\r\n" \ #size "\r\n" \ "..." - const char a[] = X(FFFFFFFFFFFFFFFE); /* 2^64-2 */ + const char a[] = X(FFFFFFFFFFFFFFE); /* 2^64 / 16 - 1 */ const char b[] = X(FFFFFFFFFFFFFFFF); /* 2^64-1 */ const char c[] = X(10000000000000000); /* 2^64 */ #undef X @@ -3133,20 +3249,6 @@ create_large_chunked_message (int body_size_in_kb, const char* headers) return buf; } -void -test_status_complete (void) -{ - parser_init(HTTP_RESPONSE); - parser->data = 0; - http_parser_settings settings = settings_null; - settings.on_status_complete = status_complete_cb; - - char *response = "don't mind me, just a simple response"; - http_parser_execute(parser, &settings, response, strlen(response)); - assert(parser->data == (void*)0); // the status_complete callback was never called - assert(parser->http_errno == HPE_INVALID_CONSTANT); // the errno for an invalid status line -} - /* Verify that we can pause parsing at any of the bytes in the * message and still get the result that we're expecting. */ void @@ -3280,6 +3382,7 @@ main (void) ,.http_major= 1 ,.http_minor= 0 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 2 ,.headers= { { "Transfer-Encoding", "chunked" } @@ -3468,8 +3571,6 @@ main (void) , &requests[CONNECT_REQUEST] ); - test_status_complete(); - puts("requests okay"); return 0;