diff options
author | Ben Noordhuis <info@bnoordhuis.nl> | 2015-01-28 00:54:05 +0300 |
---|---|---|
committer | Ben Noordhuis <info@bnoordhuis.nl> | 2015-01-28 18:56:44 +0300 |
commit | 660509694cfd4de59df0548eabbe18c97d75c63a (patch) | |
tree | 01cedf84a24d7d1af93391eb12b627857e2a31be /deps/http_parser/http_parser.c | |
parent | 90ddb46d522c37d2bc2eb68a6e0c9d52f9fbba42 (diff) |
deps: roll back http_parser to 2.3.0
Commit 598efcbe ("deps: update http_parser to 2.4.1") introduced a
regression in HTTP Upgrade header handling.
Fixes: https://github.com/iojs/io.js/issues/627
PR-URL: https://github.com/iojs/io.js/pull/628
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
Diffstat (limited to 'deps/http_parser/http_parser.c')
-rw-r--r-- | deps/http_parser/http_parser.c | 619 |
1 files changed, 232 insertions, 387 deletions
diff --git a/deps/http_parser/http_parser.c b/deps/http_parser/http_parser.c index c0e7ca15fb4..70cc9bd37b2 100644 --- a/deps/http_parser/http_parser.c +++ b/deps/http_parser/http_parser.c @@ -56,41 +56,19 @@ do { \ parser->http_errno = (e); \ } while(0) -#define CURRENT_STATE() p_state -#define UPDATE_STATE(V) p_state = (enum state) (V); -#define RETURN(V) \ -do { \ - parser->state = CURRENT_STATE(); \ - return (V); \ -} while (0); -#define REEXECUTE() \ - --p; \ - break; - - -#ifdef __GNUC__ -# define LIKELY(X) __builtin_expect(!!(X), 1) -# define UNLIKELY(X) __builtin_expect(!!(X), 0) -#else -# define LIKELY(X) (X) -# define UNLIKELY(X) (X) -#endif - /* Run the notify callback FOR, returning ER if it fails */ #define CALLBACK_NOTIFY_(FOR, ER) \ do { \ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ \ - if (LIKELY(settings->on_##FOR)) { \ - parser->state = CURRENT_STATE(); \ - if (UNLIKELY(0 != settings->on_##FOR(parser))) { \ + if (settings->on_##FOR) { \ + if (0 != settings->on_##FOR(parser)) { \ SET_ERRNO(HPE_CB_##FOR); \ } \ - UPDATE_STATE(parser->state); \ \ /* We either errored above or got paused; get out */ \ - if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ return (ER); \ } \ } \ @@ -108,16 +86,13 @@ do { \ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ \ if (FOR##_mark) { \ - if (LIKELY(settings->on_##FOR)) { \ - parser->state = CURRENT_STATE(); \ - if (UNLIKELY(0 != \ - settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \ + if (settings->on_##FOR) { \ + if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) { \ SET_ERRNO(HPE_CB_##FOR); \ } \ - UPDATE_STATE(parser->state); \ \ /* We either errored above or got paused; get out */ \ - if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ return (ER); \ } \ } \ @@ -141,26 +116,6 @@ do { \ } \ } while (0) -/* 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 - * embedders against denial-of-service attacks where the attacker feeds - * us a never-ending header that the embedder keeps buffering. - * - * 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 - * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger - * than any reasonable request or response so this should never affect - * day-to-day operation. - */ -#define COUNT_HEADER_SIZE(V) \ -do { \ - parser->nread += (V); \ - if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \ - SET_ERRNO(HPE_HEADER_OVERFLOW); \ - goto error; \ - } \ -} while (0) - #define PROXY_CONNECTION "proxy-connection" #define CONNECTION "connection" @@ -379,16 +334,12 @@ enum header_states , h_upgrade , h_matching_transfer_encoding_chunked - , h_matching_connection_token_start , h_matching_connection_keep_alive , h_matching_connection_close - , h_matching_connection_upgrade - , h_matching_connection_token , h_transfer_encoding_chunked , h_connection_keep_alive , h_connection_close - , h_connection_upgrade }; enum http_host_state @@ -420,8 +371,6 @@ enum http_host_state (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ (c) == '$' || (c) == ',') -#define STRICT_TOKEN(c) (tokens[(unsigned char)c]) - #if HTTP_PARSER_STRICT #define TOKEN(c) (tokens[(unsigned char)c]) #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) @@ -637,7 +586,6 @@ size_t http_parser_execute (http_parser *parser, const char *url_mark = 0; const char *body_mark = 0; const char *status_mark = 0; - enum state p_state = (enum state) parser->state; /* We're in an error state. Don't bother doing anything. */ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { @@ -645,7 +593,7 @@ size_t http_parser_execute (http_parser *parser, } if (len == 0) { - switch (CURRENT_STATE()) { + switch (parser->state) { case s_body_identity_eof: /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if * we got paused. @@ -666,11 +614,11 @@ size_t http_parser_execute (http_parser *parser, } - if (CURRENT_STATE() == s_header_field) + if (parser->state == s_header_field) header_field_mark = data; - if (CURRENT_STATE() == s_header_value) + if (parser->state == s_header_value) header_value_mark = data; - switch (CURRENT_STATE()) { + switch (parser->state) { case s_req_path: case s_req_schema: case s_req_schema_slash: @@ -687,23 +635,38 @@ size_t http_parser_execute (http_parser *parser, case s_res_status: status_mark = data; break; - default: - break; } for (p=data; p != data + len; p++) { ch = *p; - if (PARSING_HEADER(CURRENT_STATE())) - COUNT_HEADER_SIZE(1); + if (PARSING_HEADER(parser->state)) { + ++parser->nread; + /* 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 + * embedders against denial-of-service attacks where the attacker feeds + * us a never-ending header that the embedder keeps buffering. + * + * 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 + * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger + * than any reasonable request or response so this should never affect + * day-to-day operation. + */ + if (parser->nread > HTTP_MAX_HEADER_SIZE) { + SET_ERRNO(HPE_HEADER_OVERFLOW); + goto error; + } + } - switch (CURRENT_STATE()) { + reexecute_byte: + switch (parser->state) { case s_dead: /* this state is used after a 'Connection: close' message * the parser will error out if it reads another message */ - if (LIKELY(ch == CR || ch == LF)) + if (ch == CR || ch == LF) break; SET_ERRNO(HPE_CLOSED_CONNECTION); @@ -717,13 +680,13 @@ size_t http_parser_execute (http_parser *parser, parser->content_length = ULLONG_MAX; if (ch == 'H') { - UPDATE_STATE(s_res_or_resp_H); + parser->state = s_res_or_resp_H; CALLBACK_NOTIFY(message_begin); } else { parser->type = HTTP_REQUEST; - UPDATE_STATE(s_start_req); - REEXECUTE(); + parser->state = s_start_req; + goto reexecute_byte; } break; @@ -732,9 +695,9 @@ size_t http_parser_execute (http_parser *parser, case s_res_or_resp_H: if (ch == 'T') { parser->type = HTTP_RESPONSE; - UPDATE_STATE(s_res_HT); + parser->state = s_res_HT; } else { - if (UNLIKELY(ch != 'E')) { + if (ch != 'E') { SET_ERRNO(HPE_INVALID_CONSTANT); goto error; } @@ -742,7 +705,7 @@ size_t http_parser_execute (http_parser *parser, parser->type = HTTP_REQUEST; parser->method = HTTP_HEAD; parser->index = 2; - UPDATE_STATE(s_req_method); + parser->state = s_req_method; } break; @@ -753,7 +716,7 @@ size_t http_parser_execute (http_parser *parser, switch (ch) { case 'H': - UPDATE_STATE(s_res_H); + parser->state = s_res_H; break; case CR: @@ -771,39 +734,39 @@ size_t http_parser_execute (http_parser *parser, case s_res_H: STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_res_HT); + parser->state = s_res_HT; break; case s_res_HT: STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_res_HTT); + parser->state = s_res_HTT; break; case s_res_HTT: STRICT_CHECK(ch != 'P'); - UPDATE_STATE(s_res_HTTP); + parser->state = s_res_HTTP; break; case s_res_HTTP: STRICT_CHECK(ch != '/'); - UPDATE_STATE(s_res_first_http_major); + parser->state = s_res_first_http_major; break; case s_res_first_http_major: - if (UNLIKELY(ch < '0' || ch > '9')) { + if (ch < '0' || ch > '9') { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major = ch - '0'; - UPDATE_STATE(s_res_http_major); + parser->state = s_res_http_major; break; /* major HTTP version or dot */ case s_res_http_major: { if (ch == '.') { - UPDATE_STATE(s_res_first_http_minor); + parser->state = s_res_first_http_minor; break; } @@ -815,7 +778,7 @@ size_t http_parser_execute (http_parser *parser, parser->http_major *= 10; parser->http_major += ch - '0'; - if (UNLIKELY(parser->http_major > 999)) { + if (parser->http_major > 999) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } @@ -825,24 +788,24 @@ size_t http_parser_execute (http_parser *parser, /* first digit of minor HTTP version */ case s_res_first_http_minor: - if (UNLIKELY(!IS_NUM(ch))) { + if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor = ch - '0'; - UPDATE_STATE(s_res_http_minor); + parser->state = s_res_http_minor; break; /* minor HTTP version or end of request line */ case s_res_http_minor: { if (ch == ' ') { - UPDATE_STATE(s_res_first_status_code); + parser->state = s_res_first_status_code; break; } - if (UNLIKELY(!IS_NUM(ch))) { + if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } @@ -850,7 +813,7 @@ size_t http_parser_execute (http_parser *parser, parser->http_minor *= 10; parser->http_minor += ch - '0'; - if (UNLIKELY(parser->http_minor > 999)) { + if (parser->http_minor > 999) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } @@ -869,7 +832,7 @@ size_t http_parser_execute (http_parser *parser, goto error; } parser->status_code = ch - '0'; - UPDATE_STATE(s_res_status_code); + parser->state = s_res_status_code; break; } @@ -878,13 +841,13 @@ size_t http_parser_execute (http_parser *parser, if (!IS_NUM(ch)) { switch (ch) { case ' ': - UPDATE_STATE(s_res_status_start); + parser->state = s_res_status_start; break; case CR: - UPDATE_STATE(s_res_line_almost_done); + parser->state = s_res_line_almost_done; break; case LF: - UPDATE_STATE(s_header_field_start); + parser->state = s_header_field_start; break; default: SET_ERRNO(HPE_INVALID_STATUS); @@ -896,7 +859,7 @@ size_t http_parser_execute (http_parser *parser, parser->status_code *= 10; parser->status_code += ch - '0'; - if (UNLIKELY(parser->status_code > 999)) { + if (parser->status_code > 999) { SET_ERRNO(HPE_INVALID_STATUS); goto error; } @@ -907,30 +870,30 @@ size_t http_parser_execute (http_parser *parser, case s_res_status_start: { if (ch == CR) { - UPDATE_STATE(s_res_line_almost_done); + parser->state = s_res_line_almost_done; break; } if (ch == LF) { - UPDATE_STATE(s_header_field_start); + parser->state = s_header_field_start; break; } MARK(status); - UPDATE_STATE(s_res_status); + parser->state = s_res_status; parser->index = 0; break; } case s_res_status: if (ch == CR) { - UPDATE_STATE(s_res_line_almost_done); + parser->state = s_res_line_almost_done; CALLBACK_DATA(status); break; } if (ch == LF) { - UPDATE_STATE(s_header_field_start); + parser->state = s_header_field_start; CALLBACK_DATA(status); break; } @@ -939,7 +902,7 @@ size_t http_parser_execute (http_parser *parser, case s_res_line_almost_done: STRICT_CHECK(ch != LF); - UPDATE_STATE(s_header_field_start); + parser->state = s_header_field_start; break; case s_start_req: @@ -949,7 +912,7 @@ size_t http_parser_execute (http_parser *parser, parser->flags = 0; parser->content_length = ULLONG_MAX; - if (UNLIKELY(!IS_ALPHA(ch))) { + if (!IS_ALPHA(ch)) { SET_ERRNO(HPE_INVALID_METHOD); goto error; } @@ -962,7 +925,7 @@ size_t http_parser_execute (http_parser *parser, case 'G': parser->method = HTTP_GET; break; case 'H': parser->method = HTTP_HEAD; break; case 'L': parser->method = HTTP_LOCK; break; - case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; + case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break; case 'N': parser->method = HTTP_NOTIFY; break; case 'O': parser->method = HTTP_OPTIONS; break; case 'P': parser->method = HTTP_POST; @@ -976,7 +939,7 @@ size_t http_parser_execute (http_parser *parser, SET_ERRNO(HPE_INVALID_METHOD); goto error; } - UPDATE_STATE(s_req_method); + parser->state = s_req_method; CALLBACK_NOTIFY(message_begin); @@ -986,14 +949,14 @@ size_t http_parser_execute (http_parser *parser, case s_req_method: { const char *matcher; - if (UNLIKELY(ch == '\0')) { + if (ch == '\0') { SET_ERRNO(HPE_INVALID_METHOD); goto error; } matcher = method_strings[parser->method]; if (ch == ' ' && matcher[parser->index] == '\0') { - UPDATE_STATE(s_req_spaces_before_url); + parser->state = s_req_spaces_before_url; } else if (ch == matcher[parser->index]) { ; /* nada */ } else if (parser->method == HTTP_CONNECT) { @@ -1014,8 +977,6 @@ size_t http_parser_execute (http_parser *parser, parser->method = HTTP_MSEARCH; } else if (parser->index == 2 && ch == 'A') { parser->method = HTTP_MKACTIVITY; - } else if (parser->index == 3 && ch == 'A') { - parser->method = HTTP_MKCALENDAR; } else { SET_ERRNO(HPE_INVALID_METHOD); goto error; @@ -1074,11 +1035,11 @@ size_t http_parser_execute (http_parser *parser, MARK(url); if (parser->method == HTTP_CONNECT) { - UPDATE_STATE(s_req_server_start); + parser->state = s_req_server_start; } - UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); - if (UNLIKELY(CURRENT_STATE() == s_dead)) { + parser->state = parse_url_char((enum state)parser->state, ch); + if (parser->state == s_dead) { SET_ERRNO(HPE_INVALID_URL); goto error; } @@ -1099,8 +1060,8 @@ size_t http_parser_execute (http_parser *parser, SET_ERRNO(HPE_INVALID_URL); goto error; default: - UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); - if (UNLIKELY(CURRENT_STATE() == s_dead)) { + parser->state = parse_url_char((enum state)parser->state, ch); + if (parser->state == s_dead) { SET_ERRNO(HPE_INVALID_URL); goto error; } @@ -1119,21 +1080,21 @@ size_t http_parser_execute (http_parser *parser, { switch (ch) { case ' ': - UPDATE_STATE(s_req_http_start); + parser->state = s_req_http_start; CALLBACK_DATA(url); break; case CR: case LF: parser->http_major = 0; parser->http_minor = 9; - UPDATE_STATE((ch == CR) ? + parser->state = (ch == CR) ? s_req_line_almost_done : - s_header_field_start); + s_header_field_start; CALLBACK_DATA(url); break; default: - UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); - if (UNLIKELY(CURRENT_STATE() == s_dead)) { + parser->state = parse_url_char((enum state)parser->state, ch); + if (parser->state == s_dead) { SET_ERRNO(HPE_INVALID_URL); goto error; } @@ -1144,7 +1105,7 @@ size_t http_parser_execute (http_parser *parser, case s_req_http_start: switch (ch) { case 'H': - UPDATE_STATE(s_req_http_H); + parser->state = s_req_http_H; break; case ' ': break; @@ -1156,44 +1117,44 @@ size_t http_parser_execute (http_parser *parser, case s_req_http_H: STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_req_http_HT); + parser->state = s_req_http_HT; break; case s_req_http_HT: STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_req_http_HTT); + parser->state = s_req_http_HTT; break; case s_req_http_HTT: STRICT_CHECK(ch != 'P'); - UPDATE_STATE(s_req_http_HTTP); + parser->state = s_req_http_HTTP; break; case s_req_http_HTTP: STRICT_CHECK(ch != '/'); - UPDATE_STATE(s_req_first_http_major); + parser->state = s_req_first_http_major; break; /* first digit of major HTTP version */ case s_req_first_http_major: - if (UNLIKELY(ch < '1' || ch > '9')) { + if (ch < '1' || ch > '9') { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major = ch - '0'; - UPDATE_STATE(s_req_http_major); + parser->state = s_req_http_major; break; /* major HTTP version or dot */ case s_req_http_major: { if (ch == '.') { - UPDATE_STATE(s_req_first_http_minor); + parser->state = s_req_first_http_minor; break; } - if (UNLIKELY(!IS_NUM(ch))) { + if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } @@ -1201,7 +1162,7 @@ size_t http_parser_execute (http_parser *parser, parser->http_major *= 10; parser->http_major += ch - '0'; - if (UNLIKELY(parser->http_major > 999)) { + if (parser->http_major > 999) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } @@ -1211,31 +1172,31 @@ size_t http_parser_execute (http_parser *parser, /* first digit of minor HTTP version */ case s_req_first_http_minor: - if (UNLIKELY(!IS_NUM(ch))) { + if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor = ch - '0'; - UPDATE_STATE(s_req_http_minor); + parser->state = s_req_http_minor; break; /* minor HTTP version or end of request line */ case s_req_http_minor: { if (ch == CR) { - UPDATE_STATE(s_req_line_almost_done); + parser->state = s_req_line_almost_done; break; } if (ch == LF) { - UPDATE_STATE(s_header_field_start); + parser->state = s_header_field_start; break; } /* XXX allow spaces after digit? */ - if (UNLIKELY(!IS_NUM(ch))) { + if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } @@ -1243,7 +1204,7 @@ size_t http_parser_execute (http_parser *parser, parser->http_minor *= 10; parser->http_minor += ch - '0'; - if (UNLIKELY(parser->http_minor > 999)) { + if (parser->http_minor > 999) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } @@ -1254,32 +1215,32 @@ size_t http_parser_execute (http_parser *parser, /* end of request line */ case s_req_line_almost_done: { - if (UNLIKELY(ch != LF)) { + if (ch != LF) { SET_ERRNO(HPE_LF_EXPECTED); goto error; } - UPDATE_STATE(s_header_field_start); + parser->state = s_header_field_start; break; } case s_header_field_start: { if (ch == CR) { - UPDATE_STATE(s_headers_almost_done); + parser->state = s_headers_almost_done; break; } if (ch == LF) { /* they might be just sending \n instead of \r\n so this would be * the second \n to denote the end of headers*/ - UPDATE_STATE(s_headers_almost_done); - REEXECUTE(); + parser->state = s_headers_almost_done; + goto reexecute_byte; } c = TOKEN(ch); - if (UNLIKELY(!c)) { + if (!c) { SET_ERRNO(HPE_INVALID_HEADER_TOKEN); goto error; } @@ -1287,7 +1248,7 @@ size_t http_parser_execute (http_parser *parser, MARK(header_field); parser->index = 0; - UPDATE_STATE(s_header_field); + parser->state = s_header_field; switch (c) { case 'c': @@ -1315,14 +1276,9 @@ size_t http_parser_execute (http_parser *parser, case s_header_field: { - const char* start = p; - for (; p != data + len; p++) { - ch = *p; - c = TOKEN(ch); - - if (!c) - break; + c = TOKEN(ch); + if (c) { switch (parser->header_state) { case h_general: break; @@ -1423,17 +1379,23 @@ size_t http_parser_execute (http_parser *parser, assert(0 && "Unknown header_state"); break; } + break; } - COUNT_HEADER_SIZE(p - start); + if (ch == ':') { + parser->state = s_header_value_discard_ws; + CALLBACK_DATA(header_field); + break; + } - if (p == data + len) { - --p; + if (ch == CR) { + parser->state = s_header_almost_done; + CALLBACK_DATA(header_field); break; } - if (ch == ':') { - UPDATE_STATE(s_header_value_discard_ws); + if (ch == LF) { + parser->state = s_header_field_start; CALLBACK_DATA(header_field); break; } @@ -1446,12 +1408,12 @@ size_t http_parser_execute (http_parser *parser, if (ch == ' ' || ch == '\t') break; if (ch == CR) { - UPDATE_STATE(s_header_value_discard_ws_almost_done); + parser->state = s_header_value_discard_ws_almost_done; break; } if (ch == LF) { - UPDATE_STATE(s_header_value_discard_lws); + parser->state = s_header_value_discard_lws; break; } @@ -1461,7 +1423,7 @@ size_t http_parser_execute (http_parser *parser, { MARK(header_value); - UPDATE_STATE(s_header_value); + parser->state = s_header_value; parser->index = 0; c = LOWER(ch); @@ -1482,7 +1444,7 @@ size_t http_parser_execute (http_parser *parser, break; case h_content_length: - if (UNLIKELY(!IS_NUM(ch))) { + if (!IS_NUM(ch)) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); goto error; } @@ -1497,17 +1459,11 @@ size_t http_parser_execute (http_parser *parser, /* looking for 'Connection: close' */ } else if (c == 'c') { parser->header_state = h_matching_connection_close; - } else if (c == 'u') { - parser->header_state = h_matching_connection_upgrade; } else { - parser->header_state = h_matching_connection_token; + parser->header_state = h_general; } break; - /* Multi-value `Connection` header */ - case h_matching_connection_token_start: - break; - default: parser->header_state = h_general; break; @@ -1517,187 +1473,98 @@ size_t http_parser_execute (http_parser *parser, case s_header_value: { - const char* start = p; - enum header_states h_state = (enum header_states) parser->header_state; - for (; p != data + len; p++) { - ch = *p; - if (ch == CR) { - UPDATE_STATE(s_header_almost_done); - parser->header_state = h_state; - CALLBACK_DATA(header_value); - break; - } - if (ch == LF) { - UPDATE_STATE(s_header_almost_done); - COUNT_HEADER_SIZE(p - start); - parser->header_state = h_state; - CALLBACK_DATA_NOADVANCE(header_value); - REEXECUTE(); - } - - c = LOWER(ch); - - switch (h_state) { - case h_general: - { - const char* p_cr; - const char* p_lf; - size_t limit = data + len - p; - - limit = MIN(limit, HTTP_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; - } - --p; - - break; - } + if (ch == CR) { + parser->state = s_header_almost_done; + CALLBACK_DATA(header_value); + break; + } - case h_connection: - case h_transfer_encoding: - assert(0 && "Shouldn't get here."); - break; + if (ch == LF) { + parser->state = s_header_almost_done; + CALLBACK_DATA_NOADVANCE(header_value); + goto reexecute_byte; + } - case h_content_length: - { - uint64_t t; + c = LOWER(ch); - if (ch == ' ') break; + switch (parser->header_state) { + case h_general: + break; - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - parser->header_state = h_state; - goto error; - } + case h_connection: + case h_transfer_encoding: + assert(0 && "Shouldn't get here."); + break; - t = parser->content_length; - t *= 10; - t += ch - '0'; + case h_content_length: + { + uint64_t t; - /* Overflow? Test against a conservative limit for simplicity. */ - if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - parser->header_state = h_state; - goto error; - } + if (ch == ' ') break; - parser->content_length = t; - break; + if (!IS_NUM(ch)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; } - /* Transfer-Encoding: chunked */ - case h_matching_transfer_encoding_chunked: - parser->index++; - if (parser->index > sizeof(CHUNKED)-1 - || c != CHUNKED[parser->index]) { - h_state = h_general; - } else if (parser->index == sizeof(CHUNKED)-2) { - h_state = h_transfer_encoding_chunked; - } - break; + t = parser->content_length; + t *= 10; + t += ch - '0'; - case h_matching_connection_token_start: - /* looking for 'Connection: keep-alive' */ - if (c == 'k') { - h_state = h_matching_connection_keep_alive; - /* looking for 'Connection: close' */ - } else if (c == 'c') { - h_state = h_matching_connection_close; - } else if (c == 'u') { - h_state = h_matching_connection_upgrade; - } else if (STRICT_TOKEN(c)) { - h_state = h_matching_connection_token; - } else if (c == ' ' || c == '\t') { - /* Skip lws */ - } else { - h_state = h_general; - } - break; - - /* looking for 'Connection: keep-alive' */ - case h_matching_connection_keep_alive: - parser->index++; - if (parser->index > sizeof(KEEP_ALIVE)-1 - || c != KEEP_ALIVE[parser->index]) { - h_state = h_matching_connection_token; - } else if (parser->index == sizeof(KEEP_ALIVE)-2) { - h_state = h_connection_keep_alive; - } - break; + /* Overflow? Test against a conservative limit for simplicity. */ + if ((ULLONG_MAX - 10) / 10 < parser->content_length) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } - /* looking for 'Connection: close' */ - case h_matching_connection_close: - parser->index++; - if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { - h_state = h_matching_connection_token; - } else if (parser->index == sizeof(CLOSE)-2) { - h_state = h_connection_close; - } - break; + parser->content_length = t; + break; + } - /* looking for 'Connection: upgrade' */ - case h_matching_connection_upgrade: - parser->index++; - if (parser->index > sizeof(UPGRADE) - 1 || - c != UPGRADE[parser->index]) { - h_state = h_matching_connection_token; - } else if (parser->index == sizeof(UPGRADE)-2) { - h_state = h_connection_upgrade; - } - break; + /* Transfer-Encoding: chunked */ + case h_matching_transfer_encoding_chunked: + parser->index++; + if (parser->index > sizeof(CHUNKED)-1 + || c != CHUNKED[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CHUNKED)-2) { + parser->header_state = h_transfer_encoding_chunked; + } + break; - case h_matching_connection_token: - if (ch == ',') { - h_state = h_matching_connection_token_start; - parser->index = 0; - } - break; + /* looking for 'Connection: keep-alive' */ + case h_matching_connection_keep_alive: + parser->index++; + if (parser->index > sizeof(KEEP_ALIVE)-1 + || c != KEEP_ALIVE[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(KEEP_ALIVE)-2) { + parser->header_state = h_connection_keep_alive; + } + break; - case h_transfer_encoding_chunked: - if (ch != ' ') h_state = h_general; - break; + /* looking for 'Connection: close' */ + case h_matching_connection_close: + parser->index++; + if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CLOSE)-2) { + parser->header_state = h_connection_close; + } + break; - case h_connection_keep_alive: - case h_connection_close: - case h_connection_upgrade: - if (ch == ',') { - if (h_state == h_connection_keep_alive) { - parser->flags |= F_CONNECTION_KEEP_ALIVE; - } else if (h_state == h_connection_close) { - parser->flags |= F_CONNECTION_CLOSE; - } else if (h_state == h_connection_upgrade) { - parser->flags |= F_CONNECTION_UPGRADE; - } - h_state = h_matching_connection_token_start; - parser->index = 0; - } else if (ch != ' ') { - h_state = h_matching_connection_token; - } - break; + case h_transfer_encoding_chunked: + case h_connection_keep_alive: + case h_connection_close: + if (ch != ' ') parser->header_state = h_general; + break; - default: - UPDATE_STATE(s_header_value); - h_state = h_general; - break; - } + default: + parser->state = s_header_value; + parser->header_state = h_general; + break; } - parser->header_state = h_state; - - COUNT_HEADER_SIZE(p - start); - - if (p == data + len) - --p; break; } @@ -1705,15 +1572,15 @@ size_t http_parser_execute (http_parser *parser, { STRICT_CHECK(ch != LF); - UPDATE_STATE(s_header_value_lws); + parser->state = s_header_value_lws; break; } case s_header_value_lws: { if (ch == ' ' || ch == '\t') { - UPDATE_STATE(s_header_value_start); - REEXECUTE(); + parser->state = s_header_value_start; + goto reexecute_byte; } /* finished the header */ @@ -1727,52 +1594,32 @@ size_t http_parser_execute (http_parser *parser, case h_transfer_encoding_chunked: parser->flags |= F_CHUNKED; break; - case h_connection_upgrade: - parser->flags |= F_CONNECTION_UPGRADE; - break; default: break; } - UPDATE_STATE(s_header_field_start); - REEXECUTE(); + parser->state = s_header_field_start; + goto reexecute_byte; } case s_header_value_discard_ws_almost_done: { STRICT_CHECK(ch != LF); - UPDATE_STATE(s_header_value_discard_lws); + parser->state = s_header_value_discard_lws; break; } case s_header_value_discard_lws: { if (ch == ' ' || ch == '\t') { - UPDATE_STATE(s_header_value_discard_ws); + parser->state = s_header_value_discard_ws; break; } else { - switch (parser->header_state) { - case h_connection_keep_alive: - parser->flags |= F_CONNECTION_KEEP_ALIVE; - break; - case h_connection_close: - parser->flags |= F_CONNECTION_CLOSE; - break; - case h_connection_upgrade: - parser->flags |= F_CONNECTION_UPGRADE; - break; - case h_transfer_encoding_chunked: - parser->flags |= F_CHUNKED; - break; - default: - break; - } - /* header value was empty */ MARK(header_value); - UPDATE_STATE(s_header_field_start); + parser->state = s_header_field_start; CALLBACK_DATA_NOADVANCE(header_value); - REEXECUTE(); + goto reexecute_byte; } } @@ -1782,18 +1629,16 @@ size_t http_parser_execute (http_parser *parser, if (parser->flags & F_TRAILING) { /* End of a chunked request */ - UPDATE_STATE(NEW_MESSAGE()); + parser->state = NEW_MESSAGE(); CALLBACK_NOTIFY(message_complete); break; } - UPDATE_STATE(s_headers_done); + parser->state = s_headers_done; /* Set this here so that on_headers_complete() callbacks can see it */ parser->upgrade = - ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) == - (F_UPGRADE | F_CONNECTION_UPGRADE) || - parser->method == HTTP_CONNECT); + (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT); /* Here we call the headers_complete callback. This is somewhat * different than other callbacks because if the user returns 1, we @@ -1815,15 +1660,15 @@ size_t http_parser_execute (http_parser *parser, default: SET_ERRNO(HPE_CB_headers_complete); - RETURN(p - data); /* Error */ + return p - data; /* Error */ } } if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { - RETURN(p - data); + return p - data; } - REEXECUTE(); + goto reexecute_byte; } case s_headers_done: @@ -1834,34 +1679,34 @@ size_t http_parser_execute (http_parser *parser, /* Exit, the rest of the connect is in a different protocol. */ if (parser->upgrade) { - UPDATE_STATE(NEW_MESSAGE()); + parser->state = NEW_MESSAGE(); CALLBACK_NOTIFY(message_complete); - RETURN((p - data) + 1); + return (p - data) + 1; } if (parser->flags & F_SKIPBODY) { - UPDATE_STATE(NEW_MESSAGE()); + parser->state = NEW_MESSAGE(); CALLBACK_NOTIFY(message_complete); } else if (parser->flags & F_CHUNKED) { /* chunked encoding - ignore Content-Length header */ - UPDATE_STATE(s_chunk_size_start); + parser->state = s_chunk_size_start; } else { if (parser->content_length == 0) { /* Content-Length header given but zero: Content-Length: 0\r\n */ - UPDATE_STATE(NEW_MESSAGE()); + parser->state = NEW_MESSAGE(); CALLBACK_NOTIFY(message_complete); } else if (parser->content_length != ULLONG_MAX) { /* Content-Length header given and non-zero */ - UPDATE_STATE(s_body_identity); + parser->state = s_body_identity; } else { if (parser->type == HTTP_REQUEST || !http_message_needs_eof(parser)) { /* Assume content-length 0 - read the next */ - UPDATE_STATE(NEW_MESSAGE()); + parser->state = NEW_MESSAGE(); CALLBACK_NOTIFY(message_complete); } else { /* Read body until EOF */ - UPDATE_STATE(s_body_identity_eof); + parser->state = s_body_identity_eof; } } } @@ -1887,7 +1732,7 @@ size_t http_parser_execute (http_parser *parser, p += to_read - 1; if (parser->content_length == 0) { - UPDATE_STATE(s_message_done); + parser->state = s_message_done; /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. * @@ -1899,7 +1744,7 @@ size_t http_parser_execute (http_parser *parser, * important for applications, but let's keep it for now. */ CALLBACK_DATA_(body, p - body_mark + 1, p - data); - REEXECUTE(); + goto reexecute_byte; } break; @@ -1913,7 +1758,7 @@ size_t http_parser_execute (http_parser *parser, break; case s_message_done: - UPDATE_STATE(NEW_MESSAGE()); + parser->state = NEW_MESSAGE(); CALLBACK_NOTIFY(message_complete); break; @@ -1923,13 +1768,13 @@ size_t http_parser_execute (http_parser *parser, assert(parser->flags & F_CHUNKED); unhex_val = unhex[(unsigned char)ch]; - if (UNLIKELY(unhex_val == -1)) { + if (unhex_val == -1) { SET_ERRNO(HPE_INVALID_CHUNK_SIZE); goto error; } parser->content_length = unhex_val; - UPDATE_STATE(s_chunk_size); + parser->state = s_chunk_size; break; } @@ -1940,7 +1785,7 @@ size_t http_parser_execute (http_parser *parser, assert(parser->flags & F_CHUNKED); if (ch == CR) { - UPDATE_STATE(s_chunk_size_almost_done); + parser->state = s_chunk_size_almost_done; break; } @@ -1948,7 +1793,7 @@ size_t http_parser_execute (http_parser *parser, if (unhex_val == -1) { if (ch == ';' || ch == ' ') { - UPDATE_STATE(s_chunk_parameters); + parser->state = s_chunk_parameters; break; } @@ -1961,7 +1806,7 @@ size_t http_parser_execute (http_parser *parser, t += unhex_val; /* Overflow? Test against a conservative limit for simplicity. */ - if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) { + if ((ULLONG_MAX - 16) / 16 < parser->content_length) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); goto error; } @@ -1975,7 +1820,7 @@ size_t http_parser_execute (http_parser *parser, assert(parser->flags & F_CHUNKED); /* just ignore this shit. TODO check for overflow */ if (ch == CR) { - UPDATE_STATE(s_chunk_size_almost_done); + parser->state = s_chunk_size_almost_done; break; } break; @@ -1990,9 +1835,9 @@ size_t http_parser_execute (http_parser *parser, if (parser->content_length == 0) { parser->flags |= F_TRAILING; - UPDATE_STATE(s_header_field_start); + parser->state = s_header_field_start; } else { - UPDATE_STATE(s_chunk_data); + parser->state = s_chunk_data; } break; } @@ -2014,7 +1859,7 @@ size_t http_parser_execute (http_parser *parser, p += to_read - 1; if (parser->content_length == 0) { - UPDATE_STATE(s_chunk_data_almost_done); + parser->state = s_chunk_data_almost_done; } break; @@ -2024,7 +1869,7 @@ size_t http_parser_execute (http_parser *parser, assert(parser->flags & F_CHUNKED); assert(parser->content_length == 0); STRICT_CHECK(ch != CR); - UPDATE_STATE(s_chunk_data_done); + parser->state = s_chunk_data_done; CALLBACK_DATA(body); break; @@ -2032,7 +1877,7 @@ size_t http_parser_execute (http_parser *parser, assert(parser->flags & F_CHUNKED); STRICT_CHECK(ch != LF); parser->nread = 0; - UPDATE_STATE(s_chunk_size_start); + parser->state = s_chunk_size_start; break; default: @@ -2064,14 +1909,14 @@ size_t http_parser_execute (http_parser *parser, CALLBACK_DATA_NOADVANCE(body); CALLBACK_DATA_NOADVANCE(status); - RETURN(len); + return len; error: if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { SET_ERRNO(HPE_UNKNOWN); } - RETURN(p - data); + return (p - data); } @@ -2297,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); |