Skip to content
This repository was archived by the owner on Nov 6, 2022. It is now read-only.

Commit e2e467b

Browse files
committed
Update http-parser to 2.6.1
Includes parsing improvements to ensure closer HTTP spec conformance Adaption of nodejs/node@4f4c8ab: Author: James M Snell <[email protected]> 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: nodejs-private/node-private#26 Reviewed-By: Rod Vagg <[email protected]> Reviewed-By: Сковорода Никита Андреевич <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> PR-URL: #279 Reviewed-By: James M Snell <[email protected]>
1 parent 4e382f9 commit e2e467b

File tree

4 files changed

+201
-8
lines changed

4 files changed

+201
-8
lines changed

Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ PLATFORM ?= $(shell sh -c 'uname -s | tr "[A-Z]" "[a-z]"')
2222
HELPER ?=
2323
BINEXT ?=
2424
ifeq (darwin,$(PLATFORM))
25-
SONAME ?= libhttp_parser.2.6.0.dylib
25+
SONAME ?= libhttp_parser.2.6.1.dylib
2626
SOEXT ?= dylib
2727
else ifeq (wine,$(PLATFORM))
2828
CC = winegcc
2929
BINEXT = .exe.so
3030
HELPER = wine
3131
else
32-
SONAME ?= libhttp_parser.so.2.6.0
32+
SONAME ?= libhttp_parser.so.2.6.1
3333
SOEXT ?= so
3434
endif
3535

http_parser.c

