Skip to content

Commit

Permalink
more robust chunked response parsing (fixes #2554)
Browse files Browse the repository at this point in the history
handle chunked parts across multiple reads

fixes https://redmine.lighttpd.net/issues/2554
where Happstack performs multiple writes to send HTTP chunked header,
HTTP chunked body, and CRLF ending HTTP chunked body

supercedes #8

handle additional chunked case of split CR and LF
reference
  https://github.com/lighttpd/weighttp/pull/9/files#r52358620
  thx stbuehler

detect CR not followed by LF (error)

handle NIL in chunked stream
(should not occur in HTTP headers or chunked header,
 but could occur in chunked body)

x-ref:
  "weighttp closing connection too early(?)"
  https://redmine.lighttpd.net/issues/2554

github: closes #8, closes #9
  • Loading branch information
gstrauss committed Apr 15, 2016
1 parent 8f2147e commit 9257077
Showing 1 changed file with 25 additions and 14 deletions.
39 changes: 25 additions & 14 deletions src/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,11 @@ static uint8_t client_parse(Client *client, int size) {
status_code += *str - '0';
}

// look for next \r\n
end = memchr(end, '\r', client->buffer_offset);
if (!end || *(end+1) != '\n')
return (!end || *(end+1) == '\0') && client->buffer_offset < 1024 ? 1 : 0;

if (status_code >= 200 && status_code < 300) {
client->worker->stats.req_2xx++;
client->status_success = 1;
Expand All @@ -352,19 +357,14 @@ static uint8_t client_parse(Client *client, int size) {
return 0;
}

// look for next \r\n
end = strchr(end, '\r');
if (!end || *(end+1) != '\n')
return 0;

client->parser_offset = end + 2 - client->buffer;
client->parser_state = PARSER_HEADER;
case PARSER_HEADER:
//printf("parse (HEADER)\n");
/* look for Content-Length and Connection header */
while (NULL != (end = strchr(&client->buffer[client->parser_offset], '\r'))) {
while (NULL != (end = memchr(&client->buffer[client->parser_offset], '\r', client->buffer_offset - client->parser_offset))) {
if (*(end+1) != '\n')
return 0;
return *(end+1) == '\0' && client->buffer_offset - client->parser_offset < 1024 ? 1 : 0;

if (end == &client->buffer[client->parser_offset]) {
/* body reached */
Expand Down Expand Up @@ -449,25 +449,33 @@ static uint8_t client_parse(Client *client, int size) {
else if (*str >= 'a' && *str <= 'z')
client->chunk_size += 10 + *str - 'a';
else
return 0;
return 0; /*(src < end checked above)*/
}

str = strstr(str, "\r\n");
if (!str)
return 0;
if (str[0] != '\r') {
str = memchr(str, '\r', end-str);
if (!str) {
client->chunk_size = -1;
return size < 1024 ? 1 : 0;
}
}
if (str[1] != '\n') {
client->chunk_size = -1;
return str+1 == end ? 1 : 0;
}
str += 2;

//printf("---------- chunk size: %"PRIi64", %d read, %d offset, data: '%s'\n", client->chunk_size, size, client->parser_offset, str);

size -= str - &client->buffer[client->parser_offset];
client->parser_offset = str - client->buffer;

if (client->chunk_size == 0) {
/* chunk of size 0 marks end of content body */
client->state = CLIENT_END;
client->success = client->status_success ? 1 : 0;
return 1;
}

size -= str - &client->buffer[client->parser_offset];
client->parser_offset = str - client->buffer;
}

/* consume chunk till chunk_size is reached */
Expand All @@ -482,6 +490,9 @@ static uint8_t client_parse(Client *client, int size) {
//printf("---------- chunk consuming: %d, received: %"PRIi64" of %"PRIi64", offset: %d\n", consume_max, client->chunk_received, client->chunk_size, client->parser_offset);

if (client->chunk_received == client->chunk_size) {
if (size - consume_max < 2)
return 1;

if (client->buffer[client->parser_offset] != '\r' || client->buffer[client->parser_offset+1] != '\n')
return 0;

Expand Down

0 comments on commit 9257077

Please sign in to comment.