diff --git a/deps/http_parser/AUTHORS b/deps/http_parser/AUTHORS index c3c2b459df..51b53b1253 100644 --- a/deps/http_parser/AUTHORS +++ b/deps/http_parser/AUTHORS @@ -47,3 +47,4 @@ Charlie Somerville Fedor Indutny runner Alexis Campailla +David Wragg diff --git a/deps/http_parser/Makefile b/deps/http_parser/Makefile index 504c5250d5..3ce463b88b 100644 --- a/deps/http_parser/Makefile +++ b/deps/http_parser/Makefile @@ -19,7 +19,7 @@ # IN THE SOFTWARE. PLATFORM ?= $(shell sh -c 'uname -s | tr "[A-Z]" "[a-z]"') -SONAME ?= libhttp_parser.so.2.2.1 +SONAME ?= libhttp_parser.so.2.3 CC?=gcc AR?=ar diff --git a/deps/http_parser/http_parser.c b/deps/http_parser/http_parser.c index a131a38611..70cc9bd37b 100644 --- a/deps/http_parser/http_parser.c +++ b/deps/http_parser/http_parser.c @@ -280,6 +280,9 @@ enum state , s_header_field_start , s_header_field + , s_header_value_discard_ws + , s_header_value_discard_ws_almost_done + , s_header_value_discard_lws , s_header_value_start , s_header_value , s_header_value_lws @@ -1380,7 +1383,7 @@ size_t http_parser_execute (http_parser *parser, } if (ch == ':') { - parser->state = s_header_value_start; + parser->state = s_header_value_discard_ws; CALLBACK_DATA(header_field); break; } @@ -1401,28 +1404,28 @@ size_t http_parser_execute (http_parser *parser, goto error; } - case s_header_value_start: - { + case s_header_value_discard_ws: if (ch == ' ' || ch == '\t') break; - MARK(header_value); - - parser->state = s_header_value; - parser->index = 0; - if (ch == CR) { - parser->header_state = h_general; - parser->state = s_header_almost_done; - CALLBACK_DATA(header_value); + parser->state = s_header_value_discard_ws_almost_done; break; } if (ch == LF) { - parser->state = s_header_field_start; - CALLBACK_DATA(header_value); + parser->state = s_header_value_discard_lws; break; } + /* FALLTHROUGH */ + + case s_header_value_start: + { + MARK(header_value); + + parser->state = s_header_value; + parser->index = 0; + c = LOWER(ch); switch (parser->header_state) { @@ -1570,7 +1573,17 @@ size_t http_parser_execute (http_parser *parser, STRICT_CHECK(ch != LF); parser->state = s_header_value_lws; + break; + } + + case s_header_value_lws: + { + if (ch == ' ' || ch == '\t') { + parser->state = s_header_value_start; + goto reexecute_byte; + } + /* finished the header */ switch (parser->header_state) { case h_connection_keep_alive: parser->flags |= F_CONNECTION_KEEP_ALIVE; @@ -1585,19 +1598,29 @@ size_t http_parser_execute (http_parser *parser, break; } + parser->state = s_header_field_start; + goto reexecute_byte; + } + + case s_header_value_discard_ws_almost_done: + { + STRICT_CHECK(ch != LF); + parser->state = s_header_value_discard_lws; break; } - case s_header_value_lws: + case s_header_value_discard_lws: { - if (ch == ' ' || ch == '\t') - parser->state = s_header_value_start; - else - { + if (ch == ' ' || ch == '\t') { + parser->state = s_header_value_discard_ws; + break; + } else { + /* header value was empty */ + MARK(header_value); parser->state = s_header_field_start; + CALLBACK_DATA_NOADVANCE(header_value); goto reexecute_byte; } - break; } case s_headers_almost_done: diff --git a/deps/http_parser/http_parser.h b/deps/http_parser/http_parser.h index 3e2881630d..ec61a1287f 100644 --- a/deps/http_parser/http_parser.h +++ b/deps/http_parser/http_parser.h @@ -26,8 +26,8 @@ extern "C" { /* Also update SONAME in the Makefile whenever you change these. */ #define HTTP_PARSER_VERSION_MAJOR 2 -#define HTTP_PARSER_VERSION_MINOR 2 -#define HTTP_PARSER_VERSION_PATCH 1 +#define HTTP_PARSER_VERSION_MINOR 3 +#define HTTP_PARSER_VERSION_PATCH 0 #include #if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) diff --git a/deps/http_parser/test.c b/deps/http_parser/test.c index 9aaa5adfcf..9799dc6d34 100644 --- a/deps/http_parser/test.c +++ b/deps/http_parser/test.c @@ -608,8 +608,14 @@ const struct message requests[] = " mno \r\n" "\t \tqrs\r\n" "Line2: \t line2\t\r\n" + "Line3:\r\n" + " line3\r\n" + "Line4: \r\n" + " \r\n" + "Connection:\r\n" + " close\r\n" "\r\n" - ,.should_keep_alive= TRUE + ,.should_keep_alive= FALSE ,.message_complete_on_eof= FALSE ,.http_major= 1 ,.http_minor= 1 @@ -618,9 +624,12 @@ const struct message requests[] = ,.fragment= "" ,.request_path= "/" ,.request_url= "/" - ,.num_headers= 2 - ,.headers= { { "Line1", "abcdefghijklmno qrs" } + ,.num_headers= 5 + ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" } , { "Line2", "line2\t" } + , { "Line3", "line3" } + , { "Line4", "" } + , { "Connection", "close" }, } ,.body= "" } @@ -904,6 +913,43 @@ const struct message requests[] = ,.body= "" } +#define LINE_FOLDING_IN_HEADER_WITH_LF 34 +, {.name= "line folding in header value" + ,.type= HTTP_REQUEST + ,.raw= "GET / HTTP/1.1\n" + "Line1: abc\n" + "\tdef\n" + " ghi\n" + "\t\tjkl\n" + " mno \n" + "\t \tqrs\n" + "Line2: \t line2\t\n" + "Line3:\n" + " line3\n" + "Line4: \n" + " \n" + "Connection:\n" + " close\n" + "\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/" + ,.request_url= "/" + ,.num_headers= 5 + ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" } + , { "Line2", "line2\t" } + , { "Line3", "line3" } + , { "Line4", "" } + , { "Connection", "close" }, + } + ,.body= "" + } + , {.name= NULL } /* sentinel */ }; diff --git a/test/simple/test-http-multi-line-headers.js b/test/simple/test-http-multi-line-headers.js index 92bf9ecc82..9415abadcb 100644 --- a/test/simple/test-http-multi-line-headers.js +++ b/test/simple/test-http-multi-line-headers.js @@ -47,7 +47,7 @@ var server = net.createServer(function(conn) { server.listen(common.PORT, function() { http.get({host: '127.0.0.1', port: common.PORT}, function(res) { assert.equal(res.headers['content-type'], - 'text/plain;x-unix-mode=0600;name="hello.txt"'); + 'text/plain; x-unix-mode=0600; name="hello.txt"'); gotResponse = true; res.destroy(); });