summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Strauss <gstrauss@gluelogic.com>2016-04-14 22:16:46 -0400
committerGlenn Strauss <gstrauss@gluelogic.com>2016-04-14 22:16:46 -0400
commit925707722f4408f7c912772848505d8330fb6319 (patch)
tree50c0bb01ec8946788f78d5de77f89eefe3d9a14c
parent8f2147e429b4072eae0bc5390fb326b56edece8d (diff)
downloadweighttp-925707722f4408f7c912772848505d8330fb6319.tar.gz
weighttp-925707722f4408f7c912772848505d8330fb6319.zip
more robust chunked response parsing (fixes #2554)
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 https://github.com/lighttpd/weighttp/pull/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
-rw-r--r--src/client.c39
1 files changed, 25 insertions, 14 deletions
diff --git a/src/client.c b/src/client.c
index dd48265..d34265c 100644
--- a/src/client.c
+++ b/src/client.c
@@ -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;
@@ -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 */
@@ -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 */
@@ -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;