summaryrefslogtreecommitdiff
path: root/deps/http_parser
diff options
context:
space:
mode:
authorBen Noordhuis <info@bnoordhuis.nl>2012-08-30 00:06:47 +0200
committerBen Noordhuis <info@bnoordhuis.nl>2012-08-30 00:06:47 +0200
commit4784ea1a29dc4bace1c9a20f9f6a6376a7eecb7d (patch)
tree5158fb86ffe6116149abd95c5beaf2cccc5ce6cd /deps/http_parser
parent8bec26122d6de0b230f74731c1d09da267c95add (diff)
downloadandroid-node-v8-4784ea1a29dc4bace1c9a20f9f6a6376a7eecb7d.tar.gz
android-node-v8-4784ea1a29dc4bace1c9a20f9f6a6376a7eecb7d.tar.bz2
android-node-v8-4784ea1a29dc4bace1c9a20f9f6a6376a7eecb7d.zip
deps: upgrade http_parser to ad3b631
Diffstat (limited to 'deps/http_parser')
-rw-r--r--deps/http_parser/.gitignore3
-rw-r--r--deps/http_parser/.mailmap1
-rw-r--r--deps/http_parser/AUTHORS5
-rw-r--r--deps/http_parser/Makefile8
-rw-r--r--deps/http_parser/http_parser.c372
-rw-r--r--deps/http_parser/http_parser.h13
-rw-r--r--deps/http_parser/test.c1422
-rw-r--r--deps/http_parser/url_parser.c44
8 files changed, 1639 insertions, 229 deletions
diff --git a/deps/http_parser/.gitignore b/deps/http_parser/.gitignore
index cfadcbe0a6..3b868c5073 100644
--- a/deps/http_parser/.gitignore
+++ b/deps/http_parser/.gitignore
@@ -4,5 +4,8 @@ tags
test
test_g
test_fast
+url_parser
*.mk
*.Makefile
+*.so
+*.a
diff --git a/deps/http_parser/.mailmap b/deps/http_parser/.mailmap
index c25ea8692f..c73c0f93e4 100644
--- a/deps/http_parser/.mailmap
+++ b/deps/http_parser/.mailmap
@@ -2,3 +2,4 @@
# git log --all --reverse --format='%aN <%aE>' | perl -ne 'BEGIN{print "# Authors ordered by first contribution.\n"} print unless $h{$_}; $h{$_} = 1' > AUTHORS
Ryan Dahl <ry@tinyclouds.org>
Salman Haq <salman.haq@asti-usa.com>
+Simon Zimmermann <simonz05@gmail.com>
diff --git a/deps/http_parser/AUTHORS b/deps/http_parser/AUTHORS
index abe99dee44..c0d2fe4d52 100644
--- a/deps/http_parser/AUTHORS
+++ b/deps/http_parser/AUTHORS
@@ -30,3 +30,8 @@ James McLaughlin <jamie@lacewing-project.org>
David Gwynne <loki@animata.net>
LE ROUX Thomas <thomas@procheo.fr>
Randy Rizun <rrizun@ortivawireless.com>
+Andre Louis Caron <andre.louis.caron@usherbrooke.ca>
+Simon Zimmermann <simonz05@gmail.com>
+Erik Dubbelboer <erik@dubbelboer.com>
+Martell Malone <martellmalone@gmail.com>
+Bertrand Paquet <bpaquet@octo.com>
diff --git a/deps/http_parser/Makefile b/deps/http_parser/Makefile
index 8d90f8dddb..d9bf839b20 100644
--- a/deps/http_parser/Makefile
+++ b/deps/http_parser/Makefile
@@ -31,6 +31,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 $@
+url_parser: http_parser_g.o url_parser.o
+ $(CC) $(CFLAGS_DEBUG) $(LDFLAGS) http_parser_g.o url_parser.o -o $@
+
+url_parser.o: url_parser.c http_parser.h Makefile
+ $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) -c url_parser.c -o $@
+
http_parser.o: http_parser.c http_parser.h Makefile
$(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c http_parser.c
@@ -53,6 +59,6 @@ tags: http_parser.c http_parser.h test.c
ctags $^
clean:
- rm -f *.o *.a test test_fast test_g http_parser.tar tags libhttp_parser.so libhttp_parser.o
+ rm -f *.o *.a test test_fast test_g url_parser http_parser.tar tags libhttp_parser.so libhttp_parser.o
.PHONY: clean package test-run test-run-timed test-valgrind
diff --git a/deps/http_parser/http_parser.c b/deps/http_parser/http_parser.c
index acd41307f2..5e0a950a6f 100644
--- a/deps/http_parser/http_parser.c
+++ b/deps/http_parser/http_parser.c
@@ -37,6 +37,19 @@
# define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+#ifndef BIT_AT
+# define BIT_AT(a, i) \
+ (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
+ (1 << ((unsigned int) (i) & 7))))
+#endif
+
+#ifndef ELEM_AT
+# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
+#endif
#if HTTP_PARSER_DEBUG
#define SET_ERRNO(e) \
@@ -184,40 +197,48 @@ static const int8_t unhex[256] =
};
-static const uint8_t normal_url_char[256] = {
+#if HTTP_PARSER_STRICT
+# define T(v) 0
+#else
+# define T(v) v
+#endif
+
+
+static const uint8_t normal_url_char[32] = {
/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
- 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
- 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
- 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
- 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
- 0, 1, 1, 0, 1, 1, 1, 1,
+ 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
- 1, 1, 1, 1, 1, 1, 1, 1,
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
- 1, 1, 1, 1, 1, 1, 1, 1,
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
- 1, 1, 1, 1, 1, 1, 1, 0,
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
- 1, 1, 1, 1, 1, 1, 1, 1,
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
- 1, 1, 1, 1, 1, 1, 1, 1,
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
- 1, 1, 1, 1, 1, 1, 1, 1,
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
- 1, 1, 1, 1, 1, 1, 1, 1,
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
- 1, 1, 1, 1, 1, 1, 1, 1,
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
- 1, 1, 1, 1, 1, 1, 1, 1,
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
- 1, 1, 1, 1, 1, 1, 1, 1,
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
- 1, 1, 1, 1, 1, 1, 1, 0, };
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, };
+#undef T
enum state
{ s_dead = 1 /* important that this is > 0 */
@@ -245,13 +266,9 @@ enum state
, s_req_schema
, s_req_schema_slash
, s_req_schema_slash_slash
- , s_req_host_start
- , s_req_host_v6_start
- , s_req_host_v6
- , s_req_host_v6_end
- , s_req_host
- , s_req_port_start
- , s_req_port
+ , s_req_server_start
+ , s_req_server
+ , s_req_server_with_at
, s_req_path
, s_req_query_string_start
, s_req_query_string
@@ -329,6 +346,19 @@ enum header_states
, h_connection_close
};
+enum http_host_state
+ {
+ s_http_host_dead = 1
+ , s_http_userinfo_start
+ , s_http_userinfo
+ , s_http_host_start
+ , s_http_host_v6_start
+ , s_http_host
+ , s_http_host_v6
+ , s_http_host_v6_end
+ , s_http_host_port_start
+ , s_http_host_port
+};
/* Macros for character classes; depends on strict-mode */
#define CR '\r'
@@ -338,15 +368,21 @@ enum header_states
#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
+#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \
+ (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
+ (c) == ')')
+#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
+ (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
+ (c) == '$' || (c) == ',')
#if HTTP_PARSER_STRICT
#define TOKEN(c) (tokens[(unsigned char)c])
-#define IS_URL_CHAR(c) (normal_url_char[(unsigned char) (c)])
+#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
#else
#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c])
#define IS_URL_CHAR(c) \
- (normal_url_char[(unsigned char) (c)] || ((c) & 0x80))
+ (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
#define IS_HOST_CHAR(c) \
(IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
#endif
@@ -380,7 +416,7 @@ static struct {
};
#undef HTTP_STRERROR_GEN
-int http_message_needs_eof(http_parser *parser);
+int http_message_needs_eof(const http_parser *parser);
/* Our URL parser.
*
@@ -396,7 +432,15 @@ int http_message_needs_eof(http_parser *parser);
static enum state
parse_url_char(enum state s, const char ch)
{
- assert(!isspace(ch));
+ if (ch == ' ' || ch == '\r' || ch == '\n') {
+ return s_dead;
+ }
+
+#if HTTP_PARSER_STRICT
+ if (ch == '\t' || ch == '\f') {
+ return s_dead;
+ }
+#endif
switch (s) {
case s_req_spaces_before_url:
@@ -434,67 +478,33 @@ parse_url_char(enum state s, const char ch)
case s_req_schema_slash_slash:
if (ch == '/') {
- return s_req_host_start;
+ return s_req_server_start;
}
break;
- case s_req_host_start:
- if (ch == '[') {
- return s_req_host_v6_start;
+ case s_req_server_with_at:
+ if (ch == '@') {
+ return s_dead;
}
- if (IS_HOST_CHAR(ch)) {
- return s_req_host;
- }
-
- break;
-
- case s_req_host:
- if (IS_HOST_CHAR(ch)) {
- return s_req_host;
- }
-
- /* FALLTHROUGH */
- case s_req_host_v6_end:
- switch (ch) {
- case ':':
- return s_req_port_start;
-
- case '/':
- return s_req_path;
-
- case '?':
- return s_req_query_string_start;
+ /* FALLTHROUGH */
+ case s_req_server_start:
+ case s_req_server:
+ if (ch == '/') {
+ return s_req_path;
}
- break;
-
- case s_req_host_v6:
- if (ch == ']') {
- return s_req_host_v6_end;
+ if (ch == '?') {
+ return s_req_query_string_start;
}
- /* FALLTHROUGH */
- case s_req_host_v6_start:
- if (IS_HEX(ch) || ch == ':') {
- return s_req_host_v6;
+ if (ch == '@') {
+ return s_req_server_with_at;
}
- break;
-
- case s_req_port:
- switch (ch) {
- case '/':
- return s_req_path;
- case '?':
- return s_req_query_string_start;
- }
-
- /* FALLTHROUGH */
- case s_req_port_start:
- if (IS_NUM(ch)) {
- return s_req_port;
+ if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
+ return s_req_server;
}
break;
@@ -616,13 +626,9 @@ size_t http_parser_execute (http_parser *parser,
case s_req_schema:
case s_req_schema_slash:
case s_req_schema_slash_slash:
- case s_req_host_start:
- case s_req_host_v6_start:
- case s_req_host_v6:
- case s_req_host_v6_end:
- case s_req_host:
- case s_req_port_start:
- case s_req_port:
+ case s_req_server_start:
+ case s_req_server:
+ case s_req_server_with_at:
case s_req_query_string_start:
case s_req_query_string:
case s_req_fragment_start:
@@ -983,7 +989,7 @@ size_t http_parser_execute (http_parser *parser,
MARK(url);
if (parser->method == HTTP_CONNECT) {
- parser->state = s_req_host_start;
+ parser->state = s_req_server_start;
}
parser->state = parse_url_char((enum state)parser->state, ch);
@@ -998,10 +1004,7 @@ size_t http_parser_execute (http_parser *parser,
case s_req_schema:
case s_req_schema_slash:
case s_req_schema_slash_slash:
- case s_req_host_start:
- case s_req_host_v6_start:
- case s_req_host_v6:
- case s_req_port_start:
+ case s_req_server_start:
{
switch (ch) {
/* No whitespace allowed here */
@@ -1021,9 +1024,8 @@ size_t http_parser_execute (http_parser *parser,
break;
}
- case s_req_host:
- case s_req_host_v6_end:
- case s_req_port:
+ case s_req_server:
+ case s_req_server_with_at:
case s_req_path:
case s_req_query_string_start:
case s_req_query_string:
@@ -1852,7 +1854,7 @@ error:
/* Does the parser need to see an EOF to find the end of the message? */
int
-http_message_needs_eof (http_parser *parser)
+http_message_needs_eof (const http_parser *parser)
{
if (parser->type == HTTP_REQUEST) {
return 0;
@@ -1875,7 +1877,7 @@ http_message_needs_eof (http_parser *parser)
int
-http_should_keep_alive (http_parser *parser)
+http_should_keep_alive (const http_parser *parser)
{
if (parser->http_major > 0 && parser->http_minor > 0) {
/* HTTP/1.1 */
@@ -1893,9 +1895,10 @@ http_should_keep_alive (http_parser *parser)
}
-const char * http_method_str (enum http_method m)
+const char *
+http_method_str (enum http_method m)
{
- return method_strings[m];
+ return ELEM_AT(method_strings, m, "<unknown>");
}
@@ -1922,6 +1925,144 @@ http_errno_description(enum http_errno err) {
return http_strerror_tab[err].description;
}
+static enum http_host_state
+http_parse_host_char(enum http_host_state s, const char ch) {
+ switch(s) {
+ case s_http_userinfo:
+ case s_http_userinfo_start:
+ if (ch == '@') {
+ return s_http_host_start;
+ }
+
+ if (IS_USERINFO_CHAR(ch)) {
+ return s_http_userinfo;
+ }
+ break;
+
+ case s_http_host_start:
+ if (ch == '[') {
+ return s_http_host_v6_start;
+ }
+
+ if (IS_HOST_CHAR(ch)) {
+ return s_http_host;
+ }
+
+ break;
+
+ case s_http_host:
+ if (IS_HOST_CHAR(ch)) {
+ return s_http_host;
+ }
+
+ /* FALLTHROUGH */
+ case s_http_host_v6_end:
+ if (ch == ':') {
+ return s_http_host_port_start;
+ }
+
+ break;
+
+ case s_http_host_v6:
+ if (ch == ']') {
+ return s_http_host_v6_end;
+ }
+
+ /* FALLTHROUGH */
+ case s_http_host_v6_start:
+ if (IS_HEX(ch) || ch == ':') {
+ return s_http_host_v6;
+ }
+
+ break;
+
+ case s_http_host_port:
+ case s_http_host_port_start:
+ if (IS_NUM(ch)) {
+ return s_http_host_port;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ return s_http_host_dead;
+}
+
+static int
+http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
+ enum http_host_state s;
+
+ const char *p;
+ size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
+
+ u->field_data[UF_HOST].len = 0;
+
+ s = found_at ? s_http_userinfo_start : s_http_host_start;
+
+ for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
+ enum http_host_state new_s = http_parse_host_char(s, *p);
+
+ if (new_s == s_http_host_dead) {
+ return 1;
+ }
+
+ switch(new_s) {
+ case s_http_host:
+ if (s != s_http_host) {
+ u->field_data[UF_HOST].off = p - buf;
+ }
+ u->field_data[UF_HOST].len++;
+ break;
+
+ case s_http_host_v6:
+ if (s != s_http_host_v6) {
+ u->field_data[UF_HOST].off = p - buf;
+ }
+ u->field_data[UF_HOST].len++;
+ break;
+
+ case s_http_host_port:
+ if (s != s_http_host_port) {
+ u->field_data[UF_PORT].off = p - buf;
+ u->field_data[UF_PORT].len = 0;
+ u->field_set |= (1 << UF_PORT);
+ }
+ u->field_data[UF_PORT].len++;
+ break;
+
+ case s_http_userinfo:
+ if (s != s_http_userinfo) {
+ u->field_data[UF_USERINFO].off = p - buf ;
+ u->field_data[UF_USERINFO].len = 0;
+ u->field_set |= (1 << UF_USERINFO);
+ }
+ u->field_data[UF_USERINFO].len++;
+ break;
+
+ default:
+ break;
+ }
+ s = new_s;
+ }
+
+ /* Make sure we don't end somewhere unexpected */
+ switch (s) {
+ case s_http_host_start:
+ case s_http_host_v6_start:
+ case s_http_host_v6:
+ case s_http_host_port_start:
+ case s_http_userinfo:
+ case s_http_userinfo_start:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
int
http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
struct http_parser_url *u)
@@ -1929,9 +2070,10 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
enum state s;
const char *p;
enum http_parser_url_fields uf, old_uf;
+ int found_at = 0;
u->port = u->field_set = 0;
- s = is_connect ? s_req_host_start : s_req_spaces_before_url;
+ s = is_connect ? s_req_server_start : s_req_spaces_before_url;
uf = old_uf = UF_MAX;
for (p = buf; p < buf + buflen; p++) {
@@ -1945,10 +2087,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
/* Skip delimeters */
case s_req_schema_slash:
case s_req_schema_slash_slash:
- case s_req_host_start:
- case s_req_host_v6_start:
- case s_req_host_v6_end:
- case s_req_port_start:
+ case s_req_server_start:
case s_req_query_string_start:
case s_req_fragment_start:
continue;
@@ -1957,13 +2096,12 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
uf = UF_SCHEMA;
break;
- case s_req_host:
- case s_req_host_v6:
- uf = UF_HOST;
- break;
+ case s_req_server_with_at:
+ found_at = 1;
- case s_req_port:
- uf = UF_PORT;
+ /* FALLTROUGH */
+ case s_req_server:
+ uf = UF_HOST;
break;
case s_req_path:
@@ -1996,21 +2134,17 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
old_uf = uf;
}
- /* CONNECT requests can only contain "hostname:port" */
- if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
- return 1;
+ /* host must be present if there is a schema */
+ /* parsing http:///toto will fail */
+ if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) {
+ if (http_parse_host(buf, u, found_at) != 0) {
+ return 1;
+ }
}
- /* Make sure we don't end somewhere unexpected */
- switch (s) {
- case s_req_host_v6_start:
- case s_req_host_v6:
- case s_req_host_v6_end:
- case s_req_host:
- case s_req_port_start:
+ /* CONNECT requests can only contain "hostname:port" */
+ if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
return 1;
- default:
- break;
}
if (u->field_set & (1 << UF_PORT)) {
diff --git a/deps/http_parser/http_parser.h b/deps/http_parser/http_parser.h
index 8ed41803e8..7908ad03bb 100644
--- a/deps/http_parser/http_parser.h
+++ b/deps/http_parser/http_parser.h
@@ -29,6 +29,7 @@ extern "C" {
#include <sys/types.h>
#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
+#include <BaseTsd.h>
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
@@ -37,9 +38,8 @@ typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
-
-typedef unsigned int size_t;
-typedef int ssize_t;
+typedef SIZE_T size_t;
+typedef SSIZE_T ssize_t;
#else
#include <stdint.h>
#endif
@@ -256,7 +256,8 @@ enum http_parser_url_fields
, UF_PATH = 3
, UF_QUERY = 4
, UF_FRAGMENT = 5
- , UF_MAX = 6
+ , UF_USERINFO = 6
+ , UF_MAX = 7
};
@@ -288,12 +289,12 @@ size_t http_parser_execute(http_parser *parser,
/* If http_should_keep_alive() in the on_headers_complete or
- * on_message_complete callback returns true, then this will be should be
+ * on_message_complete callback returns 0, then this should be
* the last message on the connection.
* If you are the server, respond with the "Connection: close" header.
* If you are the client, close the connection.
*/
-int http_should_keep_alive(http_parser *parser);
+int http_should_keep_alive(const http_parser *parser);
/* Returns a string version of the HTTP method. */
const char *http_method_str(enum http_method m);
diff --git a/deps/http_parser/test.c b/deps/http_parser/test.c
index 6af0e787e9..81e0c3bddf 100644
--- a/deps/http_parser/test.c
+++ b/deps/http_parser/test.c
@@ -44,9 +44,15 @@ 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;
+ const char *host;
+ const char *userinfo;
+ uint16_t port;
int num_headers;
enum { NONE=0, FIELD, VALUE } last_header_element;
char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
@@ -67,6 +73,7 @@ static int currently_parsing_eof;
static struct message messages[5];
static int num_messages;
+static http_parser_settings *current_pause_parser;
/* * R E Q U E S T S * */
const struct message requests[] =
@@ -83,6 +90,9 @@ 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=
@@ -111,6 +121,9 @@ 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=
@@ -137,6 +150,9 @@ 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=
@@ -155,6 +171,9 @@ 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
@@ -171,6 +190,9 @@ 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= ""
@@ -187,6 +209,9 @@ 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=
@@ -207,6 +232,9 @@ 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=
@@ -229,6 +257,9 @@ 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=
@@ -253,6 +284,9 @@ 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=
@@ -276,6 +310,9 @@ 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=
@@ -301,6 +338,9 @@ 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=
@@ -326,6 +366,9 @@ 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=
@@ -343,6 +386,9 @@ 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= { }
@@ -366,6 +412,9 @@ 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" }
@@ -386,6 +435,9 @@ 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= {}
@@ -404,6 +456,9 @@ 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= { }
@@ -428,6 +483,9 @@ const struct message requests[] =
,.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"
@@ -456,6 +514,9 @@ const struct message requests[] =
,.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="some data\r\nand yet even more data"
@@ -475,6 +536,9 @@ 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= {}
@@ -491,6 +555,9 @@ const struct message requests[] =
,.http_major= 0
,.http_minor= 9
,.method= HTTP_GET
+ ,.query_string= ""
+ ,.fragment= ""
+ ,.request_path= "/"
,.request_url= "/"
,.num_headers= 0
,.headers= {}
@@ -510,6 +577,9 @@ 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" }
@@ -519,7 +589,7 @@ const struct message requests[] =
,.body= ""
}
-#define LINE_FOLDING_IN_HEADER 20
+#define LINE_FOLDING_IN_HEADER 21
, {.name= "line folding in header value"
,.type= HTTP_REQUEST
,.raw= "GET / HTTP/1.1\r\n"
@@ -536,6 +606,9 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
+ ,.query_string= ""
+ ,.fragment= ""
+ ,.request_path= "/"
,.request_url= "/"
,.num_headers= 2
,.headers= { { "Line1", "abcdefghijklmno qrs" }
@@ -545,7 +618,7 @@ const struct message requests[] =
}
-#define QUERY_TERMINATED_HOST 21
+#define QUERY_TERMINATED_HOST 22
, {.name= "host terminated by a query string"
,.type= HTTP_REQUEST
,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
@@ -555,13 +628,17 @@ 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"
+ ,.host= "hypnotoad.org"
,.num_headers= 0
,.headers= { }
,.body= ""
}
-#define QUERY_TERMINATED_HOSTPORT 22
+#define QUERY_TERMINATED_HOSTPORT 23
, {.name= "host:port terminated by a query string"
,.type= HTTP_REQUEST
,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
@@ -571,13 +648,18 @@ 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"
+ ,.host= "hypnotoad.org"
+ ,.port= 1234
,.num_headers= 0
,.headers= { }
,.body= ""
}
-#define SPACE_TERMINATED_HOSTPORT 23
+#define SPACE_TERMINATED_HOSTPORT 24
, {.name= "host:port terminated by a space"
,.type= HTTP_REQUEST
,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
@@ -587,14 +669,71 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
+ ,.query_string= ""
+ ,.fragment= ""
+ ,.request_path= ""
,.request_url= "http://hypnotoad.org:1234"
+ ,.host= "hypnotoad.org"
+ ,.port= 1234
,.num_headers= 0
,.headers= { }
,.body= ""
}
+#define PATCH_REQ 25
+, {.name = "PATCH request"
+ ,.type= HTTP_REQUEST
+ ,.raw= "PATCH /file.txt HTTP/1.1\r\n"
+ "Host: www.example.com\r\n"
+ "Content-Type: application/example\r\n"
+ "If-Match: \"e0023aa4e\"\r\n"
+ "Content-Length: 10\r\n"
+ "\r\n"
+ "cccccccccc"
+ ,.should_keep_alive= TRUE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.method= HTTP_PATCH
+ ,.query_string= ""
+ ,.fragment= ""
+ ,.request_path= "/file.txt"
+ ,.request_url= "/file.txt"
+ ,.num_headers= 4
+ ,.headers= { { "Host", "www.example.com" }
+ , { "Content-Type", "application/example" }
+ , { "If-Match", "\"e0023aa4e\"" }
+ , { "Content-Length", "10" }
+ }
+ ,.body= "cccccccccc"
+ }
+
+#define CONNECT_CAPS_REQUEST 26
+, {.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
+ ,.query_string= ""
+ ,.fragment= ""
+ ,.request_path= ""
+ ,.request_url= "HOME0.NETSCAPE.COM:443"
+ ,.num_headers= 2
+ ,.upgrade=""
+ ,.headers= { { "User-agent", "Mozilla/1.1N" }
+ , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
+ }
+ ,.body= ""
+ }
+
#if !HTTP_PARSER_STRICT
-#define UTF8_PATH_REQ 24
+#define UTF8_PATH_REQ 27
, {.name= "utf-8 path request"
,.type= HTTP_REQUEST
,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
@@ -605,6 +744,9 @@ 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" }
@@ -612,7 +754,7 @@ const struct message requests[] =
,.body= ""
}
-#define HOSTNAME_UNDERSCORE 25
+#define HOSTNAME_UNDERSCORE 28
, {.name = "hostname underscore"
,.type= HTTP_REQUEST
,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n"
@@ -624,6 +766,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=""
@@ -634,52 +779,124 @@ const struct message requests[] =
}
#endif /* !HTTP_PARSER_STRICT */
-#define PATCH_REQ 26
-, {.name = "PATCH request"
- ,.type= HTTP_REQUEST
- ,.raw= "PATCH /file.txt HTTP/1.1\r\n"
+/* see https://github.com/ry/http-parser/issues/47 */
+#define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 29
+, {.name = "eat CRLF between requests, no \"Connection: close\" header"
+ ,.raw= "POST / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
- "Content-Type: application/example\r\n"
- "If-Match: \"e0023aa4e\"\r\n"
- "Content-Length: 10\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Content-Length: 4\r\n"
"\r\n"
- "cccccccccc"
+ "q=42\r\n" /* note the trailing CRLF */
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.http_major= 1
,.http_minor= 1
- ,.method= HTTP_PATCH
- ,.request_url= "/file.txt"
+ ,.method= HTTP_POST
+ ,.query_string= ""
+ ,.fragment= ""
+ ,.request_path= "/"
+ ,.request_url= "/"
+ ,.num_headers= 3
+ ,.upgrade= 0
+ ,.headers= { { "Host", "www.example.com" }
+ , { "Content-Type", "application/x-www-form-urlencoded" }
+ , { "Content-Length", "4" }
+ }
+ ,.body= "q=42"
+ }
+
+/* see https://github.com/ry/http-parser/issues/47 */
+#define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 30
+, {.name = "eat CRLF between requests even if \"Connection: close\" is set"
+ ,.raw= "POST / HTTP/1.1\r\n"
+ "Host: www.example.com\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Content-Length: 4\r\n"
+ "Connection: close\r\n"
+ "\r\n"
+ "q=42\r\n" /* note the trailing CRLF */
+ ,.should_keep_alive= FALSE
+ ,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.method= HTTP_POST
+ ,.query_string= ""
+ ,.fragment= ""
+ ,.request_path= "/"
+ ,.request_url= "/"
,.num_headers= 4
+ ,.upgrade= 0
,.headers= { { "Host", "www.example.com" }
- , { "Content-Type", "application/example" }
- , { "If-Match", "\"e0023aa4e\"" }
- , { "Content-Length", "10" }
+ , { "Content-Type", "application/x-www-form-urlencoded" }
+ , { "Content-Length", "4" }
+ , { "Connection", "close" }
}
- ,.body= "cccccccccc"
+ ,.body= "q=42"
}
-#define CONNECT_CAPS_REQUEST 27
-, {.name = "connect caps request"
+#define PURGE_REQ 31
+, {.name = "PURGE 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"
+ ,.raw= "PURGE /file.txt HTTP/1.1\r\n"
+ "Host: www.example.com\r\n"
"\r\n"
- ,.should_keep_alive= FALSE
+ ,.should_keep_alive= TRUE
,.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=" }
- }
+ ,.http_minor= 1
+ ,.method= HTTP_PURGE
+ ,.query_string= ""
+ ,.fragment= ""
+ ,.request_path= "/file.txt"
+ ,.request_url= "/file.txt"
+ ,.num_headers= 1
+ ,.headers= { { "Host", "www.example.com" } }
,.body= ""
}
+#define SEARCH_REQ 32
+, {.name = "SEARCH request"
+ ,.type= HTTP_REQUEST
+ ,.raw= "SEARCH / HTTP/1.1\r\n"
+ "Host: www.example.com\r\n"
+ "\r\n"
+ ,.should_keep_alive= TRUE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.method= HTTP_SEARCH
+ ,.query_string= ""
+ ,.fragment= ""
+ ,.request_path= "/"
+ ,.request_url= "/"
+ ,.num_headers= 1
+ ,.headers= { { "Host", "www.example.com" } }
+ ,.body= ""
+ }
+
+#define PROXY_WITH_BASIC_AUTH 33
+, {.name= "host:port and basic_auth"
+ ,.type= HTTP_REQUEST
+ ,.raw= "GET http://a%12:b!&*$@hypnotoad.org:1234/toto HTTP/1.1\r\n"
+ "\r\n"
+ ,.should_keep_alive= TRUE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.method= HTTP_GET
+ ,.fragment= ""
+ ,.request_path= "/toto"
+ ,.request_url= "http://a%12:b!&*$@hypnotoad.org:1234/toto"
+ ,.host= "hypnotoad.org"
+ ,.userinfo= "a%12:b!&*$"
+ ,.port= 1234
+ ,.num_headers= 0
+ ,.headers= { }
+ ,.body= ""
+ }
+
+
, {.name= NULL } /* sentinel */
};
@@ -780,8 +997,8 @@ const struct message responses[] =
, {.name= "404 no headers no body"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
- ,.should_keep_alive= TRUE
- ,.message_complete_on_eof= FALSE
+ ,.should_keep_alive= FALSE
+ ,.message_complete_on_eof= TRUE
,.http_major= 1
,.http_minor= 1
,.status_code= 404
@@ -795,8 +1012,8 @@ const struct message responses[] =
, {.name= "301 no response phrase"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 301\r\n\r\n"
- ,.should_keep_alive = TRUE
- ,.message_complete_on_eof= FALSE
+ ,.should_keep_alive = FALSE
+ ,.message_complete_on_eof= TRUE
,.http_major= 1
,.http_minor= 1
,.status_code= 301
@@ -945,40 +1162,7 @@ const struct message responses[] =
,.body= ""
}
-#define SPACE_IN_FIELD_RES 9
-/* Should handle spaces in header fields */
-, {.name= "field space"
- ,.type= HTTP_RESPONSE
- ,.raw= "HTTP/1.1 200 OK\r\n"
- "Server: Microsoft-IIS/6.0\r\n"
- "X-Powered-By: ASP.NET\r\n"
- "en-US Content-Type: text/xml\r\n" /* this is the problem */
- "Content-Type: text/xml\r\n"
- "Content-Length: 16\r\n"
- "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
- "Connection: keep-alive\r\n"
- "\r\n"
- "<xml>hello</xml>" /* fake body */
- ,.should_keep_alive= TRUE
- ,.message_complete_on_eof= FALSE
- ,.http_major= 1
- ,.http_minor= 1
- ,.status_code= 200
- ,.num_headers= 7
- ,.headers=
- { { "Server", "Microsoft-IIS/6.0" }
- , { "X-Powered-By", "ASP.NET" }
- , { "en-US Content-Type", "text/xml" }
- , { "Content-Type", "text/xml" }
- , { "Content-Length", "16" }
- , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
- , { "Connection", "keep-alive" }
- }
- ,.body= "<xml>hello</xml>"
- }
-
-
-#define RES_FIELD_UNDERSCORE 10
+#define RES_FIELD_UNDERSCORE 9
/* Should handle spaces in header fields */
, {.name= "field underscore"
,.type= HTTP_RESPONSE
@@ -1018,7 +1202,7 @@ const struct message responses[] =
,.body= ""
}
-#define NON_ASCII_IN_STATUS_LINE 11
+#define NON_ASCII_IN_STATUS_LINE 10
/* Should handle non-ASCII in status line */
, {.name= "non-ASCII in status line"
,.type= HTTP_RESPONSE
@@ -1041,7 +1225,7 @@ const struct message responses[] =
,.body= ""
}
-#define HTTP_VERSION_0_9 12
+#define HTTP_VERSION_0_9 11
/* Should handle HTTP/0.9 */
, {.name= "http version 0.9"
,.type= HTTP_RESPONSE
@@ -1057,8 +1241,175 @@ const struct message responses[] =
{}
,.body= ""
}
-, {.name= NULL } /* sentinel */
+#define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12
+/* The client should wait for the server's EOF. That is, when neither
+ * content-length nor transfer-encoding is specified, the end of body
+ * is specified by the EOF.
+ */
+, {.name= "neither content-length nor transfer-encoding response"
+ ,.type= HTTP_RESPONSE
+ ,.raw= "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/plain\r\n"
+ "\r\n"
+ "hello world"
+ ,.should_keep_alive= FALSE
+ ,.message_complete_on_eof= TRUE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.status_code= 200
+ ,.num_headers= 1
+ ,.headers=
+ { { "Content-Type", "text/plain" }
+ }
+ ,.body= "hello world"
+ }
+
+#define NO_BODY_HTTP10_KA_200 13
+, {.name= "HTTP/1.0 with keep-alive and EOF-terminated 200 status"
+ ,.type= HTTP_RESPONSE
+ ,.raw= "HTTP/1.0 200 OK\r\n"
+ "Connection: keep-alive\r\n"
+ "\r\n"
+ ,.should_keep_alive= FALSE
+ ,.message_complete_on_eof= TRUE
+ ,.http_major= 1
+ ,.http_minor= 0
+ ,.status_code= 200
+ ,.num_headers= 1
+ ,.headers=
+ { { "Connection", "keep-alive" }
+ }
+ ,.body_size= 0
+ ,.body= ""
+ }
+
+#define NO_BODY_HTTP10_KA_204 14
+, {.name= "HTTP/1.0 with keep-alive and a 204 status"
+ ,.type= HTTP_RESPONSE
+ ,.raw= "HTTP/1.0 204 No content\r\n"
+ "Connection: keep-alive\r\n"
+ "\r\n"
+ ,.should_keep_alive= TRUE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 0
+ ,.status_code= 204
+ ,.num_headers= 1
+ ,.headers=
+ { { "Connection", "keep-alive" }
+ }
+ ,.body_size= 0
+ ,.body= ""
+ }
+
+#define NO_BODY_HTTP11_KA_200 15
+, {.name= "HTTP/1.1 with an EOF-terminated 200 status"
+ ,.type= HTTP_RESPONSE
+ ,.raw= "HTTP/1.1 200 OK\r\n"
+ "\r\n"
+ ,.should_keep_alive= FALSE
+ ,.message_complete_on_eof= TRUE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.status_code= 200
+ ,.num_headers= 0
+ ,.headers={}
+ ,.body_size= 0
+ ,.body= ""
+ }
+
+#define NO_BODY_HTTP11_KA_204 16
+, {.name= "HTTP/1.1 with a 204 status"
+ ,.type= HTTP_RESPONSE
+ ,.raw= "HTTP/1.1 204 No content\r\n"
+ "\r\n"
+ ,.should_keep_alive= TRUE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.status_code= 204
+ ,.num_headers= 0
+ ,.headers={}
+ ,.body_size= 0
+ ,.body= ""
+ }
+
+#define NO_BODY_HTTP11_NOKA_204 17
+, {.name= "HTTP/1.1 with a 204 status and keep-alive disabled"
+ ,.type= HTTP_RESPONSE
+ ,.raw= "HTTP/1.1 204 No content\r\n"
+ "Connection: close\r\n"
+ "\r\n"
+ ,.should_keep_alive= FALSE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.status_code= 204
+ ,.num_headers= 1
+ ,.headers=
+ { { "Connection", "close" }
+ }
+ ,.body_size= 0
+ ,.body= ""
+ }
+
+#define NO_BODY_HTTP11_KA_CHUNKED_200 18
+, {.name= "HTTP/1.1 with chunked endocing and a 200 response"
+ ,.type= HTTP_RESPONSE
+ ,.raw= "HTTP/1.1 200 OK\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "\r\n"
+ "0\r\n"
+ "\r\n"
+ ,.should_keep_alive= TRUE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.status_code= 200
+ ,.num_headers= 1
+ ,.headers=
+ { { "Transfer-Encoding", "chunked" }
+ }
+ ,.body_size= 0
+ ,.body= ""
+ }
+
+#if !HTTP_PARSER_STRICT
+#define SPACE_IN_FIELD_RES 19
+/* Should handle spaces in header fields */
+, {.name= "field space"
+ ,.type= HTTP_RESPONSE
+ ,.raw= "HTTP/1.1 200 OK\r\n"
+ "Server: Microsoft-IIS/6.0\r\n"
+ "X-Powered-By: ASP.NET\r\n"
+ "en-US Content-Type: text/xml\r\n" /* this is the problem */
+ "Content-Type: text/xml\r\n"
+ "Content-Length: 16\r\n"
+ "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
+ "Connection: keep-alive\r\n"
+ "\r\n"
+ "<xml>hello</xml>" /* fake body */
+ ,.should_keep_alive= TRUE
+ ,.message_complete_on_eof= FALSE
+ ,.http_major= 1
+ ,.http_minor= 1
+ ,.status_code= 200
+ ,.num_headers= 7
+ ,.headers=
+ { { "Server", "Microsoft-IIS/6.0" }
+ , { "X-Powered-By", "ASP.NET" }
+ , { "en-US Content-Type", "text/xml" }
+ , { "Content-Type", "text/xml" }
+ , { "Content-Length", "16" }
+ , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
+ , { "Connection", "keep-alive" }
+ }
+ ,.body= "<xml>hello</xml>"
+ }
+#endif /* !HTTP_PARSER_STRICT */
+
+, {.name= NULL } /* sentinel */
};
int
@@ -1148,7 +1499,7 @@ message_complete_cb (http_parser *p)
"value in both on_message_complete and on_headers_complete "
"but it doesn't! ***\n\n");
assert(0);
- exit(1);
+ abort();
}
messages[num_messages].message_complete_cb_called = TRUE;
@@ -1158,6 +1509,146 @@ message_complete_cb (http_parser *p)
return 0;
}
+/* These dontcall_* callbacks exist so that we can verify that when we're
+ * paused, no additional callbacks are invoked */
+int
+dontcall_message_begin_cb (http_parser *p)
+{
+ if (p) { } // gcc
+ fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n");
+ abort();
+}
+
+int
+dontcall_header_field_cb (http_parser *p, const char *buf, size_t len)
+{
+ if (p || buf || len) { } // gcc
+ fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n");
+ abort();
+}
+
+int
+dontcall_header_value_cb (http_parser *p, const char *buf, size_t len)
+{
+ if (p || buf || len) { } // gcc
+ fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n");
+ abort();
+}
+
+int
+dontcall_request_url_cb (http_parser *p, const char *buf, size_t len)
+{
+ if (p || buf || len) { } // gcc
+ fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n");
+ abort();
+}
+
+int
+dontcall_body_cb (http_parser *p, const char *buf, size_t len)
+{
+ if (p || buf || len) { } // gcc
+ fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n");
+ abort();
+}
+
+int
+dontcall_headers_complete_cb (http_parser *p)
+{
+ if (p) { } // gcc
+ fprintf(stderr, "\n\n*** on_headers_complete() called on paused "
+ "parser ***\n\n");
+ abort();
+}
+
+int
+dontcall_message_complete_cb (http_parser *p)
+{
+ if (p) { } // gcc
+ fprintf(stderr, "\n\n*** on_message_complete() called on paused "
+ "parser ***\n\n");
+ abort();
+}
+
+static http_parser_settings settings_dontcall =
+ {.on_message_begin = dontcall_message_begin_cb
+ ,.on_header_field = dontcall_header_field_cb
+ ,.on_header_value = dontcall_header_value_cb
+ ,.on_url = dontcall_request_url_cb
+ ,.on_body = dontcall_body_cb
+ ,.on_headers_complete = dontcall_headers_complete_cb
+ ,.on_message_complete = dontcall_message_complete_cb
+ };
+
+/* These pause_* callbacks always pause the parser and just invoke the regular
+ * callback that tracks content. Before returning, we overwrite the parser
+ * settings to point to the _dontcall variety so that we can verify that
+ * the pause actually did, you know, pause. */
+int
+pause_message_begin_cb (http_parser *p)
+{
+ http_parser_pause(p, 1);
+ *current_pause_parser = settings_dontcall;
+ return message_begin_cb(p);
+}
+
+int
+pause_header_field_cb (http_parser *p, const char *buf, size_t len)
+{
+ http_parser_pause(p, 1);
+ *current_pause_parser = settings_dontcall;
+ return header_field_cb(p, buf, len);
+}
+
+int
+pause_header_value_cb (http_parser *p, const char *buf, size_t len)
+{
+ http_parser_pause(p, 1);
+ *current_pause_parser = settings_dontcall;
+ return header_value_cb(p, buf, len);
+}
+
+int
+pause_request_url_cb (http_parser *p, const char *buf, size_t len)
+{
+ http_parser_pause(p, 1);
+ *current_pause_parser = settings_dontcall;
+ return request_url_cb(p, buf, len);
+}
+
+int
+pause_body_cb (http_parser *p, const char *buf, size_t len)
+{
+ http_parser_pause(p, 1);
+ *current_pause_parser = settings_dontcall;
+ return body_cb(p, buf, len);
+}
+
+int
+pause_headers_complete_cb (http_parser *p)
+{
+ http_parser_pause(p, 1);
+ *current_pause_parser = settings_dontcall;
+ return headers_complete_cb(p);
+}
+
+int
+pause_message_complete_cb (http_parser *p)
+{
+ http_parser_pause(p, 1);
+ *current_pause_parser = settings_dontcall;
+ return message_complete_cb(p);
+}
+
+static http_parser_settings settings_pause =
+ {.on_message_begin = pause_message_begin_cb
+ ,.on_header_field = pause_header_field_cb
+ ,.on_header_value = pause_header_value_cb
+ ,.on_url = pause_request_url_cb
+ ,.on_body = pause_body_cb
+ ,.on_headers_complete = pause_headers_complete_cb
+ ,.on_message_complete = pause_message_complete_cb
+ };
+
static http_parser_settings settings =
{.on_message_begin = message_begin_cb
,.on_header_field = header_field_cb
@@ -1227,6 +1718,17 @@ size_t parse_count_body (const char *buf, size_t len)
return nparsed;
}
+size_t parse_pause (const char *buf, size_t len)
+{
+ size_t nparsed;
+ http_parser_settings s = settings_pause;
+
+ currently_parsing_eof = (len == 0);
+ current_pause_parser = &s;
+ nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
+ return nparsed;
+}
+
static inline int
check_str_eq (const struct message *m,
const char *prop,
@@ -1267,6 +1769,20 @@ check_num_eq (const struct message *m,
#define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
+#define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn) \
+do { \
+ char ubuf[256]; \
+ \
+ if ((u)->field_set & (1 << (fn))) { \
+ memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off, \
+ (u)->field_data[(fn)].len); \
+ ubuf[(u)->field_data[(fn)].len] = '\0'; \
+ } else { \
+ ubuf[0] = '\0'; \
+ } \
+ \
+ check_str_eq(expected, #prop, expected->prop, ubuf); \
+} while(0)
int
message_eq (int index, const struct message *expected)
@@ -1292,6 +1808,36 @@ message_eq (int index, const struct message *expected)
MESSAGE_CHECK_STR_EQ(expected, m, request_url);
+
+ /* Check URL components; we can't do this w/ CONNECT since it doesn't
+ * send us a well-formed URL.
+ */
+ if (*m->request_url && m->method != HTTP_CONNECT) {
+ struct http_parser_url u;
+
+ if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) {
+ fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n",
+ m->request_url);
+ abort();
+ }
+
+ if (expected->host) {
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST);
+ }
+
+ if (expected->userinfo) {
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO);
+ }
+
+ m->port = (u.field_set & (1 << UF_PORT)) ?
+ u.port : 0;
+
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY);
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT);
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH);
+ MESSAGE_CHECK_NUM_EQ(expected, m, port);
+ }
+
if (expected->body_size) {
MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
} else {
@@ -1358,7 +1904,7 @@ upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
/* Check the portion of the response after its specified upgrade */
if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
- exit(1);
+ abort();
}
/* Fix up the response so that message_eq() will verify the beginning
@@ -1374,7 +1920,7 @@ upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
va_end(ap);
printf("\n\n*** Error: expected a message with upgrade ***\n");
- exit(1);
+ abort();
}
static void
@@ -1420,6 +1966,563 @@ print_error (const char *raw, size_t error_location)
fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
}
+void
+test_preserve_data (void)
+{
+ char my_data[] = "application-specific data";
+ http_parser parser;
+ parser.data = my_data;
+ http_parser_init(&parser, HTTP_REQUEST);
+ if (parser.data != my_data) {
+ printf("\n*** parser.data not preserved accross http_parser_init ***\n\n");
+ abort();
+ }
+}
+
+struct url_test {
+ const char *name;
+ const char *url;
+ int is_connect;
+ struct http_parser_url u;
+ int rv;
+};
+
+const struct url_test url_tests[] =
+{ {.name="proxy request"
+ ,.url="http://hostname/"
+ ,.is_connect=0
+ ,.u=
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
+ ,.port=0
+ ,.field_data=
+ {{ 0, 4 } /* UF_SCHEMA */
+ ,{ 7, 8 } /* UF_HOST */
+ ,{ 0, 0 } /* UF_PORT */
+ ,{ 15, 1 } /* UF_PATH */
+ ,{ 0, 0 } /* UF_QUERY */
+ ,{ 0, 0 } /* UF_FRAGMENT */
+ ,{ 0, 0 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="proxy request with port"
+ ,.url="http://hostname:444/"
+ ,.is_connect=0
+ ,.u=
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
+ ,.port=444
+ ,.field_data=
+ {{ 0, 4 } /* UF_SCHEMA */
+ ,{ 7, 8 } /* UF_HOST */
+ ,{ 16, 3 } /* UF_PORT */
+ ,{ 19, 1 } /* UF_PATH */
+ ,{ 0, 0 } /* UF_QUERY */
+ ,{ 0, 0 } /* UF_FRAGMENT */
+ ,{ 0, 0 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="CONNECT request"
+ ,.url="hostname:443"
+ ,.is_connect=1
+ ,.u=
+ {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
+ ,.port=443
+ ,.field_data=
+ {{ 0, 0 } /* UF_SCHEMA */
+ ,{ 0, 8 } /* UF_HOST */
+ ,{ 9, 3 } /* UF_PORT */
+ ,{ 0, 0 } /* UF_PATH */
+ ,{ 0, 0 } /* UF_QUERY */
+ ,{ 0, 0 } /* UF_FRAGMENT */
+ ,{ 0, 0 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="CONNECT request but not connect"
+ ,.url="hostname:443"
+ ,.is_connect=0
+ ,.rv=1
+ }
+
+, {.name="proxy ipv6 request"
+ ,.url="http://[1:2::3:4]/"
+ ,.is_connect=0
+ ,.u=
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
+ ,.port=0
+ ,.field_data=
+ {{ 0, 4 } /* UF_SCHEMA */
+ ,{ 8, 8 } /* UF_HOST */
+ ,{ 0, 0 } /* UF_PORT */
+ ,{ 17, 1 } /* UF_PATH */
+ ,{ 0, 0 } /* UF_QUERY */
+ ,{ 0, 0 } /* UF_FRAGMENT */
+ ,{ 0, 0 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="proxy ipv6 request with port"
+ ,.url="http://[1:2::3:4]:67/"
+ ,.is_connect=0
+ ,.u=
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
+ ,.port=67
+ ,.field_data=
+ {{ 0, 4 } /* UF_SCHEMA */
+ ,{ 8, 8 } /* UF_HOST */
+ ,{ 18, 2 } /* UF_PORT */
+ ,{ 20, 1 } /* UF_PATH */
+ ,{ 0, 0 } /* UF_QUERY */
+ ,{ 0, 0 } /* UF_FRAGMENT */
+ ,{ 0, 0 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="CONNECT ipv6 address"
+ ,.url="[1:2::3:4]:443"
+ ,.is_connect=1
+ ,.u=
+ {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
+ ,.port=443
+ ,.field_data=
+ {{ 0, 0 } /* UF_SCHEMA */
+ ,{ 1, 8 } /* UF_HOST */
+ ,{ 11, 3 } /* UF_PORT */
+ ,{ 0, 0 } /* UF_PATH */
+ ,{ 0, 0 } /* UF_QUERY */
+ ,{ 0, 0 } /* UF_FRAGMENT */
+ ,{ 0, 0 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="extra ? in query string"
+ ,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css,"
+ "fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css,"
+ "fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css"
+ ,.is_connect=0
+ ,.u=
+ {.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY)
+ ,.port=0
+ ,.field_data=
+ {{ 0, 4 } /* UF_SCHEMA */
+ ,{ 7, 10 } /* UF_HOST */
+ ,{ 0, 0 } /* UF_PORT */
+ ,{ 17, 12 } /* UF_PATH */
+ ,{ 30,187 } /* UF_QUERY */
+ ,{ 0, 0 } /* UF_FRAGMENT */
+ ,{ 0, 0 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="space URL encoded"
+ ,.url="/toto.html?toto=a%20b"
+ ,.is_connect=0
+ ,.u=
+ {.field_set= (1<<UF_PATH) | (1<<UF_QUERY)
+ ,.port=0
+ ,.field_data=
+ {{ 0, 0 } /* UF_SCHEMA */
+ ,{ 0, 0 } /* UF_HOST */
+ ,{ 0, 0 } /* UF_PORT */
+ ,{ 0, 10 } /* UF_PATH */
+ ,{ 11, 10 } /* UF_QUERY */
+ ,{ 0, 0 } /* UF_FRAGMENT */
+ ,{ 0, 0 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+
+, {.name="URL fragment"
+ ,.url="/toto.html#titi"
+ ,.is_connect=0
+ ,.u=
+ {.field_set= (1<<UF_PATH) | (1<<UF_FRAGMENT)
+ ,.port=0
+ ,.field_data=
+ {{ 0, 0 } /* UF_SCHEMA */
+ ,{ 0, 0 } /* UF_HOST */
+ ,{ 0, 0 } /* UF_PORT */
+ ,{ 0, 10 } /* UF_PATH */
+ ,{ 0, 0 } /* UF_QUERY */
+ ,{ 11, 4 } /* UF_FRAGMENT */
+ ,{ 0, 0 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="complex URL fragment"
+ ,.url="http://www.webmasterworld.com/r.cgi?f=21&d=8405&url="
+ "http://www.example.com/index.html?foo=bar&hello=world#midpage"
+ ,.is_connect=0
+ ,.u=
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY) |\
+ (1<<UF_FRAGMENT)
+ ,.port=0
+ ,.field_data=
+ {{ 0, 4 } /* UF_SCHEMA */
+ ,{ 7, 22 } /* UF_HOST */
+ ,{ 0, 0 } /* UF_PORT */
+ ,{ 29, 6 } /* UF_PATH */
+ ,{ 36, 69 } /* UF_QUERY */
+ ,{106, 7 } /* UF_FRAGMENT */
+ ,{ 0, 0 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="complex URL from node js url parser doc"
+ ,.url="http://host.com:8080/p/a/t/h?query=string#hash"
+ ,.is_connect=0
+ ,.u=
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
+ (1<<UF_QUERY) | (1<<UF_FRAGMENT)
+ ,.port=8080
+ ,.field_data=
+ {{ 0, 4 } /* UF_SCHEMA */
+ ,{ 7, 8 } /* UF_HOST */
+ ,{ 16, 4 } /* UF_PORT */
+ ,{ 20, 8 } /* UF_PATH */
+ ,{ 29, 12 } /* UF_QUERY */
+ ,{ 42, 4 } /* UF_FRAGMENT */
+ ,{ 0, 0 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="complex URL with basic auth from node js url parser doc"
+ ,.url="http://a:b@host.com:8080/p/a/t/h?query=string#hash"
+ ,.is_connect=0
+ ,.u=
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
+ (1<<UF_QUERY) | (1<<UF_FRAGMENT) | (1<<UF_USERINFO)
+ ,.port=8080
+ ,.field_data=
+ {{ 0, 4 } /* UF_SCHEMA */
+ ,{ 11, 8 } /* UF_HOST */
+ ,{ 20, 4 } /* UF_PORT */
+ ,{ 24, 8 } /* UF_PATH */
+ ,{ 33, 12 } /* UF_QUERY */
+ ,{ 46, 4 } /* UF_FRAGMENT */
+ ,{ 7, 3 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="double @"
+ ,.url="http://a:b@@hostname:443/"
+ ,.is_connect=0
+ ,.rv=1
+ }
+
+, {.name="proxy empty host"
+ ,.url="http://:443/"
+ ,.is_connect=0
+ ,.rv=1
+ }
+
+, {.name="proxy empty port"
+ ,.url="http://hostname:/"
+ ,.is_connect=0
+ ,.rv=1
+ }
+
+, {.name="CONNECT with basic auth"
+ ,.url="a:b@hostname:443"
+ ,.is_connect=1
+ ,.rv=1
+ }
+
+, {.name="CONNECT empty host"
+ ,.url=":443"
+ ,.is_connect=1
+ ,.rv=1
+ }
+
+, {.name="CONNECT empty port"
+ ,.url="hostname:"
+ ,.is_connect=1
+ ,.rv=1
+ }
+
+, {.name="CONNECT with extra bits"
+ ,.url="hostname:443/"
+ ,.is_connect=1
+ ,.rv=1
+ }
+
+, {.name="space in URL"
+ ,.url="/foo bar/"
+ ,.rv=1 /* s_dead */
+ }
+
+, {.name="proxy basic auth with space url encoded"
+ ,.url="http://a%20:b@host.com/"
+ ,.is_connect=0
+ ,.u=
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
+ ,.port=0
+ ,.field_data=
+ {{ 0, 4 } /* UF_SCHEMA */
+ ,{ 14, 8 } /* UF_HOST */
+ ,{ 0, 0 } /* UF_PORT */
+ ,{ 22, 1 } /* UF_PATH */
+ ,{ 0, 0 } /* UF_QUERY */
+ ,{ 0, 0 } /* UF_FRAGMENT */
+ ,{ 7, 6 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="carriage return in URL"
+ ,.url="/foo\rbar/"
+ ,.rv=1 /* s_dead */
+ }
+
+, {.name="proxy double : in URL"
+ ,.url="http://hostname::443/"
+ ,.rv=1 /* s_dead */
+ }
+
+, {.name="proxy basic auth with double :"
+ ,.url="http://a::b@host.com/"
+ ,.is_connect=0
+ ,.u=
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
+ ,.port=0
+ ,.field_data=
+ {{ 0, 4 } /* UF_SCHEMA */
+ ,{ 12, 8 } /* UF_HOST */
+ ,{ 0, 0 } /* UF_PORT */
+ ,{ 20, 1 } /* UF_PATH */
+ ,{ 0, 0 } /* UF_QUERY */
+ ,{ 0, 0 } /* UF_FRAGMENT */
+ ,{ 7, 4 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="line feed in URL"
+ ,.url="/foo\nbar/"
+ ,.rv=1 /* s_dead */
+ }
+
+, {.name="proxy empty basic auth"
+ ,.url="http://@hostname/fo"
+ ,.u=
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
+ ,.port=0
+ ,.field_data=
+ {{ 0, 4 } /* UF_SCHEMA */
+ ,{ 8, 8 } /* UF_HOST */
+ ,{ 0, 0 } /* UF_PORT */
+ ,{ 16, 3 } /* UF_PATH */
+ ,{ 0, 0 } /* UF_QUERY */
+ ,{ 0, 0 } /* UF_FRAGMENT */
+ ,{ 0, 0 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+, {.name="proxy line feed in hostname"
+ ,.url="http://host\name/fo"
+ ,.rv=1 /* s_dead */
+ }
+
+, {.name="proxy % in hostname"
+ ,.url="http://host%name/fo"
+ ,.rv=1 /* s_dead */
+ }
+
+, {.name="proxy ; in hostname"
+ ,.url="http://host;ame/fo"
+ ,.rv=1 /* s_dead */
+ }
+
+, {.name="proxy basic auth with unreservedchars"
+ ,.url="http://a!;-_!=+$@host.com/"
+ ,.is_connect=0
+ ,.u=
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
+ ,.port=0
+ ,.field_data=
+ {{ 0, 4 } /* UF_SCHEMA */
+ ,{ 17, 8 } /* UF_HOST */
+ ,{ 0, 0 } /* UF_PORT */
+ ,{ 25, 1 } /* UF_PATH */
+ ,{ 0, 0 } /* UF_QUERY */
+ ,{ 0, 0 } /* UF_FRAGMENT */
+ ,{ 7, 9 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="proxy only empty basic auth"
+ ,.url="http://@/fo"
+ ,.rv=1 /* s_dead */
+ }
+
+, {.name="proxy only basic auth"
+ ,.url="http://toto@/fo"
+ ,.rv=1 /* s_dead */
+ }
+
+, {.name="proxy emtpy hostname"
+ ,.url="http:///fo"
+ ,.rv=1 /* s_dead */
+ }
+
+, {.name="proxy = in URL"
+ ,.url="http://host=ame/fo"
+ ,.rv=1 /* s_dead */
+ }
+
+#if HTTP_PARSER_STRICT
+
+, {.name="tab in URL"
+ ,.url="/foo\tbar/"
+ ,.rv=1 /* s_dead */
+ }
+
+, {.name="form feed in URL"
+ ,.url="/foo\fbar/"
+ ,.rv=1 /* s_dead */
+ }
+
+#else /* !HTTP_PARSER_STRICT */
+
+, {.name="tab in URL"
+ ,.url="/foo\tbar/"
+ ,.u=
+ {.field_set=(1 << UF_PATH)
+ ,.field_data=
+ {{ 0, 0 } /* UF_SCHEMA */
+ ,{ 0, 0 } /* UF_HOST */
+ ,{ 0, 0 } /* UF_PORT */
+ ,{ 0, 9 } /* UF_PATH */
+ ,{ 0, 0 } /* UF_QUERY */
+ ,{ 0, 0 } /* UF_FRAGMENT */
+ ,{ 0, 0 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.name="form feed in URL"
+ ,.url="/foo\fbar/"
+ ,.u=
+ {.field_set=(1 << UF_PATH)
+ ,.field_data=
+ {{ 0, 0 } /* UF_SCHEMA */
+ ,{ 0, 0 } /* UF_HOST */
+ ,{ 0, 0 } /* UF_PORT */
+ ,{ 0, 9 } /* UF_PATH */
+ ,{ 0, 0 } /* UF_QUERY */
+ ,{ 0, 0 } /* UF_FRAGMENT */
+ ,{ 0, 0 } /* UF_USERINFO */
+ }
+ }
+ ,.rv=0
+ }
+#endif
+};
+
+void
+dump_url (const char *url, const struct http_parser_url *u)
+{
+ unsigned int i;
+
+ printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
+ for (i = 0; i < UF_MAX; i++) {
+ if ((u->field_set & (1 << i)) == 0) {
+ printf("\tfield_data[%u]: unset\n", i);
+ continue;
+ }
+
+ printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n",
+ i,
+ u->field_data[i].off,
+ u->field_data[i].len,
+ u->field_data[i].len,
+ url + u->field_data[i].off);
+ }
+}
+
+void
+test_parse_url (void)
+{
+ struct http_parser_url u;
+ const struct url_test *test;
+ unsigned int i;
+ int rv;
+
+ for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) {
+ test = &url_tests[i];
+ memset(&u, 0, sizeof(u));
+
+ rv = http_parser_parse_url(test->url,
+ strlen(test->url),
+ test->is_connect,
+ &u);
+
+ if (test->rv == 0) {
+ if (rv != 0) {
+ printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
+ "unexpected rv %d ***\n\n", test->url, test->name, rv);
+ abort();
+ }
+
+ if (memcmp(&u, &test->u, sizeof(u)) != 0) {
+ printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n",
+ test->url, test->name);
+
+ printf("target http_parser_url:\n");
+ dump_url(test->url, &test->u);
+ printf("result http_parser_url:\n");
+ dump_url(test->url, &u);
+
+ abort();
+ }
+ } else {
+ /* test->rv != 0 */
+ if (rv == 0) {
+ printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
+ "unexpected rv %d ***\n\n", test->url, test->name, rv);
+ abort();
+ }
+ }
+ }
+}
+
+void
+test_method_str (void)
+{
+ assert(0 == strcmp("GET", http_method_str(HTTP_GET)));
+ assert(0 == strcmp("<unknown>", http_method_str(1337)));
+}
void
test_message (const struct message *message)
@@ -1444,7 +2547,7 @@ test_message (const struct message *message)
if (read != msg1len) {
print_error(msg1, read);
- exit(1);
+ abort();
}
}
@@ -1458,24 +2561,24 @@ test_message (const struct message *message)
if (read != msg2len) {
print_error(msg2, read);
- exit(1);
+ abort();
}
read = parse(NULL, 0);
if (read != 0) {
print_error(message->raw, read);
- exit(1);
+ abort();
}
test:
if (num_messages != 1) {
printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
- exit(1);
+ abort();
}
- if(!message_eq(0, message)) exit(1);
+ if(!message_eq(0, message)) abort();
parser_free();
}
@@ -1496,7 +2599,7 @@ test_message_count_body (const struct message *message)
read = parse_count_body(message->raw + i, toread);
if (read != toread) {
print_error(message->raw, read);
- exit(1);
+ abort();
}
}
@@ -1504,15 +2607,15 @@ test_message_count_body (const struct message *message)
read = parse_count_body(NULL, 0);
if (read != 0) {
print_error(message->raw, read);
- exit(1);
+ abort();
}
if (num_messages != 1) {
printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
- exit(1);
+ abort();
}
- if(!message_eq(0, message)) exit(1);
+ if(!message_eq(0, message)) abort();
parser_free();
}
@@ -1544,7 +2647,7 @@ test_simple (const char *buf, enum http_errno err_expected)
#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);
+ abort();
}
}
@@ -1573,7 +2676,54 @@ test_header_overflow_error (int req)
}
fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n");
- exit(1);
+ abort();
+}
+
+static void
+test_content_length_overflow (const char *buf, size_t buflen, int expect_ok)
+{
+ http_parser parser;
+ http_parser_init(&parser, HTTP_RESPONSE);
+ http_parser_execute(&parser, &settings_null, buf, buflen);
+
+ if (expect_ok)
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK);
+ else
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH);
+}
+
+void
+test_header_content_length_overflow_error (void)
+{
+#define X(size) \
+ "HTTP/1.1 200 OK\r\n" \
+ "Content-Length: " #size "\r\n" \
+ "\r\n"
+ const char a[] = X(18446744073709551614); /* 2^64-2 */
+ const char b[] = X(18446744073709551615); /* 2^64-1 */
+ const char c[] = X(18446744073709551616); /* 2^64 */
+#undef X
+ test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
+ test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
+ test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
+}
+
+void
+test_chunk_content_length_overflow_error (void)
+{
+#define X(size) \
+ "HTTP/1.1 200 OK\r\n" \
+ "Transfer-Encoding: chunked\r\n" \
+ "\r\n" \
+ #size "\r\n" \
+ "..."
+ const char a[] = X(FFFFFFFFFFFFFFFE); /* 2^64-2 */
+ const char b[] = X(FFFFFFFFFFFFFFFF); /* 2^64-1 */
+ const char c[] = X(10000000000000000); /* 2^64 */
+#undef X
+ test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
+ test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
+ test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
}
void
@@ -1584,8 +2734,8 @@ test_no_overflow_long_body (int req, size_t length)
size_t parsed;
size_t i;
char buf1[3000];
- size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %zu\r\n\r\n",
- req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", length);
+ size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %lu\r\n\r\n",
+ req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", (unsigned long)length);
parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
if (parsed != buf1len)
goto err;
@@ -1603,10 +2753,10 @@ test_no_overflow_long_body (int req, size_t length)
err:
fprintf(stderr,
- "\n*** error in test_no_overflow_long_body %s of length %zu ***\n",
+ "\n*** error in test_no_overflow_long_body %s of length %lu ***\n",
req ? "REQUEST" : "RESPONSE",
- length);
- exit(1);
+ (unsigned long)length);
+ abort();
}
void
@@ -1638,26 +2788,26 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
if (read != strlen(total)) {
print_error(total, read);
- exit(1);
+ abort();
}
read = parse(NULL, 0);
if (read != 0) {
print_error(total, read);
- exit(1);
+ abort();
}
test:
if (message_count != num_messages) {
fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
- exit(1);
+ abort();
}
- if (!message_eq(0, r1)) exit(1);
- if (message_count > 1 && !message_eq(1, r2)) exit(1);
- if (message_count > 2 && !message_eq(2, r3)) exit(1);
+ if (!message_eq(0, r1)) abort();
+ if (message_count > 1 && !message_eq(1, r2)) abort();
+ if (message_count > 2 && !message_eq(2, r3)) abort();
parser_free();
}
@@ -1780,7 +2930,7 @@ test:
fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1);
fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2);
fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3);
- exit(1);
+ abort();
}
// user required to free the result
@@ -1814,6 +2964,58 @@ create_large_chunked_message (int body_size_in_kb, const char* headers)
return buf;
}
+/* Verify that we can pause parsing at any of the bytes in the
+ * message and still get the result that we're expecting. */
+void
+test_message_pause (const struct message *msg)
+{
+ char *buf = (char*) msg->raw;
+ size_t buflen = strlen(msg->raw);
+ size_t nread;
+
+ parser_init(msg->type);
+
+ do {
+ nread = parse_pause(buf, buflen);
+
+ // We can only set the upgrade buffer once we've gotten our message
+ // completion callback.
+ if (messages[0].message_complete_cb_called &&
+ msg->upgrade &&
+ parser->upgrade) {
+ messages[0].upgrade = buf + nread;
+ goto test;
+ }
+
+ if (nread < buflen) {
+
+ // Not much do to if we failed a strict-mode check
+ if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
+ parser_free();
+ return;
+ }
+
+ assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
+ }
+
+ buf += nread;
+ buflen -= nread;
+ http_parser_pause(parser, 0);
+ } while (buflen > 0);
+
+ nread = parse_pause(NULL, 0);
+ assert (nread == 0);
+
+test:
+ if (num_messages != 1) {
+ printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
+ abort();
+ }
+
+ if(!message_eq(0, msg)) abort();
+
+ parser_free();
+}
int
main (void)
@@ -1828,6 +3030,11 @@ main (void)
for (request_count = 0; requests[request_count].name; request_count++);
for (response_count = 0; responses[response_count].name; response_count++);
+ //// API
+ test_preserve_data();
+ test_parse_url();
+ test_method_str();
+
//// OVERFLOW CONDITIONS
test_header_overflow_error(HTTP_REQUEST);
@@ -1838,6 +3045,9 @@ main (void)
test_no_overflow_long_body(HTTP_RESPONSE, 1000);
test_no_overflow_long_body(HTTP_RESPONSE, 100000);
+ test_header_content_length_overflow_error();
+ test_chunk_content_length_overflow_error();
+
//// RESPONSES
for (i = 0; i < response_count; i++) {
@@ -1845,6 +3055,10 @@ main (void)
}
for (i = 0; i < response_count; i++) {
+ test_message_pause(&responses[i]);
+ }
+
+ for (i = 0; i < response_count; i++) {
if (!responses[i].should_keep_alive) continue;
for (j = 0; j < response_count; j++) {
if (!responses[j].should_keep_alive) continue;
@@ -1888,7 +3102,7 @@ main (void)
printf("response scan 1/2 ");
test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
- , &responses[NO_HEADERS_NO_BODY_404]
+ , &responses[NO_BODY_HTTP10_KA_204]
, &responses[NO_REASON_PHRASE]
);
@@ -2019,7 +3233,9 @@ main (void)
test_message(&requests[i]);
}
-
+ for (i = 0; i < request_count; i++) {
+ test_message_pause(&requests[i]);
+ }
for (i = 0; i < request_count; i++) {
if (!requests[i].should_keep_alive) continue;
diff --git a/deps/http_parser/url_parser.c b/deps/http_parser/url_parser.c
new file mode 100644
index 0000000000..b1f9c979f2
--- /dev/null
+++ b/deps/http_parser/url_parser.c
@@ -0,0 +1,44 @@
+#include "http_parser.h"
+#include <stdio.h>
+#include <string.h>
+
+void
+dump_url (const char *url, const struct http_parser_url *u)
+{
+ unsigned int i;
+
+ printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
+ for (i = 0; i < UF_MAX; i++) {
+ if ((u->field_set & (1 << i)) == 0) {
+ printf("\tfield_data[%u]: unset\n", i);
+ continue;
+ }
+
+ printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n",
+ i,
+ u->field_data[i].off,
+ u->field_data[i].len,
+ u->field_data[i].len,
+ url + u->field_data[i].off);
+ }
+}
+
+int main(int argc, char ** argv) {
+ if (argc != 3) {
+ printf("Syntax : %s connect|get url\n", argv[0]);
+ return 1;
+ }
+ struct http_parser_url u;
+ int len = strlen(argv[2]);
+ int connect = strcmp("connect", argv[1]) == 0 ? 1 : 0;
+ printf("Parsing %s, connect %d\n", argv[2], connect);
+
+ int result = http_parser_parse_url(argv[2], len, connect, &u);
+ if (result != 0) {
+ printf("Parse error : %d\n", result);
+ return result;
+ }
+ printf("Parse ok, result : \n");
+ dump_url(argv[2], &u);
+ return 0;
+} \ No newline at end of file