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

Commit e13b274

Browse files
veshijbnoordhuis
authored andcommitted
Allow Content-Length and Transfer-Encoding: chunked
Fixes: #517 PR-URL: #518 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Pierce Lopez <[email protected]>
1 parent 4b99e42 commit e13b274

File tree

3 files changed

+64
-12
lines changed

3 files changed

+64
-12
lines changed

Diff for: http_parser.c

+17-10
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,8 @@ size_t http_parser_execute (http_parser *parser,
653653
const char *status_mark = 0;
654654
enum state p_state = (enum state) parser->state;
655655
const unsigned int lenient = parser->lenient_http_headers;
656+
const unsigned int allow_chunked_length = parser->allow_chunked_length;
657+
656658
uint32_t nread = parser->nread;
657659

658660
/* We're in an error state. Don't bother doing anything. */
@@ -731,7 +733,7 @@ size_t http_parser_execute (http_parser *parser,
731733
if (ch == CR || ch == LF)
732734
break;
733735
parser->flags = 0;
734-
parser->extra_flags = 0;
736+
parser->uses_transfer_encoding = 0;
735737
parser->content_length = ULLONG_MAX;
736738

737739
if (ch == 'H') {
@@ -769,7 +771,7 @@ size_t http_parser_execute (http_parser *parser,
769771
if (ch == CR || ch == LF)
770772
break;
771773
parser->flags = 0;
772-
parser->extra_flags = 0;
774+
parser->uses_transfer_encoding = 0;
773775
parser->content_length = ULLONG_MAX;
774776

775777
if (ch == 'H') {
@@ -927,7 +929,7 @@ size_t http_parser_execute (http_parser *parser,
927929
if (ch == CR || ch == LF)
928930
break;
929931
parser->flags = 0;
930-
parser->extra_flags = 0;
932+
parser->uses_transfer_encoding = 0;
931933
parser->content_length = ULLONG_MAX;
932934

933935
if (UNLIKELY(!IS_ALPHA(ch))) {
@@ -1341,7 +1343,7 @@ size_t http_parser_execute (http_parser *parser,
13411343
parser->header_state = h_general;
13421344
} else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
13431345
parser->header_state = h_transfer_encoding;
1344-
parser->extra_flags |= F_TRANSFER_ENCODING >> 8;
1346+
parser->uses_transfer_encoding = 1;
13451347
}
13461348
break;
13471349

@@ -1801,14 +1803,19 @@ size_t http_parser_execute (http_parser *parser,
18011803
REEXECUTE();
18021804
}
18031805

1804-
/* Cannot us transfer-encoding and a content-length header together
1806+
/* Cannot use transfer-encoding and a content-length header together
18051807
per the HTTP specification. (RFC 7230 Section 3.3.3) */
1806-
if ((parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) &&
1808+
if ((parser->uses_transfer_encoding == 1) &&
18071809
(parser->flags & F_CONTENTLENGTH)) {
18081810
/* Allow it for lenient parsing as long as `Transfer-Encoding` is
1809-
* not `chunked`
1811+
* not `chunked` or allow_length_with_encoding is set
18101812
*/
1811-
if (!lenient || (parser->flags & F_CHUNKED)) {
1813+
if (parser->flags & F_CHUNKED) {
1814+
if (!allow_chunked_length) {
1815+
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
1816+
goto error;
1817+
}
1818+
} else if (!lenient) {
18121819
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
18131820
goto error;
18141821
}
@@ -1889,7 +1896,7 @@ size_t http_parser_execute (http_parser *parser,
18891896
/* chunked encoding - ignore Content-Length header,
18901897
* prepare for a chunk */
18911898
UPDATE_STATE(s_chunk_size_start);
1892-
} else if (parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) {
1899+
} else if (parser->uses_transfer_encoding == 1) {
18931900
if (parser->type == HTTP_REQUEST && !lenient) {
18941901
/* RFC 7230 3.3.3 */
18951902

@@ -2165,7 +2172,7 @@ http_message_needs_eof (const http_parser *parser)
21652172
}
21662173

21672174
/* RFC 7230 3.3.3, see `s_headers_almost_done` */
2168-
if ((parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) &&
2175+
if ((parser->uses_transfer_encoding == 1) &&
21692176
(parser->flags & F_CHUNKED) == 0) {
21702177
return 1;
21712178
}

Diff for: http_parser.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,6 @@ enum flags
227227
, F_UPGRADE = 1 << 5
228228
, F_SKIPBODY = 1 << 6
229229
, F_CONTENTLENGTH = 1 << 7
230-
, F_TRANSFER_ENCODING = 1 << 8 /* Never set in http_parser.flags */
231230
};
232231

233232

@@ -302,7 +301,10 @@ struct http_parser {
302301
unsigned int state : 7; /* enum state from http_parser.c */
303302
unsigned int header_state : 7; /* enum header_state from http_parser.c */
304303
unsigned int index : 5; /* index into current matcher */
305-
unsigned int extra_flags : 2;
304+
unsigned int uses_transfer_encoding : 1; /* Transfer-Encoding header is present */
305+
unsigned int allow_chunked_length : 1; /* Allow headers with both
306+
* `Content-Length` and
307+
* `Transfer-Encoding: chunked` set */
306308
unsigned int lenient_http_headers : 1;
307309

308310
uint32_t nread; /* # bytes read in various scenarios */

Diff for: test.c

+43
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ struct message {
8282
int status_cb_called;
8383
int message_complete_on_eof;
8484
int body_is_final;
85+
int allow_chunked_length;
8586
};
8687

8788
static int currently_parsing_eof;
@@ -1293,6 +1294,37 @@ const struct message requests[] =
12931294
,.num_chunks_complete= 2
12941295
,.chunk_lengths= { 0x1e }
12951296
}
1297+
1298+
#define CHUNKED_CONTENT_LENGTH 46
1299+
, {.name= "chunked with content-length set, allow_chunked_length flag is set"
1300+
,.type= HTTP_REQUEST
1301+
,.raw= "POST /chunked_w_content_length HTTP/1.1\r\n"
1302+
"Content-Length: 10\r\n"
1303+
"Transfer-Encoding: chunked\r\n"
1304+
"\r\n"
1305+
"5; ilovew3;whattheluck=aretheseparametersfor\r\nhello\r\n"
1306+
"6; blahblah; blah\r\n world\r\n"
1307+
"0\r\n"
1308+
"\r\n"
1309+
,.allow_chunked_length = 1
1310+
,.should_keep_alive= TRUE
1311+
,.message_complete_on_eof= FALSE
1312+
,.http_major= 1
1313+
,.http_minor= 1
1314+
,.method= HTTP_POST
1315+
,.query_string= ""
1316+
,.fragment= ""
1317+
,.request_path= "/chunked_w_content_length"
1318+
,.request_url= "/chunked_w_content_length"
1319+
,.content_length= 10
1320+
,.num_headers= 2
1321+
,.headers={ { "Content-Length", "10"}
1322+
, { "Transfer-Encoding", "chunked" }
1323+
}
1324+
,.body= "hello world"
1325+
,.num_chunks_complete= 3
1326+
,.chunk_lengths= { 5, 6 }
1327+
}
12961328
};
12971329

12981330
/* * R E S P O N S E S * */
@@ -3582,6 +3614,9 @@ test_message (const struct message *message)
35823614
size_t msg1len;
35833615
for (msg1len = 0; msg1len < raw_len; msg1len++) {
35843616
parser_init(message->type);
3617+
if (message->allow_chunked_length) {
3618+
parser.allow_chunked_length = 1;
3619+
}
35853620

35863621
size_t read;
35873622
const char *msg1 = message->raw;
@@ -4023,6 +4058,11 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
40234058
strcat(total, r3->raw);
40244059

40254060
parser_init(r1->type);
4061+
if (r1->allow_chunked_length ||
4062+
r2->allow_chunked_length ||
4063+
r3->allow_chunked_length) {
4064+
parser.allow_chunked_length = 1;
4065+
}
40264066

40274067
size_t read;
40284068

@@ -4225,6 +4265,9 @@ test_message_pause (const struct message *msg)
42254265
size_t nread;
42264266

42274267
parser_init(msg->type);
4268+
if (msg->allow_chunked_length) {
4269+
parser.allow_chunked_length = 1;
4270+
}
42284271

42294272
do {
42304273
nread = parse_pause(buf, buflen);

0 commit comments

Comments
 (0)