Update http-parser to 2.6.1

Includes parsing improvements to ensure closer HTTP spec conformance

Adaption of nodejs/node@4f4c8ab3b4cea246d2ece6ca006fe280241d84a4:

    Author: James M Snell <jasnell@gmail.com>
    Date:   Wed Feb 3 17:28:48 2016 -0800

    deps: update http-parser to version 2.6.1

    includes parsing improvements to ensure closer HTTP spec conformance

    PR-URL: https://github.com/nodejs/node-private/pull/26
    Reviewed-By: Rod Vagg <r@va.gg>
    Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
    Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>

PR-URL: https://github.com/nodejs/http-parser/pull/279
Reviewed-By: James M Snell <jasnell@gmail.com>
make-http-max-header-size-gyp-configurable
James M Snell 9 years ago
parent 4e382f96e6
commit e2e467b912

@ -22,14 +22,14 @@ PLATFORM ?= $(shell sh -c 'uname -s | tr "[A-Z]" "[a-z]"')
HELPER ?= HELPER ?=
BINEXT ?= BINEXT ?=
ifeq (darwin,$(PLATFORM)) ifeq (darwin,$(PLATFORM))
SONAME ?= libhttp_parser.2.6.0.dylib SONAME ?= libhttp_parser.2.6.1.dylib
SOEXT ?= dylib SOEXT ?= dylib
else ifeq (wine,$(PLATFORM)) else ifeq (wine,$(PLATFORM))
CC = winegcc CC = winegcc
BINEXT = .exe.so BINEXT = .exe.so
HELPER = wine HELPER = wine
else else
SONAME ?= libhttp_parser.so.2.6.0 SONAME ?= libhttp_parser.so.2.6.1
SOEXT ?= so SOEXT ?= so
endif endif

@ -435,6 +435,12 @@ enum http_host_state
(IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
#endif #endif
/**
* Verify that a char is a valid visible (printable) US-ASCII
* character or %x80-FF
**/
#define IS_HEADER_CHAR(ch) \
(ch == CR || ch == LF || ch == 9 || (ch > 31 && ch != 127))
#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) #define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
@ -639,6 +645,7 @@ size_t http_parser_execute (http_parser *parser,
const char *body_mark = 0; const char *body_mark = 0;
const char *status_mark = 0; const char *status_mark = 0;
enum state p_state = (enum state) parser->state; enum state p_state = (enum state) parser->state;
const unsigned int lenient = parser->lenient_http_headers;
/* We're in an error state. Don't bother doing anything. */ /* We're in an error state. Don't bother doing anything. */
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
@ -1408,7 +1415,12 @@ reexecute:
|| c != CONTENT_LENGTH[parser->index]) { || c != CONTENT_LENGTH[parser->index]) {
parser->header_state = h_general; parser->header_state = h_general;
} else if (parser->index == sizeof(CONTENT_LENGTH)-2) { } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
if (parser->flags & F_CONTENTLENGTH) {
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
goto error;
}
parser->header_state = h_content_length; parser->header_state = h_content_length;
parser->flags |= F_CONTENTLENGTH;
} }
break; break;
@ -1560,6 +1572,11 @@ reexecute:
REEXECUTE(); REEXECUTE();
} }
if (!lenient && !IS_HEADER_CHAR(ch)) {
SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
goto error;
}
c = LOWER(ch); c = LOWER(ch);
switch (h_state) { switch (h_state) {
@ -1727,7 +1744,10 @@ reexecute:
case s_header_almost_done: case s_header_almost_done:
{ {
STRICT_CHECK(ch != LF); if (UNLIKELY(ch != LF)) {
SET_ERRNO(HPE_LF_EXPECTED);
goto error;
}
UPDATE_STATE(s_header_value_lws); UPDATE_STATE(s_header_value_lws);
break; break;
@ -1811,6 +1831,14 @@ reexecute:
REEXECUTE(); REEXECUTE();
} }
/* Cannot use chunked encoding and a content-length header together
per the HTTP specification. */
if ((parser->flags & F_CHUNKED) &&
(parser->flags & F_CONTENTLENGTH)) {
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
goto error;
}
UPDATE_STATE(s_headers_done); UPDATE_STATE(s_headers_done);
/* Set this here so that on_headers_complete() callbacks can see it */ /* Set this here so that on_headers_complete() callbacks can see it */

@ -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 6 #define HTTP_PARSER_VERSION_MINOR 6
#define HTTP_PARSER_VERSION_PATCH 0 #define HTTP_PARSER_VERSION_PATCH 1
#include <sys/types.h> #include <sys/types.h>
#if defined(_WIN32) && !defined(__MINGW32__) && \ #if defined(_WIN32) && !defined(__MINGW32__) && \
@ -148,6 +148,7 @@ enum flags
, F_TRAILING = 1 << 4 , F_TRAILING = 1 << 4
, F_UPGRADE = 1 << 5 , F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6 , F_SKIPBODY = 1 << 6
, F_CONTENTLENGTH = 1 << 7
}; };
@ -190,6 +191,8 @@ enum flags
XX(INVALID_HEADER_TOKEN, "invalid character in header") \ XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \ XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \ "invalid character in content-length header") \
XX(UNEXPECTED_CONTENT_LENGTH, \
"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_CONSTANT, "invalid constant string") \ XX(INVALID_CONSTANT, "invalid constant string") \
@ -214,10 +217,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 : 7; /* F_* values from 'flags' enum; semi-public */ 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 : 8; /* enum header_state from http_parser.c */ unsigned int header_state : 7; /* enum header_state from http_parser.c */
unsigned int index : 8; /* index into current matcher */ unsigned int index : 7; /* index into current matcher */
unsigned int lenient_http_headers : 1;
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) */

