summaryrefslogtreecommitdiff
path: root/deps/http_parser/test.c
diff options
context:
space:
mode:
Diffstat (limited to 'deps/http_parser/test.c')
-rw-r--r--deps/http_parser/test.c961
1 files changed, 888 insertions, 73 deletions
diff --git a/deps/http_parser/test.c b/deps/http_parser/test.c
index 6af0e787e9..184ba243fc 100644
--- a/deps/http_parser/test.c
+++ b/deps/http_parser/test.c
@@ -44,9 +44,13 @@ 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;
+ uint16_t port;
int num_headers;
enum { NONE=0, FIELD, VALUE } last_header_element;
char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
@@ -67,6 +71,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 +88,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 +119,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 +148,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 +169,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 +188,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 +207,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 +230,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 +255,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 +282,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 +308,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 +336,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 +364,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 +384,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 +410,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 +433,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 +454,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 +481,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 +512,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 +534,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 +553,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 +575,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" }
@@ -536,6 +604,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" }
@@ -555,6 +626,9 @@ 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= { }
@@ -571,7 +645,11 @@ 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"
+ ,.port= 1234
,.num_headers= 0
,.headers= { }
,.body= ""
@@ -587,14 +665,70 @@ const struct message requests[] =
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
+ ,.query_string= ""
+ ,.fragment= ""
+ ,.request_path= ""
,.request_url= "http://hypnotoad.org:1234"
+ ,.port= 1234
,.num_headers= 0
,.headers= { }
,.body= ""
}
+#define PATCH_REQ 24
+, {.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 25
+, {.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 26
, {.name= "utf-8 path request"
,.type= HTTP_REQUEST
,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
@@ -605,6 +739,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 +749,7 @@ const struct message requests[] =
,.body= ""
}
-#define HOSTNAME_UNDERSCORE 25
+#define HOSTNAME_UNDERSCORE 27
, {.name = "hostname underscore"
,.type= HTTP_REQUEST
,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n"
@@ -624,6 +761,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,49 +774,79 @@ 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 28
+, {.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 29
+, {.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 30
+, {.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= ""
}
@@ -780,8 +950,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 +965,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 +1115,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 +1155,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 +1178,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 +1194,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
@@ -1158,6 +1462,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");
+ exit(1);
+}
+
+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");
+ exit(1);
+}
+
+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");
+ exit(1);
+}
+
+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");
+ exit(1);
+}
+
+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");
+ exit(1);
+}
+
+int
+dontcall_headers_complete_cb (http_parser *p)
+{
+ if (p) { } // gcc
+ fprintf(stderr, "\n\n*** on_headers_complete() called on paused "
+ "parser ***\n\n");
+ exit(1);
+}
+
+int
+dontcall_message_complete_cb (http_parser *p)
+{
+ if (p) { } // gcc
+ fprintf(stderr, "\n\n*** on_message_complete() called on paused "
+ "parser ***\n\n");
+ exit(1);
+}
+
+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 +1671,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 +1722,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 +1761,28 @@ 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);
+ exit(1);
+ }
+
+ 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 {
@@ -1420,6 +1911,218 @@ 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");
+ exit(1);
+ }
+}
+
+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 */
+ }
+ }
+ ,.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 */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.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 */
+ }
+ }
+ ,.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 */
+ }
+ }
+ ,.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 */
+ }
+ }
+ ,.rv=0
+ }
+
+, {.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 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
+ }
+};
+
+void
+dump_url (const char *url, const struct http_parser_url *u)
+{
+ char part[512];
+ 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;
+ }
+
+ memcpy(part, url + u->field_data[i].off, u->field_data[i].len);
+ part[u->field_data[i].len] = '\0';
+
+ printf("\tfield_data[%u]: off: %u len: %u part: \"%s\"\n",
+ i,
+ u->field_data[i].off,
+ u->field_data[i].len,
+ part);
+ }
+}
+
+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);
+ exit(1);
+ }
+
+ 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);
+
+ exit(1);
+ }
+ } 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);
+ exit(1);
+ }
+ }
+ }
+}
void
test_message (const struct message *message)
@@ -1576,6 +2279,53 @@ test_header_overflow_error (int req)
exit(1);
}
+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
test_no_overflow_long_body (int req, size_t length)
{
@@ -1814,6 +2564,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);
+ exit(1);
+ }
+
+ if(!message_eq(0, msg)) exit(1);
+
+ parser_free();
+}
int
main (void)
@@ -1828,6 +2630,10 @@ 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();
+
//// OVERFLOW CONDITIONS
test_header_overflow_error(HTTP_REQUEST);
@@ -1838,6 +2644,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 +2654,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 +2701,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 +2832,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;