http-parser updated to latest version.

This commit is contained in:
XMRig 2020-03-04 18:30:53 +07:00
parent c80ef54b60
commit b0dda2b5b3
No known key found for this signature in database
GPG key ID: 446A53638BE94409
2 changed files with 101 additions and 34 deletions

View file

@ -381,7 +381,10 @@ enum header_states
, h_transfer_encoding , h_transfer_encoding
, h_upgrade , h_upgrade
, h_matching_transfer_encoding_token_start
, h_matching_transfer_encoding_chunked , h_matching_transfer_encoding_chunked
, h_matching_transfer_encoding_token
, h_matching_connection_token_start , h_matching_connection_token_start
, h_matching_connection_keep_alive , h_matching_connection_keep_alive
, h_matching_connection_close , h_matching_connection_close
@ -1257,9 +1260,9 @@ reexecute:
switch (parser->header_state) { switch (parser->header_state) {
case h_general: { case h_general: {
size_t limit = data + len - p; size_t left = data + len - p;
limit = MIN(limit, max_header_size); const char* pe = p + MIN(left, max_header_size);
while (p+1 < data + limit && TOKEN(p[1])) { while (p+1 < pe && TOKEN(p[1])) {
p++; p++;
} }
break; break;
@ -1335,6 +1338,7 @@ reexecute:
parser->header_state = h_general; parser->header_state = h_general;
} else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
parser->header_state = h_transfer_encoding; parser->header_state = h_transfer_encoding;
parser->flags |= F_TRANSFER_ENCODING;
} }
break; break;
@ -1416,10 +1420,14 @@ reexecute:
if ('c' == c) { if ('c' == c) {
parser->header_state = h_matching_transfer_encoding_chunked; parser->header_state = h_matching_transfer_encoding_chunked;
} else { } else {
parser->header_state = h_general; parser->header_state = h_matching_transfer_encoding_token;
} }
break; break;
/* Multi-value `Transfer-Encoding` header */
case h_matching_transfer_encoding_token_start:
break;
case h_content_length: case h_content_length:
if (UNLIKELY(!IS_NUM(ch))) { if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
@ -1496,28 +1504,25 @@ reexecute:
switch (h_state) { switch (h_state) {
case h_general: case h_general:
{ {
const char* p_cr; size_t left = data + len - p;
const char* p_lf; const char* pe = p + MIN(left, max_header_size);
size_t limit = data + len - p;
limit = MIN(limit, max_header_size); for (; p != pe; p++) {
ch = *p;
p_cr = (const char*) memchr(p, CR, limit); if (ch == CR || ch == LF) {
p_lf = (const char*) memchr(p, LF, limit); --p;
if (p_cr != NULL) { break;
if (p_lf != NULL && p_cr >= p_lf) }
p = p_lf; if (!lenient && !IS_HEADER_CHAR(ch)) {
else SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
p = p_cr; goto error;
} else if (UNLIKELY(p_lf != NULL)) { }
p = p_lf; }
} else { if (p == data + len)
p = data + len; --p;
break;
} }
--p;
break;
}
case h_connection: case h_connection:
case h_transfer_encoding: case h_transfer_encoding:
@ -1566,16 +1571,41 @@ reexecute:
goto error; goto error;
/* Transfer-Encoding: chunked */ /* Transfer-Encoding: chunked */
case h_matching_transfer_encoding_token_start:
/* looking for 'Transfer-Encoding: chunked' */
if ('c' == c) {
h_state = h_matching_transfer_encoding_chunked;
} else if (STRICT_TOKEN(c)) {
/* TODO(indutny): similar code below does this, but why?
* At the very least it seems to be inconsistent given that
* h_matching_transfer_encoding_token does not check for
* `STRICT_TOKEN`
*/
h_state = h_matching_transfer_encoding_token;
} else if (c == ' ' || c == '\t') {
/* Skip lws */
} else {
h_state = h_general;
}
break;
case h_matching_transfer_encoding_chunked: case h_matching_transfer_encoding_chunked:
parser->index++; parser->index++;
if (parser->index > sizeof(CHUNKED)-1 if (parser->index > sizeof(CHUNKED)-1
|| c != CHUNKED[parser->index]) { || c != CHUNKED[parser->index]) {
h_state = h_general; h_state = h_matching_transfer_encoding_token;
} else if (parser->index == sizeof(CHUNKED)-2) { } else if (parser->index == sizeof(CHUNKED)-2) {
h_state = h_transfer_encoding_chunked; h_state = h_transfer_encoding_chunked;
} }
break; break;
case h_matching_transfer_encoding_token:
if (ch == ',') {
h_state = h_matching_transfer_encoding_token_start;
parser->index = 0;
}
break;
case h_matching_connection_token_start: case h_matching_connection_token_start:
/* looking for 'Connection: keep-alive' */ /* looking for 'Connection: keep-alive' */
if (c == 'k') { if (c == 'k') {
@ -1634,7 +1664,7 @@ reexecute:
break; break;
case h_transfer_encoding_chunked: case h_transfer_encoding_chunked:
if (ch != ' ') h_state = h_general; if (ch != ' ') h_state = h_matching_transfer_encoding_token;
break; break;
case h_connection_keep_alive: case h_connection_keep_alive:
@ -1768,12 +1798,17 @@ reexecute:
REEXECUTE(); REEXECUTE();
} }
/* Cannot use chunked encoding and a content-length header together /* Cannot us transfer-encoding and a content-length header together
per the HTTP specification. */ per the HTTP specification. (RFC 7230 Section 3.3.3) */
if ((parser->flags & F_CHUNKED) && if ((parser->flags & F_TRANSFER_ENCODING) &&
(parser->flags & F_CONTENTLENGTH)) { (parser->flags & F_CONTENTLENGTH)) {
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); /* Allow it for lenient parsing as long as `Transfer-Encoding` is
goto error; * not `chunked`
*/
if (!lenient || (parser->flags & F_CHUNKED)) {
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
goto error;
}
} }
UPDATE_STATE(s_headers_done); UPDATE_STATE(s_headers_done);
@ -1848,8 +1883,31 @@ reexecute:
UPDATE_STATE(NEW_MESSAGE()); UPDATE_STATE(NEW_MESSAGE());
CALLBACK_NOTIFY(message_complete); CALLBACK_NOTIFY(message_complete);
} else if (parser->flags & F_CHUNKED) { } else if (parser->flags & F_CHUNKED) {
/* chunked encoding - ignore Content-Length header */ /* chunked encoding - ignore Content-Length header,
* prepare for a chunk */
UPDATE_STATE(s_chunk_size_start); UPDATE_STATE(s_chunk_size_start);
} else if (parser->flags & F_TRANSFER_ENCODING) {
if (parser->type == HTTP_REQUEST && !lenient) {
/* RFC 7230 3.3.3 */
/* If a Transfer-Encoding header field
* is present in a request and the chunked transfer coding is not
* the final encoding, the message body length cannot be determined
* reliably; the server MUST respond with the 400 (Bad Request)
* status code and then close the connection.
*/
SET_ERRNO(HPE_INVALID_TRANSFER_ENCODING);
RETURN(p - data); /* Error */
} else {
/* RFC 7230 3.3.3 */
/* If a Transfer-Encoding header field is present in a response and
* the chunked transfer coding is not the final encoding, the
* message body length is determined by reading the connection until
* it is closed by the server.
*/
UPDATE_STATE(s_body_identity_eof);
}
} else { } else {
if (parser->content_length == 0) { if (parser->content_length == 0) {
/* Content-Length header given but zero: Content-Length: 0\r\n */ /* Content-Length header given but zero: Content-Length: 0\r\n */
@ -2103,6 +2161,12 @@ http_message_needs_eof (const http_parser *parser)
return 0; return 0;
} }
/* RFC 7230 3.3.3, see `s_headers_almost_done` */
if ((parser->flags & F_TRANSFER_ENCODING) &&
(parser->flags & F_CHUNKED) == 0) {
return 1;
}
if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
return 0; return 0;
} }

View file

@ -27,7 +27,7 @@ extern "C" {
/* Also update SONAME in the Makefile whenever you change these. */ /* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2 #define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 9 #define HTTP_PARSER_VERSION_MINOR 9
#define HTTP_PARSER_VERSION_PATCH 0 #define HTTP_PARSER_VERSION_PATCH 3
#include <stddef.h> #include <stddef.h>
#if defined(_WIN32) && !defined(__MINGW32__) && \ #if defined(_WIN32) && !defined(__MINGW32__) && \
@ -225,6 +225,7 @@ enum flags
, F_UPGRADE = 1 << 5 , F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6 , F_SKIPBODY = 1 << 6
, F_CONTENTLENGTH = 1 << 7 , F_CONTENTLENGTH = 1 << 7
, F_TRANSFER_ENCODING = 1 << 8
}; };
@ -271,6 +272,8 @@ enum flags
"unexpected content-length header") \ "unexpected content-length header") \
XX(INVALID_CHUNK_SIZE, \ XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \ "invalid character in chunk size header") \
XX(INVALID_TRANSFER_ENCODING, \
"request has invalid transfer-encoding") \
XX(INVALID_CONSTANT, "invalid constant string") \ XX(INVALID_CONSTANT, "invalid constant string") \
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
XX(STRICT, "strict mode assertion failed") \ XX(STRICT, "strict mode assertion failed") \
@ -293,11 +296,11 @@ enum http_errno {
struct http_parser { struct http_parser {
/** PRIVATE **/ /** PRIVATE **/
unsigned int type : 2; /* enum http_parser_type */ unsigned int type : 2; /* enum http_parser_type */
unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
unsigned int state : 7; /* enum state from http_parser.c */ unsigned int state : 7; /* enum state from http_parser.c */
unsigned int header_state : 7; /* enum header_state from http_parser.c */ unsigned int header_state : 7; /* enum header_state from http_parser.c */
unsigned int index : 7; /* index into current matcher */ unsigned int index : 7; /* index into current matcher */
unsigned int lenient_http_headers : 1; unsigned int lenient_http_headers : 1;
unsigned int flags : 16; /* F_* values from 'flags' enum; semi-public */
uint32_t nread; /* # bytes read in various scenarios */ uint32_t nread; /* # bytes read in various scenarios */
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */