123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- Subject: Allow Content-Length and Transfer-Encoding: chunked
- Origin: v2.9.4-8-ge13b274 <https://github.com/joyent/http-parser/commit/e13b274>
- Upstream-Author: Oleg Guba <oleg@dropbox.com>
- Date: Fri Jul 10 11:49:58 2020 +0200
- Fixes: https://github.com/nodejs/http-parser/issues/517
- PR-URL: https://github.com/nodejs/http-parser/pull/518
- Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
- Reviewed-By: Pierce Lopez <pierce.lopez@gmail.com>
- --- a/http_parser.c
- +++ b/http_parser.c
- @@ -653,6 +653,8 @@
- const char *status_mark = 0;
- enum state p_state = (enum state) parser->state;
- const unsigned int lenient = parser->lenient_http_headers;
- + const unsigned int allow_chunked_length = parser->allow_chunked_length;
- +
- uint32_t nread = parser->nread;
-
- /* We're in an error state. Don't bother doing anything. */
- @@ -731,7 +733,7 @@
- if (ch == CR || ch == LF)
- break;
- parser->flags = 0;
- - parser->extra_flags = 0;
- + parser->uses_transfer_encoding = 0;
- parser->content_length = ULLONG_MAX;
-
- if (ch == 'H') {
- @@ -769,7 +771,7 @@
- if (ch == CR || ch == LF)
- break;
- parser->flags = 0;
- - parser->extra_flags = 0;
- + parser->uses_transfer_encoding = 0;
- parser->content_length = ULLONG_MAX;
-
- if (ch == 'H') {
- @@ -927,7 +929,7 @@
- if (ch == CR || ch == LF)
- break;
- parser->flags = 0;
- - parser->extra_flags = 0;
- + parser->uses_transfer_encoding = 0;
- parser->content_length = ULLONG_MAX;
-
- if (UNLIKELY(!IS_ALPHA(ch))) {
- @@ -1341,7 +1343,7 @@
- parser->header_state = h_general;
- } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
- parser->header_state = h_transfer_encoding;
- - parser->extra_flags |= F_TRANSFER_ENCODING >> 8;
- + parser->uses_transfer_encoding = 1;
- }
- break;
-
- @@ -1801,14 +1803,19 @@
- REEXECUTE();
- }
-
- - /* Cannot us transfer-encoding and a content-length header together
- + /* Cannot use transfer-encoding and a content-length header together
- per the HTTP specification. (RFC 7230 Section 3.3.3) */
- - if ((parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) &&
- + if ((parser->uses_transfer_encoding == 1) &&
- (parser->flags & F_CONTENTLENGTH)) {
- /* Allow it for lenient parsing as long as `Transfer-Encoding` is
- - * not `chunked`
- + * not `chunked` or allow_length_with_encoding is set
- */
- - if (!lenient || (parser->flags & F_CHUNKED)) {
- + if (parser->flags & F_CHUNKED) {
- + if (!allow_chunked_length) {
- + SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
- + goto error;
- + }
- + } else if (!lenient) {
- SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
- goto error;
- }
- @@ -1889,7 +1896,7 @@
- /* chunked encoding - ignore Content-Length header,
- * prepare for a chunk */
- UPDATE_STATE(s_chunk_size_start);
- - } else if (parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) {
- + } else if (parser->uses_transfer_encoding == 1) {
- if (parser->type == HTTP_REQUEST && !lenient) {
- /* RFC 7230 3.3.3 */
-
- @@ -2165,7 +2172,7 @@
- }
-
- /* RFC 7230 3.3.3, see `s_headers_almost_done` */
- - if ((parser->extra_flags & (F_TRANSFER_ENCODING >> 8)) &&
- + if ((parser->uses_transfer_encoding == 1) &&
- (parser->flags & F_CHUNKED) == 0) {
- return 1;
- }
- --- a/http_parser.h
- +++ b/http_parser.h
- @@ -225,7 +225,6 @@
- , F_UPGRADE = 1 << 5
- , F_SKIPBODY = 1 << 6
- , F_CONTENTLENGTH = 1 << 7
- - , F_TRANSFER_ENCODING = 1 << 8 /* Never set in http_parser.flags */
- };
-
-
- @@ -300,7 +299,10 @@
- unsigned int state : 7; /* enum state from http_parser.c */
- unsigned int header_state : 7; /* enum header_state from http_parser.c */
- unsigned int index : 5; /* index into current matcher */
- - unsigned int extra_flags : 2;
- + unsigned int uses_transfer_encoding : 1; /* Transfer-Encoding header is present */
- + unsigned int allow_chunked_length : 1; /* Allow headers with both
- + * `Content-Length` and
- + * `Transfer-Encoding: chunked` set */
- unsigned int lenient_http_headers : 1;
-
- uint32_t nread; /* # bytes read in various scenarios */
- --- a/test.c
- +++ b/test.c
- @@ -82,6 +82,7 @@
- int status_cb_called;
- int message_complete_on_eof;
- int body_is_final;
- + int allow_chunked_length;
- };
-
- static int currently_parsing_eof;
- @@ -1293,6 +1294,37 @@
- ,.num_chunks_complete= 2
- ,.chunk_lengths= { 0x1e }
- }
- +
- +#define CHUNKED_CONTENT_LENGTH 46
- +, {.name= "chunked with content-length set, allow_chunked_length flag is set"
- + ,.type= HTTP_REQUEST
- + ,.raw= "POST /chunked_w_content_length HTTP/1.1\r\n"
- + "Content-Length: 10\r\n"
- + "Transfer-Encoding: chunked\r\n"
- + "\r\n"
- + "5; ilovew3;whattheluck=aretheseparametersfor\r\nhello\r\n"
- + "6; blahblah; blah\r\n world\r\n"
- + "0\r\n"
- + "\r\n"
- + ,.allow_chunked_length = 1
- + ,.should_keep_alive= TRUE
- + ,.message_complete_on_eof= FALSE
- + ,.http_major= 1
- + ,.http_minor= 1
- + ,.method= HTTP_POST
- + ,.query_string= ""
- + ,.fragment= ""
- + ,.request_path= "/chunked_w_content_length"
- + ,.request_url= "/chunked_w_content_length"
- + ,.content_length= 10
- + ,.num_headers= 2
- + ,.headers={ { "Content-Length", "10"}
- + , { "Transfer-Encoding", "chunked" }
- + }
- + ,.body= "hello world"
- + ,.num_chunks_complete= 3
- + ,.chunk_lengths= { 5, 6 }
- + }
- };
-
- /* * R E S P O N S E S * */
- @@ -3582,6 +3614,9 @@
- size_t msg1len;
- for (msg1len = 0; msg1len < raw_len; msg1len++) {
- parser_init(message->type);
- + if (message->allow_chunked_length) {
- + parser.allow_chunked_length = 1;
- + }
-
- size_t read;
- const char *msg1 = message->raw;
- @@ -4023,6 +4058,11 @@
- strcat(total, r3->raw);
-
- parser_init(r1->type);
- + if (r1->allow_chunked_length ||
- + r2->allow_chunked_length ||
- + r3->allow_chunked_length) {
- + parser.allow_chunked_length = 1;
- + }
-
- size_t read;
-
- @@ -4225,6 +4265,9 @@
- size_t nread;
-
- parser_init(msg->type);
- + if (msg->allow_chunked_length) {
- + parser.allow_chunked_length = 1;
- + }
-
- do {
- nread = parse_pause(buf, buflen);
|