161
test.c

@ -3270,6 +3270,155 @@ test_simple (const char *buf, enum http_errno err_expected)
} }
} }
void
test_invalid_header_content (int req, const char* str)
{
http_parser parser;
http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
size_t parsed;
const char *buf;
buf = req ?
"GET / HTTP/1.1\r\n" :
"HTTP/1.1 200 OK\r\n";
parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
assert(parsed == strlen(buf));
buf = str;
size_t buflen = strlen(buf);
parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
if (parsed != buflen) {
assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_HEADER_TOKEN);
return;
}
fprintf(stderr,
"\n*** Error expected but none in invalid header content test ***\n");
abort();
}
void
test_invalid_header_field_content_error (int req)
{
test_invalid_header_content(req, "Foo: F\01ailure");
test_invalid_header_content(req, "Foo: B\02ar");
}
void
test_invalid_header_field (int req, const char* str)
{
http_parser parser;
http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
size_t parsed;
const char *buf;
buf = req ?
"GET / HTTP/1.1\r\n" :
"HTTP/1.1 200 OK\r\n";
parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
assert(parsed == strlen(buf));
buf = str;
size_t buflen = strlen(buf);
parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
if (parsed != buflen) {
assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_HEADER_TOKEN);
return;
}
fprintf(stderr,
"\n*** Error expected but none in invalid header token test ***\n");
abort();
}
void
test_invalid_header_field_token_error (int req)
{
test_invalid_header_field(req, "Fo@: Failure");
test_invalid_header_field(req, "Foo\01\test: Bar");
}
void
test_double_content_length_error (int req)
{
http_parser parser;
http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
size_t parsed;
const char *buf;
buf = req ?
"GET / HTTP/1.1\r\n" :
"HTTP/1.1 200 OK\r\n";
parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
assert(parsed == strlen(buf));
buf = "Content-Length: 0\r\nContent-Length: 1\r\n\r\n";
size_t buflen = strlen(buf);
parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
if (parsed != buflen) {
assert(HTTP_PARSER_ERRNO(&parser) == HPE_MULTIPLE_CONTENT_LENGTH);
return;
}
fprintf(stderr,
"\n*** Error expected but none in double content-length test ***\n");
abort();
}
void
test_chunked_content_length_error (int req)
{
http_parser parser;
http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
size_t parsed;
const char *buf;
buf = req ?
"GET / HTTP/1.1\r\n" :
"HTTP/1.1 200 OK\r\n";
parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
assert(parsed == strlen(buf));
buf = "Transfer-Encoding: chunked\r\nContent-Length: 1\r\n\r\n";
size_t buflen = strlen(buf);
parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
if (parsed != buflen) {
assert(HTTP_PARSER_ERRNO(&parser) == HPE_CHUNKED_WITH_CONTENT_LENGTH);
return;
}
fprintf(stderr,
"\n*** Error expected but none in chunked content-length test ***\n");
abort();
}
void
test_header_cr_no_lf_error (int req)
{
http_parser parser;
http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
size_t parsed;
const char *buf;
buf = req ?
"GET / HTTP/1.1\r\n" :
"HTTP/1.1 200 OK\r\n";
parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
assert(parsed == strlen(buf));
buf = "Foo: 1\rBar: 1\r\n\r\n";
size_t buflen = strlen(buf);
parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
if (parsed != buflen) {
assert(HTTP_PARSER_ERRNO(&parser) == HPE_LF_EXPECTED);
return;
}
fprintf(stderr,
"\n*** Error expected but none in header whitespace test ***\n");
abort();
}
void void
test_header_overflow_error (int req) test_header_overflow_error (int req)
{ {
@ -3696,6 +3845,18 @@ main (void)
test_header_content_length_overflow_error(); test_header_content_length_overflow_error();
test_chunk_content_length_overflow_error(); test_chunk_content_length_overflow_error();
//// HEADER FIELD CONDITIONS
test_double_content_length_error(HTTP_REQUEST);
test_chunked_content_length_error(HTTP_REQUEST);
test_header_cr_no_lf_error(HTTP_REQUEST);
test_invalid_header_field_token_error(HTTP_REQUEST);
test_invalid_header_field_content_error(HTTP_REQUEST);
test_double_content_length_error(HTTP_RESPONSE);
test_chunked_content_length_error(HTTP_RESPONSE);
test_header_cr_no_lf_error(HTTP_RESPONSE);
test_invalid_header_field_token_error(HTTP_RESPONSE);
test_invalid_header_field_content_error(HTTP_RESPONSE);
//// RESPONSES //// RESPONSES
for (i = 0; i < response_count; i++) { for (i = 0; i < response_count; i++) {

Loading…
Cancel
Save