From efd801f41034b6d8f23441327257de624b6a0e5c Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 21 Aug 2009 01:48:20 +0200 Subject: [PATCH] Bug Fix: Connection:close with missing Content-Length. The test and bug report are from tomika. --- http_parser.h | 2 +- http_parser.rl | 50 +++++++++++++++++++++++++++++++++++++++++--------- test.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 10 deletions(-) diff --git a/http_parser.h b/http_parser.h index d68e117..6e84316 100644 --- a/http_parser.h +++ b/http_parser.h @@ -96,7 +96,7 @@ struct http_parser { unsigned short method; /* requests only */ short version; short keep_alive; - size_t content_length; + ssize_t content_length; /** PUBLIC **/ void *data; /* A pointer to get hook to the "connection" or "socket" object */ diff --git a/http_parser.rl b/http_parser.rl index 019882d..fe1a3fa 100644 --- a/http_parser.rl +++ b/http_parser.rl @@ -27,9 +27,10 @@ #include /* parser->flags */ -#define EATING 0x01 -#define ERROR 0x02 -#define CHUNKED 0x04 +#define EATING 0x01 +#define ERROR 0x02 +#define CHUNKED 0x04 +#define EAT_FOREVER 0x10 static int unhex[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 @@ -79,7 +80,7 @@ do { \ parser->method = 0; \ parser->version = HTTP_VERSION_OTHER; \ parser->keep_alive = -1; \ - parser->content_length = 0; \ + parser->content_length = -1; \ parser->body_read = 0 #define END_REQUEST \ @@ -226,6 +227,7 @@ do { \ } action content_length { + if (parser->content_length == -1) parser->content_length = 0; if (parser->content_length > INT_MAX) { parser->flags |= ERROR; return 0; @@ -281,11 +283,25 @@ do { \ if (parser->flags & CHUNKED) { fnext ChunkedBody; } else { - /* this is pretty stupid. i'd prefer to combine this with skip_chunk_data */ - parser->chunk_size = parser->content_length; + /* this is pretty stupid. i'd prefer to combine this with + * skip_chunk_data */ + if (parser->content_length < 0) { + /* If we didn't get a content length; if not keep-alive + * just read body until EOF */ + if (!http_parser_should_keep_alive(parser)) { + parser->flags |= EAT_FOREVER; + parser->chunk_size = REMAINING; + } else { + /* Otherwise, if keep-alive, then assume the message + * has no body. */ + parser->chunk_size = parser->content_length = 0; + } + } else { + parser->chunk_size = parser->content_length; + } p += 1; - SKIP_BODY(MIN(REMAINING, parser->content_length)); + SKIP_BODY(MIN(REMAINING, parser->chunk_size)); if (callback_return_value != 0) { parser->flags |= ERROR; @@ -444,11 +460,27 @@ http_parser_execute (http_parser *parser, const char *buffer, size_t len) { size_t tmp; // REMOVE ME this is extremely hacky int callback_return_value = 0; - const char *p, *pe; + const char *p, *pe, *eof; int cs = parser->cs; p = buffer; - pe = buffer+len; + pe = buffer+len; + eof = len ? NULL : pe; + + if (parser->flags & EAT_FOREVER) { + if (len == 0) { + if (parser->on_message_complete) { + callback_return_value = parser->on_message_complete(parser); + if (callback_return_value != 0) parser->flags |= ERROR; + } + } else { + if (parser->on_body) { + callback_return_value = parser->on_body(parser, p, len); + if (callback_return_value != 0) parser->flags |= ERROR; + } + } + return len; + } if (0 < parser->chunk_size && (parser->flags & EATING)) { /* eat body */ diff --git a/test.c b/test.c index db6ddf9..ab635da 100644 --- a/test.c +++ b/test.c @@ -344,6 +344,45 @@ const struct message responses[] = "\r\n" } +, {.name= "no content-length response" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n" + "Server: Apache\r\n" + "X-Powered-By: Servlet/2.5 JSP/2.1\r\n" + "Content-Type: text/xml; charset=utf-8\r\n" + "Connection: close\r\n" + "\r\n" + "\n" + "\n" + " \n" + " \n" + " SOAP-ENV:Client\n" + " Client Error\n" + " \n" + " \n" + "" + ,.should_keep_alive= FALSE + ,.status_code= 200 + ,.num_headers= 5 + ,.headers= + { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" } + , { "Server", "Apache" } + , { "X-Powered-By", "Servlet/2.5 JSP/2.1" } + , { "Content-Type", "text/xml; charset=utf-8" } + , { "Connection", "close" } + } + ,.body= "\n" + "\n" + " \n" + " \n" + " SOAP-ENV:Client\n" + " Client Error\n" + " \n" + " \n" + "" + } + , {.name= "404 no headers no body" ,.type= HTTP_RESPONSE ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"