Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2011-08-06 13:58:44 +0400
committerRyan Dahl <ry@tinyclouds.org>2011-08-06 14:04:42 +0400
commitc3ccbea5cde63f6fea5729769370f43fe50cac8b (patch)
tree18b3eb60387af6059b65f02f9236433faf70cabd /deps/http_parser
parentbbb38b89184a2e570c02fd1908ae0d5393c9dbc0 (diff)
Upgrade http_parser to 965f91bc76b2d1601e23
Diffstat (limited to 'deps/http_parser')
-rw-r--r--deps/http_parser/Makefile38
-rw-r--r--deps/http_parser/README.md4
-rw-r--r--deps/http_parser/http_parser.c334
-rw-r--r--deps/http_parser/http_parser.gyp50
-rw-r--r--deps/http_parser/http_parser.h94
-rw-r--r--deps/http_parser/test.c376
6 files changed, 608 insertions, 288 deletions
diff --git a/deps/http_parser/Makefile b/deps/http_parser/Makefile
index 4eceeaae757..efac2167f05 100644
--- a/deps/http_parser/Makefile
+++ b/deps/http_parser/Makefile
@@ -1,39 +1,45 @@
-CPPFLAGS?=-Wall -Wextra -Werror -I.
-OPT_DEBUG=$(CPPFLAGS) -O0 -g -DHTTP_PARSER_STRICT=1
-OPT_FAST=$(CPPFLAGS) -O3 -DHTTP_PARSER_STRICT=0
-
CC?=gcc
AR?=ar
+CPPFLAGS += -I.
+CPPFLAGS_DEBUG = $(CPPFLAGS) -DHTTP_PARSER_STRICT=1 -DHTTP_PARSER_DEBUG=1
+CPPFLAGS_DEBUG += $(CPPFLAGS_DEBUG_EXTRA)
+CPPFLAGS_FAST = $(CPPFLAGS) -DHTTP_PARSER_STRICT=0 -DHTTP_PARSER_DEBUG=0
+CPPFLAGS_FAST += $(CPPFLAGS_FAST_EXTRA)
+
+CFLAGS += -Wall -Wextra -Werror
+CFLAGS_DEBUG = $(CFLAGS) -O0 -g $(CFLAGS_DEBUG_EXTRA)
+CFLAGS_FAST = $(CFLAGS) -O3 $(CFLAGS_FAST_EXTRA)
+
test: test_g test_fast
./test_g
./test_fast
test_g: http_parser_g.o test_g.o
- $(CC) $(OPT_DEBUG) http_parser_g.o test_g.o -o $@
+ $(CC) $(CFLAGS_DEBUG) $(LDFLAGS) http_parser_g.o test_g.o -o $@
test_g.o: test.c http_parser.h Makefile
- $(CC) $(OPT_DEBUG) -c test.c -o $@
-
-test.o: test.c http_parser.h Makefile
- $(CC) $(OPT_FAST) -c test.c -o $@
+ $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) -c test.c -o $@
http_parser_g.o: http_parser.c http_parser.h Makefile
- $(CC) $(OPT_DEBUG) -c http_parser.c -o $@
+ $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) -c http_parser.c -o $@
-test-valgrind: test_g
- valgrind ./test_g
+test_fast: http_parser.o test.o http_parser.h
+ $(CC) $(CFLAGS_FAST) $(LDFLAGS) http_parser.o test.o -o $@
-http_parser.o: http_parser.c http_parser.h Makefile
- $(CC) $(OPT_FAST) -c http_parser.c
+test.o: test.c http_parser.h Makefile
+ $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c test.c -o $@
-test_fast: http_parser.o test.c http_parser.h
- $(CC) $(OPT_FAST) http_parser.o test.c -o $@
+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
+test-valgrind: test_g
+ valgrind ./test_g
+
package: http_parser.o
$(AR) rcs libhttp_parser.a http_parser.o
diff --git a/deps/http_parser/README.md b/deps/http_parser/README.md
index 72332fb35bd..405dd5f342c 100644
--- a/deps/http_parser/README.md
+++ b/deps/http_parser/README.md
@@ -24,7 +24,7 @@ The parser extracts the following information from HTTP messages:
* Response status code
* Transfer-Encoding
* HTTP version
- * Request path, query string, fragment
+ * Request URL
* Message body
@@ -126,7 +126,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_path, on_query_string, on_uri, on_fragment,
+ Callbacks: (requests only) on_uri,
(common) on_header_field, on_header_value, on_body;
Callbacks must return 0 on success. Returning a non-zero value indicates
diff --git a/deps/http_parser/http_parser.c b/deps/http_parser/http_parser.c
index 1453d411b0c..e905747a320 100644
--- a/deps/http_parser/http_parser.c
+++ b/deps/http_parser/http_parser.c
@@ -31,10 +31,27 @@
#endif
+#if HTTP_PARSER_DEBUG
+#define SET_ERRNO(e) \
+do { \
+ parser->http_errno = (e); \
+ parser->error_lineno = __LINE__; \
+} while (0)
+#else
+#define SET_ERRNO(e) \
+do { \
+ parser->http_errno = (e); \
+} while(0)
+#endif
+
+
#define CALLBACK2(FOR) \
do { \
if (settings->on_##FOR) { \
- if (0 != settings->on_##FOR(parser)) return (p - data); \
+ if (0 != settings->on_##FOR(parser)) { \
+ SET_ERRNO(HPE_CB_##FOR); \
+ return (p - data); \
+ } \
} \
} while (0)
@@ -44,7 +61,7 @@ do { \
FOR##_mark = p; \
} while (0)
-#define CALLBACK_NOCLEAR(FOR) \
+#define CALLBACK(FOR) \
do { \
if (FOR##_mark) { \
if (settings->on_##FOR) { \
@@ -52,20 +69,15 @@ do { \
FOR##_mark, \
p - FOR##_mark)) \
{ \
+ SET_ERRNO(HPE_CB_##FOR); \
return (p - data); \
} \
} \
+ FOR##_mark = NULL; \
} \
} while (0)
-#define CALLBACK(FOR) \
-do { \
- CALLBACK_NOCLEAR(FOR); \
- FOR##_mark = NULL; \
-} while (0)
-
-
#define PROXY_CONNECTION "proxy-connection"
#define CONNECTION "connection"
#define CONTENT_LENGTH "content-length"
@@ -241,6 +253,7 @@ enum state
, s_header_field
, s_header_value_start
, s_header_value
+ , s_header_value_lws
, s_header_almost_done
@@ -299,7 +312,7 @@ enum header_states
#define LF '\n'
#define LOWER(c) (unsigned char)(c | 0x20)
#define TOKEN(c) (tokens[(unsigned char)c])
-#define IS_ALPHA(c) ((c) >= 'a' && (c) <= 'z')
+#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
@@ -318,7 +331,13 @@ enum header_states
#if HTTP_PARSER_STRICT
-# define STRICT_CHECK(cond) if (cond) goto error
+# define STRICT_CHECK(cond) \
+do { \
+ if (cond) { \
+ SET_ERRNO(HPE_STRICT); \
+ goto error; \
+ } \
+} while (0)
# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
#else
# define STRICT_CHECK(cond)
@@ -326,20 +345,39 @@ enum header_states
#endif
+/* Map errno values to strings for human-readable output */
+#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
+static struct {
+ const char *name;
+ const char *description;
+} http_strerror_tab[] = {
+ HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
+};
+#undef HTTP_STRERROR_GEN
+
+
size_t http_parser_execute (http_parser *parser,
const http_parser_settings *settings,
const char *data,
size_t len)
{
char c, ch;
+ int8_t unhex_val;
const char *p = data, *pe;
int64_t to_read;
-
- enum state state = (enum state) parser->state;
- enum header_states header_state = (enum header_states) parser->header_state;
+ enum state state;
+ enum header_states header_state;
uint64_t index = parser->index;
uint64_t nread = parser->nread;
+ /* We're in an error state. Don't bother doing anything. */
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+ return 0;
+ }
+
+ state = (enum state) parser->state;
+ header_state = (enum header_states) parser->header_state;
+
if (len == 0) {
switch (state) {
case s_body_identity_eof:
@@ -353,7 +391,8 @@ size_t http_parser_execute (http_parser *parser,
return 0;
default:
- return 1; // error
+ SET_ERRNO(HPE_INVALID_EOF_STATE);
+ return 1;
}
}
@@ -362,21 +401,12 @@ size_t http_parser_execute (http_parser *parser,
separated. */
const char *header_field_mark = 0;
const char *header_value_mark = 0;
- const char *fragment_mark = 0;
- const char *query_string_mark = 0;
- const char *path_mark = 0;
const char *url_mark = 0;
if (state == s_header_field)
header_field_mark = data;
if (state == s_header_value)
header_value_mark = data;
- if (state == s_req_fragment)
- fragment_mark = data;
- if (state == s_req_query_string)
- query_string_mark = data;
- if (state == s_req_path)
- path_mark = data;
if (state == s_req_path || state == s_req_schema || state == s_req_schema_slash
|| state == s_req_schema_slash_slash || state == s_req_port
|| state == s_req_query_string_start || state == s_req_query_string
@@ -390,7 +420,10 @@ size_t http_parser_execute (http_parser *parser,
if (PARSING_HEADER(state)) {
++nread;
/* Buffer overflow attack */
- if (nread > HTTP_MAX_HEADER_SIZE) goto error;
+ if (nread > HTTP_MAX_HEADER_SIZE) {
+ SET_ERRNO(HPE_HEADER_OVERFLOW);
+ goto error;
+ }
}
switch (state) {
@@ -399,6 +432,7 @@ size_t http_parser_execute (http_parser *parser,
/* this state is used after a 'Connection: close' message
* the parser will error out if it reads another message
*/
+ SET_ERRNO(HPE_CLOSED_CONNECTION);
goto error;
case s_start_req_or_res:
@@ -424,7 +458,11 @@ size_t http_parser_execute (http_parser *parser,
parser->type = HTTP_RESPONSE;
state = s_res_HT;
} else {
- if (ch != 'E') goto error;
+ if (ch != 'E') {
+ SET_ERRNO(HPE_INVALID_CONSTANT);
+ goto error;
+ }
+
parser->type = HTTP_REQUEST;
parser->method = HTTP_HEAD;
index = 2;
@@ -449,6 +487,7 @@ size_t http_parser_execute (http_parser *parser,
break;
default:
+ SET_ERRNO(HPE_INVALID_CONSTANT);
goto error;
}
break;
@@ -475,7 +514,11 @@ size_t http_parser_execute (http_parser *parser,
break;
case s_res_first_http_major:
- if (ch < '1' || ch > '9') goto error;
+ if (ch < '1' || ch > '9') {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
parser->http_major = ch - '0';
state = s_res_http_major;
break;
@@ -488,18 +531,29 @@ size_t http_parser_execute (http_parser *parser,
break;
}
- if (!IS_NUM(ch)) goto error;
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
parser->http_major *= 10;
parser->http_major += ch - '0';
- if (parser->http_major > 999) goto error;
+ if (parser->http_major > 999) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
break;
}
/* first digit of minor HTTP version */
case s_res_first_http_minor:
- if (!IS_NUM(ch)) goto error;
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
parser->http_minor = ch - '0';
state = s_res_http_minor;
break;
@@ -512,12 +566,19 @@ size_t http_parser_execute (http_parser *parser,
break;
}
- if (!IS_NUM(ch)) goto error;
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
parser->http_minor *= 10;
parser->http_minor += ch - '0';
- if (parser->http_minor > 999) goto error;
+ if (parser->http_minor > 999) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
break;
}
@@ -527,6 +588,8 @@ size_t http_parser_execute (http_parser *parser,
if (ch == ' ') {
break;
}
+
+ SET_ERRNO(HPE_INVALID_STATUS);
goto error;
}
parser->status_code = ch - '0';
@@ -548,6 +611,7 @@ size_t http_parser_execute (http_parser *parser,
state = s_header_field_start;
break;
default:
+ SET_ERRNO(HPE_INVALID_STATUS);
goto error;
}
break;
@@ -556,7 +620,11 @@ size_t http_parser_execute (http_parser *parser,
parser->status_code *= 10;
parser->status_code += ch - '0';
- if (parser->status_code > 999) goto error;
+ if (parser->status_code > 999) {
+ SET_ERRNO(HPE_INVALID_STATUS);
+ goto error;
+ }
+
break;
}
@@ -588,7 +656,10 @@ size_t http_parser_execute (http_parser *parser,
CALLBACK2(message_begin);
- if (!IS_ALPHA(LOWER(ch))) goto error;
+ if (!IS_ALPHA(ch)) {
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
start_req_method_assign:
parser->method = (enum http_method) 0;
@@ -609,7 +680,9 @@ size_t http_parser_execute (http_parser *parser,
case 'S': parser->method = HTTP_SUBSCRIBE; break;
case 'T': parser->method = HTTP_TRACE; break;
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
- default: goto error;
+ default:
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
}
state = s_req_method;
break;
@@ -617,8 +690,10 @@ size_t http_parser_execute (http_parser *parser,
case s_req_method:
{
- if (ch == '\0')
+ if (ch == '\0') {
+ SET_ERRNO(HPE_INVALID_METHOD);
goto error;
+ }
const char *matcher = method_strings[parser->method];
if (ch == ' ' && matcher[index] == '\0') {
@@ -630,6 +705,8 @@ size_t http_parser_execute (http_parser *parser,
parser->method = HTTP_CHECKOUT;
} else if (index == 2 && ch == 'P') {
parser->method = HTTP_COPY;
+ } else {
+ goto error;
}
} else if (parser->method == HTTP_MKCOL) {
if (index == 1 && ch == 'O') {
@@ -640,18 +717,25 @@ size_t http_parser_execute (http_parser *parser,
parser->method = HTTP_MSEARCH;
} else if (index == 2 && ch == 'A') {
parser->method = HTTP_MKACTIVITY;
+ } else {
+ goto error;
+ }
+ } else if (index == 1 && parser->method == HTTP_POST) {
+ if (ch == 'R') {
+ parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
+ } else if (ch == 'U') {
+ parser->method = HTTP_PUT;
+ } else if (ch == 'A') {
+ parser->method = HTTP_PATCH;
+ } else {
+ goto error;
}
- } else if (index == 1 && parser->method == HTTP_POST && ch == 'R') {
- parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
- } else if (index == 1 && parser->method == HTTP_POST && ch == 'U') {
- parser->method = HTTP_PUT;
- } else if (index == 1 && parser->method == HTTP_POST && ch == 'A') {
- parser->method = HTTP_PATCH;
} else if (index == 2 && parser->method == HTTP_UNLOCK && ch == 'S') {
parser->method = HTTP_UNSUBSCRIBE;
} else if (index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
parser->method = HTTP_PROPPATCH;
} else {
+ SET_ERRNO(HPE_INVALID_METHOD);
goto error;
}
@@ -664,13 +748,10 @@ size_t http_parser_execute (http_parser *parser,
if (ch == '/' || ch == '*') {
MARK(url);
- MARK(path);
state = s_req_path;
break;
}
- c = LOWER(ch);
-
/* Proxied requests are followed by scheme of an absolute URI (alpha).
* CONNECT is followed by a hostname, which begins with alphanum.
* All other methods are followed by '/' or '*' (handled above).
@@ -681,20 +762,20 @@ size_t http_parser_execute (http_parser *parser,
break;
}
+ SET_ERRNO(HPE_INVALID_URL);
goto error;
}
case s_req_schema:
{
- c = LOWER(ch);
-
- if (IS_ALPHA(c)) break;
+ if (IS_ALPHA(ch)) break;
if (ch == ':') {
state = s_req_schema_slash;
break;
}
+ SET_ERRNO(HPE_INVALID_URL);
goto error;
}
@@ -710,14 +791,12 @@ size_t http_parser_execute (http_parser *parser,
case s_req_host:
{
- c = LOWER(ch);
if (IS_HOST_CHAR(ch)) break;
switch (ch) {
case ':':
state = s_req_port;
break;
case '/':
- MARK(path);
state = s_req_path;
break;
case ' ':
@@ -732,6 +811,7 @@ size_t http_parser_execute (http_parser *parser,
state = s_req_query_string_start;
break;
default:
+ SET_ERRNO(HPE_INVALID_HOST);
goto error;
}
break;
@@ -742,7 +822,6 @@ size_t http_parser_execute (http_parser *parser,
if (IS_NUM(ch)) break;
switch (ch) {
case '/':
- MARK(path);
state = s_req_path;
break;
case ' ':
@@ -757,6 +836,7 @@ size_t http_parser_execute (http_parser *parser,
state = s_req_query_string_start;
break;
default:
+ SET_ERRNO(HPE_INVALID_PORT);
goto error;
}
break;
@@ -769,32 +849,28 @@ size_t http_parser_execute (http_parser *parser,
switch (ch) {
case ' ':
CALLBACK(url);
- CALLBACK(path);
state = s_req_http_start;
break;
case CR:
CALLBACK(url);
- CALLBACK(path);
parser->http_major = 0;
parser->http_minor = 9;
state = s_req_line_almost_done;
break;
case LF:
CALLBACK(url);
- CALLBACK(path);
parser->http_major = 0;
parser->http_minor = 9;
state = s_header_field_start;
break;
case '?':
- CALLBACK(path);
state = s_req_query_string_start;
break;
case '#':
- CALLBACK(path);
state = s_req_fragment_start;
break;
default:
+ SET_ERRNO(HPE_INVALID_PATH);
goto error;
}
break;
@@ -803,7 +879,6 @@ size_t http_parser_execute (http_parser *parser,
case s_req_query_string_start:
{
if (IS_URL_CHAR(ch)) {
- MARK(query_string);
state = s_req_query_string;
break;
}
@@ -831,6 +906,7 @@ size_t http_parser_execute (http_parser *parser,
state = s_req_fragment_start;
break;
default:
+ SET_ERRNO(HPE_INVALID_QUERY_STRING);
goto error;
}
break;
@@ -846,28 +922,25 @@ size_t http_parser_execute (http_parser *parser,
break;
case ' ':
CALLBACK(url);
- CALLBACK(query_string);
state = s_req_http_start;
break;
case CR:
CALLBACK(url);
- CALLBACK(query_string);
parser->http_major = 0;
parser->http_minor = 9;
state = s_req_line_almost_done;
break;
case LF:
CALLBACK(url);
- CALLBACK(query_string);
parser->http_major = 0;
parser->http_minor = 9;
state = s_header_field_start;
break;
case '#':
- CALLBACK(query_string);
state = s_req_fragment_start;
break;
default:
+ SET_ERRNO(HPE_INVALID_QUERY_STRING);
goto error;
}
break;
@@ -876,7 +949,6 @@ size_t http_parser_execute (http_parser *parser,
case s_req_fragment_start:
{
if (IS_URL_CHAR(ch)) {
- MARK(fragment);
state = s_req_fragment;
break;
}
@@ -899,12 +971,12 @@ size_t http_parser_execute (http_parser *parser,
state = s_header_field_start;
break;
case '?':
- MARK(fragment);
state = s_req_fragment;
break;
case '#':
break;
default:
+ SET_ERRNO(HPE_INVALID_FRAGMENT);
goto error;
}
break;
@@ -917,19 +989,16 @@ size_t http_parser_execute (http_parser *parser,
switch (ch) {
case ' ':
CALLBACK(url);
- CALLBACK(fragment);
state = s_req_http_start;
break;
case CR:
CALLBACK(url);
- CALLBACK(fragment);
parser->http_major = 0;
parser->http_minor = 9;
state = s_req_line_almost_done;
break;
case LF:
CALLBACK(url);
- CALLBACK(fragment);
parser->http_major = 0;
parser->http_minor = 9;
state = s_header_field_start;
@@ -938,6 +1007,7 @@ size_t http_parser_execute (http_parser *parser,
case '#':
break;
default:
+ SET_ERRNO(HPE_INVALID_FRAGMENT);
goto error;
}
break;
@@ -951,6 +1021,7 @@ size_t http_parser_execute (http_parser *parser,
case ' ':
break;
default:
+ SET_ERRNO(HPE_INVALID_CONSTANT);
goto error;
}
break;
@@ -977,7 +1048,11 @@ size_t http_parser_execute (http_parser *parser,
/* first digit of major HTTP version */
case s_req_first_http_major:
- if (ch < '1' || ch > '9') goto error;
+ if (ch < '1' || ch > '9') {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
parser->http_major = ch - '0';
state = s_req_http_major;
break;
@@ -990,18 +1065,29 @@ size_t http_parser_execute (http_parser *parser,
break;
}
- if (!IS_NUM(ch)) goto error;
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
parser->http_major *= 10;
parser->http_major += ch - '0';
- if (parser->http_major > 999) goto error;
+ if (parser->http_major > 999) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
break;
}
/* first digit of minor HTTP version */
case s_req_first_http_minor:
- if (!IS_NUM(ch)) goto error;
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
parser->http_minor = ch - '0';
state = s_req_http_minor;
break;
@@ -1021,24 +1107,36 @@ size_t http_parser_execute (http_parser *parser,
/* XXX allow spaces after digit? */
- if (!IS_NUM(ch)) goto error;
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
parser->http_minor *= 10;
parser->http_minor += ch - '0';
- if (parser->http_minor > 999) goto error;
+ if (parser->http_minor > 999) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
break;
}
/* end of request line */
case s_req_line_almost_done:
{
- if (ch != LF) goto error;
+ if (ch != LF) {
+ SET_ERRNO(HPE_LF_EXPECTED);
+ goto error;
+ }
+
state = s_header_field_start;
break;
}
case s_header_field_start:
+ header_field_start:
{
if (ch == CR) {
state = s_headers_almost_done;
@@ -1054,7 +1152,10 @@ size_t http_parser_execute (http_parser *parser,
c = TOKEN(ch);
- if (!c) goto error;
+ if (!c) {
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+ goto error;
+ }
MARK(header_field);
@@ -1211,20 +1312,19 @@ size_t http_parser_execute (http_parser *parser,
break;
}
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
goto error;
}
case s_header_value_start:
{
- if (ch == ' ') break;
+ if (ch == ' ' || ch == '\t') break;
MARK(header_value);
state = s_header_value;
index = 0;
- c = LOWER(ch);
-
if (ch == CR) {
CALLBACK(header_value);
header_state = h_general;
@@ -1238,6 +1338,8 @@ size_t http_parser_execute (http_parser *parser,
break;
}
+ c = LOWER(ch);
+
switch (header_state) {
case h_upgrade:
parser->flags |= F_UPGRADE;
@@ -1254,7 +1356,11 @@ size_t http_parser_execute (http_parser *parser,
break;
case h_content_length:
- if (!IS_NUM(ch)) goto error;
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ goto error;
+ }
+
parser->content_length = ch - '0';
break;
@@ -1279,7 +1385,6 @@ size_t http_parser_execute (http_parser *parser,
case s_header_value:
{
- c = LOWER(ch);
if (ch == CR) {
CALLBACK(header_value);
@@ -1292,6 +1397,8 @@ size_t http_parser_execute (http_parser *parser,
goto header_almost_done;
}
+ c = LOWER(ch);
+
switch (header_state) {
case h_general:
break;
@@ -1303,7 +1410,11 @@ size_t http_parser_execute (http_parser *parser,
case h_content_length:
if (ch == ' ') break;
- if (!IS_NUM(ch)) goto error;
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ goto error;
+ }
+
parser->content_length *= 10;
parser->content_length += ch - '0';
break;
@@ -1359,7 +1470,7 @@ size_t http_parser_execute (http_parser *parser,
{
STRICT_CHECK(ch != LF);
- state = s_header_field_start;
+ state = s_header_value_lws;
switch (header_state) {
case h_connection_keep_alive:
@@ -1377,6 +1488,18 @@ size_t http_parser_execute (http_parser *parser,
break;
}
+ case s_header_value_lws:
+ {
+ if (ch == ' ' || ch == '\t')
+ state = s_header_value_start;
+ else
+ {
+ state = s_header_field_start;
+ goto header_field_start;
+ }
+ break;
+ }
+
case s_headers_almost_done:
headers_almost_done:
{
@@ -1412,6 +1535,7 @@ size_t http_parser_execute (http_parser *parser,
default:
parser->state = state;
+ SET_ERRNO(HPE_CB_headers_complete);
return p - data; /* Error */
}
}
@@ -1419,7 +1543,7 @@ size_t http_parser_execute (http_parser *parser,
/* Exit, the rest of the connect is in a different protocol. */
if (parser->upgrade) {
CALLBACK2(message_complete);
- return (p - data);
+ return (p - data) + 1;
}
if (parser->flags & F_SKIPBODY) {
@@ -1478,9 +1602,13 @@ size_t http_parser_execute (http_parser *parser,
assert(nread == 1);
assert(parser->flags & F_CHUNKED);
- c = unhex[(unsigned char)ch];
- if (c == -1) goto error;
- parser->content_length = c;
+ unhex_val = unhex[(unsigned char)ch];
+ if (unhex_val == -1) {
+ SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+ goto error;
+ }
+
+ parser->content_length = unhex_val;
state = s_chunk_size;
break;
}
@@ -1494,18 +1622,20 @@ size_t http_parser_execute (http_parser *parser,
break;
}
- c = unhex[(unsigned char)ch];
+ unhex_val = unhex[(unsigned char)ch];
- if (c == -1) {
+ if (unhex_val == -1) {
if (ch == ';' || ch == ' ') {
state = s_chunk_parameters;
break;
}
+
+ SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
goto error;
}
parser->content_length *= 16;
- parser->content_length += c;
+ parser->content_length += unhex_val;
break;
}
@@ -1569,16 +1699,14 @@ size_t http_parser_execute (http_parser *parser,
default:
assert(0 && "unhandled state");
+ SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
goto error;
}
}
- CALLBACK_NOCLEAR(header_field);
- CALLBACK_NOCLEAR(header_value);
- CALLBACK_NOCLEAR(fragment);
- CALLBACK_NOCLEAR(query_string);
- CALLBACK_NOCLEAR(path);
- CALLBACK_NOCLEAR(url);
+ CALLBACK(header_field);
+ CALLBACK(header_value);
+ CALLBACK(url);
parser->state = state;
parser->header_state = header_state;
@@ -1588,7 +1716,10 @@ size_t http_parser_execute (http_parser *parser,
return len;
error:
- parser->state = s_dead;
+ if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
+ SET_ERRNO(HPE_UNKNOWN);
+ }
+
return (p - data);
}
@@ -1629,4 +1760,17 @@ http_parser_init (http_parser *parser, enum http_parser_type t)
parser->upgrade = 0;
parser->flags = 0;
parser->method = 0;
+ parser->http_errno = 0;
+}
+
+const char *
+http_errno_name(enum http_errno err) {
+ assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
+ return http_strerror_tab[err].name;
+}
+
+const char *
+http_errno_description(enum http_errno err) {
+ assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
+ return http_strerror_tab[err].description;
}
diff --git a/deps/http_parser/http_parser.gyp b/deps/http_parser/http_parser.gyp
new file mode 100644
index 00000000000..e9183b8d55d
--- /dev/null
+++ b/deps/http_parser/http_parser.gyp
@@ -0,0 +1,50 @@
+# This file is used with the GYP meta build system.
+# http://code.google.com/p/gyp/
+# To build try this:
+# svn co http://gyp.googlecode.com/svn/trunk gyp
+# ./gyp/gyp -f make --depth=`pwd` http_parser.gyp
+# ./out/Debug/test
+{
+ 'target_defaults': {
+ 'configurations': {
+ 'Debug': {
+ 'defines': [ 'DEBUG', '_DEBUG' ]
+ },
+ 'Release': {
+ 'defines': [ 'NDEBUG' ]
+ }
+ }
+ },
+
+ 'targets': [
+ {
+ 'target_name': 'http_parser',
+ 'type': 'static_library',
+ 'include_dirs': [ '.' ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [ '.' ],
+ },
+ 'defines': [ 'HTTP_PARSER_STRICT=0' ],
+ 'sources': [ './http_parser.c', ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'msvs_settings': {
+ 'VCCLCompilerTool': {
+ # Compile as C++. http_parser.c is actually C99, but C++ is
+ # close enough in this case.
+ 'CompileAs': 2,
+ },
+ },
+ }]
+ ],
+ },
+
+ {
+ 'target_name': 'test',
+ 'type': 'executable',
+ 'dependencies': [ 'http_parser' ],
+ 'sources': [ 'test.c' ]
+ }
+ ]
+}
+
diff --git a/deps/http_parser/http_parser.h b/deps/http_parser/http_parser.h
index 6a54a2d6357..7d395208e9f 100644
--- a/deps/http_parser/http_parser.h
+++ b/deps/http_parser/http_parser.h
@@ -28,7 +28,7 @@ extern "C" {
#define HTTP_PARSER_VERSION_MINOR 0
#include <sys/types.h>
-#if defined(_WIN32) && !defined(__MINGW32__)
+#if defined(_WIN32) && !defined(__MINGW32__) && !defined(_MSC_VER)
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
@@ -51,6 +51,13 @@ typedef int ssize_t;
# define HTTP_PARSER_STRICT 1
#endif
+/* Compile with -DHTTP_PARSER_DEBUG=1 to add extra debugging information to
+ * the error reporting facility.
+ */
+#ifndef HTTP_PARSER_DEBUG
+# define HTTP_PARSER_DEBUG 0
+#endif
+
/* Maximium header size allowed */
#define HTTP_MAX_HEADER_SIZE (80*1024)
@@ -58,6 +65,7 @@ typedef int ssize_t;
typedef struct http_parser http_parser;
typedef struct http_parser_settings http_parser_settings;
+typedef struct http_parser_result http_parser_result;
/* Callbacks should return non-zero to indicate an error. The parser will
@@ -125,6 +133,72 @@ enum flags
};
+/* Map for errno-related constants
+ *
+ * The provided argument should be a macro that takes 2 arguments.
+ */
+#define HTTP_ERRNO_MAP(XX) \
+ /* No error */ \
+ XX(OK, "success") \
+ \
+ /* Callback-related errors */ \
+ XX(CB_message_begin, "the on_message_begin callback failed") \
+ XX(CB_path, "the on_path callback failed") \
+ XX(CB_query_string, "the on_query_string callback failed") \
+ XX(CB_url, "the on_url callback failed") \
+ XX(CB_fragment, "the on_fragment callback failed") \
+ XX(CB_header_field, "the on_header_field callback failed") \
+ XX(CB_header_value, "the on_header_value callback failed") \
+ XX(CB_headers_complete, "the on_headers_complete callback failed") \
+ XX(CB_body, "the on_body callback failed") \
+ XX(CB_message_complete, "the on_message_complete callback failed") \
+ \
+ /* Parsing-related errors */ \
+ XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
+ XX(HEADER_OVERFLOW, \
+ "too many header bytes seen; overflow detected") \
+ XX(CLOSED_CONNECTION, \
+ "data received after completed connection: close message") \
+ XX(INVALID_VERSION, "invalid HTTP version") \
+ XX(INVALID_STATUS, "invalid HTTP status code") \
+ XX(INVALID_METHOD, "invalid HTTP method") \
+ XX(INVALID_URL, "invalid URL") \
+ XX(INVALID_HOST, "invalid host") \
+ XX(INVALID_PORT, "invalid port") \
+ XX(INVALID_PATH, "invalid path") \
+ XX(INVALID_QUERY_STRING, "invalid query string") \
+ XX(INVALID_FRAGMENT, "invalid fragment") \
+ XX(LF_EXPECTED, "LF character expected") \
+ XX(INVALID_HEADER_TOKEN, "invalid character in header") \
+ XX(INVALID_CONTENT_LENGTH, \
+ "invalid character in content-length header") \
+ XX(INVALID_CHUNK_SIZE, \
+ "invalid character in chunk size header") \
+ XX(INVALID_CONSTANT, "invalid constant string") \
+ XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
+ XX(STRICT, "strict mode assertion failed") \
+ XX(UNKNOWN, "an unknown error occurred")
+
+
+/* Define HPE_* values for each errno value above */
+#define HTTP_ERRNO_GEN(n, s) HPE_##n,
+enum http_errno {
+ HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
+};
+#undef HTTP_ERRNO_GEN
+
+
+/* Get an http_errno value from an http_parser */
+#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
+
+/* Get the line number that generated the current error */
+#if HTTP_PARSER_DEBUG
+#define HTTP_PARSER_ERRNO_LINE(p) ((p)->error_lineno)
+#else
+#define HTTP_PARSER_ERRNO_LINE(p) 0
+#endif
+
+
struct http_parser {
/** PRIVATE **/
unsigned char type : 2;
@@ -141,13 +215,18 @@ struct http_parser {
unsigned short http_minor;
unsigned short status_code; /* responses only */
unsigned char method; /* requests only */
+ unsigned char http_errno : 7;
/* 1 = Upgrade header was present and the parser has exited because of that.
* 0 = No upgrade header present.
* Should be checked when http_parser_execute() returns in addition to
* error checking.
*/
- char upgrade;
+ char upgrade : 1;
+
+#if HTTP_PARSER_DEBUG
+ uint32_t error_lineno;
+#endif
/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */
@@ -156,10 +235,7 @@ struct http_parser {
struct http_parser_settings {
http_cb on_message_begin;
- http_data_cb on_path;
- http_data_cb on_query_string;
http_data_cb on_url;
- http_data_cb on_fragment;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
@@ -186,7 +262,13 @@ size_t http_parser_execute(http_parser *parser,
int http_should_keep_alive(http_parser *parser);
/* Returns a string version of the HTTP method. */
-const char *http_method_str(enum http_method);
+const char *http_method_str(enum http_method m);
+
+/* Return a string name of the given error */
+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);
#ifdef __cplusplus
}
diff --git a/deps/http_parser/test.c b/deps/http_parser/test.c
index a4b80a2e606..7d95b0eb9c5 100644
--- a/deps/http_parser/test.c
+++ b/deps/http_parser/test.c
@@ -44,10 +44,7 @@ struct message {
enum http_parser_type type;
enum http_method method;
int status_code;
- char request_path[MAX_ELEMENT_SIZE];
char request_url[MAX_ELEMENT_SIZE];
- char fragment[MAX_ELEMENT_SIZE];
- char query_string[MAX_ELEMENT_SIZE];
char body[MAX_ELEMENT_SIZE];
size_t body_size;
int num_headers;
@@ -55,7 +52,7 @@ struct message {
char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
int should_keep_alive;
- int upgrade;
+ const char *upgrade; // upgraded body
unsigned short http_major;
unsigned short http_minor;
@@ -86,9 +83,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/test"
,.request_url= "/test"
,.num_headers= 3
,.headers=
@@ -117,9 +111,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/favicon.ico"
,.request_url= "/favicon.ico"
,.num_headers= 8
,.headers=
@@ -146,9 +137,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/dumbfuck"
,.request_url= "/dumbfuck"
,.num_headers= 1
,.headers=
@@ -167,9 +155,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
- ,.query_string= "page=1"
- ,.fragment= "posts-17408"
- ,.request_path= "/forums/1/topics/2375"
/* XXX request url does include fragment? */
,.request_url= "/forums/1/topics/2375?page=1#posts-17408"
,.num_headers= 0
@@ -186,9 +171,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/get_no_headers_no_body/world"
,.request_url= "/get_no_headers_no_body/world"
,.num_headers= 0
,.body= ""
@@ -205,9 +187,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/get_one_header_no_body"
,.request_url= "/get_one_header_no_body"
,.num_headers= 1
,.headers=
@@ -228,9 +207,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 0
,.method= HTTP_GET
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/get_funky_content_length_body_hello"
,.request_url= "/get_funky_content_length_body_hello"
,.num_headers= 1
,.headers=
@@ -253,9 +229,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_POST
- ,.query_string= "q=search"
- ,.fragment= "hey"
- ,.request_path= "/post_identity_body_world"
,.request_url= "/post_identity_body_world?q=search#hey"
,.num_headers= 3
,.headers=
@@ -280,9 +253,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_POST
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/post_chunked_all_your_base"
,.request_url= "/post_chunked_all_your_base"
,.num_headers= 1
,.headers=
@@ -306,9 +276,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_POST
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/two_chunks_mult_zero_end"
,.request_url= "/two_chunks_mult_zero_end"
,.num_headers= 1
,.headers=
@@ -334,9 +301,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_POST
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/chunked_w_trailing_headers"
,.request_url= "/chunked_w_trailing_headers"
,.num_headers= 3
,.headers=
@@ -362,9 +326,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_POST
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/chunked_w_bullshit_after_length"
,.request_url= "/chunked_w_bullshit_after_length"
,.num_headers= 1
,.headers=
@@ -382,9 +343,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
- ,.query_string= "foo=\"bar\""
- ,.fragment= ""
- ,.request_path= "/with_\"stupid\"_quotes"
,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\""
,.num_headers= 0
,.headers= { }
@@ -408,9 +366,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 0
,.method= HTTP_GET
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/test"
,.request_url= "/test"
,.num_headers= 3
,.headers= { { "Host", "0.0.0.0:5000" }
@@ -431,9 +386,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
- ,.query_string= "foo=bar?baz"
- ,.fragment= ""
- ,.request_path= "/test.cgi"
,.request_url= "/test.cgi?foo=bar?baz"
,.num_headers= 0
,.headers= {}
@@ -452,9 +404,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/test"
,.request_url= "/test"
,.num_headers= 0
,.headers= { }
@@ -473,17 +422,15 @@ const struct message requests[] =
"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=1
+ ,.upgrade="Hot diggity dogg"
,.headers= { { "Host", "example.com" }
, { "Connection", "Upgrade" }
, { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
@@ -502,17 +449,16 @@ const struct message requests[] =
"User-agent: Mozilla/1.1N\r\n"
"Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
"\r\n"
+ "some data\r\n"
+ "and yet even more data"
,.should_keep_alive= FALSE
,.message_complete_on_eof= FALSE
,.http_major= 1
,.http_minor= 0
,.method= HTTP_CONNECT
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= ""
,.request_url= "0-home0.netscape.com:443"
,.num_headers= 2
- ,.upgrade=1
+ ,.upgrade="some data\r\nand yet even more data"
,.headers= { { "User-agent", "Mozilla/1.1N" }
, { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
}
@@ -529,9 +475,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_REPORT
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/test"
,.request_url= "/test"
,.num_headers= 0
,.headers= {}
@@ -548,9 +491,6 @@ const struct message requests[] =
,.http_major= 0
,.http_minor= 9
,.method= HTTP_GET
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/"
,.request_url= "/"
,.num_headers= 0
,.headers= {}
@@ -570,9 +510,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_MSEARCH
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "*"
,.request_url= "*"
,.num_headers= 3
,.headers= { { "HOST", "239.255.255.250:1900" }
@@ -582,6 +519,32 @@ const struct message requests[] =
,.body= ""
}
+#define LINE_FOLDING_IN_HEADER 20
+, {.name= "line folding in header value"
+ ,.type= HTTP_REQUEST
+ ,.raw= "GET / HTTP/1.1\r\n"
+ "Line1: abc\r\n"
+ "\tdef\r\n"
+ " ghi\r\n"
+ "\t\tjkl\r\n"
+ " mno \r\n"
+ "\t \tqrs\r\n"
+ "Line2: \t line2\t\r\n"
+ "\r\n"
+ ,.should_keep_alive= TRUE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.method= HTTP_GET
+ ,.request_url= "/"
+ ,.num_headers= 2
+ ,.headers= { { "Line1", "abcdefghijklmno qrs" }
+ , { "Line2", "line2\t" }
+ }
+ ,.body= ""
+ }
+
+
#define QUERY_TERMINATED_HOST 21
, {.name= "host terminated by a query string"
,.type= HTTP_REQUEST
@@ -592,9 +555,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
- ,.query_string= "hail=all"
- ,.fragment= ""
- ,.request_path= ""
,.request_url= "http://hypnotoad.org?hail=all"
,.num_headers= 0
,.headers= { }
@@ -611,9 +571,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
- ,.query_string= "hail=all"
- ,.fragment= ""
- ,.request_path= ""
,.request_url= "http://hypnotoad.org:1234?hail=all"
,.num_headers= 0
,.headers= { }
@@ -630,9 +587,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= ""
,.request_url= "http://hypnotoad.org:1234"
,.num_headers= 0
,.headers= { }
@@ -651,9 +605,6 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
- ,.query_string= "q=1"
- ,.fragment= "narf"
- ,.request_path= "/δ¶/δt/pope"
,.request_url= "/δ¶/δt/pope?q=1#narf"
,.num_headers= 1
,.headers= { {"Host", "github.com" }
@@ -673,12 +624,9 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 0
,.method= HTTP_CONNECT
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= ""
,.request_url= "home_0.netscape.com:443"
,.num_headers= 2
- ,.upgrade=1
+ ,.upgrade=""
,.headers= { { "User-agent", "Mozilla/1.1N" }
, { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
}
@@ -701,12 +649,8 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_PATCH
- ,.query_string= ""
- ,.fragment= ""
- ,.request_path= "/file.txt"
,.request_url= "/file.txt"
,.num_headers= 4
- ,.upgrade=0
,.headers= { { "Host", "www.example.com" }
, { "Content-Type", "application/example" }
, { "If-Match", "\"e0023aa4e\"" }
@@ -715,6 +659,27 @@ const struct message requests[] =
,.body= "cccccccccc"
}
+#define CONNECT_CAPS_REQUEST 27
+, {.name = "connect caps request"
+ ,.type= HTTP_REQUEST
+ ,.raw= "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\n"
+ "User-agent: Mozilla/1.1N\r\n"
+ "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
+ "\r\n"
+ ,.should_keep_alive= FALSE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 0
+ ,.method= HTTP_CONNECT
+ ,.request_url= "HOME0.NETSCAPE.COM:443"
+ ,.num_headers= 2
+ ,.upgrade=""
+ ,.headers= { { "User-agent", "Mozilla/1.1N" }
+ , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
+ }
+ ,.body= ""
+ }
+
, {.name= NULL } /* sentinel */
};
@@ -1081,14 +1046,6 @@ const struct message responses[] =
};
int
-request_path_cb (http_parser *p, const char *buf, size_t len)
-{
- assert(p == parser);
- strncat(messages[num_messages].request_path, buf, len);
- return 0;
-}
-
-int
request_url_cb (http_parser *p, const char *buf, size_t len)
{
assert(p == parser);
@@ -1097,22 +1054,6 @@ request_url_cb (http_parser *p, const char *buf, size_t len)
}
int
-query_string_cb (http_parser *p, const char *buf, size_t len)
-{
- assert(p == parser);
- strncat(messages[num_messages].query_string, buf, len);
- return 0;
-}
-
-int
-fragment_cb (http_parser *p, const char *buf, size_t len)
-{
- assert(p == parser);
- strncat(messages[num_messages].fragment, buf, len);
- return 0;
-}
-
-int
header_field_cb (http_parser *p, const char *buf, size_t len)
{
assert(p == parser);
@@ -1205,10 +1146,7 @@ static http_parser_settings settings =
{.on_message_begin = message_begin_cb
,.on_header_field = header_field_cb
,.on_header_value = header_value_cb
- ,.on_path = request_path_cb
,.on_url = request_url_cb
- ,.on_fragment = fragment_cb
- ,.on_query_string = query_string_cb
,.on_body = body_cb
,.on_headers_complete = headers_complete_cb
,.on_message_complete = message_complete_cb
@@ -1218,10 +1156,7 @@ static http_parser_settings settings_count_body =
{.on_message_begin = message_begin_cb
,.on_header_field = header_field_cb
,.on_header_value = header_value_cb
- ,.on_path = request_path_cb
,.on_url = request_url_cb
- ,.on_fragment = fragment_cb
- ,.on_query_string = query_string_cb
,.on_body = count_body_cb
,.on_headers_complete = headers_complete_cb
,.on_message_complete = message_complete_cb
@@ -1231,10 +1166,7 @@ static http_parser_settings settings_null =
{.on_message_begin = 0
,.on_header_field = 0
,.on_header_value = 0
- ,.on_path = 0
,.on_url = 0
- ,.on_fragment = 0
- ,.on_query_string = 0
,.on_body = 0
,.on_headers_complete = 0
,.on_message_complete = 0
@@ -1284,7 +1216,13 @@ check_str_eq (const struct message *m,
const char *prop,
const char *expected,
const char *found) {
- if (0 != strcmp(expected, found)) {
+ if ((expected == NULL) != (found == NULL)) {
+ printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
+ printf("expected %s\n", (expected == NULL) ? "NULL" : expected);
+ printf(" found %s\n", (found == NULL) ? "NULL" : found);
+ return 0;
+ }
+ if (expected != NULL && 0 != strcmp(expected, found)) {
printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
printf("expected '%s'\n", expected);
printf(" found '%s'\n", found);
@@ -1337,9 +1275,6 @@ message_eq (int index, const struct message *expected)
assert(m->message_complete_cb_called);
- MESSAGE_CHECK_STR_EQ(expected, m, request_path);
- MESSAGE_CHECK_STR_EQ(expected, m, query_string);
- MESSAGE_CHECK_STR_EQ(expected, m, fragment);
MESSAGE_CHECK_STR_EQ(expected, m, request_url);
if (expected->body_size) {
MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
@@ -1357,13 +1292,81 @@ message_eq (int index, const struct message *expected)
if (!r) return 0;
}
+ MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
+
return 1;
}
+/* Given a sequence of varargs messages, return the number of them that the
+ * parser should successfully parse, taking into account that upgraded
+ * messages prevent all subsequent messages from being parsed.
+ */
+size_t
+count_parsed_messages(const size_t nmsgs, ...) {
+ size_t i;
+ va_list ap;
+
+ va_start(ap, nmsgs);
+
+ for (i = 0; i < nmsgs; i++) {
+ struct message *m = va_arg(ap, struct message *);
+
+ if (m->upgrade) {
+ va_end(ap);
+ return i + 1;
+ }
+ }
+
+ va_end(ap);
+ return nmsgs;
+}
+
+/* Given a sequence of bytes and the number of these that we were able to
+ * parse, verify that upgrade bodies are correct.
+ */
+void
+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++) {
+ struct message *m = va_arg(ap, struct message *);
+
+ off += strlen(m->raw);
+
+ if (m->upgrade) {
+ off -= strlen(m->upgrade);
+
+ /* Check the portion of the response after its specified upgrade */
+ if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
+ exit(1);
+ }
+
+ /* Fix up the response so that message_eq() will verify the beginning
+ * of the upgrade */
+ *(body + nread + strlen(m->upgrade)) = '\0';
+ messages[num_messages -1 ].upgrade = body + nread;
+
+ va_end(ap);
+ return;
+ }
+ }
+
+ va_end(ap);
+ printf("\n\n*** Error: expected a message with upgrade ***\n");
+
+ exit(1);
+}
+
static void
print_error (const char *raw, size_t error_location)
{
- fprintf(stderr, "\n*** parse error ***\n\n");
+ fprintf(stderr, "\n*** %s:%d -- %s ***\n\n",
+ "http_parser.c", HTTP_PARSER_ERRNO_LINE(parser),
+ http_errno_description(HTTP_PARSER_ERRNO(parser)));
int this_line = 0, char_len = 0;
size_t i, j, len = strlen(raw), error_location_line = 0;
@@ -1418,7 +1421,10 @@ test_message (const struct message *message)
if (msg1len) {
read = parse(msg1, msg1len);
- if (message->upgrade && parser->upgrade) goto test;
+ if (message->upgrade && parser->upgrade) {
+ messages[num_messages - 1].upgrade = msg1 + read;
+ goto test;
+ }
if (read != msg1len) {
print_error(msg1, read);
@@ -1429,7 +1435,10 @@ test_message (const struct message *message)
read = parse(msg2, msg2len);
- if (message->upgrade && parser->upgrade) goto test;
+ if (message->upgrade && parser->upgrade) {
+ messages[num_messages - 1].upgrade = msg2 + read;
+ goto test;
+ }
if (read != msg2len) {
print_error(msg2, read);
@@ -1438,8 +1447,6 @@ test_message (const struct message *message)
read = parse(NULL, 0);
- if (message->upgrade && parser->upgrade) goto test;
-
if (read != 0) {
print_error(message->raw, read);
exit(1);
@@ -1495,21 +1502,32 @@ test_message_count_body (const struct message *message)
}
void
-test_simple (const char *buf, int should_pass)
+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));
+ err = HTTP_PARSER_ERRNO(parser);
parsed = parse(NULL, 0);
pass &= (parsed == 0);
parser_free();
- if (pass != should_pass) {
- fprintf(stderr, "\n*** test_simple expected %s ***\n\n%s", should_pass ? "success" : "error", buf);
+ /* In strict mode, allow us to pass with an unexpected HPE_STRICT as
+ * long as the caller isn't expecting success.
+ */
+#if HTTP_PARSER_STRICT
+ if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
+#else
+ if (err_expected != err) {
+#endif
+ fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n",
+ http_errno_name(err_expected), http_errno_name(err), buf);
exit(1);
}
}
@@ -1526,10 +1544,14 @@ test_header_overflow_error (int req)
assert(parsed == strlen(buf));
buf = "header-key: header-value\r\n";
+ size_t buflen = strlen(buf);
+
int i;
for (i = 0; i < 10000; i++) {
- if (http_parser_execute(&parser, &settings_null, buf, strlen(buf)) != strlen(buf)) {
+ parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
+ if (parsed != buflen) {
//fprintf(stderr, "error found on iter %d\n", i);
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
return;
}
}
@@ -1574,12 +1596,7 @@ test_no_overflow_long_body (int req, size_t length)
void
test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
{
- int message_count = 1;
- if (!r1->upgrade) {
- message_count++;
- if (!r2->upgrade) message_count++;
- }
- int has_upgrade = (message_count < 3 || r3->upgrade);
+ int message_count = count_parsed_messages(3, r1, r2, r3);
char total[ strlen(r1->raw)
+ strlen(r2->raw)
@@ -1598,7 +1615,10 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
read = parse(total, strlen(total));
- if (has_upgrade && parser->upgrade) goto test;
+ if (parser->upgrade) {
+ upgrade_message_fix(total, read, 3, r1, r2, r3);
+ goto test;
+ }
if (read != strlen(total)) {
print_error(total, read);
@@ -1607,8 +1627,6 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
read = parse(NULL, 0);
- if (has_upgrade && parser->upgrade) goto test;
-
if (read != 0) {
print_error(total, read);
exit(1);
@@ -1622,12 +1640,8 @@ test:
}
if (!message_eq(0, r1)) exit(1);
- if (message_count > 1) {
- if (!message_eq(1, r2)) exit(1);
- if (message_count > 2) {
- if (!message_eq(2, r3)) exit(1);
- }
- }
+ if (message_count > 1 && !message_eq(1, r2)) exit(1);
+ if (message_count > 2 && !message_eq(2, r3)) exit(1);
parser_free();
}
@@ -1656,6 +1670,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
int ops = 0 ;
size_t buf1_len, buf2_len, buf3_len;
+ int message_count = count_parsed_messages(3, r1, r2, r3);
int i,j,type_both;
for (type_both = 0; type_both < 2; type_both ++ ) {
@@ -1684,27 +1699,27 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
read = parse(buf1, buf1_len);
- if (r3->upgrade && parser->upgrade) goto test;
+ if (parser->upgrade) goto test;
if (read != buf1_len) {
print_error(buf1, read);
goto error;
}
- read = parse(buf2, buf2_len);
+ read += parse(buf2, buf2_len);
- if (r3->upgrade && parser->upgrade) goto test;
+ if (parser->upgrade) goto test;
- if (read != buf2_len) {
+ if (read != buf1_len + buf2_len) {
print_error(buf2, read);
goto error;
}
- read = parse(buf3, buf3_len);
+ read += parse(buf3, buf3_len);
- if (r3->upgrade && parser->upgrade) goto test;
+ if (parser->upgrade) goto test;
- if (read != buf3_len) {
+ if (read != buf1_len + buf2_len + buf3_len) {
print_error(buf3, read);
goto error;
}
@@ -1712,9 +1727,13 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
parse(NULL, 0);
test:
+ if (parser->upgrade) {
+ upgrade_message_fix(total, read, 3, r1, r2, r3);
+ }
- if (3 != num_messages) {
- fprintf(stderr, "\n\nParser didn't see 3 messages only %d\n", num_messages);
+ if (message_count != num_messages) {
+ fprintf(stderr, "\n\nParser didn't see %d messages only %d\n",
+ message_count, num_messages);
goto error;
}
@@ -1723,12 +1742,12 @@ test:
goto error;
}
- if (!message_eq(1, r2)) {
+ if (message_count > 1 && !message_eq(1, r2)) {
fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
goto error;
}
- if (!message_eq(2, r3)) {
+ if (message_count > 2 && !message_eq(2, r3)) {
fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
goto error;
}
@@ -1868,13 +1887,13 @@ main (void)
/// REQUESTS
- test_simple("hello world", 0);
- test_simple("GET / HTP/1.1\r\n\r\n", 0);
+ test_simple("hello world", HPE_INVALID_METHOD);
+ test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
- test_simple("ASDF / HTTP/1.1\r\n\r\n", 0);
- test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", 0);
- test_simple("GETA / HTTP/1.1\r\n\r\n", 0);
+ test_simple("ASDF / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
+ test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
+ test_simple("GETA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
// Well-formed but incomplete
test_simple("GET / HTTP/1.1\r\n"
@@ -1882,7 +1901,7 @@ main (void)
"Content-Length: 6\r\n"
"\r\n"
"fooba",
- 0);
+ HPE_OK);
static const char *all_methods[] = {
"DELETE",
@@ -1900,12 +1919,31 @@ main (void)
"PROPFIND",
"PROPPATCH",
"UNLOCK",
+ "REPORT",
+ "MKACTIVITY",
+ "CHECKOUT",
+ "MERGE",
+ "M-SEARCH",
+ "NOTIFY",
+ "SUBSCRIBE",
+ "UNSUBSCRIBE",
+ "PATCH",
0 };
const char **this_method;
for (this_method = all_methods; *this_method; this_method++) {
char buf[200];
sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
- test_simple(buf, 1);
+ test_simple(buf, HPE_OK);
+ }
+
+ static const char *bad_methods[] = {
+ "C******",
+ "M****",
+ 0 };
+ for (this_method = bad_methods; *this_method; this_method++) {
+ char buf[200];
+ sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
+ test_simple(buf, HPE_UNKNOWN);
}
const char *dumbfuck2 =
@@ -1943,7 +1981,7 @@ main (void)
"\tRA==\r\n"
"\t-----END CERTIFICATE-----\r\n"
"\r\n";
- test_simple(dumbfuck2, 0);
+ test_simple(dumbfuck2, HPE_OK);
#if 0
// NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body