summaryrefslogtreecommitdiff
path: root/deps/http_parser
diff options
context:
space:
mode:
authorFedor Indutny <fedor@indutny.com>2015-01-14 02:57:45 +0300
committerFedor Indutny <fedor@indutny.com>2015-01-14 03:10:18 +0300
commitd790f612218bb34580bfbc6a11027d69b7c148fe (patch)
treedc64a5459a87388cf1c2459715cc6bd9637bdb4b /deps/http_parser
parent03d199276e21c1fa08d8df14eeb654c90cc5aa20 (diff)
downloadandroid-node-v8-d790f612218bb34580bfbc6a11027d69b7c148fe.tar.gz
android-node-v8-d790f612218bb34580bfbc6a11027d69b7c148fe.tar.bz2
android-node-v8-d790f612218bb34580bfbc6a11027d69b7c148fe.zip
deps: update http_parser to 2.4.0
PR-URL: https://github.com/iojs/io.js/pull/345 Reviewed-By: Rod Vagg <rod@vagg.org>
Diffstat (limited to 'deps/http_parser')
-rw-r--r--deps/http_parser/.gitignore1
-rw-r--r--deps/http_parser/.mailmap1
-rw-r--r--deps/http_parser/.travis.yml2
-rw-r--r--deps/http_parser/AUTHORS15
-rw-r--r--deps/http_parser/CONTRIBUTIONS4
-rw-r--r--deps/http_parser/Makefile10
-rw-r--r--deps/http_parser/README.md10
-rw-r--r--deps/http_parser/bench.c111
-rw-r--r--deps/http_parser/contrib/parsertrace.c12
-rw-r--r--deps/http_parser/http_parser.c617
-rw-r--r--deps/http_parser/http_parser.h30
-rw-r--r--deps/http_parser/test.c52
12 files changed, 601 insertions, 264 deletions
diff --git a/deps/http_parser/.gitignore b/deps/http_parser/.gitignore
index 594f3047f4..32cb51b2d3 100644
--- a/deps/http_parser/.gitignore
+++ b/deps/http_parser/.gitignore
@@ -5,6 +5,7 @@ tags
test
test_g
test_fast
+bench
url_parser
parsertrace
parsertrace_g
diff --git a/deps/http_parser/.mailmap b/deps/http_parser/.mailmap
index 75a187c568..278d141263 100644
--- a/deps/http_parser/.mailmap
+++ b/deps/http_parser/.mailmap
@@ -5,3 +5,4 @@ Salman Haq <salman.haq@asti-usa.com>
Simon Zimmermann <simonz05@gmail.com>
Thomas LE ROUX <thomas@november-eleven.fr> LE ROUX Thomas <thomas@procheo.fr>
Thomas LE ROUX <thomas@november-eleven.fr> Thomas LE ROUX <thomas@procheo.fr>
+Fedor Indutny <fedor@indutny.com>
diff --git a/deps/http_parser/.travis.yml b/deps/http_parser/.travis.yml
index ae85af020a..4b038e6e62 100644
--- a/deps/http_parser/.travis.yml
+++ b/deps/http_parser/.travis.yml
@@ -10,4 +10,4 @@ script:
notifications:
email: false
irc:
- - "irc.freenode.net#libuv"
+ - "irc.freenode.net#node-ci"
diff --git a/deps/http_parser/AUTHORS b/deps/http_parser/AUTHORS
index 51b53b1253..29cdbb16d3 100644
--- a/deps/http_parser/AUTHORS
+++ b/deps/http_parser/AUTHORS
@@ -39,12 +39,25 @@ BogDan Vatra <bogdan@kde.org>
Peter Faiman <peter@thepicard.org>
Corey Richardson <corey@octayn.net>
Tóth Tamás <tomika_nospam@freemail.hu>
-Patrik Stutz <patrik.stutz@gmail.com>
Cam Swords <cam.swords@gmail.com>
Chris Dickinson <christopher.s.dickinson@gmail.com>
Uli Köhler <ukoehler@btronik.de>
Charlie Somerville <charlie@charliesomerville.com>
+Patrik Stutz <patrik.stutz@gmail.com>
Fedor Indutny <fedor.indutny@gmail.com>
runner <runner.mei@gmail.com>
Alexis Campailla <alexis@janeasystems.com>
David Wragg <david@wragg.org>
+Vinnie Falco <vinnie.falco@gmail.com>
+Alex Butum <alexbutum@linux.com>
+Rex Feng <rexfeng@gmail.com>
+Alex Kocharin <alex@kocharin.ru>
+Mark Koopman <markmontymark@yahoo.com>
+Helge Heß <me@helgehess.eu>
+Alexis La Goutte <alexis.lagoutte@gmail.com>
+George Miroshnykov <george.miroshnykov@gmail.com>
+Maciej Małecki <me@mmalecki.com>
+Marc O'Morain <github.com@marcomorain.com>
+Jeff Pinner <jpinner@twitter.com>
+Timothy J Fontaine <tjfontaine@gmail.com>
+Akagi201 <akagi201@gmail.com>
diff --git a/deps/http_parser/CONTRIBUTIONS b/deps/http_parser/CONTRIBUTIONS
deleted file mode 100644
index 11ba31e4b9..0000000000
--- a/deps/http_parser/CONTRIBUTIONS
+++ /dev/null
@@ -1,4 +0,0 @@
-Contributors must agree to the Contributor License Agreement before patches
-can be accepted.
-
-http://spreadsheets2.google.com/viewform?hl=en&formkey=dDJXOGUwbzlYaWM4cHN1MERwQS1CSnc6MQ
diff --git a/deps/http_parser/Makefile b/deps/http_parser/Makefile
index 3ce463b88b..2959065a77 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.3
+SONAME ?= libhttp_parser.so.2.4
CC?=gcc
AR?=ar
@@ -29,10 +29,12 @@ CPPFLAGS_DEBUG = $(CPPFLAGS) -DHTTP_PARSER_STRICT=1
CPPFLAGS_DEBUG += $(CPPFLAGS_DEBUG_EXTRA)
CPPFLAGS_FAST = $(CPPFLAGS) -DHTTP_PARSER_STRICT=0
CPPFLAGS_FAST += $(CPPFLAGS_FAST_EXTRA)
+CPPFLAGS_BENCH = $(CPPFLAGS_FAST)
CFLAGS += -Wall -Wextra -Werror
CFLAGS_DEBUG = $(CFLAGS) -O0 -g $(CFLAGS_DEBUG_EXTRA)
CFLAGS_FAST = $(CFLAGS) -O3 $(CFLAGS_FAST_EXTRA)
+CFLAGS_BENCH = $(CFLAGS_FAST) -Wno-unused-parameter
CFLAGS_LIB = $(CFLAGS_FAST) -fPIC
LDFLAGS_LIB = $(LDFLAGS) -shared
@@ -61,6 +63,12 @@ test_fast: http_parser.o test.o http_parser.h
test.o: test.c http_parser.h Makefile
$(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c test.c -o $@
+bench: http_parser.o bench.o
+ $(CC) $(CFLAGS_BENCH) $(LDFLAGS) http_parser.o bench.o -o $@
+
+bench.o: bench.c http_parser.h Makefile
+ $(CC) $(CPPFLAGS_BENCH) $(CFLAGS_BENCH) -c bench.c -o $@
+
http_parser.o: http_parser.c http_parser.h Makefile
$(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c http_parser.c
diff --git a/deps/http_parser/README.md b/deps/http_parser/README.md
index 0bf5d359ac..7c54dd42d0 100644
--- a/deps/http_parser/README.md
+++ b/deps/http_parser/README.md
@@ -61,7 +61,7 @@ if (recved < 0) {
}
/* Start up / continue the parser.
- * Note we pass recved==0 to signal that EOF has been recieved.
+ * Note we pass recved==0 to signal that EOF has been received.
*/
nparsed = http_parser_execute(parser, &settings, buf, recved);
@@ -75,7 +75,7 @@ if (parser->upgrade) {
HTTP 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
-`0` as the forth parameter to `http_parser_execute()`. Callbacks and errors
+`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.
@@ -110,7 +110,7 @@ followed by non-HTTP data.
information the Web Socket 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
+body, issuing both on_headers_complete and on_message_complete callbacks. However
http_parser_execute() will stop parsing at the end of the headers and return.
The user is expected to check if `parser->upgrade` has been set to 1 after
@@ -131,7 +131,7 @@ There are two types of callbacks:
* notification `typedef int (*http_cb) (http_parser*);`
Callbacks: on_message_begin, on_headers_complete, on_message_complete.
* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
- Callbacks: (requests only) on_uri,
+ Callbacks: (requests only) on_url,
(common) on_header_field, on_header_value, on_body;
Callbacks must return 0 on success. Returning a non-zero value indicates
@@ -145,7 +145,7 @@ buffer to avoid copying memory around if this fits your application.
Reading headers may be a tricky task if you read/parse headers partially.
Basically, you need to remember whether last header callback was field or value
-and apply following logic:
+and apply the following logic:
(on_header_field and on_header_value shortened to on_h_*)
------------------------ ------------ --------------------------------------------
diff --git a/deps/http_parser/bench.c b/deps/http_parser/bench.c
new file mode 100644
index 0000000000..5b452fa1cd
--- /dev/null
+++ b/deps/http_parser/bench.c
@@ -0,0 +1,111 @@
+/* Copyright Fedor Indutny. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#include "http_parser.h"
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+
+static const char data[] =
+ "POST /joyent/http-parser HTTP/1.1\r\n"
+ "Host: github.com\r\n"
+ "DNT: 1\r\n"
+ "Accept-Encoding: gzip, deflate, sdch\r\n"
+ "Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\r\n"
+ "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) "
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
+ "Chrome/39.0.2171.65 Safari/537.36\r\n"
+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,"
+ "image/webp,*/*;q=0.8\r\n"
+ "Referer: https://github.com/joyent/http-parser\r\n"
+ "Connection: keep-alive\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n\r\n";
+static const size_t data_len = sizeof(data) - 1;
+
+static int on_info(http_parser* p) {
+ return 0;
+}
+
+
+static int on_data(http_parser* p, const char *at, size_t length) {
+ return 0;
+}
+
+static http_parser_settings settings = {
+ .on_message_begin = on_info,
+ .on_headers_complete = on_info,
+ .on_message_complete = on_info,
+ .on_header_field = on_data,
+ .on_header_value = on_data,
+ .on_url = on_data,
+ .on_status = on_data,
+ .on_body = on_data
+};
+
+int bench(int iter_count, int silent) {
+ struct http_parser parser;
+ int i;
+ int err;
+ struct timeval start;
+ struct timeval end;
+ float rps;
+
+ if (!silent) {
+ err = gettimeofday(&start, NULL);
+ assert(err == 0);
+ }
+
+ for (i = 0; i < iter_count; i++) {
+ size_t parsed;
+ http_parser_init(&parser, HTTP_REQUEST);
+
+ parsed = http_parser_execute(&parser, &settings, data, data_len);
+ assert(parsed == data_len);
+ }
+
+ if (!silent) {
+ err = gettimeofday(&end, NULL);
+ assert(err == 0);
+
+ fprintf(stdout, "Benchmark result:\n");
+
+ rps = (float) (end.tv_sec - start.tv_sec) +
+ (end.tv_usec - start.tv_usec) * 1e-6f;
+ fprintf(stdout, "Took %f seconds to run\n", rps);
+
+ rps = (float) iter_count / rps;
+ fprintf(stdout, "%f req/sec\n", rps);
+ fflush(stdout);
+ }
+
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ if (argc == 2 && strcmp(argv[1], "infinite") == 0) {
+ for (;;)
+ bench(5000000, 1);
+ return 0;
+ } else {
+ return bench(5000000, 0);
+ }
+}
diff --git a/deps/http_parser/contrib/parsertrace.c b/deps/http_parser/contrib/parsertrace.c
index c9bc71ec01..e7153680f4 100644
--- a/deps/http_parser/contrib/parsertrace.c
+++ b/deps/http_parser/contrib/parsertrace.c
@@ -111,14 +111,14 @@ int main(int argc, char* argv[]) {
FILE* file = fopen(filename, "r");
if (file == NULL) {
perror("fopen");
- return EXIT_FAILURE;
+ goto fail;
}
fseek(file, 0, SEEK_END);
long file_length = ftell(file);
if (file_length == -1) {
perror("ftell");
- return EXIT_FAILURE;
+ goto fail;
}
fseek(file, 0, SEEK_SET);
@@ -126,7 +126,7 @@ int main(int argc, char* argv[]) {
if (fread(data, 1, file_length, file) != (size_t)file_length) {
fprintf(stderr, "couldn't read entire file\n");
free(data);
- return EXIT_FAILURE;
+ goto fail;
}
http_parser_settings settings;
@@ -149,8 +149,12 @@ int main(int argc, char* argv[]) {
"Error: %s (%s)\n",
http_errno_description(HTTP_PARSER_ERRNO(&parser)),
http_errno_name(HTTP_PARSER_ERRNO(&parser)));
- return EXIT_FAILURE;
+ goto fail;
}
return EXIT_SUCCESS;
+
+fail:
+ fclose(file);
+ return EXIT_FAILURE;
}
diff --git a/deps/http_parser/http_parser.c b/deps/http_parser/http_parser.c
index 70cc9bd37b..23077d1dfe 100644
--- a/deps/http_parser/http_parser.c
+++ b/deps/http_parser/http_parser.c
@@ -56,19 +56,41 @@ do { \
parser->http_errno = (e); \
} while(0)
+#define CURRENT_STATE() p_state
+#define UPDATE_STATE(V) p_state = (V);
+#define RETURN(V) \
+do { \
+ parser->state = CURRENT_STATE(); \
+ return (V); \
+} while (0);
+#define REEXECUTE() \
+ --p; \
+ break;
+
+
+#ifdef __GNUC__
+# define LIKELY(X) __builtin_expect(!!(X), 1)
+# define UNLIKELY(X) __builtin_expect(!!(X), 0)
+#else
+# define LIKELY(X) (X)
+# define UNLIKELY(X) (X)
+#endif
+
/* Run the notify callback FOR, returning ER if it fails */
#define CALLBACK_NOTIFY_(FOR, ER) \
do { \
assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
\
- if (settings->on_##FOR) { \
- if (0 != settings->on_##FOR(parser)) { \
+ if (LIKELY(settings->on_##FOR)) { \
+ parser->state = CURRENT_STATE(); \
+ if (UNLIKELY(0 != settings->on_##FOR(parser))) { \
SET_ERRNO(HPE_CB_##FOR); \
} \
+ UPDATE_STATE(parser->state); \
\
/* We either errored above or got paused; get out */ \
- if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \
+ if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \
return (ER); \
} \
} \
@@ -86,13 +108,16 @@ do { \
assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
\
if (FOR##_mark) { \
- if (settings->on_##FOR) { \
- if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) { \
+ if (LIKELY(settings->on_##FOR)) { \
+ parser->state = CURRENT_STATE(); \
+ if (UNLIKELY(0 != \
+ settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \
SET_ERRNO(HPE_CB_##FOR); \
} \
+ UPDATE_STATE(parser->state); \
\
/* We either errored above or got paused; get out */ \
- if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \
+ if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \
return (ER); \
} \
} \
@@ -116,6 +141,26 @@ do { \
} \
} while (0)
+/* Don't allow the total size of the HTTP headers (including the status
+ * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
+ * embedders against denial-of-service attacks where the attacker feeds
+ * us a never-ending header that the embedder keeps buffering.
+ *
+ * This check is arguably the responsibility of embedders but we're doing
+ * it on the embedder's behalf because most won't bother and this way we
+ * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
+ * than any reasonable request or response so this should never affect
+ * day-to-day operation.
+ */
+#define COUNT_HEADER_SIZE(V) \
+do { \
+ parser->nread += (V); \
+ if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \
+ SET_ERRNO(HPE_HEADER_OVERFLOW); \
+ goto error; \
+ } \
+} while (0)
+
#define PROXY_CONNECTION "proxy-connection"
#define CONNECTION "connection"
@@ -334,12 +379,16 @@ enum header_states
, h_upgrade
, h_matching_transfer_encoding_chunked
+ , h_matching_connection_token_start
, h_matching_connection_keep_alive
, h_matching_connection_close
+ , h_matching_connection_upgrade
+ , h_matching_connection_token
, h_transfer_encoding_chunked
, h_connection_keep_alive
, h_connection_close
+ , h_connection_upgrade
};
enum http_host_state
@@ -371,6 +420,8 @@ enum http_host_state
(c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
(c) == '$' || (c) == ',')
+#define STRICT_TOKEN(c) (tokens[(unsigned char)c])
+
#if HTTP_PARSER_STRICT
#define TOKEN(c) (tokens[(unsigned char)c])
#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
@@ -586,6 +637,7 @@ size_t http_parser_execute (http_parser *parser,
const char *url_mark = 0;
const char *body_mark = 0;
const char *status_mark = 0;
+ enum state p_state = parser->state;
/* We're in an error state. Don't bother doing anything. */
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
@@ -593,7 +645,7 @@ size_t http_parser_execute (http_parser *parser,
}
if (len == 0) {
- switch (parser->state) {
+ switch (CURRENT_STATE()) {
case s_body_identity_eof:
/* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
* we got paused.
@@ -614,11 +666,11 @@ size_t http_parser_execute (http_parser *parser,
}
- if (parser->state == s_header_field)
+ if (CURRENT_STATE() == s_header_field)
header_field_mark = data;
- if (parser->state == s_header_value)
+ if (CURRENT_STATE() == s_header_value)
header_value_mark = data;
- switch (parser->state) {
+ switch (CURRENT_STATE()) {
case s_req_path:
case s_req_schema:
case s_req_schema_slash:
@@ -635,38 +687,23 @@ size_t http_parser_execute (http_parser *parser,
case s_res_status:
status_mark = data;
break;
+ default:
+ break;
}
for (p=data; p != data + len; p++) {
ch = *p;
- if (PARSING_HEADER(parser->state)) {
- ++parser->nread;
- /* Don't allow the total size of the HTTP headers (including the status
- * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
- * embedders against denial-of-service attacks where the attacker feeds
- * us a never-ending header that the embedder keeps buffering.
- *
- * This check is arguably the responsibility of embedders but we're doing
- * it on the embedder's behalf because most won't bother and this way we
- * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
- * than any reasonable request or response so this should never affect
- * day-to-day operation.
- */
- if (parser->nread > HTTP_MAX_HEADER_SIZE) {
- SET_ERRNO(HPE_HEADER_OVERFLOW);
- goto error;
- }
- }
+ if (PARSING_HEADER(CURRENT_STATE()))
+ COUNT_HEADER_SIZE(1);
- reexecute_byte:
- switch (parser->state) {
+ switch (CURRENT_STATE()) {
case s_dead:
/* this state is used after a 'Connection: close' message
* the parser will error out if it reads another message
*/
- if (ch == CR || ch == LF)
+ if (LIKELY(ch == CR || ch == LF))
break;
SET_ERRNO(HPE_CLOSED_CONNECTION);
@@ -680,13 +717,13 @@ size_t http_parser_execute (http_parser *parser,
parser->content_length = ULLONG_MAX;
if (ch == 'H') {
- parser->state = s_res_or_resp_H;
+ UPDATE_STATE(s_res_or_resp_H);
CALLBACK_NOTIFY(message_begin);
} else {
parser->type = HTTP_REQUEST;
- parser->state = s_start_req;
- goto reexecute_byte;
+ UPDATE_STATE(s_start_req);
+ REEXECUTE();
}
break;
@@ -695,9 +732,9 @@ size_t http_parser_execute (http_parser *parser,
case s_res_or_resp_H:
if (ch == 'T') {
parser->type = HTTP_RESPONSE;
- parser->state = s_res_HT;
+ UPDATE_STATE(s_res_HT);
} else {
- if (ch != 'E') {
+ if (UNLIKELY(ch != 'E')) {
SET_ERRNO(HPE_INVALID_CONSTANT);
goto error;
}
@@ -705,7 +742,7 @@ size_t http_parser_execute (http_parser *parser,
parser->type = HTTP_REQUEST;
parser->method = HTTP_HEAD;
parser->index = 2;
- parser->state = s_req_method;
+ UPDATE_STATE(s_req_method);
}
break;
@@ -716,7 +753,7 @@ size_t http_parser_execute (http_parser *parser,
switch (ch) {
case 'H':
- parser->state = s_res_H;
+ UPDATE_STATE(s_res_H);
break;
case CR:
@@ -734,39 +771,39 @@ size_t http_parser_execute (http_parser *parser,
case s_res_H:
STRICT_CHECK(ch != 'T');
- parser->state = s_res_HT;
+ UPDATE_STATE(s_res_HT);
break;
case s_res_HT:
STRICT_CHECK(ch != 'T');
- parser->state = s_res_HTT;
+ UPDATE_STATE(s_res_HTT);
break;
case s_res_HTT:
STRICT_CHECK(ch != 'P');
- parser->state = s_res_HTTP;
+ UPDATE_STATE(s_res_HTTP);
break;
case s_res_HTTP:
STRICT_CHECK(ch != '/');
- parser->state = s_res_first_http_major;
+ UPDATE_STATE(s_res_first_http_major);
break;
case s_res_first_http_major:
- if (ch < '0' || ch > '9') {
+ if (UNLIKELY(ch < '0' || ch > '9')) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_major = ch - '0';
- parser->state = s_res_http_major;
+ UPDATE_STATE(s_res_http_major);
break;
/* major HTTP version or dot */
case s_res_http_major:
{
if (ch == '.') {
- parser->state = s_res_first_http_minor;
+ UPDATE_STATE(s_res_first_http_minor);
break;
}
@@ -778,7 +815,7 @@ size_t http_parser_execute (http_parser *parser,
parser->http_major *= 10;
parser->http_major += ch - '0';
- if (parser->http_major > 999) {
+ if (UNLIKELY(parser->http_major > 999)) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
@@ -788,24 +825,24 @@ size_t http_parser_execute (http_parser *parser,
/* first digit of minor HTTP version */
case s_res_first_http_minor:
- if (!IS_NUM(ch)) {
+ if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_minor = ch - '0';
- parser->state = s_res_http_minor;
+ UPDATE_STATE(s_res_http_minor);
break;
/* minor HTTP version or end of request line */
case s_res_http_minor:
{
if (ch == ' ') {
- parser->state = s_res_first_status_code;
+ UPDATE_STATE(s_res_first_status_code);
break;
}
- if (!IS_NUM(ch)) {
+ if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
@@ -813,7 +850,7 @@ size_t http_parser_execute (http_parser *parser,
parser->http_minor *= 10;
parser->http_minor += ch - '0';
- if (parser->http_minor > 999) {
+ if (UNLIKELY(parser->http_minor > 999)) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
@@ -832,7 +869,7 @@ size_t http_parser_execute (http_parser *parser,
goto error;
}
parser->status_code = ch - '0';
- parser->state = s_res_status_code;
+ UPDATE_STATE(s_res_status_code);
break;
}
@@ -841,13 +878,13 @@ size_t http_parser_execute (http_parser *parser,
if (!IS_NUM(ch)) {
switch (ch) {
case ' ':
- parser->state = s_res_status_start;
+ UPDATE_STATE(s_res_status_start);
break;
case CR:
- parser->state = s_res_line_almost_done;
+ UPDATE_STATE(s_res_line_almost_done);
break;
case LF:
- parser->state = s_header_field_start;
+ UPDATE_STATE(s_header_field_start);
break;
default:
SET_ERRNO(HPE_INVALID_STATUS);
@@ -859,7 +896,7 @@ size_t http_parser_execute (http_parser *parser,
parser->status_code *= 10;
parser->status_code += ch - '0';
- if (parser->status_code > 999) {
+ if (UNLIKELY(parser->status_code > 999)) {
SET_ERRNO(HPE_INVALID_STATUS);
goto error;
}
@@ -870,30 +907,30 @@ size_t http_parser_execute (http_parser *parser,
case s_res_status_start:
{
if (ch == CR) {
- parser->state = s_res_line_almost_done;
+ UPDATE_STATE(s_res_line_almost_done);
break;
}
if (ch == LF) {
- parser->state = s_header_field_start;
+ UPDATE_STATE(s_header_field_start);
break;
}
MARK(status);
- parser->state = s_res_status;
+ UPDATE_STATE(s_res_status);
parser->index = 0;
break;
}
case s_res_status:
if (ch == CR) {
- parser->state = s_res_line_almost_done;
+ UPDATE_STATE(s_res_line_almost_done);
CALLBACK_DATA(status);
break;
}
if (ch == LF) {
- parser->state = s_header_field_start;
+ UPDATE_STATE(s_header_field_start);
CALLBACK_DATA(status);
break;
}
@@ -902,7 +939,7 @@ size_t http_parser_execute (http_parser *parser,
case s_res_line_almost_done:
STRICT_CHECK(ch != LF);
- parser->state = s_header_field_start;
+ UPDATE_STATE(s_header_field_start);
break;
case s_start_req:
@@ -912,7 +949,7 @@ size_t http_parser_execute (http_parser *parser,
parser->flags = 0;
parser->content_length = ULLONG_MAX;
- if (!IS_ALPHA(ch)) {
+ if (UNLIKELY(!IS_ALPHA(ch))) {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
@@ -925,7 +962,7 @@ size_t http_parser_execute (http_parser *parser,
case 'G': parser->method = HTTP_GET; break;
case 'H': parser->method = HTTP_HEAD; break;
case 'L': parser->method = HTTP_LOCK; break;
- case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ 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;
@@ -939,7 +976,7 @@ size_t http_parser_execute (http_parser *parser,
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
- parser->state = s_req_method;
+ UPDATE_STATE(s_req_method);
CALLBACK_NOTIFY(message_begin);
@@ -949,14 +986,14 @@ size_t http_parser_execute (http_parser *parser,
case s_req_method:
{
const char *matcher;
- if (ch == '\0') {
+ if (UNLIKELY(ch == '\0')) {
SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
matcher = method_strings[parser->method];
if (ch == ' ' && matcher[parser->index] == '\0') {
- parser->state = s_req_spaces_before_url;
+ UPDATE_STATE(s_req_spaces_before_url);
} else if (ch == matcher[parser->index]) {
; /* nada */
} else if (parser->method == HTTP_CONNECT) {
@@ -977,6 +1014,8 @@ size_t http_parser_execute (http_parser *parser,
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;
@@ -1035,11 +1074,11 @@ size_t http_parser_execute (http_parser *parser,
MARK(url);
if (parser->method == HTTP_CONNECT) {
- parser->state = s_req_server_start;
+ UPDATE_STATE(s_req_server_start);
}
- parser->state = parse_url_char((enum state)parser->state, ch);
- if (parser->state == s_dead) {
+ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+ if (UNLIKELY(CURRENT_STATE() == s_dead)) {
SET_ERRNO(HPE_INVALID_URL);
goto error;
}
@@ -1060,8 +1099,8 @@ size_t http_parser_execute (http_parser *parser,
SET_ERRNO(HPE_INVALID_URL);
goto error;
default:
- parser->state = parse_url_char((enum state)parser->state, ch);
- if (parser->state == s_dead) {
+ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+ if (UNLIKELY(CURRENT_STATE() == s_dead)) {
SET_ERRNO(HPE_INVALID_URL);
goto error;
}
@@ -1080,21 +1119,21 @@ size_t http_parser_execute (http_parser *parser,
{
switch (ch) {
case ' ':
- parser->state = s_req_http_start;
+ UPDATE_STATE(s_req_http_start);
CALLBACK_DATA(url);
break;
case CR:
case LF:
parser->http_major = 0;
parser->http_minor = 9;
- parser->state = (ch == CR) ?
+ UPDATE_STATE((ch == CR) ?
s_req_line_almost_done :
- s_header_field_start;
+ s_header_field_start);
CALLBACK_DATA(url);
break;
default:
- parser->state = parse_url_char((enum state)parser->state, ch);
- if (parser->state == s_dead) {
+ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+ if (UNLIKELY(CURRENT_STATE() == s_dead)) {
SET_ERRNO(HPE_INVALID_URL);
goto error;
}
@@ -1105,7 +1144,7 @@ size_t http_parser_execute (http_parser *parser,
case s_req_http_start:
switch (ch) {
case 'H':
- parser->state = s_req_http_H;
+ UPDATE_STATE(s_req_http_H);
break;
case ' ':
break;
@@ -1117,44 +1156,44 @@ size_t http_parser_execute (http_parser *parser,
case s_req_http_H:
STRICT_CHECK(ch != 'T');
- parser->state = s_req_http_HT;
+ UPDATE_STATE(s_req_http_HT);
break;
case s_req_http_HT:
STRICT_CHECK(ch != 'T');
- parser->state = s_req_http_HTT;
+ UPDATE_STATE(s_req_http_HTT);
break;
case s_req_http_HTT:
STRICT_CHECK(ch != 'P');
- parser->state = s_req_http_HTTP;
+ UPDATE_STATE(s_req_http_HTTP);
break;
case s_req_http_HTTP:
STRICT_CHECK(ch != '/');
- parser->state = s_req_first_http_major;
+ UPDATE_STATE(s_req_first_http_major);
break;
/* first digit of major HTTP version */
case s_req_first_http_major:
- if (ch < '1' || ch > '9') {
+ if (UNLIKELY(ch < '1' || ch > '9')) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_major = ch - '0';
- parser->state = s_req_http_major;
+ UPDATE_STATE(s_req_http_major);
break;
/* major HTTP version or dot */
case s_req_http_major:
{
if (ch == '.') {
- parser->state = s_req_first_http_minor;
+ UPDATE_STATE(s_req_first_http_minor);
break;
}
- if (!IS_NUM(ch)) {
+ if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
@@ -1162,7 +1201,7 @@ size_t http_parser_execute (http_parser *parser,
parser->http_major *= 10;
parser->http_major += ch - '0';
- if (parser->http_major > 999) {
+ if (UNLIKELY(parser->http_major > 999)) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
@@ -1172,31 +1211,31 @@ size_t http_parser_execute (http_parser *parser,
/* first digit of minor HTTP version */
case s_req_first_http_minor:
- if (!IS_NUM(ch)) {
+ if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_minor = ch - '0';
- parser->state = s_req_http_minor;
+ UPDATE_STATE(s_req_http_minor);
break;
/* minor HTTP version or end of request line */
case s_req_http_minor:
{
if (ch == CR) {
- parser->state = s_req_line_almost_done;
+ UPDATE_STATE(s_req_line_almost_done);
break;
}
if (ch == LF) {
- parser->state = s_header_field_start;
+ UPDATE_STATE(s_header_field_start);
break;
}
/* XXX allow spaces after digit? */
- if (!IS_NUM(ch)) {
+ if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
@@ -1204,7 +1243,7 @@ size_t http_parser_execute (http_parser *parser,
parser->http_minor *= 10;
parser->http_minor += ch - '0';
- if (parser->http_minor > 999) {
+ if (UNLIKELY(parser->http_minor > 999)) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
@@ -1215,32 +1254,32 @@ size_t http_parser_execute (http_parser *parser,
/* end of request line */
case s_req_line_almost_done:
{
- if (ch != LF) {
+ if (UNLIKELY(ch != LF)) {
SET_ERRNO(HPE_LF_EXPECTED);
goto error;
}
- parser->state = s_header_field_start;
+ UPDATE_STATE(s_header_field_start);
break;
}
case s_header_field_start:
{
if (ch == CR) {
- parser->state = s_headers_almost_done;
+ UPDATE_STATE(s_headers_almost_done);
break;
}
if (ch == LF) {
/* they might be just sending \n instead of \r\n so this would be
* the second \n to denote the end of headers*/
- parser->state = s_headers_almost_done;
- goto reexecute_byte;
+ UPDATE_STATE(s_headers_almost_done);
+ REEXECUTE();
}
c = TOKEN(ch);
- if (!c) {
+ if (UNLIKELY(!c)) {
SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
goto error;
}
@@ -1248,7 +1287,7 @@ size_t http_parser_execute (http_parser *parser,
MARK(header_field);
parser->index = 0;
- parser->state = s_header_field;
+ UPDATE_STATE(s_header_field);
switch (c) {
case 'c':
@@ -1276,9 +1315,14 @@ size_t http_parser_execute (http_parser *parser,
case s_header_field:
{
- c = TOKEN(ch);
+ const char* start = p;
+ for (; p != data + len; p++) {
+ ch = *p;
+ c = TOKEN(ch);
+
+ if (!c)
+ break;
- if (c) {
switch (parser->header_state) {
case h_general:
break;
@@ -1379,23 +1423,17 @@ size_t http_parser_execute (http_parser *parser,
assert(0 && "Unknown header_state");
break;
}
- break;
}
- if (ch == ':') {
- parser->state = s_header_value_discard_ws;
- CALLBACK_DATA(header_field);
- break;
- }
+ COUNT_HEADER_SIZE(p - start);
- if (ch == CR) {
- parser->state = s_header_almost_done;
- CALLBACK_DATA(header_field);
+ if (p == data + len) {
+ --p;
break;
}
- if (ch == LF) {
- parser->state = s_header_field_start;
+ if (ch == ':') {
+ UPDATE_STATE(s_header_value_discard_ws);
CALLBACK_DATA(header_field);
break;
}
@@ -1408,12 +1446,12 @@ size_t http_parser_execute (http_parser *parser,
if (ch == ' ' || ch == '\t') break;
if (ch == CR) {
- parser->state = s_header_value_discard_ws_almost_done;
+ UPDATE_STATE(s_header_value_discard_ws_almost_done);
break;
}
if (ch == LF) {
- parser->state = s_header_value_discard_lws;
+ UPDATE_STATE(s_header_value_discard_lws);
break;
}
@@ -1423,7 +1461,7 @@ size_t http_parser_execute (http_parser *parser,
{
MARK(header_value);
- parser->state = s_header_value;
+ UPDATE_STATE(s_header_value);
parser->index = 0;
c = LOWER(ch);
@@ -1444,7 +1482,7 @@ size_t http_parser_execute (http_parser *parser,
break;
case h_content_length:
- if (!IS_NUM(ch)) {
+ if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
goto error;
}
@@ -1459,11 +1497,17 @@ size_t http_parser_execute (http_parser *parser,
/* looking for 'Connection: close' */
} else if (c == 'c') {
parser->header_state = h_matching_connection_close;
+ } else if (c == 'u') {
+ parser->header_state = h_matching_connection_upgrade;
} else {
- parser->header_state = h_general;
+ parser->header_state = h_matching_connection_token;
}
break;
+ /* Multi-value `Connection` header */
+ case h_matching_connection_token_start:
+ break;
+
default:
parser->header_state = h_general;
break;
@@ -1473,98 +1517,185 @@ size_t http_parser_execute (http_parser *parser,
case s_header_value:
{
+ const char* start = p;
+ enum header_states h_state = parser->header_state;
+ for (; p != data + len; p++) {
+ ch = *p;
+ if (ch == CR) {
+ UPDATE_STATE(s_header_almost_done);
+ parser->header_state = h_state;
+ CALLBACK_DATA(header_value);
+ break;
+ }
- if (ch == CR) {
- parser->state = s_header_almost_done;
- CALLBACK_DATA(header_value);
- break;
- }
+ if (ch == LF) {
+ UPDATE_STATE(s_header_almost_done);
+ COUNT_HEADER_SIZE(p - start);
+ parser->header_state = h_state;
+ CALLBACK_DATA_NOADVANCE(header_value);
+ REEXECUTE();
+ }
- if (ch == LF) {
- parser->state = s_header_almost_done;
- CALLBACK_DATA_NOADVANCE(header_value);
- goto reexecute_byte;
- }
+ c = LOWER(ch);
- c = LOWER(ch);
+ switch (h_state) {
+ case h_general:
+ {
+ const char* p_cr;
+ const char* p_lf;
+ size_t limit = data + len - p;
+
+ limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
+
+ p_cr = memchr(p, CR, limit);
+ p_lf = memchr(p, LF, limit);
+ if (p_cr != NULL) {
+ if (p_lf != NULL && p_cr >= p_lf)
+ p = p_lf;
+ else
+ p = p_cr;
+ } else if (UNLIKELY(p_lf != NULL)) {
+ p = p_lf;
+ } else {
+ p = data + len;
+ }
+ --p;
- switch (parser->header_state) {
- case h_general:
- break;
+ break;
+ }
- case h_connection:
- case h_transfer_encoding:
- assert(0 && "Shouldn't get here.");
- break;
+ case h_connection:
+ case h_transfer_encoding:
+ assert(0 && "Shouldn't get here.");
+ break;
- case h_content_length:
- {
- uint64_t t;
+ case h_content_length:
+ {
+ uint64_t t;
- if (ch == ' ') break;
+ if (ch == ' ') break;
- if (!IS_NUM(ch)) {
- SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
- goto error;
- }
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ parser->header_state = h_state;
+ goto error;
+ }
- t = parser->content_length;
- t *= 10;
- t += ch - '0';
+ t = parser->content_length;
+ t *= 10;
+ t += ch - '0';
- /* Overflow? Test against a conservative limit for simplicity. */
- if ((ULLONG_MAX - 10) / 10 < parser->content_length) {
- SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
- goto error;
+ /* Overflow? Test against a conservative limit for simplicity. */
+ if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ parser->header_state = h_state;
+ goto error;
+ }
+
+ parser->content_length = t;
+ break;
}
- parser->content_length = t;
- break;
- }
+ /* Transfer-Encoding: chunked */
+ case h_matching_transfer_encoding_chunked:
+ parser->index++;
+ if (parser->index > sizeof(CHUNKED)-1
+ || c != CHUNKED[parser->index]) {
+ h_state = h_general;
+ } else if (parser->index == sizeof(CHUNKED)-2) {
+ h_state = h_transfer_encoding_chunked;
+ }
+ break;
- /* Transfer-Encoding: chunked */
- case h_matching_transfer_encoding_chunked:
- parser->index++;
- if (parser->index > sizeof(CHUNKED)-1
- || c != CHUNKED[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(CHUNKED)-2) {
- parser->header_state = h_transfer_encoding_chunked;
- }
- break;
+ case h_matching_connection_token_start:
+ /* looking for 'Connection: keep-alive' */
+ if (c == 'k') {
+ h_state = h_matching_connection_keep_alive;
+ /* looking for 'Connection: close' */
+ } else if (c == 'c') {
+ h_state = h_matching_connection_close;
+ } else if (c == 'u') {
+ h_state = h_matching_connection_upgrade;
+ } else if (STRICT_TOKEN(c)) {
+ h_state = h_matching_connection_token;
+ } else {
+ h_state = h_general;
+ }
+ break;
- /* looking for 'Connection: keep-alive' */
- case h_matching_connection_keep_alive:
- parser->index++;
- if (parser->index > sizeof(KEEP_ALIVE)-1
- || c != KEEP_ALIVE[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
- parser->header_state = h_connection_keep_alive;
- }
- break;
+ /* looking for 'Connection: keep-alive' */
+ case h_matching_connection_keep_alive:
+ parser->index++;
+ if (parser->index > sizeof(KEEP_ALIVE)-1
+ || c != KEEP_ALIVE[parser->index]) {
+ h_state = h_matching_connection_token;
+ } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
+ h_state = h_connection_keep_alive;
+ }
+ break;
- /* looking for 'Connection: close' */
- case h_matching_connection_close:
- parser->index++;
- if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
- parser->header_state = h_general;
- } else if (parser->index == sizeof(CLOSE)-2) {
- parser->header_state = h_connection_close;
- }
- break;
+ /* looking for 'Connection: close' */
+ case h_matching_connection_close:
+ parser->index++;
+ if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
+ h_state = h_matching_connection_token;
+ } else if (parser->index == sizeof(CLOSE)-2) {
+ h_state = h_connection_close;
+ }
+ break;
- case h_transfer_encoding_chunked:
- case h_connection_keep_alive:
- case h_connection_close:
- if (ch != ' ') parser->header_state = h_general;
- break;
+ /* looking for 'Connection: upgrade' */
+ case h_matching_connection_upgrade:
+ parser->index++;
+ if (parser->index > sizeof(UPGRADE) - 1 ||
+ c != UPGRADE[parser->index]) {
+ h_state = h_matching_connection_token;
+ } else if (parser->index == sizeof(UPGRADE)-2) {
+ h_state = h_connection_upgrade;
+ }
+ break;
- default:
- parser->state = s_header_value;
- parser->header_state = h_general;
- break;
+ case h_matching_connection_token:
+ if (ch == ',') {
+ h_state = h_matching_connection_token_start;
+ parser->index = 0;
+ }
+ break;
+
+ case h_transfer_encoding_chunked:
+ if (ch != ' ') h_state = h_general;
+ break;
+
+ case h_connection_keep_alive:
+ case h_connection_close:
+ case h_connection_upgrade:
+ if (ch == ',') {
+ if (h_state == h_connection_keep_alive) {
+ parser->flags |= F_CONNECTION_KEEP_ALIVE;
+ } else if (h_state == h_connection_close) {
+ parser->flags |= F_CONNECTION_CLOSE;
+ } else if (h_state == h_connection_upgrade) {
+ parser->flags |= F_CONNECTION_UPGRADE;
+ }
+ h_state = h_matching_connection_token_start;
+ parser->index = 0;
+ } else if (ch != ' ') {
+ h_state = h_matching_connection_token;
+ }
+ break;
+
+ default:
+ UPDATE_STATE(s_header_value);
+ h_state = h_general;
+ break;
+ }
}
+ parser->header_state = h_state;
+
+ COUNT_HEADER_SIZE(p - start);
+
+ if (p == data + len)
+ --p;
break;
}
@@ -1572,15 +1703,15 @@ size_t http_parser_execute (http_parser *parser,
{
STRICT_CHECK(ch != LF);
- parser->state = s_header_value_lws;
+ UPDATE_STATE(s_header_value_lws);
break;
}
case s_header_value_lws:
{
if (ch == ' ' || ch == '\t') {
- parser->state = s_header_value_start;
- goto reexecute_byte;
+ UPDATE_STATE(s_header_value_start);
+ REEXECUTE();
}
/* finished the header */
@@ -1594,32 +1725,52 @@ size_t http_parser_execute (http_parser *parser,
case h_transfer_encoding_chunked:
parser->flags |= F_CHUNKED;
break;
+ case h_connection_upgrade:
+ parser->flags |= F_CONNECTION_UPGRADE;
+ break;
default:
break;
}
- parser->state = s_header_field_start;
- goto reexecute_byte;
+ UPDATE_STATE(s_header_field_start);
+ REEXECUTE();
}
case s_header_value_discard_ws_almost_done:
{
STRICT_CHECK(ch != LF);
- parser->state = s_header_value_discard_lws;
+ UPDATE_STATE(s_header_value_discard_lws);
break;
}
case s_header_value_discard_lws:
{
if (ch == ' ' || ch == '\t') {
- parser->state = s_header_value_discard_ws;
+ UPDATE_STATE(s_header_value_discard_ws);
break;
} else {
+ switch (parser->header_state) {
+ case h_connection_keep_alive:
+ parser->flags |= F_CONNECTION_KEEP_ALIVE;
+ break;
+ case h_connection_close:
+ parser->flags |= F_CONNECTION_CLOSE;
+ break;
+ case h_connection_upgrade:
+ parser->flags |= F_CONNECTION_UPGRADE;
+ break;
+ case h_transfer_encoding_chunked:
+ parser->flags |= F_CHUNKED;
+ break;
+ default:
+ break;
+ }
+
/* header value was empty */
MARK(header_value);
- parser->state = s_header_field_start;
+ UPDATE_STATE(s_header_field_start);
CALLBACK_DATA_NOADVANCE(header_value);
- goto reexecute_byte;
+ REEXECUTE();
}
}
@@ -1629,16 +1780,18 @@ size_t http_parser_execute (http_parser *parser,
if (parser->flags & F_TRAILING) {
/* End of a chunked request */
- parser->state = NEW_MESSAGE();
+ UPDATE_STATE(NEW_MESSAGE());
CALLBACK_NOTIFY(message_complete);
break;
}
- parser->state = s_headers_done;
+ UPDATE_STATE(s_headers_done);
/* Set this here so that on_headers_complete() callbacks can see it */
parser->upgrade =
- (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT);
+ ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) ==
+ (F_UPGRADE | F_CONNECTION_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
@@ -1660,15 +1813,15 @@ size_t http_parser_execute (http_parser *parser,
default:
SET_ERRNO(HPE_CB_headers_complete);
- return p - data; /* Error */
+ RETURN(p - data); /* Error */
}
}
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
- return p - data;
+ RETURN(p - data);
}
- goto reexecute_byte;
+ REEXECUTE();
}
case s_headers_done:
@@ -1679,34 +1832,34 @@ size_t http_parser_execute (http_parser *parser,
/* Exit, the rest of the connect is in a different protocol. */
if (parser->upgrade) {
- parser->state = NEW_MESSAGE();
+ UPDATE_STATE(NEW_MESSAGE());
CALLBACK_NOTIFY(message_complete);
- return (p - data) + 1;
+ RETURN((p - data) + 1);
}
if (parser->flags & F_SKIPBODY) {
- parser->state = NEW_MESSAGE();
+ UPDATE_STATE(NEW_MESSAGE());
CALLBACK_NOTIFY(message_complete);
} else if (parser->flags & F_CHUNKED) {
/* chunked encoding - ignore Content-Length header */
- parser->state = s_chunk_size_start;
+ UPDATE_STATE(s_chunk_size_start);
} else {
if (parser->content_length == 0) {
/* Content-Length header given but zero: Content-Length: 0\r\n */
- parser->state = NEW_MESSAGE();
+ UPDATE_STATE(NEW_MESSAGE());
CALLBACK_NOTIFY(message_complete);
} else if (parser->content_length != ULLONG_MAX) {
/* Content-Length header given and non-zero */
- parser->state = s_body_identity;
+ UPDATE_STATE(s_body_identity);
} else {
if (parser->type == HTTP_REQUEST ||
!http_message_needs_eof(parser)) {
/* Assume content-length 0 - read the next */
- parser->state = NEW_MESSAGE();
+ UPDATE_STATE(NEW_MESSAGE());
CALLBACK_NOTIFY(message_complete);
} else {
/* Read body until EOF */
- parser->state = s_body_identity_eof;
+ UPDATE_STATE(s_body_identity_eof);
}
}
}
@@ -1732,7 +1885,7 @@ size_t http_parser_execute (http_parser *parser,
p += to_read - 1;
if (parser->content_length == 0) {
- parser->state = s_message_done;
+ UPDATE_STATE(s_message_done);
/* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
*
@@ -1744,7 +1897,7 @@ size_t http_parser_execute (http_parser *parser,
* important for applications, but let's keep it for now.
*/
CALLBACK_DATA_(body, p - body_mark + 1, p - data);
- goto reexecute_byte;
+ REEXECUTE();
}
break;
@@ -1758,7 +1911,7 @@ size_t http_parser_execute (http_parser *parser,
break;
case s_message_done:
- parser->state = NEW_MESSAGE();
+ UPDATE_STATE(NEW_MESSAGE());
CALLBACK_NOTIFY(message_complete);
break;
@@ -1768,13 +1921,13 @@ size_t http_parser_execute (http_parser *parser,
assert(parser->flags & F_CHUNKED);
unhex_val = unhex[(unsigned char)ch];
- if (unhex_val == -1) {
+ if (UNLIKELY(unhex_val == -1)) {
SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
goto error;
}
parser->content_length = unhex_val;
- parser->state = s_chunk_size;
+ UPDATE_STATE(s_chunk_size);
break;
}
@@ -1785,7 +1938,7 @@ size_t http_parser_execute (http_parser *parser,
assert(parser->flags & F_CHUNKED);
if (ch == CR) {
- parser->state = s_chunk_size_almost_done;
+ UPDATE_STATE(s_chunk_size_almost_done);
break;
}
@@ -1793,7 +1946,7 @@ size_t http_parser_execute (http_parser *parser,
if (unhex_val == -1) {
if (ch == ';' || ch == ' ') {
- parser->state = s_chunk_parameters;
+ UPDATE_STATE(s_chunk_parameters);
break;
}
@@ -1806,7 +1959,7 @@ size_t http_parser_execute (http_parser *parser,
t += unhex_val;
/* Overflow? Test against a conservative limit for simplicity. */
- if ((ULLONG_MAX - 16) / 16 < parser->content_length) {
+ if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
goto error;
}
@@ -1820,7 +1973,7 @@ size_t http_parser_execute (http_parser *parser,
assert(parser->flags & F_CHUNKED);
/* just ignore this shit. TODO check for overflow */
if (ch == CR) {
- parser->state = s_chunk_size_almost_done;
+ UPDATE_STATE(s_chunk_size_almost_done);
break;
}
break;
@@ -1835,9 +1988,9 @@ size_t http_parser_execute (http_parser *parser,
if (parser->content_length == 0) {
parser->flags |= F_TRAILING;
- parser->state = s_header_field_start;
+ UPDATE_STATE(s_header_field_start);
} else {
- parser->state = s_chunk_data;
+ UPDATE_STATE(s_chunk_data);
}
break;
}
@@ -1859,7 +2012,7 @@ size_t http_parser_execute (http_parser *parser,
p += to_read - 1;
if (parser->content_length == 0) {
- parser->state = s_chunk_data_almost_done;
+ UPDATE_STATE(s_chunk_data_almost_done);
}
break;
@@ -1869,7 +2022,7 @@ size_t http_parser_execute (http_parser *parser,
assert(parser->flags & F_CHUNKED);
assert(parser->content_length == 0);
STRICT_CHECK(ch != CR);
- parser->state = s_chunk_data_done;
+ UPDATE_STATE(s_chunk_data_done);
CALLBACK_DATA(body);
break;
@@ -1877,7 +2030,7 @@ size_t http_parser_execute (http_parser *parser,
assert(parser->flags & F_CHUNKED);
STRICT_CHECK(ch != LF);
parser->nread = 0;
- parser->state = s_chunk_size_start;
+ UPDATE_STATE(s_chunk_size_start);
break;
default:
@@ -1909,14 +2062,14 @@ size_t http_parser_execute (http_parser *parser,
CALLBACK_DATA_NOADVANCE(body);
CALLBACK_DATA_NOADVANCE(status);
- return len;
+ RETURN(len);
error:
if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
SET_ERRNO(HPE_UNKNOWN);
}
- return (p - data);
+ RETURN(p - data);
}
@@ -2142,7 +2295,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
u->port = u->field_set = 0;
s = is_connect ? s_req_server_start : s_req_spaces_before_url;
- uf = old_uf = UF_MAX;
+ old_uf = UF_MAX;
for (p = buf; p < buf + buflen; p++) {
s = parse_url_char(s, *p);
diff --git a/deps/http_parser/http_parser.h b/deps/http_parser/http_parser.h
index ec61a1287f..e06f10fe73 100644
--- a/deps/http_parser/http_parser.h
+++ b/deps/http_parser/http_parser.h
@@ -26,7 +26,7 @@ extern "C" {
/* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2
-#define HTTP_PARSER_VERSION_MINOR 3
+#define HTTP_PARSER_VERSION_MINOR 4
#define HTTP_PARSER_VERSION_PATCH 0
#include <sys/types.h>
@@ -52,9 +52,16 @@ typedef unsigned __int64 uint64_t;
# define HTTP_PARSER_STRICT 1
#endif
-/* Maximium header size allowed */
-#define HTTP_MAX_HEADER_SIZE (80*1024)
-
+/* Maximium header size allowed. If the macro is not defined
+ * before including this header then the default is used. To
+ * change the maximum header size, define the macro in the build
+ * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
+ * the effective limit on the size of the header, define the macro
+ * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
+ */
+#ifndef HTTP_MAX_HEADER_SIZE
+# define HTTP_MAX_HEADER_SIZE (80*1024)
+#endif
typedef struct http_parser http_parser;
typedef struct http_parser_settings http_parser_settings;
@@ -69,7 +76,7 @@ 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.
*
- * http_data_cb does not return data chunks. It will be call arbitrarally
+ * 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.
*/
@@ -110,6 +117,8 @@ typedef int (*http_cb) (http_parser*);
/* RFC-5789 */ \
XX(24, PATCH, PATCH) \
XX(25, PURGE, PURGE) \
+ /* CalDAV */ \
+ XX(26, MKCALENDAR, MKCALENDAR) \
enum http_method
{
@@ -127,9 +136,10 @@ enum flags
{ F_CHUNKED = 1 << 0
, F_CONNECTION_KEEP_ALIVE = 1 << 1
, F_CONNECTION_CLOSE = 1 << 2
- , F_TRAILING = 1 << 3
- , F_UPGRADE = 1 << 4
- , F_SKIPBODY = 1 << 5
+ , F_CONNECTION_UPGRADE = 1 << 3
+ , F_TRAILING = 1 << 4
+ , F_UPGRADE = 1 << 5
+ , F_SKIPBODY = 1 << 6
};
@@ -271,13 +281,15 @@ struct http_parser_url {
* unsigned major = (version >> 16) & 255;
* unsigned minor = (version >> 8) & 255;
* unsigned patch = version & 255;
- * printf("http_parser v%u.%u.%u\n", major, minor, version);
+ * printf("http_parser v%u.%u.%u\n", major, minor, patch);
*/
unsigned long http_parser_version(void);
void http_parser_init(http_parser *parser, enum http_parser_type type);
+/* Executes the parser. Returns number of parsed bytes. Sets
+ * `parser->http_errno` on error. */
size_t http_parser_execute(http_parser *parser,
const http_parser_settings *settings,
const char *data,
diff --git a/deps/http_parser/test.c b/deps/http_parser/test.c
index 9799dc6d34..6c45d59d0c 100644
--- a/deps/http_parser/test.c
+++ b/deps/http_parser/test.c
@@ -950,6 +950,42 @@ const struct message requests[] =
,.body= ""
}
+#define CONNECTION_MULTI 35
+, {.name = "multiple connection header values with folding"
+ ,.type= HTTP_REQUEST
+ ,.raw= "GET /demo HTTP/1.1\r\n"
+ "Host: example.com\r\n"
+ "Connection: Something,\r\n"
+ " Upgrade, ,Keep-Alive\r\n"
+ "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
+ "Sec-WebSocket-Protocol: sample\r\n"
+ "Upgrade: WebSocket\r\n"
+ "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
+ "Origin: http://example.com\r\n"
+ "\r\n"
+ "Hot diggity dogg"
+ ,.should_keep_alive= TRUE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.method= HTTP_GET
+ ,.query_string= ""
+ ,.fragment= ""
+ ,.request_path= "/demo"
+ ,.request_url= "/demo"
+ ,.num_headers= 7
+ ,.upgrade="Hot diggity dogg"
+ ,.headers= { { "Host", "example.com" }
+ , { "Connection", "Something, Upgrade, ,Keep-Alive" }
+ , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
+ , { "Sec-WebSocket-Protocol", "sample" }
+ , { "Upgrade", "WebSocket" }
+ , { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" }
+ , { "Origin", "http://example.com" }
+ }
+ ,.body= ""
+ }
+
, {.name= NULL } /* sentinel */
};
@@ -2207,7 +2243,6 @@ print_error (const char *raw, size_t error_location)
break;
case '\n':
- char_len = 2;
fprintf(stderr, "\\n\n");
if (this_line) goto print;
@@ -2910,15 +2945,11 @@ test_simple (const char *buf, enum http_errno err_expected)
{
parser_init(HTTP_REQUEST);
- size_t parsed;
- int pass;
enum http_errno err;
- parsed = parse(buf, strlen(buf));
- pass = (parsed == strlen(buf));
+ parse(buf, strlen(buf));
err = HTTP_PARSER_ERRNO(parser);
- parsed = parse(NULL, 0);
- pass &= (parsed == 0);
+ parse(NULL, 0);
parser_free();
@@ -3476,6 +3507,13 @@ main (void)
test_simple(buf, HPE_INVALID_METHOD);
}
+ // illegal header field name line folding
+ test_simple("GET / HTTP/1.1\r\n"
+ "name\r\n"
+ " : value\r\n"
+ "\r\n",
+ HPE_INVALID_HEADER_TOKEN);
+
const char *dumbfuck2 =
"GET / HTTP/1.1\r\n"
"X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n"