From 36a7034802ae33f1d4e8b7502ae4c378e5677997 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Wed, 8 Jun 2016 01:00:25 -0400 Subject: [PATCH] deps: update to http-parser 2.7.0 Adds `2` as a return value of `on_headers_complete`, this mode will be used to fix handling responses to `CONNECT` requests. See: https://github.com/nodejs/node/pull/6198 PR-URL: https://github.com/nodejs/node/pull/6279 Reviewed-By: Brian White Reviewed-By: James M Snell --- deps/http_parser/AUTHORS | 1 + deps/http_parser/Makefile | 31 +++- deps/http_parser/README.md | 71 +++++++- deps/http_parser/contrib/url_parser.c | 3 +- deps/http_parser/http_parser.c | 157 +++++++++--------- deps/http_parser/http_parser.h | 48 ++++-- deps/http_parser/test.c | 226 +++++++++++++++++++++++--- 7 files changed, 415 insertions(+), 122 deletions(-) diff --git a/deps/http_parser/AUTHORS b/deps/http_parser/AUTHORS index 8e2df1d06e6f69..5323b685caefb9 100644 --- a/deps/http_parser/AUTHORS +++ b/deps/http_parser/AUTHORS @@ -65,3 +65,4 @@ Romain Giraud Jay Satiro Arne Steen Kjell Schubert +Olivier Mengué diff --git a/deps/http_parser/Makefile b/deps/http_parser/Makefile index b96b3e266bcf5f..5f4eb2252f241b 100644 --- a/deps/http_parser/Makefile +++ b/deps/http_parser/Makefile @@ -19,7 +19,19 @@ # IN THE SOFTWARE. PLATFORM ?= $(shell sh -c 'uname -s | tr "[A-Z]" "[a-z]"') -SONAME ?= libhttp_parser.so.2.5.2 +HELPER ?= +BINEXT ?= +ifeq (darwin,$(PLATFORM)) +SONAME ?= libhttp_parser.2.7.0.dylib +SOEXT ?= dylib +else ifeq (wine,$(PLATFORM)) +CC = winegcc +BINEXT = .exe.so +HELPER = wine +else +SONAME ?= libhttp_parser.so.2.7.0 +SOEXT ?= so +endif CC?=gcc AR?=ar @@ -53,8 +65,8 @@ LDFLAGS_LIB += -Wl,-soname=$(SONAME) endif test: test_g test_fast - ./test_g - ./test_fast + $(HELPER) ./test_g$(BINEXT) + $(HELPER) ./test_fast$(BINEXT) test_g: http_parser_g.o test_g.o $(CC) $(CFLAGS_DEBUG) $(LDFLAGS) http_parser_g.o test_g.o -o $@ @@ -81,7 +93,7 @@ http_parser.o: http_parser.c http_parser.h Makefile $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c http_parser.c test-run-timed: test_fast - while(true) do time ./test_fast > /dev/null; done + while(true) do time $(HELPER) ./test_fast$(BINEXT) > /dev/null; done test-valgrind: test_g valgrind ./test_g @@ -102,10 +114,10 @@ url_parser_g: http_parser_g.o contrib/url_parser.c $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) $^ -o $@ parsertrace: http_parser.o contrib/parsertrace.c - $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) $^ -o parsertrace + $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) $^ -o parsertrace$(BINEXT) parsertrace_g: http_parser_g.o contrib/parsertrace.c - $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) $^ -o parsertrace_g + $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) $^ -o parsertrace_g$(BINEXT) tags: http_parser.c http_parser.h test.c ctags $^ @@ -113,12 +125,12 @@ tags: http_parser.c http_parser.h test.c install: library $(INSTALL) -D http_parser.h $(INCLUDEDIR)/http_parser.h $(INSTALL) -D $(SONAME) $(LIBDIR)/$(SONAME) - ln -s $(LIBDIR)/$(SONAME) $(LIBDIR)/libhttp_parser.so + ln -s $(LIBDIR)/$(SONAME) $(LIBDIR)/libhttp_parser.$(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.so + ln -s $(LIBDIR)/$(SONAME) $(LIBDIR)/libhttp_parser.$(SOEXT) uninstall: rm $(INCLUDEDIR)/http_parser.h @@ -128,7 +140,8 @@ uninstall: clean: rm -f *.o *.a tags test test_fast test_g \ http_parser.tar libhttp_parser.so.* \ - url_parser url_parser_g parsertrace parsertrace_g + url_parser url_parser_g parsertrace parsertrace_g \ + *.exe *.exe.so contrib/url_parser.c: http_parser.h contrib/parsertrace.c: http_parser.h diff --git a/deps/http_parser/README.md b/deps/http_parser/README.md index 7c54dd42d087c3..439b30998d43dd 100644 --- a/deps/http_parser/README.md +++ b/deps/http_parser/README.md @@ -1,7 +1,7 @@ HTTP Parser =========== -[![Build Status](https://travis-ci.org/joyent/http-parser.png?branch=master)](https://travis-ci.org/joyent/http-parser) +[![Build Status](https://api.travis-ci.org/nodejs/http-parser.svg?branch=master)](https://travis-ci.org/nodejs/http-parser) This is a parser for HTTP messages written in C. It parses both requests and responses. The parser is designed to be used in performance HTTP @@ -94,7 +94,7 @@ The Special Problem of Upgrade ------------------------------ HTTP supports upgrading the connection to a different protocol. An -increasingly common example of this is the Web Socket protocol which sends +increasingly common example of this is the WebSocket protocol which sends a request like GET /demo HTTP/1.1 @@ -106,8 +106,8 @@ a request like followed by non-HTTP data. -(See http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more -information the Web Socket protocol.) +(See [RFC6455](https://tools.ietf.org/html/rfc6455) for more information the +WebSocket protocol.) To support this, the parser will treat this as a normal HTTP message without a body, issuing both on_headers_complete and on_message_complete callbacks. However @@ -137,6 +137,69 @@ There are two types of callbacks: Callbacks must return 0 on success. Returning a non-zero value indicates error to the parser, making it exit immediately. +For cases where it is necessary to pass local information to/from a callback, +the `http_parser` object's `data` field can be used. +An example of such a case is when using threads to handle a socket connection, +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 +multi-threaded contexts. + +Example: +```c + typedef struct { + socket_t sock; + void* buffer; + int buf_len; + } custom_data_t; + + +int my_url_callback(http_parser* parser, const char *at, size_t length) { + /* access to thread local custom_data_t struct. + Use this access save parsed data for later use into thread local + buffer, or communicate over socket + */ + parser->data; + ... + return 0; +} + +... + +void http_parser_thread(socket_t sock) { + int nparsed = 0; + /* allocate memory for user data */ + custom_data_t *my_data = malloc(sizeof(custom_data_t)); + + /* some information for use by callbacks. + * achieves thread -> callback information flow */ + my_data->sock = sock; + + /* instantiate a thread-local parser */ + http_parser *parser = malloc(sizeof(http_parser)); + http_parser_init(parser, HTTP_REQUEST); /* initialise parser */ + /* this custom data reference is accessible through the reference to the + parser supplied to callback functions */ + parser->data = my_data; + + http_parser_settings settings; /* set up callbacks */ + settings.on_url = my_url_callback; + + /* execute parser */ + nparsed = http_parser_execute(parser, &settings, buf, recved); + + ... + /* parsed information copied from callback. + can now perform action on data copied into thread-local memory from callbacks. + achieves callback -> thread information flow */ + my_data->buffer; + ... +} + +``` + 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 diff --git a/deps/http_parser/contrib/url_parser.c b/deps/http_parser/contrib/url_parser.c index 6650b414af9065..f235bed9e483dc 100644 --- a/deps/http_parser/contrib/url_parser.c +++ b/deps/http_parser/contrib/url_parser.c @@ -35,6 +35,7 @@ int main(int argc, char ** argv) { connect = strcmp("connect", argv[1]) == 0 ? 1 : 0; printf("Parsing %s, connect %d\n", argv[2], connect); + http_parser_url_init(&u); result = http_parser_parse_url(argv[2], len, connect, &u); if (result != 0) { printf("Parse error : %d\n", result); @@ -43,4 +44,4 @@ int main(int argc, char ** argv) { printf("Parse ok, result : \n"); dump_url(argv[2], &u); return 0; -} \ No newline at end of file +} diff --git a/deps/http_parser/http_parser.c b/deps/http_parser/http_parser.c index 228d130cb5e30f..3c896ffadcc0db 100644 --- a/deps/http_parser/http_parser.c +++ b/deps/http_parser/http_parser.c @@ -123,7 +123,7 @@ do { \ FOR##_mark = NULL; \ } \ } while (0) - + /* Run the data callback FOR and consume the current byte */ #define CALLBACK_DATA(FOR) \ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) @@ -400,6 +400,8 @@ enum http_host_state , s_http_host , s_http_host_v6 , s_http_host_v6_end + , s_http_host_v6_zone_start + , s_http_host_v6_zone , s_http_host_port_start , s_http_host_port }; @@ -643,7 +645,6 @@ size_t http_parser_execute (http_parser *parser, const char *body_mark = 0; const char *status_mark = 0; enum state p_state = (enum state) parser->state; - const unsigned int lenient = parser->lenient_http_headers; /* We're in an error state. Don't bother doing anything. */ @@ -965,21 +966,23 @@ size_t http_parser_execute (http_parser *parser, parser->method = (enum http_method) 0; parser->index = 1; switch (ch) { + case 'A': parser->method = HTTP_ACL; break; + case 'B': parser->method = HTTP_BIND; break; case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; case 'D': parser->method = HTTP_DELETE; break; case 'G': parser->method = HTTP_GET; break; case 'H': parser->method = HTTP_HEAD; break; - case 'L': parser->method = HTTP_LOCK; break; + case 'L': parser->method = HTTP_LOCK; /* or LINK */ break; case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; case 'N': parser->method = HTTP_NOTIFY; break; case 'O': parser->method = HTTP_OPTIONS; break; case 'P': parser->method = HTTP_POST; /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ break; - case 'R': parser->method = HTTP_REPORT; break; + case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break; case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break; case 'T': parser->method = HTTP_TRACE; break; - case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; + case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break; default: SET_ERRNO(HPE_INVALID_METHOD); goto error; @@ -1004,69 +1007,40 @@ size_t http_parser_execute (http_parser *parser, UPDATE_STATE(s_req_spaces_before_url); } else if (ch == matcher[parser->index]) { ; /* nada */ - } else if (parser->method == HTTP_CONNECT) { - if (parser->index == 1 && ch == 'H') { - parser->method = HTTP_CHECKOUT; - } else if (parser->index == 2 && ch == 'P') { - parser->method = HTTP_COPY; - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - } else if (parser->method == HTTP_MKCOL) { - if (parser->index == 1 && ch == 'O') { - parser->method = HTTP_MOVE; - } else if (parser->index == 1 && ch == 'E') { - parser->method = HTTP_MERGE; - } else if (parser->index == 1 && ch == '-') { - parser->method = HTTP_MSEARCH; - } else if (parser->index == 2 && ch == 'A') { - parser->method = HTTP_MKACTIVITY; - } else if (parser->index == 3 && ch == 'A') { - parser->method = HTTP_MKCALENDAR; - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - } else if (parser->method == HTTP_SUBSCRIBE) { - if (parser->index == 1 && ch == 'E') { - parser->method = HTTP_SEARCH; - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - } else if (parser->index == 1 && parser->method == HTTP_POST) { - if (ch == 'R') { - parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */ - } else if (ch == 'U') { - parser->method = HTTP_PUT; /* or HTTP_PURGE */ - } else if (ch == 'A') { - parser->method = HTTP_PATCH; - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - } else if (parser->index == 2) { - if (parser->method == HTTP_PUT) { - if (ch == 'R') { - parser->method = HTTP_PURGE; - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - } else if (parser->method == HTTP_UNLOCK) { - if (ch == 'S') { - parser->method = HTTP_UNSUBSCRIBE; - } else { + } else if (IS_ALPHA(ch)) { + + switch (parser->method << 16 | parser->index << 8 | ch) { +#define XX(meth, pos, ch, new_meth) \ + case (HTTP_##meth << 16 | pos << 8 | ch): \ + parser->method = HTTP_##new_meth; break; + + XX(POST, 1, 'U', PUT) + XX(POST, 1, 'A', PATCH) + XX(CONNECT, 1, 'H', CHECKOUT) + XX(CONNECT, 2, 'P', COPY) + XX(MKCOL, 1, 'O', MOVE) + XX(MKCOL, 1, 'E', MERGE) + 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 { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; } - } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') { - parser->method = HTTP_PROPPATCH; + } else if (ch == '-' && + parser->index == 1 && + parser->method == HTTP_MKCOL) { + parser->method = HTTP_MSEARCH; } else { SET_ERRNO(HPE_INVALID_METHOD); goto error; @@ -1838,6 +1812,9 @@ size_t http_parser_execute (http_parser *parser, case 0: break; + case 2: + parser->upgrade = 1; + case 1: parser->flags |= F_SKIPBODY; break; @@ -1857,11 +1834,12 @@ size_t http_parser_execute (http_parser *parser, case s_headers_done: { + int hasBody; STRICT_CHECK(ch != LF); parser->nread = 0; - int hasBody = parser->flags & F_CHUNKED || + hasBody = parser->flags & F_CHUNKED || (parser->content_length > 0 && parser->content_length != ULLONG_MAX); if (parser->upgrade && (parser->method == HTTP_CONNECT || (parser->flags & F_SKIPBODY) || !hasBody)) { @@ -1886,8 +1864,7 @@ size_t http_parser_execute (http_parser *parser, /* Content-Length header given and non-zero */ UPDATE_STATE(s_body_identity); } else { - if (parser->type == HTTP_REQUEST || - !http_message_needs_eof(parser)) { + if (!http_message_needs_eof(parser)) { /* Assume content-length 0 - read the next */ UPDATE_STATE(NEW_MESSAGE()); CALLBACK_NOTIFY(message_complete); @@ -2182,15 +2159,13 @@ http_parser_settings_init(http_parser_settings *settings) const char * http_errno_name(enum http_errno err) { - assert(((size_t) err) < - (sizeof(http_strerror_tab) / sizeof(http_strerror_tab[0]))); + assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab)); return http_strerror_tab[err].name; } const char * http_errno_description(enum http_errno err) { - assert(((size_t) err) < - (sizeof(http_strerror_tab) / sizeof(http_strerror_tab[0]))); + assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab)); return http_strerror_tab[err].description; } @@ -2243,6 +2218,23 @@ http_parse_host_char(enum http_host_state s, const char ch) { return s_http_host_v6; } + if (s == s_http_host_v6 && ch == '%') { + return s_http_host_v6_zone_start; + } + break; + + case s_http_host_v6_zone: + if (ch == ']') { + return s_http_host_v6_end; + } + + /* FALLTHROUGH */ + case s_http_host_v6_zone_start: + /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */ + if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' || + ch == '~') { + return s_http_host_v6_zone; + } break; case s_http_host_port: @@ -2266,6 +2258,8 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { const char *p; size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; + assert(u->field_set & (1 << UF_HOST)); + u->field_data[UF_HOST].len = 0; s = found_at ? s_http_userinfo_start : s_http_host_start; @@ -2292,6 +2286,11 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { u->field_data[UF_HOST].len++; break; + case s_http_host_v6_zone_start: + case s_http_host_v6_zone: + u->field_data[UF_HOST].len++; + break; + case s_http_host_port: if (s != s_http_host_port) { u->field_data[UF_PORT].off = p - buf; @@ -2321,6 +2320,8 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { case s_http_host_start: case s_http_host_v6_start: case s_http_host_v6: + case s_http_host_v6_zone_start: + case s_http_host_v6_zone: case s_http_host_port_start: case s_http_userinfo: case s_http_userinfo_start: @@ -2332,6 +2333,11 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { return 0; } +void +http_parser_url_init(struct http_parser_url *u) { + memset(u, 0, sizeof(*u)); +} + int http_parser_parse_url(const char *buf, size_t buflen, int is_connect, struct http_parser_url *u) @@ -2405,7 +2411,12 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect, /* host must be present if there is a schema */ /* parsing http:///toto will fail */ - if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) { + if ((u->field_set & (1 << UF_SCHEMA)) && + (u->field_set & (1 << UF_HOST)) == 0) { + return 1; + } + + if (u->field_set & (1 << UF_HOST)) { if (http_parse_host(buf, u, found_at) != 0) { return 1; } diff --git a/deps/http_parser/http_parser.h b/deps/http_parser/http_parser.h index 39b3f23c5a13f5..105ae510a8ab57 100644 --- a/deps/http_parser/http_parser.h +++ b/deps/http_parser/http_parser.h @@ -26,11 +26,12 @@ extern "C" { /* Also update SONAME in the Makefile whenever you change these. */ #define HTTP_PARSER_VERSION_MAJOR 2 -#define HTTP_PARSER_VERSION_MINOR 5 -#define HTTP_PARSER_VERSION_PATCH 2 +#define HTTP_PARSER_VERSION_MINOR 7 +#define HTTP_PARSER_VERSION_PATCH 0 #include -#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) +#if defined(_WIN32) && !defined(__MINGW32__) && \ + (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__) #include #include typedef __int8 int8_t; @@ -76,6 +77,11 @@ typedef struct http_parser_settings http_parser_settings; * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: * chunked' headers that indicate the presence of a body. * + * Returning `2` from on_headers_complete will tell parser that it should not + * expect neither a body nor any futher responses on this connection. This is + * useful for handling responses to a CONNECT request which may not contain + * `Upgrade` or `Connection: upgrade` headers. + * * http_data_cb does not return data chunks. It will be called arbitrarily * many times for each string. E.G. you might get 10 callbacks for "on_url" * each providing just a few characters more data. @@ -95,7 +101,7 @@ typedef int (*http_cb) (http_parser*); XX(5, CONNECT, CONNECT) \ XX(6, OPTIONS, OPTIONS) \ XX(7, TRACE, TRACE) \ - /* webdav */ \ + /* WebDAV */ \ XX(8, COPY, COPY) \ XX(9, LOCK, LOCK) \ XX(10, MKCOL, MKCOL) \ @@ -104,21 +110,28 @@ typedef int (*http_cb) (http_parser*); XX(13, PROPPATCH, PROPPATCH) \ XX(14, SEARCH, SEARCH) \ XX(15, UNLOCK, UNLOCK) \ + XX(16, BIND, BIND) \ + XX(17, REBIND, REBIND) \ + XX(18, UNBIND, UNBIND) \ + XX(19, ACL, ACL) \ /* subversion */ \ - XX(16, REPORT, REPORT) \ - XX(17, MKACTIVITY, MKACTIVITY) \ - XX(18, CHECKOUT, CHECKOUT) \ - XX(19, MERGE, MERGE) \ + XX(20, REPORT, REPORT) \ + XX(21, MKACTIVITY, MKACTIVITY) \ + XX(22, CHECKOUT, CHECKOUT) \ + XX(23, MERGE, MERGE) \ /* upnp */ \ - XX(20, MSEARCH, M-SEARCH) \ - XX(21, NOTIFY, NOTIFY) \ - XX(22, SUBSCRIBE, SUBSCRIBE) \ - XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \ + XX(24, MSEARCH, M-SEARCH) \ + XX(25, NOTIFY, NOTIFY) \ + XX(26, SUBSCRIBE, SUBSCRIBE) \ + XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ /* RFC-5789 */ \ - XX(24, PATCH, PATCH) \ - XX(25, PURGE, PURGE) \ + XX(28, PATCH, PATCH) \ + XX(29, PURGE, PURGE) \ /* CalDAV */ \ - XX(26, MKCALENDAR, MKCALENDAR) \ + XX(30, MKCALENDAR, MKCALENDAR) \ + /* RFC-2068, section 19.6.1.2 */ \ + XX(31, LINK, LINK) \ + XX(32, UNLINK, UNLINK) \ enum http_method { @@ -145,7 +158,7 @@ enum flags /* Map for errno-related constants - * + * * The provided argument should be a macro that takes 2 arguments. */ #define HTTP_ERRNO_MAP(XX) \ @@ -329,6 +342,9 @@ const char *http_errno_name(enum http_errno err); /* Return a string description of the given error */ const char *http_errno_description(enum http_errno err); +/* Initialize all http_parser_url members to 0 */ +void http_parser_url_init(struct http_parser_url *u); + /* Parse a URL; return nonzero on failure */ int http_parser_parse_url(const char *buf, size_t buflen, int is_connect, diff --git a/deps/http_parser/test.c b/deps/http_parser/test.c index 5f754d83c6ac6a..456a78add05e68 100644 --- a/deps/http_parser/test.c +++ b/deps/http_parser/test.c @@ -1101,6 +1101,58 @@ const struct message requests[] = ,.body= "" } +/* Examples from the Internet draft for LINK/UNLINK methods: + * https://tools.ietf.org/id/draft-snell-link-method-01.html#rfc.section.5 + */ + +#define LINK_REQUEST 40 +, {.name = "link request" + ,.type= HTTP_REQUEST + ,.raw= "LINK /images/my_dog.jpg HTTP/1.1\r\n" + "Host: example.com\r\n" + "Link: ; rel=\"tag\"\r\n" + "Link: ; rel=\"tag\"\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_LINK + ,.request_path= "/images/my_dog.jpg" + ,.request_url= "/images/my_dog.jpg" + ,.query_string= "" + ,.fragment= "" + ,.num_headers= 3 + ,.headers= { { "Host", "example.com" } + , { "Link", "; rel=\"tag\"" } + , { "Link", "; rel=\"tag\"" } + } + ,.body= "" + } + +#define UNLINK_REQUEST 41 +, {.name = "link request" + ,.type= HTTP_REQUEST + ,.raw= "UNLINK /images/my_dog.jpg HTTP/1.1\r\n" + "Host: example.com\r\n" + "Link: ; rel=\"tag\"\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_UNLINK + ,.request_path= "/images/my_dog.jpg" + ,.request_url= "/images/my_dog.jpg" + ,.query_string= "" + ,.fragment= "" + ,.num_headers= 2 + ,.headers= { { "Host", "example.com" } + , { "Link", "; rel=\"tag\"" } + } + ,.body= "" + } + , {.name= NULL } /* sentinel */ }; @@ -2121,6 +2173,20 @@ pause_chunk_complete_cb (http_parser *p) return chunk_complete_cb(p); } +int +connect_headers_complete_cb (http_parser *p) +{ + headers_complete_cb(p); + return 1; +} + +int +connect_message_complete_cb (http_parser *p) +{ + messages[num_messages].should_keep_alive = http_should_keep_alive(parser); + return message_complete_cb(p); +} + static http_parser_settings settings_pause = {.on_message_begin = pause_message_begin_cb ,.on_header_field = pause_header_field_cb @@ -2160,6 +2226,19 @@ static http_parser_settings settings_count_body = ,.on_chunk_complete = chunk_complete_cb }; +static http_parser_settings settings_connect = + {.on_message_begin = message_begin_cb + ,.on_header_field = header_field_cb + ,.on_header_value = header_value_cb + ,.on_url = request_url_cb + ,.on_status = response_status_cb + ,.on_body = dontcall_body_cb + ,.on_headers_complete = connect_headers_complete_cb + ,.on_message_complete = connect_message_complete_cb + ,.on_chunk_header = chunk_header_cb + ,.on_chunk_complete = chunk_complete_cb + }; + static http_parser_settings settings_null = {.on_message_begin = 0 ,.on_header_field = 0 @@ -2223,6 +2302,14 @@ size_t parse_pause (const char *buf, size_t len) return nparsed; } +size_t parse_connect (const char *buf, size_t len) +{ + size_t nparsed; + currently_parsing_eof = (len == 0); + nparsed = http_parser_execute(parser, &settings_connect, buf, len); + return nparsed; +} + static inline int check_str_eq (const struct message *m, const char *prop, @@ -2279,7 +2366,7 @@ do { \ } while(0) int -message_eq (int index, const struct message *expected) +message_eq (int index, int connect, const struct message *expected) { int i; struct message *m = &messages[index]; @@ -2294,8 +2381,10 @@ message_eq (int index, const struct message *expected) MESSAGE_CHECK_STR_EQ(expected, m, response_status); } - MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive); - MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof); + if (!connect) { + MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive); + MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof); + } assert(m->message_begin_cb_called); assert(m->headers_complete_cb_called); @@ -2333,16 +2422,22 @@ message_eq (int index, const struct message *expected) MESSAGE_CHECK_NUM_EQ(expected, m, port); } - if (expected->body_size) { + if (connect) { + check_num_eq(m, "body_size", 0, m->body_size); + } else if (expected->body_size) { MESSAGE_CHECK_NUM_EQ(expected, m, body_size); } else { MESSAGE_CHECK_STR_EQ(expected, m, body); } - assert(m->num_chunks == m->num_chunks_complete); - MESSAGE_CHECK_NUM_EQ(expected, m, num_chunks_complete); - for (i = 0; i < m->num_chunks && i < MAX_CHUNKS; i++) { - MESSAGE_CHECK_NUM_EQ(expected, m, chunk_lengths[i]); + if (connect) { + check_num_eq(m, "num_chunks_complete", 0, m->num_chunks_complete); + } else { + assert(m->num_chunks == m->num_chunks_complete); + MESSAGE_CHECK_NUM_EQ(expected, m, num_chunks_complete); + for (i = 0; i < m->num_chunks && i < MAX_CHUNKS; i++) { + MESSAGE_CHECK_NUM_EQ(expected, m, chunk_lengths[i]); + } } MESSAGE_CHECK_NUM_EQ(expected, m, num_headers); @@ -2392,7 +2487,7 @@ upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) { va_list ap; size_t i; size_t off = 0; - + va_start(ap, nmsgs); for (i = 0; i < nmsgs; i++) { @@ -2918,6 +3013,59 @@ const struct url_test url_tests[] = ,.rv=1 /* s_dead */ } +, {.name="ipv6 address with Zone ID" + ,.url="http://[fe80::a%25eth0]/" + ,.is_connect=0 + ,.u= + {.field_set= (1< 1 && !message_eq(1, r2)) abort(); - if (message_count > 2 && !message_eq(2, r3)) abort(); + if (!message_eq(0, 0, r1)) abort(); + if (message_count > 1 && !message_eq(1, 0, r2)) abort(); + if (message_count > 2 && !message_eq(2, 0, r3)) abort(); parser_free(); } @@ -3582,17 +3730,17 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess goto error; } - if (!message_eq(0, r1)) { + if (!message_eq(0, 0, r1)) { fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n"); goto error; } - if (message_count > 1 && !message_eq(1, r2)) { + if (message_count > 1 && !message_eq(1, 0, r2)) { fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n"); goto error; } - if (message_count > 2 && !message_eq(2, r3)) { + if (message_count > 2 && !message_eq(2, 0, r3)) { fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n"); goto error; } @@ -3691,7 +3839,29 @@ test_message_pause (const struct message *msg) abort(); } - if(!message_eq(0, msg)) abort(); + if(!message_eq(0, 0, msg)) abort(); + + parser_free(); +} + +/* Verify that body and next message won't be parsed in responses to CONNECT */ +void +test_message_connect (const struct message *msg) +{ + char *buf = (char*) msg->raw; + size_t buflen = strlen(msg->raw); + size_t nread; + + parser_init(msg->type); + + nread = parse_connect(buf, buflen); + + if (num_messages != 1) { + printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name); + abort(); + } + + if(!message_eq(0, 1, msg)) abort(); parser_free(); } @@ -3762,6 +3932,10 @@ main (void) test_message_pause(&responses[i]); } + for (i = 0; i < response_count; i++) { + test_message_connect(&responses[i]); + } + for (i = 0; i < response_count; i++) { if (!responses[i].should_keep_alive) continue; for (j = 0; j < response_count; j++) { @@ -3828,6 +4002,11 @@ main (void) test_simple("GET / HTP/1.1\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" + "Test: Düsseldorf\r\n", + HPE_OK); + // Well-formed but incomplete test_simple("GET / HTTP/1.1\r\n" "Content-Type: text/plain\r\n" @@ -3851,7 +4030,12 @@ main (void) "MOVE", "PROPFIND", "PROPPATCH", + "SEARCH", "UNLOCK", + "BIND", + "REBIND", + "UNBIND", + "ACL", "REPORT", "MKACTIVITY", "CHECKOUT", @@ -3861,6 +4045,10 @@ main (void) "SUBSCRIBE", "UNSUBSCRIBE", "PATCH", + "PURGE", + "MKCALENDAR", + "LINK", + "UNLINK", 0 }; const char **this_method; for (this_method = all_methods; *this_method; this_method++) {