+29-1
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,12 @@ enum http_host_state
435435
(IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
436436
#endif
437437

438+
/**
439+
* Verify that a char is a valid visible (printable) US-ASCII
440+
* character or %x80-FF
441+
**/
442+
#define IS_HEADER_CHAR(ch) \
443+
(ch == CR || ch == LF || ch == 9 || (ch > 31 && ch != 127))
438444

439445
#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
440446

@@ -639,6 +645,7 @@ size_t http_parser_execute (http_parser *parser,
639645
const char *body_mark = 0;
640646
const char *status_mark = 0;
641647
enum state p_state = (enum state) parser->state;
648+
const unsigned int lenient = parser->lenient_http_headers;
642649

643650
/* We're in an error state. Don't bother doing anything. */
644651
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
@@ -1408,7 +1415,12 @@ size_t http_parser_execute (http_parser *parser,
14081415
|| c != CONTENT_LENGTH[parser->index]) {
14091416
parser->header_state = h_general;
14101417
} else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
1418+
if (parser->flags & F_CONTENTLENGTH) {
1419+
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
1420+
goto error;
1421+
}
14111422
parser->header_state = h_content_length;
1423+
parser->flags |= F_CONTENTLENGTH;
14121424
}
14131425
break;
14141426

@@ -1560,6 +1572,11 @@ size_t http_parser_execute (http_parser *parser,
15601572
REEXECUTE();
15611573
}
15621574

1575+
if (!lenient && !IS_HEADER_CHAR(ch)) {
1576+
SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
1577+
goto error;
1578+
}
1579+
15631580
c = LOWER(ch);
15641581

15651582
switch (h_state) {
@@ -1727,7 +1744,10 @@ size_t http_parser_execute (http_parser *parser,
17271744

17281745
case s_header_almost_done:
17291746
{
1730-
STRICT_CHECK(ch != LF);
1747+
if (UNLIKELY(ch != LF)) {
1748+
SET_ERRNO(HPE_LF_EXPECTED);
1749+
goto error;
1750+
}
17311751

17321752
UPDATE_STATE(s_header_value_lws);
17331753
break;
@@ -1811,6 +1831,14 @@ size_t http_parser_execute (http_parser *parser,
18111831
REEXECUTE();
18121832
}
18131833

1834+
/* Cannot use chunked encoding and a content-length header together
1835+
per the HTTP specification. */
1836+
if ((parser->flags & F_CHUNKED) &&
1837+
(parser->flags & F_CONTENTLENGTH)) {
1838+
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
1839+
goto error;
1840+
}
1841+
18141842
UPDATE_STATE(s_headers_done);
18151843

18161844
/* Set this here so that on_headers_complete() callbacks can see it */

http_parser.h

+8-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ extern "C" {
2727
/* Also update SONAME in the Makefile whenever you change these. */
2828
#define HTTP_PARSER_VERSION_MAJOR 2
2929
#define HTTP_PARSER_VERSION_MINOR 6
30-
#define HTTP_PARSER_VERSION_PATCH 0
30+
#define HTTP_PARSER_VERSION_PATCH 1
3131

3232
#include <sys/types.h>
3333
#if defined(_WIN32) && !defined(__MINGW32__) && \
@@ -148,6 +148,7 @@ enum flags
148148
, F_TRAILING = 1 << 4
149149
, F_UPGRADE = 1 << 5
150150
, F_SKIPBODY = 1 << 6
151+
, F_CONTENTLENGTH = 1 << 7
151152
};
152153

153154

@@ -190,6 +191,8 @@ enum flags
190191
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
191192
XX(INVALID_CONTENT_LENGTH, \
192193
"invalid character in content-length header") \
194+
XX(UNEXPECTED_CONTENT_LENGTH, \
195+
"unexpected content-length header") \
193196
XX(INVALID_CHUNK_SIZE, \
194197
"invalid character in chunk size header") \
195198
XX(INVALID_CONSTANT, "invalid constant string") \
@@ -214,10 +217,11 @@ enum http_errno {
214217
struct http_parser {
215218
/** PRIVATE **/
216219
unsigned int type : 2; /* enum http_parser_type */
217-
unsigned int flags : 7; /* F_* values from 'flags' enum; semi-public */
220+
unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
218221
unsigned int state : 7; /* enum state from http_parser.c */
219-
unsigned int header_state : 8; /* enum header_state from http_parser.c */
220-
unsigned int index : 8; /* index into current matcher */
222+
unsigned int header_state : 7; /* enum header_state from http_parser.c */
223+
unsigned int index : 7; /* index into current matcher */
224+
unsigned int lenient_http_headers : 1;
221225

222226
uint32_t nread; /* # bytes read in various scenarios */
223227
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */

test.c

+162-1
Original file line numberDiff line numberDiff line change
@@ -2444,7 +2444,7 @@ upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
24442444
va_list ap;
24452445
size_t i;
24462446
size_t off = 0;
2447-
2447+
24482448
va_start(ap, nmsgs);
24492449

24502450
for (i = 0; i < nmsgs; i++) {
@@ -3270,6 +3270,155 @@ test_simple (const char *buf, enum http_errno err_expected)
32703270
}
32713271
}
32723272

3273+
void
3274+
test_invalid_header_content (int req, const char* str)
3275+
{
3276+
http_parser parser;
3277+
http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3278+
size_t parsed;
3279+
const char *buf;
3280+
buf = req ?
3281+
"GET / HTTP/1.1\r\n" :
3282+
"HTTP/1.1 200 OK\r\n";
3283+
parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3284+
assert(parsed == strlen(buf));
3285+
3286+
buf = str;
3287+
size_t buflen = strlen(buf);
3288+
3289+
parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3290+
if (parsed != buflen) {
3291+
assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_HEADER_TOKEN);
3292+
return;
3293+
}
3294+
3295+
fprintf(stderr,
3296+
"\n*** Error expected but none in invalid header content test ***\n");
3297+
abort();
3298+
}
3299+
3300+
void
3301+
test_invalid_header_field_content_error (int req)
3302+
{
3303+
test_invalid_header_content(req, "Foo: F\01ailure");
3304+
test_invalid_header_content(req, "Foo: B\02ar");
3305+
}
3306+
3307+
void
3308+
test_invalid_header_field (int req, const char* str)
3309+
{
3310+
http_parser parser;
3311+
http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3312+
size_t parsed;
3313+
const char *buf;
3314+
buf = req ?
3315+
"GET / HTTP/1.1\r\n" :
3316+
"HTTP/1.1 200 OK\r\n";
3317+
parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3318+
assert(parsed == strlen(buf));
3319+
3320+
buf = str;
3321+
size_t buflen = strlen(buf);
3322+
3323+
parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3324+
if (parsed != buflen) {
3325+
assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_HEADER_TOKEN);
3326+
return;
3327+
}
3328+
3329+
fprintf(stderr,
3330+
"\n*** Error expected but none in invalid header token test ***\n");
3331+
abort();
3332+
}
3333+
3334+
void
3335+
test_invalid_header_field_token_error (int req)
3336+
{
3337+
test_invalid_header_field(req, "Fo@: Failure");
3338+
test_invalid_header_field(req, "Foo\01\test: Bar");
3339+
}
3340+
3341+
void
3342+
test_double_content_length_error (int req)
3343+
{
3344+
http_parser parser;
3345+
http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3346+
size_t parsed;
3347+
const char *buf;
3348+
buf = req ?
3349+
"GET / HTTP/1.1\r\n" :
3350+
"HTTP/1.1 200 OK\r\n";
3351+
parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3352+
assert(parsed == strlen(buf));
3353+
3354+
buf = "Content-Length: 0\r\nContent-Length: 1\r\n\r\n";
3355+
size_t buflen = strlen(buf);
3356+
3357+
parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3358+
if (parsed != buflen) {
3359+
assert(HTTP_PARSER_ERRNO(&parser) == HPE_MULTIPLE_CONTENT_LENGTH);
3360+
return;
3361+
}
3362+
3363+
fprintf(stderr,
3364+
"\n*** Error expected but none in double content-length test ***\n");
3365+
abort();
3366+
}
3367+
3368+
void
3369+
test_chunked_content_length_error (int req)
3370+
{
3371+
http_parser parser;
3372+
http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3373+
size_t parsed;
3374+
const char *buf;
3375+
buf = req ?
3376+
"GET / HTTP/1.1\r\n" :
3377+
"HTTP/1.1 200 OK\r\n";
3378+
parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3379+
assert(parsed == strlen(buf));
3380+
3381+
buf = "Transfer-Encoding: chunked\r\nContent-Length: 1\r\n\r\n";
3382+
size_t buflen = strlen(buf);
3383+
3384+
parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3385+
if (parsed != buflen) {
3386+
assert(HTTP_PARSER_ERRNO(&parser) == HPE_CHUNKED_WITH_CONTENT_LENGTH);
3387+
return;
3388+
}
3389+
3390+
fprintf(stderr,
3391+
"\n*** Error expected but none in chunked content-length test ***\n");
3392+
abort();
3393+
}
3394+
3395+
void
3396+
test_header_cr_no_lf_error (int req)
3397+
{
3398+
http_parser parser;
3399+
http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
3400+
size_t parsed;
3401+
const char *buf;
3402+
buf = req ?
3403+
"GET / HTTP/1.1\r\n" :
3404+
"HTTP/1.1 200 OK\r\n";
3405+
parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
3406+
assert(parsed == strlen(buf));
3407+
3408+
buf = "Foo: 1\rBar: 1\r\n\r\n";
3409+
size_t buflen = strlen(buf);
3410+
3411+
parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
3412+
if (parsed != buflen) {
3413+
assert(HTTP_PARSER_ERRNO(&parser) == HPE_LF_EXPECTED);
3414+
return;
3415+
}
3416+
3417+
fprintf(stderr,
3418+
"\n*** Error expected but none in header whitespace test ***\n");
3419+
abort();
3420+
}
3421+
32733422
void
32743423
test_header_overflow_error (int req)
32753424
{
@@ -3696,6 +3845,18 @@ main (void)
36963845
test_header_content_length_overflow_error();
36973846
test_chunk_content_length_overflow_error();
36983847

3848+
//// HEADER FIELD CONDITIONS
3849+
test_double_content_length_error(HTTP_REQUEST);
3850+
test_chunked_content_length_error(HTTP_REQUEST);
3851+
test_header_cr_no_lf_error(HTTP_REQUEST);
3852+
test_invalid_header_field_token_error(HTTP_REQUEST);
3853+
test_invalid_header_field_content_error(HTTP_REQUEST);
3854+
test_double_content_length_error(HTTP_RESPONSE);
3855+
test_chunked_content_length_error(HTTP_RESPONSE);
3856+
test_header_cr_no_lf_error(HTTP_RESPONSE);
3857+
test_invalid_header_field_token_error(HTTP_RESPONSE);
3858+
test_invalid_header_field_content_error(HTTP_RESPONSE);
3859+
36993860
//// RESPONSES
37003861

37013862
for (i = 0; i < response_count; i++) {

0 commit comments

Comments
 (0)