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

allow content-length and transfer-encoding: c… #518

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions http_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,8 @@ size_t http_parser_execute (http_parser *parser,
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. */
Expand Down Expand Up @@ -731,7 +733,7 @@ size_t http_parser_execute (http_parser *parser,
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') {
Expand Down Expand Up @@ -769,7 +771,7 @@ size_t http_parser_execute (http_parser *parser,
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') {
Expand Down Expand Up @@ -927,7 +929,7 @@ size_t http_parser_execute (http_parser *parser,
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))) {
Expand Down Expand Up @@ -1341,7 +1343,7 @@ size_t http_parser_execute (http_parser *parser,
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;

Expand Down Expand Up @@ -1801,14 +1803,19 @@ size_t http_parser_execute (http_parser *parser,
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;
}
Expand Down Expand Up @@ -1889,7 +1896,7 @@ size_t http_parser_execute (http_parser *parser,
/* 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 */

Expand Down Expand Up @@ -2165,7 +2172,7 @@ http_message_needs_eof (const http_parser *parser)
}

/* 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;
}
Expand Down
4 changes: 2 additions & 2 deletions http_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,6 @@ enum flags
, F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6
, F_CONTENTLENGTH = 1 << 7
, F_TRANSFER_ENCODING = 1 << 8 /* Never set in http_parser.flags */
};


Expand Down Expand Up @@ -302,7 +301,8 @@ struct http_parser {
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 */
Expand Down
44 changes: 41 additions & 3 deletions test.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
#define MAX_CHUNKS 16

#define MIN(a,b) ((a) < (b) ? (a) : (b))

#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))

static http_parser parser;
Expand Down Expand Up @@ -82,6 +81,7 @@ struct message {
int status_cb_called;
int message_complete_on_eof;
int body_is_final;
int allow_chunked_length;
};

static int currently_parsing_eof;
Expand Down Expand Up @@ -1293,6 +1293,37 @@ const struct message requests[] =
,.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 * */
Expand Down Expand Up @@ -3582,6 +3613,9 @@ test_message (const struct message *message)
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;
Expand Down Expand Up @@ -3646,7 +3680,6 @@ void
test_message_count_body (const struct message *message)
{
parser_init(message->type);

size_t read;
size_t l = strlen(message->raw);
size_t i, toread;
Expand Down Expand Up @@ -4023,6 +4056,9 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
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;

Expand Down Expand Up @@ -4225,6 +4261,9 @@ test_message_pause (const struct message *msg)
size_t nread;

parser_init(msg->type);
if (msg->allow_chunked_length) {
parser.allow_chunked_length = 1;
}

do {
nread = parse_pause(buf, buflen);
Expand All @@ -4244,7 +4283,6 @@ test_message_pause (const struct message *msg)
if (HTTP_PARSER_ERRNO(&parser) == HPE_STRICT) {
return;
}

assert (HTTP_PARSER_ERRNO(&parser) == HPE_PAUSED);
}

Expand Down