From 79655ebce1d61f6b425c67a7f8cea3e27eee9f0a Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Thu, 18 Jan 2018 14:32:55 +0200 Subject: [PATCH] Update C http parser to fresh upstream --- vendor/http-parser/LICENSE-MIT | 6 +- vendor/http-parser/Makefile | 39 +++-- vendor/http-parser/README.md | 10 +- vendor/http-parser/contrib/parsertrace.c | 5 +- vendor/http-parser/http_parser.c | 166 ++++++------------- vendor/http-parser/http_parser.h | 73 ++++++++- vendor/http-parser/test.c | 193 ++++++++++++++++++++++- 7 files changed, 344 insertions(+), 148 deletions(-) diff --git a/vendor/http-parser/LICENSE-MIT b/vendor/http-parser/LICENSE-MIT index 58010b38894..1ec0ab4e174 100644 --- a/vendor/http-parser/LICENSE-MIT +++ b/vendor/http-parser/LICENSE-MIT @@ -1,8 +1,4 @@ -http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright -Igor Sysoev. - -Additional changes are licensed under the same terms as NGINX and -copyright Joyent, Inc. and other Node contributors. All rights reserved. +Copyright Joyent, Inc. and other Node contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to diff --git a/vendor/http-parser/Makefile b/vendor/http-parser/Makefile index b2476dbd4a8..8b7b9bb0f70 100644 --- a/vendor/http-parser/Makefile +++ b/vendor/http-parser/Makefile @@ -21,16 +21,22 @@ PLATFORM ?= $(shell sh -c 'uname -s | tr "[A-Z]" "[a-z]"') HELPER ?= BINEXT ?= +SOLIBNAME = libhttp_parser +SOMAJOR = 2 +SOMINOR = 7 +SOREV = 1 ifeq (darwin,$(PLATFORM)) -SONAME ?= libhttp_parser.2.7.1.dylib SOEXT ?= dylib +SONAME ?= $(SOLIBNAME).$(SOMAJOR).$(SOMINOR).$(SOEXT) +LIBNAME ?= $(SOLIBNAME).$(SOMAJOR).$(SOMINOR).$(SOREV).$(SOEXT) else ifeq (wine,$(PLATFORM)) CC = winegcc BINEXT = .exe.so HELPER = wine else -SONAME ?= libhttp_parser.so.2.7.1 SOEXT ?= so +SONAME ?= $(SOLIBNAME).$(SOEXT).$(SOMAJOR).$(SOMINOR) +LIBNAME ?= $(SOLIBNAME).$(SOEXT).$(SOMAJOR).$(SOMINOR).$(SOREV) endif CC?=gcc @@ -55,11 +61,13 @@ CFLAGS_LIB = $(CFLAGS_FAST) -fPIC LDFLAGS_LIB = $(LDFLAGS) -shared INSTALL ?= install -PREFIX ?= $(DESTDIR)/usr/local +PREFIX ?= /usr/local LIBDIR = $(PREFIX)/lib INCLUDEDIR = $(PREFIX)/include -ifneq (darwin,$(PLATFORM)) +ifeq (darwin,$(PLATFORM)) +LDFLAGS_LIB += -Wl,-install_name,$(LIBDIR)/$(SONAME) +else # TODO(bnoordhuis) The native SunOS linker expects -h rather than -soname... LDFLAGS_LIB += -Wl,-soname=$(SONAME) endif @@ -102,7 +110,7 @@ libhttp_parser.o: http_parser.c http_parser.h Makefile $(CC) $(CPPFLAGS_FAST) $(CFLAGS_LIB) -c http_parser.c -o libhttp_parser.o library: libhttp_parser.o - $(CC) $(LDFLAGS_LIB) -o $(SONAME) $< + $(CC) $(LDFLAGS_LIB) -o $(LIBNAME) $< package: http_parser.o $(AR) rcs libhttp_parser.a http_parser.o @@ -123,19 +131,22 @@ tags: http_parser.c http_parser.h test.c ctags $^ install: library - $(INSTALL) -D http_parser.h $(INCLUDEDIR)/http_parser.h - $(INSTALL) -D $(SONAME) $(LIBDIR)/$(SONAME) - ln -s $(LIBDIR)/$(SONAME) $(LIBDIR)/libhttp_parser.$(SOEXT) + $(INSTALL) -D http_parser.h $(DESTDIR)$(INCLUDEDIR)/http_parser.h + $(INSTALL) -D $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(LIBNAME) + ln -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SONAME) + ln -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SOLIBNAME).$(SOEXT) install-strip: library - $(INSTALL) -D http_parser.h $(INCLUDEDIR)/http_parser.h - $(INSTALL) -D -s $(SONAME) $(LIBDIR)/$(SONAME) - ln -s $(LIBDIR)/$(SONAME) $(LIBDIR)/libhttp_parser.$(SOEXT) + $(INSTALL) -D http_parser.h $(DESTDIR)$(INCLUDEDIR)/http_parser.h + $(INSTALL) -D -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(LIBNAME) + ln -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SONAME) + ln -s $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SOLIBNAME).$(SOEXT) uninstall: - rm $(INCLUDEDIR)/http_parser.h - rm $(LIBDIR)/$(SONAME) - rm $(LIBDIR)/libhttp_parser.so + rm $(DESTDIR)$(INCLUDEDIR)/http_parser.h + rm $(DESTDIR)$(LIBDIR)/$(SOLIBNAME).$(SOEXT) + rm $(DESTDIR)$(LIBDIR)/$(SONAME) + rm $(DESTDIR)$(LIBDIR)/$(LIBNAME) clean: rm -f *.o *.a tags test test_fast test_g \ diff --git a/vendor/http-parser/README.md b/vendor/http-parser/README.md index 439b30998d4..b265d71715f 100644 --- a/vendor/http-parser/README.md +++ b/vendor/http-parser/README.md @@ -72,9 +72,9 @@ if (parser->upgrade) { } ``` -HTTP needs to know where the end of the stream is. For example, sometimes +`http_parser` needs to know where the end of the stream is. For example, sometimes servers send responses without Content-Length and expect the client to -consume input (for the body) until EOF. To tell http_parser about EOF, give +consume input (for the body) until EOF. To tell `http_parser` about EOF, give `0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors can still be encountered during an EOF, so one must still be prepared to receive them. @@ -93,7 +93,7 @@ the on_body callback. The Special Problem of Upgrade ------------------------------ -HTTP supports upgrading the connection to a different protocol. An +`http_parser` supports upgrading the connection to a different protocol. An increasingly common example of this is the WebSocket protocol which sends a request like @@ -144,7 +144,7 @@ parse a request, and then give a response over that socket. By instantiation of a thread-local struct containing relevant data (e.g. accepted socket, allocated memory for callbacks to write into, etc), a parser's callbacks are able to communicate data between the scope of the thread and the scope of the -callback in a threadsafe manner. This allows http-parser to be used in +callback in a threadsafe manner. This allows `http_parser` to be used in multi-threaded contexts. Example: @@ -202,7 +202,7 @@ void http_parser_thread(socket_t sock) { In case you parse HTTP message in chunks (i.e. `read()` request line from socket, parse, read half headers, parse, etc) your data callbacks -may be called more than once. Http-parser guarantees that data pointer is only +may be called more than once. `http_parser` guarantees that data pointer is only valid for the lifetime of callback. You can also `read()` into a heap allocated buffer to avoid copying memory around if this fits your application. diff --git a/vendor/http-parser/contrib/parsertrace.c b/vendor/http-parser/contrib/parsertrace.c index e7153680f46..3daa7f46a19 100644 --- a/vendor/http-parser/contrib/parsertrace.c +++ b/vendor/http-parser/contrib/parsertrace.c @@ -1,7 +1,4 @@ -/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev - * - * Additional changes are licensed under the same terms as NGINX and - * copyright Joyent, Inc. and other Node contributors. All rights reserved. +/* Copyright Joyent, Inc. and other Node contributors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to diff --git a/vendor/http-parser/http_parser.c b/vendor/http-parser/http_parser.c index 895bf0c7376..8d851245cb9 100644 --- a/vendor/http-parser/http_parser.c +++ b/vendor/http-parser/http_parser.c @@ -1,7 +1,4 @@ -/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev - * - * Additional changes are licensed under the same terms as NGINX and - * copyright Joyent, Inc. and other Node contributors. All rights reserved. +/* Copyright Joyent, Inc. and other Node contributors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -286,10 +283,10 @@ enum state , s_res_HT , s_res_HTT , s_res_HTTP - , s_res_first_http_major , s_res_http_major - , s_res_first_http_minor + , s_res_http_dot , s_res_http_minor + , s_res_http_end , s_res_first_status_code , s_res_status_code , s_res_status_start @@ -316,10 +313,10 @@ enum state , s_req_http_HT , s_req_http_HTT , s_req_http_HTTP - , s_req_first_http_major , s_req_http_major - , s_req_first_http_minor + , s_req_http_dot , s_req_http_minor + , s_req_http_end , s_req_line_almost_done , s_header_field_start @@ -795,75 +792,48 @@ size_t http_parser_execute (http_parser *parser, case s_res_HTTP: STRICT_CHECK(ch != '/'); - UPDATE_STATE(s_res_first_http_major); + UPDATE_STATE(s_res_http_major); break; - case s_res_first_http_major: - if (UNLIKELY(ch < '0' || ch > '9')) { + case s_res_http_major: + if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_major = ch - '0'; - UPDATE_STATE(s_res_http_major); + UPDATE_STATE(s_res_http_dot); break; - /* major HTTP version or dot */ - case s_res_http_major: + case s_res_http_dot: { - if (ch == '.') { - UPDATE_STATE(s_res_first_http_minor); - break; - } - - if (!IS_NUM(ch)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_major *= 10; - parser->http_major += ch - '0'; - - if (UNLIKELY(parser->http_major > 999)) { + if (UNLIKELY(ch != '.')) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } + UPDATE_STATE(s_res_http_minor); break; } - /* first digit of minor HTTP version */ - case s_res_first_http_minor: + case s_res_http_minor: if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor = ch - '0'; - UPDATE_STATE(s_res_http_minor); + UPDATE_STATE(s_res_http_end); break; - /* minor HTTP version or end of request line */ - case s_res_http_minor: + case s_res_http_end: { - if (ch == ' ') { - UPDATE_STATE(s_res_first_status_code); - break; - } - - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_minor *= 10; - parser->http_minor += ch - '0'; - - if (UNLIKELY(parser->http_minor > 999)) { + if (UNLIKELY(ch != ' ')) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } + UPDATE_STATE(s_res_first_status_code); break; } @@ -890,10 +860,9 @@ size_t http_parser_execute (http_parser *parser, UPDATE_STATE(s_res_status_start); break; case CR: - UPDATE_STATE(s_res_line_almost_done); - break; case LF: - UPDATE_STATE(s_header_field_start); + UPDATE_STATE(s_res_status_start); + REEXECUTE(); break; default: SET_ERRNO(HPE_INVALID_STATUS); @@ -915,19 +884,13 @@ size_t http_parser_execute (http_parser *parser, case s_res_status_start: { - if (ch == CR) { - UPDATE_STATE(s_res_line_almost_done); - break; - } - - if (ch == LF) { - UPDATE_STATE(s_header_field_start); - break; - } - MARK(status); UPDATE_STATE(s_res_status); parser->index = 0; + + if (ch == CR || ch == LF) + REEXECUTE(); + break; } @@ -1007,7 +970,7 @@ size_t http_parser_execute (http_parser *parser, UPDATE_STATE(s_req_spaces_before_url); } else if (ch == matcher[parser->index]) { ; /* nada */ - } else if (IS_ALPHA(ch)) { + } else if ((ch >= 'A' && ch <= 'Z') || ch == '-') { switch (parser->method << 16 | parser->index << 8 | ch) { #define XX(meth, pos, ch, new_meth) \ @@ -1016,31 +979,27 @@ size_t http_parser_execute (http_parser *parser, XX(POST, 1, 'U', PUT) XX(POST, 1, 'A', PATCH) + XX(POST, 1, 'R', PROPFIND) + XX(PUT, 2, 'R', PURGE) XX(CONNECT, 1, 'H', CHECKOUT) XX(CONNECT, 2, 'P', COPY) XX(MKCOL, 1, 'O', MOVE) XX(MKCOL, 1, 'E', MERGE) + XX(MKCOL, 1, '-', MSEARCH) XX(MKCOL, 2, 'A', MKACTIVITY) XX(MKCOL, 3, 'A', MKCALENDAR) XX(SUBSCRIBE, 1, 'E', SEARCH) XX(REPORT, 2, 'B', REBIND) - XX(POST, 1, 'R', PROPFIND) XX(PROPFIND, 4, 'P', PROPPATCH) - XX(PUT, 2, 'R', PURGE) XX(LOCK, 1, 'I', LINK) XX(UNLOCK, 2, 'S', UNSUBSCRIBE) XX(UNLOCK, 2, 'B', UNBIND) XX(UNLOCK, 3, 'I', UNLINK) #undef XX - default: SET_ERRNO(HPE_INVALID_METHOD); goto error; } - } else if (ch == '-' && - parser->index == 1 && - parser->method == HTTP_MKCOL) { - parser->method = HTTP_MSEARCH; } else { SET_ERRNO(HPE_INVALID_METHOD); goto error; @@ -1153,57 +1112,41 @@ size_t http_parser_execute (http_parser *parser, case s_req_http_HTTP: STRICT_CHECK(ch != '/'); - UPDATE_STATE(s_req_first_http_major); - break; - - /* first digit of major HTTP version */ - case s_req_first_http_major: - if (UNLIKELY(ch < '1' || ch > '9')) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_major = ch - '0'; UPDATE_STATE(s_req_http_major); break; - /* major HTTP version or dot */ case s_req_http_major: - { - if (ch == '.') { - UPDATE_STATE(s_req_first_http_minor); - break; - } - if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } - parser->http_major *= 10; - parser->http_major += ch - '0'; + parser->http_major = ch - '0'; + UPDATE_STATE(s_req_http_dot); + break; - if (UNLIKELY(parser->http_major > 999)) { + case s_req_http_dot: + { + if (UNLIKELY(ch != '.')) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } + UPDATE_STATE(s_req_http_minor); break; } - /* first digit of minor HTTP version */ - case s_req_first_http_minor: + case s_req_http_minor: if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_VERSION); goto error; } parser->http_minor = ch - '0'; - UPDATE_STATE(s_req_http_minor); + UPDATE_STATE(s_req_http_end); break; - /* minor HTTP version or end of request line */ - case s_req_http_minor: + case s_req_http_end: { if (ch == CR) { UPDATE_STATE(s_req_line_almost_done); @@ -1215,21 +1158,8 @@ size_t http_parser_execute (http_parser *parser, break; } - /* XXX allow spaces after digit? */ - - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_minor *= 10; - parser->http_minor += ch - '0'; - - if (UNLIKELY(parser->http_minor > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - + SET_ERRNO(HPE_INVALID_VERSION); + goto error; break; } @@ -1794,10 +1724,17 @@ size_t http_parser_execute (http_parser *parser, UPDATE_STATE(s_headers_done); /* Set this here so that on_headers_complete() callbacks can see it */ - parser->upgrade = - ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) == - (F_UPGRADE | F_CONNECTION_UPGRADE) || - parser->method == HTTP_CONNECT); + if ((parser->flags & F_UPGRADE) && + (parser->flags & F_CONNECTION_UPGRADE)) { + /* For responses, "Upgrade: foo" and "Connection: upgrade" are + * mandatory only when it is a 101 Switching Protocols response, + * otherwise it is purely informational, to announce support. + */ + parser->upgrade = + (parser->type == HTTP_REQUEST || parser->status_code == 101); + } else { + parser->upgrade = (parser->method == HTTP_CONNECT); + } /* Here we call the headers_complete callback. This is somewhat * different than other callbacks because if the user returns 1, we @@ -1816,6 +1753,7 @@ size_t http_parser_execute (http_parser *parser, case 2: parser->upgrade = 1; + /* FALLTHROUGH */ case 1: parser->flags |= F_SKIPBODY; break; @@ -2375,7 +2313,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect, case s_req_server_with_at: found_at = 1; - /* FALLTROUGH */ + /* FALLTHROUGH */ case s_req_server: uf = UF_HOST; break; diff --git a/vendor/http-parser/http_parser.h b/vendor/http-parser/http_parser.h index ea263948240..fd2a564148b 100644 --- a/vendor/http-parser/http_parser.h +++ b/vendor/http-parser/http_parser.h @@ -29,11 +29,10 @@ extern "C" { #define HTTP_PARSER_VERSION_MINOR 7 #define HTTP_PARSER_VERSION_PATCH 1 -#include +#include #if defined(_WIN32) && !defined(__MINGW32__) && \ (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__) #include -#include typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int16 int16_t; @@ -90,6 +89,76 @@ typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); typedef int (*http_cb) (http_parser*); +/* Status Codes */ +#define HTTP_STATUS_MAP(XX) \ + XX(100, CONTINUE, Continue) \ + XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \ + XX(102, PROCESSING, Processing) \ + XX(200, OK, OK) \ + XX(201, CREATED, Created) \ + XX(202, ACCEPTED, Accepted) \ + XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \ + XX(204, NO_CONTENT, No Content) \ + XX(205, RESET_CONTENT, Reset Content) \ + XX(206, PARTIAL_CONTENT, Partial Content) \ + XX(207, MULTI_STATUS, Multi-Status) \ + XX(208, ALREADY_REPORTED, Already Reported) \ + XX(226, IM_USED, IM Used) \ + XX(300, MULTIPLE_CHOICES, Multiple Choices) \ + XX(301, MOVED_PERMANENTLY, Moved Permanently) \ + XX(302, FOUND, Found) \ + XX(303, SEE_OTHER, See Other) \ + XX(304, NOT_MODIFIED, Not Modified) \ + XX(305, USE_PROXY, Use Proxy) \ + XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \ + XX(308, PERMANENT_REDIRECT, Permanent Redirect) \ + XX(400, BAD_REQUEST, Bad Request) \ + XX(401, UNAUTHORIZED, Unauthorized) \ + XX(402, PAYMENT_REQUIRED, Payment Required) \ + XX(403, FORBIDDEN, Forbidden) \ + XX(404, NOT_FOUND, Not Found) \ + XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \ + XX(406, NOT_ACCEPTABLE, Not Acceptable) \ + XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \ + XX(408, REQUEST_TIMEOUT, Request Timeout) \ + XX(409, CONFLICT, Conflict) \ + XX(410, GONE, Gone) \ + XX(411, LENGTH_REQUIRED, Length Required) \ + XX(412, PRECONDITION_FAILED, Precondition Failed) \ + XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \ + XX(414, URI_TOO_LONG, URI Too Long) \ + XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \ + XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \ + XX(417, EXPECTATION_FAILED, Expectation Failed) \ + XX(421, MISDIRECTED_REQUEST, Misdirected Request) \ + XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \ + XX(423, LOCKED, Locked) \ + XX(424, FAILED_DEPENDENCY, Failed Dependency) \ + XX(426, UPGRADE_REQUIRED, Upgrade Required) \ + XX(428, PRECONDITION_REQUIRED, Precondition Required) \ + XX(429, TOO_MANY_REQUESTS, Too Many Requests) \ + XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \ + XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \ + XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \ + XX(501, NOT_IMPLEMENTED, Not Implemented) \ + XX(502, BAD_GATEWAY, Bad Gateway) \ + XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \ + XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \ + XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \ + XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \ + XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \ + XX(508, LOOP_DETECTED, Loop Detected) \ + XX(510, NOT_EXTENDED, Not Extended) \ + XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \ + +enum http_status + { +#define XX(num, name, string) HTTP_STATUS_##name = num, + HTTP_STATUS_MAP(XX) +#undef XX + }; + + /* Request Methods */ #define HTTP_METHOD_MAP(XX) \ XX(0, DELETE, DELETE) \ diff --git a/vendor/http-parser/test.c b/vendor/http-parser/test.c index f5744aa07f8..193004e2cef 100644 --- a/vendor/http-parser/test.c +++ b/vendor/http-parser/test.c @@ -78,6 +78,7 @@ struct message { int message_begin_cb_called; int headers_complete_cb_called; int message_complete_cb_called; + int status_cb_called; int message_complete_on_eof; int body_is_final; }; @@ -1131,7 +1132,7 @@ const struct message requests[] = } #define UNLINK_REQUEST 41 -, {.name = "link request" +, {.name = "unlink request" ,.type= HTTP_REQUEST ,.raw= "UNLINK /images/my_dog.jpg HTTP/1.1\r\n" "Host: example.com\r\n" @@ -1771,6 +1772,167 @@ const struct message responses[] = ,.chunk_lengths= { 2 } } +#define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER 22 +, {.name= "HTTP 101 response with Upgrade header" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 101 Switching Protocols\r\n" + "Connection: upgrade\r\n" + "Upgrade: h2c\r\n" + "\r\n" + "proto" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 101 + ,.response_status= "Switching Protocols" + ,.upgrade= "proto" + ,.num_headers= 2 + ,.headers= + { { "Connection", "upgrade" } + , { "Upgrade", "h2c" } + } + } + +#define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER_AND_CONTENT_LENGTH 23 +, {.name= "HTTP 101 response with Upgrade and Content-Length header" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 101 Switching Protocols\r\n" + "Connection: upgrade\r\n" + "Upgrade: h2c\r\n" + "Content-Length: 4\r\n" + "\r\n" + "body" + "proto" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 101 + ,.response_status= "Switching Protocols" + ,.body= "body" + ,.upgrade= "proto" + ,.num_headers= 3 + ,.headers= + { { "Connection", "upgrade" } + , { "Upgrade", "h2c" } + , { "Content-Length", "4" } + } + } + +#define HTTP_101_RESPONSE_WITH_UPGRADE_HEADER_AND_TRANSFER_ENCODING 24 +, {.name= "HTTP 101 response with Upgrade and Transfer-Encoding header" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 101 Switching Protocols\r\n" + "Connection: upgrade\r\n" + "Upgrade: h2c\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "2\r\n" + "bo\r\n" + "2\r\n" + "dy\r\n" + "0\r\n" + "\r\n" + "proto" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 101 + ,.response_status= "Switching Protocols" + ,.body= "body" + ,.upgrade= "proto" + ,.num_headers= 3 + ,.headers= + { { "Connection", "upgrade" } + , { "Upgrade", "h2c" } + , { "Transfer-Encoding", "chunked" } + } + ,.num_chunks_complete= 3 + ,.chunk_lengths= { 2, 2 } + } + +#define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER 25 +, {.name= "HTTP 200 response with Upgrade header" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Connection: upgrade\r\n" + "Upgrade: h2c\r\n" + "\r\n" + "body" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.body= "body" + ,.upgrade= NULL + ,.num_headers= 2 + ,.headers= + { { "Connection", "upgrade" } + , { "Upgrade", "h2c" } + } + } + +#define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER_AND_CONTENT_LENGTH 26 +, {.name= "HTTP 200 response with Upgrade and Content-Length header" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Connection: upgrade\r\n" + "Upgrade: h2c\r\n" + "Content-Length: 4\r\n" + "\r\n" + "body" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 3 + ,.body= "body" + ,.upgrade= NULL + ,.headers= + { { "Connection", "upgrade" } + , { "Upgrade", "h2c" } + , { "Content-Length", "4" } + } + } + +#define HTTP_200_RESPONSE_WITH_UPGRADE_HEADER_AND_TRANSFER_ENCODING 27 +, {.name= "HTTP 200 response with Upgrade and Transfer-Encoding header" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Connection: upgrade\r\n" + "Upgrade: h2c\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "2\r\n" + "bo\r\n" + "2\r\n" + "dy\r\n" + "0\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 3 + ,.body= "body" + ,.upgrade= NULL + ,.headers= + { { "Connection", "upgrade" } + , { "Upgrade", "h2c" } + , { "Transfer-Encoding", "chunked" } + } + ,.num_chunks_complete= 3 + ,.chunk_lengths= { 2, 2 } + } + , {.name= NULL } /* sentinel */ }; @@ -1981,6 +2143,9 @@ int response_status_cb (http_parser *p, const char *buf, size_t len) { assert(p == parser); + + messages[num_messages].status_cb_called = TRUE; + strlncat(messages[num_messages].response_status, sizeof(messages[num_messages].response_status), buf, @@ -2405,6 +2570,7 @@ message_eq (int index, int connect, const struct message *expected) } else { MESSAGE_CHECK_NUM_EQ(expected, m, status_code); MESSAGE_CHECK_STR_EQ(expected, m, response_status); + assert(m->status_cb_called); } if (!connect) { @@ -2476,7 +2642,9 @@ message_eq (int index, int connect, const struct message *expected) if (!r) return 0; } - MESSAGE_CHECK_STR_EQ(expected, m, upgrade); + if (!connect) { + MESSAGE_CHECK_STR_EQ(expected, m, upgrade); + } return 1; } @@ -3313,9 +3481,11 @@ test_message_count_body (const struct message *message) } void -test_simple (const char *buf, enum http_errno err_expected) +test_simple_type (const char *buf, + enum http_errno err_expected, + enum http_parser_type type) { - parser_init(HTTP_REQUEST); + parser_init(type); enum http_errno err; @@ -3339,6 +3509,12 @@ test_simple (const char *buf, enum http_errno err_expected) } } +void +test_simple (const char *buf, enum http_errno err_expected) +{ + test_simple_type(buf, err_expected, HTTP_REQUEST); +} + void test_invalid_header_content (int req, const char* str) { @@ -3949,6 +4125,12 @@ main (void) //// RESPONSES + test_simple_type("HTP/1.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE); + test_simple_type("HTTP/01.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE); + test_simple_type("HTTP/11.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE); + test_simple_type("HTTP/1.01 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE); + test_simple_type("HTTP/1.1\t200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE); + for (i = 0; i < response_count; i++) { test_message(&responses[i]); } @@ -4026,6 +4208,9 @@ main (void) /// REQUESTS test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION); + test_simple("GET / HTTP/01.1\r\n\r\n", HPE_INVALID_VERSION); + test_simple("GET / HTTP/11.1\r\n\r\n", HPE_INVALID_VERSION); + test_simple("GET / HTTP/1.01\r\n\r\n", HPE_INVALID_VERSION); // Extended characters - see nodejs/test/parallel/test-http-headers-obstext.js test_simple("GET / HTTP/1.1\r\n"