midori_http_parser 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.gitmodules +6 -0
  4. data/.travis.yml +33 -0
  5. data/Gemfile +2 -0
  6. data/LICENSE-MIT +20 -0
  7. data/README.md +90 -0
  8. data/Rakefile +6 -0
  9. data/bench/standalone.rb +23 -0
  10. data/bench/thin.rb +58 -0
  11. data/ext/ruby_http_parser/.gitignore +1 -0
  12. data/ext/ruby_http_parser/RubyHttpParserService.java +18 -0
  13. data/ext/ruby_http_parser/ext_help.h +18 -0
  14. data/ext/ruby_http_parser/extconf.rb +24 -0
  15. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +495 -0
  16. data/ext/ruby_http_parser/ruby_http_parser.c +516 -0
  17. data/ext/ruby_http_parser/vendor/.gitkeep +0 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/AUTHORS +32 -0
  19. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +48 -0
  20. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +183 -0
  21. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +28 -0
  22. data/ext/ruby_http_parser/vendor/http-parser-java/build.xml +74 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +2175 -0
  24. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.gyp +79 -0
  25. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +304 -0
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/Http-parser.java.iml +22 -0
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/FieldData.java +41 -0
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPCallback.java +8 -0
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPDataCallback.java +34 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPErrorCallback.java +12 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +9 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +113 -0
  33. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +36 -0
  34. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParserUrl.java +76 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +256 -0
  36. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserType.java +13 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/Util.java +111 -0
  38. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPCallback.java +5 -0
  39. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPDataCallback.java +25 -0
  40. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPErrorCallback.java +7 -0
  41. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +2171 -0
  42. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +83 -0
  43. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +374 -0
  44. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/ParseUrl.java +51 -0
  45. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +69 -0
  46. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +52 -0
  47. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +16 -0
  48. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +48 -0
  49. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +212 -0
  50. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +62 -0
  51. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +117 -0
  52. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +27 -0
  53. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Url.java +127 -0
  54. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +236 -0
  55. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +59 -0
  56. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +3425 -0
  57. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +845 -0
  58. data/ext/ruby_http_parser/vendor/http-parser-java/tests.utf8 +17 -0
  59. data/ext/ruby_http_parser/vendor/http-parser-java/tools/byte_constants.rb +6 -0
  60. data/ext/ruby_http_parser/vendor/http-parser-java/tools/const_char.rb +13 -0
  61. data/ext/ruby_http_parser/vendor/http-parser-java/tools/lowcase.rb +15 -0
  62. data/ext/ruby_http_parser/vendor/http-parser-java/tools/parse_tests.rb +33 -0
  63. data/ext/ruby_http_parser/vendor/http-parser/AUTHORS +68 -0
  64. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +23 -0
  65. data/ext/ruby_http_parser/vendor/http-parser/README.md +246 -0
  66. data/ext/ruby_http_parser/vendor/http-parser/bench.c +111 -0
  67. data/ext/ruby_http_parser/vendor/http-parser/contrib/parsertrace.c +160 -0
  68. data/ext/ruby_http_parser/vendor/http-parser/contrib/url_parser.c +47 -0
  69. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +2470 -0
  70. data/ext/ruby_http_parser/vendor/http-parser/http_parser.gyp +111 -0
  71. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +432 -0
  72. data/ext/ruby_http_parser/vendor/http-parser/test.c +4226 -0
  73. data/ext/ruby_http_parser/vendor/http-parser/test_fast +0 -0
  74. data/ext/ruby_http_parser/vendor/http-parser/test_g +0 -0
  75. data/lib/http/parser.rb +1 -0
  76. data/lib/http_parser.rb +21 -0
  77. data/midori_http_parser.gemspec +24 -0
  78. data/spec/parser_spec.rb +376 -0
  79. data/spec/spec_helper.rb +1 -0
  80. data/spec/support/requests.json +631 -0
  81. data/spec/support/responses.json +375 -0
  82. data/tasks/compile.rake +42 -0
  83. data/tasks/fixtures.rake +71 -0
  84. data/tasks/spec.rake +5 -0
  85. data/tasks/submodules.rake +7 -0
  86. metadata +206 -0
@@ -0,0 +1,59 @@
1
+ package http_parser.lolevel;
2
+
3
+ import java.nio.*;
4
+ import java.util.*;
5
+
6
+ import http_parser.ParserType;
7
+
8
+ import static http_parser.lolevel.Util.*;
9
+
10
+ public class WrongContentLength {
11
+ static final String contentLength = "GET / HTTP/1.0\r\n" +
12
+ "Content-Length: 5\r\n" +
13
+ "\r\n" +
14
+ "hello" +
15
+ "hello_again";
16
+ static void test () {
17
+ p(WrongContentLength.class);
18
+ HTTPParser parser = new HTTPParser(ParserType.HTTP_REQUEST);
19
+ ByteBuffer buf = buffer(contentLength);
20
+
21
+ Settings settings = new Settings();
22
+
23
+ int read = parser.execute(settings, buf);
24
+ check (settings.msg_cmplt_called);
25
+ check ("invalid method".equals(settings.err));
26
+
27
+ }
28
+ public static void main(String [] args) {
29
+ test();
30
+ }
31
+
32
+ static class Settings extends ParserSettings {
33
+ public int bodyCount;
34
+ public boolean msg_cmplt_called;
35
+ public String err;
36
+ Settings () {
37
+ this.on_message_complete = new HTTPCallback () {
38
+ public int cb (HTTPParser p) {
39
+ check (5 == bodyCount);
40
+ msg_cmplt_called = true;
41
+ return 0;
42
+ }
43
+ };
44
+ this.on_body = new HTTPDataCallback() {
45
+ public int cb (HTTPParser p, ByteBuffer b, int pos, int len) {
46
+ bodyCount += len;
47
+ check ("hello".equals(str(b, pos, len)));
48
+ return 0;
49
+ }
50
+ };
51
+ this.on_error = new HTTPErrorCallback() {
52
+ public void cb (HTTPParser p, String mes, ByteBuffer b, int i) {
53
+ err = mes;
54
+ }
55
+ };
56
+ }
57
+ }
58
+
59
+ }
@@ -0,0 +1,3425 @@
1
+ /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2
+ *
3
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ * of this software and associated documentation files (the "Software"), to
5
+ * deal in the Software without restriction, including without limitation the
6
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ * sell copies of the Software, and to permit persons to whom the Software is
8
+ * furnished to do so, subject to the following conditions:
9
+ *
10
+ * The above copyright notice and this permission notice shall be included in
11
+ * all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19
+ * IN THE SOFTWARE.
20
+ */
21
+ #include "http_parser.h"
22
+ #include <stdlib.h>
23
+ #include <assert.h>
24
+ #include <stdio.h>
25
+ #include <stdlib.h> /* rand */
26
+ #include <string.h>
27
+ #include <stdarg.h>
28
+
29
+ #undef TRUE
30
+ #define TRUE 1
31
+ #undef FALSE
32
+ #define FALSE 0
33
+
34
+ #define MAX_HEADERS 13
35
+ #define MAX_ELEMENT_SIZE 2048
36
+
37
+ #define MIN(a,b) ((a) < (b) ? (a) : (b))
38
+
39
+ static http_parser *parser;
40
+
41
+ struct message {
42
+ const char *name; // for debugging purposes
43
+ const char *raw;
44
+ enum http_parser_type type;
45
+ enum http_method method;
46
+ int status_code;
47
+ char request_path[MAX_ELEMENT_SIZE];
48
+ char request_url[MAX_ELEMENT_SIZE];
49
+ char fragment[MAX_ELEMENT_SIZE];
50
+ char query_string[MAX_ELEMENT_SIZE];
51
+ char body[MAX_ELEMENT_SIZE];
52
+ size_t body_size;
53
+ const char *host;
54
+ const char *userinfo;
55
+ uint16_t port;
56
+ int num_headers;
57
+ enum { NONE=0, FIELD, VALUE } last_header_element;
58
+ char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
59
+ int should_keep_alive;
60
+
61
+ const char *upgrade; // upgraded body
62
+
63
+ unsigned short http_major;
64
+ unsigned short http_minor;
65
+
66
+ int message_begin_cb_called;
67
+ int headers_complete_cb_called;
68
+ int message_complete_cb_called;
69
+ int message_complete_on_eof;
70
+ int body_is_final;
71
+ };
72
+
73
+ static int currently_parsing_eof;
74
+
75
+ static struct message messages[5];
76
+ static int num_messages;
77
+ static http_parser_settings *current_pause_parser;
78
+
79
+ /* * R E Q U E S T S * */
80
+ const struct message requests[] =
81
+ #define CURL_GET 0
82
+ { {.name= "curl get"
83
+ ,.type= HTTP_REQUEST
84
+ ,.raw= "GET /test HTTP/1.1\r\n"
85
+ "User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\n"
86
+ "Host: 0.0.0.0=5000\r\n"
87
+ "Accept: */*\r\n"
88
+ "\r\n"
89
+ ,.should_keep_alive= TRUE
90
+ ,.message_complete_on_eof= FALSE
91
+ ,.http_major= 1
92
+ ,.http_minor= 1
93
+ ,.method= HTTP_GET
94
+ ,.query_string= ""
95
+ ,.fragment= ""
96
+ ,.request_path= "/test"
97
+ ,.request_url= "/test"
98
+ ,.num_headers= 3
99
+ ,.headers=
100
+ { { "User-Agent", "curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1" }
101
+ , { "Host", "0.0.0.0=5000" }
102
+ , { "Accept", "*/*" }
103
+ }
104
+ ,.body= ""
105
+ }
106
+
107
+ #define FIREFOX_GET 1
108
+ , {.name= "firefox get"
109
+ ,.type= HTTP_REQUEST
110
+ ,.raw= "GET /favicon.ico HTTP/1.1\r\n"
111
+ "Host: 0.0.0.0=5000\r\n"
112
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n"
113
+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
114
+ "Accept-Language: en-us,en;q=0.5\r\n"
115
+ "Accept-Encoding: gzip,deflate\r\n"
116
+ "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
117
+ "Keep-Alive: 300\r\n"
118
+ "Connection: keep-alive\r\n"
119
+ "\r\n"
120
+ ,.should_keep_alive= TRUE
121
+ ,.message_complete_on_eof= FALSE
122
+ ,.http_major= 1
123
+ ,.http_minor= 1
124
+ ,.method= HTTP_GET
125
+ ,.query_string= ""
126
+ ,.fragment= ""
127
+ ,.request_path= "/favicon.ico"
128
+ ,.request_url= "/favicon.ico"
129
+ ,.num_headers= 8
130
+ ,.headers=
131
+ { { "Host", "0.0.0.0=5000" }
132
+ , { "User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0" }
133
+ , { "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" }
134
+ , { "Accept-Language", "en-us,en;q=0.5" }
135
+ , { "Accept-Encoding", "gzip,deflate" }
136
+ , { "Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7" }
137
+ , { "Keep-Alive", "300" }
138
+ , { "Connection", "keep-alive" }
139
+ }
140
+ ,.body= ""
141
+ }
142
+
143
+ #define DUMBFUCK 2
144
+ , {.name= "dumbfuck"
145
+ ,.type= HTTP_REQUEST
146
+ ,.raw= "GET /dumbfuck HTTP/1.1\r\n"
147
+ "aaaaaaaaaaaaa:++++++++++\r\n"
148
+ "\r\n"
149
+ ,.should_keep_alive= TRUE
150
+ ,.message_complete_on_eof= FALSE
151
+ ,.http_major= 1
152
+ ,.http_minor= 1
153
+ ,.method= HTTP_GET
154
+ ,.query_string= ""
155
+ ,.fragment= ""
156
+ ,.request_path= "/dumbfuck"
157
+ ,.request_url= "/dumbfuck"
158
+ ,.num_headers= 1
159
+ ,.headers=
160
+ { { "aaaaaaaaaaaaa", "++++++++++" }
161
+ }
162
+ ,.body= ""
163
+ }
164
+
165
+ #define FRAGMENT_IN_URI 3
166
+ , {.name= "fragment in url"
167
+ ,.type= HTTP_REQUEST
168
+ ,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n"
169
+ "\r\n"
170
+ ,.should_keep_alive= TRUE
171
+ ,.message_complete_on_eof= FALSE
172
+ ,.http_major= 1
173
+ ,.http_minor= 1
174
+ ,.method= HTTP_GET
175
+ ,.query_string= "page=1"
176
+ ,.fragment= "posts-17408"
177
+ ,.request_path= "/forums/1/topics/2375"
178
+ /* XXX request url does include fragment? */
179
+ ,.request_url= "/forums/1/topics/2375?page=1#posts-17408"
180
+ ,.num_headers= 0
181
+ ,.body= ""
182
+ }
183
+
184
+ #define GET_NO_HEADERS_NO_BODY 4
185
+ , {.name= "get no headers no body"
186
+ ,.type= HTTP_REQUEST
187
+ ,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n"
188
+ "\r\n"
189
+ ,.should_keep_alive= TRUE
190
+ ,.message_complete_on_eof= FALSE /* would need Connection: close */
191
+ ,.http_major= 1
192
+ ,.http_minor= 1
193
+ ,.method= HTTP_GET
194
+ ,.query_string= ""
195
+ ,.fragment= ""
196
+ ,.request_path= "/get_no_headers_no_body/world"
197
+ ,.request_url= "/get_no_headers_no_body/world"
198
+ ,.num_headers= 0
199
+ ,.body= ""
200
+ }
201
+
202
+ #define GET_ONE_HEADER_NO_BODY 5
203
+ , {.name= "get one header no body"
204
+ ,.type= HTTP_REQUEST
205
+ ,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n"
206
+ "Accept: */*\r\n"
207
+ "\r\n"
208
+ ,.should_keep_alive= TRUE
209
+ ,.message_complete_on_eof= FALSE /* would need Connection: close */
210
+ ,.http_major= 1
211
+ ,.http_minor= 1
212
+ ,.method= HTTP_GET
213
+ ,.query_string= ""
214
+ ,.fragment= ""
215
+ ,.request_path= "/get_one_header_no_body"
216
+ ,.request_url= "/get_one_header_no_body"
217
+ ,.num_headers= 1
218
+ ,.headers=
219
+ { { "Accept" , "*/*" }
220
+ }
221
+ ,.body= ""
222
+ }
223
+
224
+ #define GET_FUNKY_CONTENT_LENGTH 6
225
+ , {.name= "get funky content length body hello"
226
+ ,.type= HTTP_REQUEST
227
+ ,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n"
228
+ "conTENT-Length: 5\r\n"
229
+ "\r\n"
230
+ "HELLO"
231
+ ,.should_keep_alive= FALSE
232
+ ,.message_complete_on_eof= FALSE
233
+ ,.http_major= 1
234
+ ,.http_minor= 0
235
+ ,.method= HTTP_GET
236
+ ,.query_string= ""
237
+ ,.fragment= ""
238
+ ,.request_path= "/get_funky_content_length_body_hello"
239
+ ,.request_url= "/get_funky_content_length_body_hello"
240
+ ,.num_headers= 1
241
+ ,.headers=
242
+ { { "conTENT-Length" , "5" }
243
+ }
244
+ ,.body= "HELLO"
245
+ }
246
+
247
+ #define POST_IDENTITY_BODY_WORLD 7
248
+ , {.name= "post identity body world"
249
+ ,.type= HTTP_REQUEST
250
+ ,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
251
+ "Accept: */*\r\n"
252
+ "Transfer-Encoding: identity\r\n"
253
+ "Content-Length: 5\r\n"
254
+ "\r\n"
255
+ "World"
256
+ ,.should_keep_alive= TRUE
257
+ ,.message_complete_on_eof= FALSE
258
+ ,.http_major= 1
259
+ ,.http_minor= 1
260
+ ,.method= HTTP_POST
261
+ ,.query_string= "q=search"
262
+ ,.fragment= "hey"
263
+ ,.request_path= "/post_identity_body_world"
264
+ ,.request_url= "/post_identity_body_world?q=search#hey"
265
+ ,.num_headers= 3
266
+ ,.headers=
267
+ { { "Accept", "*/*" }
268
+ , { "Transfer-Encoding", "identity" }
269
+ , { "Content-Length", "5" }
270
+ }
271
+ ,.body= "World"
272
+ }
273
+
274
+ #define POST_CHUNKED_ALL_YOUR_BASE 8
275
+ , {.name= "post - chunked body: all your base are belong to us"
276
+ ,.type= HTTP_REQUEST
277
+ ,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n"
278
+ "Transfer-Encoding: chunked\r\n"
279
+ "\r\n"
280
+ "1e\r\nall your base are belong to us\r\n"
281
+ "0\r\n"
282
+ "\r\n"
283
+ ,.should_keep_alive= TRUE
284
+ ,.message_complete_on_eof= FALSE
285
+ ,.http_major= 1
286
+ ,.http_minor= 1
287
+ ,.method= HTTP_POST
288
+ ,.query_string= ""
289
+ ,.fragment= ""
290
+ ,.request_path= "/post_chunked_all_your_base"
291
+ ,.request_url= "/post_chunked_all_your_base"
292
+ ,.num_headers= 1
293
+ ,.headers=
294
+ { { "Transfer-Encoding" , "chunked" }
295
+ }
296
+ ,.body= "all your base are belong to us"
297
+ }
298
+
299
+ #define TWO_CHUNKS_MULT_ZERO_END 9
300
+ , {.name= "two chunks ; triple zero ending"
301
+ ,.type= HTTP_REQUEST
302
+ ,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n"
303
+ "Transfer-Encoding: chunked\r\n"
304
+ "\r\n"
305
+ "5\r\nhello\r\n"
306
+ "6\r\n world\r\n"
307
+ "000\r\n"
308
+ "\r\n"
309
+ ,.should_keep_alive= TRUE
310
+ ,.message_complete_on_eof= FALSE
311
+ ,.http_major= 1
312
+ ,.http_minor= 1
313
+ ,.method= HTTP_POST
314
+ ,.query_string= ""
315
+ ,.fragment= ""
316
+ ,.request_path= "/two_chunks_mult_zero_end"
317
+ ,.request_url= "/two_chunks_mult_zero_end"
318
+ ,.num_headers= 1
319
+ ,.headers=
320
+ { { "Transfer-Encoding", "chunked" }
321
+ }
322
+ ,.body= "hello world"
323
+ }
324
+
325
+ #define CHUNKED_W_TRAILING_HEADERS 10
326
+ , {.name= "chunked with trailing headers. blech."
327
+ ,.type= HTTP_REQUEST
328
+ ,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
329
+ "Transfer-Encoding: chunked\r\n"
330
+ "\r\n"
331
+ "5\r\nhello\r\n"
332
+ "6\r\n world\r\n"
333
+ "0\r\n"
334
+ "Vary: *\r\n"
335
+ "Content-Type: text/plain\r\n"
336
+ "\r\n"
337
+ ,.should_keep_alive= TRUE
338
+ ,.message_complete_on_eof= FALSE
339
+ ,.http_major= 1
340
+ ,.http_minor= 1
341
+ ,.method= HTTP_POST
342
+ ,.query_string= ""
343
+ ,.fragment= ""
344
+ ,.request_path= "/chunked_w_trailing_headers"
345
+ ,.request_url= "/chunked_w_trailing_headers"
346
+ ,.num_headers= 3
347
+ ,.headers=
348
+ { { "Transfer-Encoding", "chunked" }
349
+ , { "Vary", "*" }
350
+ , { "Content-Type", "text/plain" }
351
+ }
352
+ ,.body= "hello world"
353
+ }
354
+
355
+ #define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
356
+ , {.name= "with bullshit after the length"
357
+ ,.type= HTTP_REQUEST
358
+ ,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
359
+ "Transfer-Encoding: chunked\r\n"
360
+ "\r\n"
361
+ "5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n"
362
+ "6; blahblah; blah\r\n world\r\n"
363
+ "0\r\n"
364
+ "\r\n"
365
+ ,.should_keep_alive= TRUE
366
+ ,.message_complete_on_eof= FALSE
367
+ ,.http_major= 1
368
+ ,.http_minor= 1
369
+ ,.method= HTTP_POST
370
+ ,.query_string= ""
371
+ ,.fragment= ""
372
+ ,.request_path= "/chunked_w_bullshit_after_length"
373
+ ,.request_url= "/chunked_w_bullshit_after_length"
374
+ ,.num_headers= 1
375
+ ,.headers=
376
+ { { "Transfer-Encoding", "chunked" }
377
+ }
378
+ ,.body= "hello world"
379
+ }
380
+
381
+ #define WITH_QUOTES 12
382
+ , {.name= "with quotes"
383
+ ,.type= HTTP_REQUEST
384
+ ,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n"
385
+ ,.should_keep_alive= TRUE
386
+ ,.message_complete_on_eof= FALSE
387
+ ,.http_major= 1
388
+ ,.http_minor= 1
389
+ ,.method= HTTP_GET
390
+ ,.query_string= "foo=\"bar\""
391
+ ,.fragment= ""
392
+ ,.request_path= "/with_\"stupid\"_quotes"
393
+ ,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\""
394
+ ,.num_headers= 0
395
+ ,.headers= { }
396
+ ,.body= ""
397
+ }
398
+
399
+ #define APACHEBENCH_GET 13
400
+ /* The server receiving this request SHOULD NOT wait for EOF
401
+ * to know that content-length == 0.
402
+ * How to represent this in a unit test? message_complete_on_eof
403
+ * Compare with NO_CONTENT_LENGTH_RESPONSE.
404
+ */
405
+ , {.name = "apachebench get"
406
+ ,.type= HTTP_REQUEST
407
+ ,.raw= "GET /test HTTP/1.0\r\n"
408
+ "Host: 0.0.0.0:5000\r\n"
409
+ "User-Agent: ApacheBench/2.3\r\n"
410
+ "Accept: */*\r\n\r\n"
411
+ ,.should_keep_alive= FALSE
412
+ ,.message_complete_on_eof= FALSE
413
+ ,.http_major= 1
414
+ ,.http_minor= 0
415
+ ,.method= HTTP_GET
416
+ ,.query_string= ""
417
+ ,.fragment= ""
418
+ ,.request_path= "/test"
419
+ ,.request_url= "/test"
420
+ ,.num_headers= 3
421
+ ,.headers= { { "Host", "0.0.0.0:5000" }
422
+ , { "User-Agent", "ApacheBench/2.3" }
423
+ , { "Accept", "*/*" }
424
+ }
425
+ ,.body= ""
426
+ }
427
+
428
+ #define QUERY_URL_WITH_QUESTION_MARK_GET 14
429
+ /* Some clients include '?' characters in query strings.
430
+ */
431
+ , {.name = "query url with question mark"
432
+ ,.type= HTTP_REQUEST
433
+ ,.raw= "GET /test.cgi?foo=bar?baz HTTP/1.1\r\n\r\n"
434
+ ,.should_keep_alive= TRUE
435
+ ,.message_complete_on_eof= FALSE
436
+ ,.http_major= 1
437
+ ,.http_minor= 1
438
+ ,.method= HTTP_GET
439
+ ,.query_string= "foo=bar?baz"
440
+ ,.fragment= ""
441
+ ,.request_path= "/test.cgi"
442
+ ,.request_url= "/test.cgi?foo=bar?baz"
443
+ ,.num_headers= 0
444
+ ,.headers= {}
445
+ ,.body= ""
446
+ }
447
+
448
+ #define PREFIX_NEWLINE_GET 15
449
+ /* Some clients, especially after a POST in a keep-alive connection,
450
+ * will send an extra CRLF before the next request
451
+ */
452
+ , {.name = "newline prefix get"
453
+ ,.type= HTTP_REQUEST
454
+ ,.raw= "\r\nGET /test HTTP/1.1\r\n\r\n"
455
+ ,.should_keep_alive= TRUE
456
+ ,.message_complete_on_eof= FALSE
457
+ ,.http_major= 1
458
+ ,.http_minor= 1
459
+ ,.method= HTTP_GET
460
+ ,.query_string= ""
461
+ ,.fragment= ""
462
+ ,.request_path= "/test"
463
+ ,.request_url= "/test"
464
+ ,.num_headers= 0
465
+ ,.headers= { }
466
+ ,.body= ""
467
+ }
468
+
469
+ #define UPGRADE_REQUEST 16
470
+ , {.name = "upgrade request"
471
+ ,.type= HTTP_REQUEST
472
+ ,.raw= "GET /demo HTTP/1.1\r\n"
473
+ "Host: example.com\r\n"
474
+ "Connection: Upgrade\r\n"
475
+ "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
476
+ "Sec-WebSocket-Protocol: sample\r\n"
477
+ "Upgrade: WebSocket\r\n"
478
+ "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
479
+ "Origin: http://example.com\r\n"
480
+ "\r\n"
481
+ "Hot diggity dogg"
482
+ ,.should_keep_alive= TRUE
483
+ ,.message_complete_on_eof= FALSE
484
+ ,.http_major= 1
485
+ ,.http_minor= 1
486
+ ,.method= HTTP_GET
487
+ ,.query_string= ""
488
+ ,.fragment= ""
489
+ ,.request_path= "/demo"
490
+ ,.request_url= "/demo"
491
+ ,.num_headers= 7
492
+ ,.upgrade="Hot diggity dogg"
493
+ ,.headers= { { "Host", "example.com" }
494
+ , { "Connection", "Upgrade" }
495
+ , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
496
+ , { "Sec-WebSocket-Protocol", "sample" }
497
+ , { "Upgrade", "WebSocket" }
498
+ , { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" }
499
+ , { "Origin", "http://example.com" }
500
+ }
501
+ ,.body= ""
502
+ }
503
+
504
+ #define CONNECT_REQUEST 17
505
+ , {.name = "connect request"
506
+ ,.type= HTTP_REQUEST
507
+ ,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n"
508
+ "User-agent: Mozilla/1.1N\r\n"
509
+ "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
510
+ "\r\n"
511
+ "some data\r\n"
512
+ "and yet even more data"
513
+ ,.should_keep_alive= FALSE
514
+ ,.message_complete_on_eof= FALSE
515
+ ,.http_major= 1
516
+ ,.http_minor= 0
517
+ ,.method= HTTP_CONNECT
518
+ ,.query_string= ""
519
+ ,.fragment= ""
520
+ ,.request_path= ""
521
+ ,.request_url= "0-home0.netscape.com:443"
522
+ ,.num_headers= 2
523
+ ,.upgrade="some data\r\nand yet even more data"
524
+ ,.headers= { { "User-agent", "Mozilla/1.1N" }
525
+ , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
526
+ }
527
+ ,.body= ""
528
+ }
529
+
530
+ #define REPORT_REQ 18
531
+ , {.name= "report request"
532
+ ,.type= HTTP_REQUEST
533
+ ,.raw= "REPORT /test HTTP/1.1\r\n"
534
+ "\r\n"
535
+ ,.should_keep_alive= TRUE
536
+ ,.message_complete_on_eof= FALSE
537
+ ,.http_major= 1
538
+ ,.http_minor= 1
539
+ ,.method= HTTP_REPORT
540
+ ,.query_string= ""
541
+ ,.fragment= ""
542
+ ,.request_path= "/test"
543
+ ,.request_url= "/test"
544
+ ,.num_headers= 0
545
+ ,.headers= {}
546
+ ,.body= ""
547
+ }
548
+
549
+ #define NO_HTTP_VERSION 19
550
+ , {.name= "request with no http version"
551
+ ,.type= HTTP_REQUEST
552
+ ,.raw= "GET /\r\n"
553
+ "\r\n"
554
+ ,.should_keep_alive= FALSE
555
+ ,.message_complete_on_eof= FALSE
556
+ ,.http_major= 0
557
+ ,.http_minor= 9
558
+ ,.method= HTTP_GET
559
+ ,.query_string= ""
560
+ ,.fragment= ""
561
+ ,.request_path= "/"
562
+ ,.request_url= "/"
563
+ ,.num_headers= 0
564
+ ,.headers= {}
565
+ ,.body= ""
566
+ }
567
+
568
+ #define MSEARCH_REQ 20
569
+ , {.name= "m-search request"
570
+ ,.type= HTTP_REQUEST
571
+ ,.raw= "M-SEARCH * HTTP/1.1\r\n"
572
+ "HOST: 239.255.255.250:1900\r\n"
573
+ "MAN: \"ssdp:discover\"\r\n"
574
+ "ST: \"ssdp:all\"\r\n"
575
+ "\r\n"
576
+ ,.should_keep_alive= TRUE
577
+ ,.message_complete_on_eof= FALSE
578
+ ,.http_major= 1
579
+ ,.http_minor= 1
580
+ ,.method= HTTP_MSEARCH
581
+ ,.query_string= ""
582
+ ,.fragment= ""
583
+ ,.request_path= "*"
584
+ ,.request_url= "*"
585
+ ,.num_headers= 3
586
+ ,.headers= { { "HOST", "239.255.255.250:1900" }
587
+ , { "MAN", "\"ssdp:discover\"" }
588
+ , { "ST", "\"ssdp:all\"" }
589
+ }
590
+ ,.body= ""
591
+ }
592
+
593
+ #define LINE_FOLDING_IN_HEADER 21
594
+ , {.name= "line folding in header value"
595
+ ,.type= HTTP_REQUEST
596
+ ,.raw= "GET / HTTP/1.1\r\n"
597
+ "Line1: abc\r\n"
598
+ "\tdef\r\n"
599
+ " ghi\r\n"
600
+ "\t\tjkl\r\n"
601
+ " mno \r\n"
602
+ "\t \tqrs\r\n"
603
+ "Line2: \t line2\t\r\n"
604
+ "\r\n"
605
+ ,.should_keep_alive= TRUE
606
+ ,.message_complete_on_eof= FALSE
607
+ ,.http_major= 1
608
+ ,.http_minor= 1
609
+ ,.method= HTTP_GET
610
+ ,.query_string= ""
611
+ ,.fragment= ""
612
+ ,.request_path= "/"
613
+ ,.request_url= "/"
614
+ ,.num_headers= 2
615
+ ,.headers= { { "Line1", "abcdefghijklmno qrs" }
616
+ , { "Line2", "line2\t" }
617
+ }
618
+ ,.body= ""
619
+ }
620
+
621
+
622
+ #define QUERY_TERMINATED_HOST 22
623
+ , {.name= "host terminated by a query string"
624
+ ,.type= HTTP_REQUEST
625
+ ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
626
+ "\r\n"
627
+ ,.should_keep_alive= TRUE
628
+ ,.message_complete_on_eof= FALSE
629
+ ,.http_major= 1
630
+ ,.http_minor= 1
631
+ ,.method= HTTP_GET
632
+ ,.query_string= "hail=all"
633
+ ,.fragment= ""
634
+ ,.request_path= ""
635
+ ,.request_url= "http://hypnotoad.org?hail=all"
636
+ ,.host= "hypnotoad.org"
637
+ ,.num_headers= 0
638
+ ,.headers= { }
639
+ ,.body= ""
640
+ }
641
+
642
+ #define QUERY_TERMINATED_HOSTPORT 23
643
+ , {.name= "host:port terminated by a query string"
644
+ ,.type= HTTP_REQUEST
645
+ ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
646
+ "\r\n"
647
+ ,.should_keep_alive= TRUE
648
+ ,.message_complete_on_eof= FALSE
649
+ ,.http_major= 1
650
+ ,.http_minor= 1
651
+ ,.method= HTTP_GET
652
+ ,.query_string= "hail=all"
653
+ ,.fragment= ""
654
+ ,.request_path= ""
655
+ ,.request_url= "http://hypnotoad.org:1234?hail=all"
656
+ ,.host= "hypnotoad.org"
657
+ ,.port= 1234
658
+ ,.num_headers= 0
659
+ ,.headers= { }
660
+ ,.body= ""
661
+ }
662
+
663
+ #define SPACE_TERMINATED_HOSTPORT 24
664
+ , {.name= "host:port terminated by a space"
665
+ ,.type= HTTP_REQUEST
666
+ ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
667
+ "\r\n"
668
+ ,.should_keep_alive= TRUE
669
+ ,.message_complete_on_eof= FALSE
670
+ ,.http_major= 1
671
+ ,.http_minor= 1
672
+ ,.method= HTTP_GET
673
+ ,.query_string= ""
674
+ ,.fragment= ""
675
+ ,.request_path= ""
676
+ ,.request_url= "http://hypnotoad.org:1234"
677
+ ,.host= "hypnotoad.org"
678
+ ,.port= 1234
679
+ ,.num_headers= 0
680
+ ,.headers= { }
681
+ ,.body= ""
682
+ }
683
+
684
+ #define PATCH_REQ 25
685
+ , {.name = "PATCH request"
686
+ ,.type= HTTP_REQUEST
687
+ ,.raw= "PATCH /file.txt HTTP/1.1\r\n"
688
+ "Host: www.example.com\r\n"
689
+ "Content-Type: application/example\r\n"
690
+ "If-Match: \"e0023aa4e\"\r\n"
691
+ "Content-Length: 10\r\n"
692
+ "\r\n"
693
+ "cccccccccc"
694
+ ,.should_keep_alive= TRUE
695
+ ,.message_complete_on_eof= FALSE
696
+ ,.http_major= 1
697
+ ,.http_minor= 1
698
+ ,.method= HTTP_PATCH
699
+ ,.query_string= ""
700
+ ,.fragment= ""
701
+ ,.request_path= "/file.txt"
702
+ ,.request_url= "/file.txt"
703
+ ,.num_headers= 4
704
+ ,.headers= { { "Host", "www.example.com" }
705
+ , { "Content-Type", "application/example" }
706
+ , { "If-Match", "\"e0023aa4e\"" }
707
+ , { "Content-Length", "10" }
708
+ }
709
+ ,.body= "cccccccccc"
710
+ }
711
+
712
+ #define CONNECT_CAPS_REQUEST 26
713
+ , {.name = "connect caps request"
714
+ ,.type= HTTP_REQUEST
715
+ ,.raw= "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\n"
716
+ "User-agent: Mozilla/1.1N\r\n"
717
+ "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
718
+ "\r\n"
719
+ ,.should_keep_alive= FALSE
720
+ ,.message_complete_on_eof= FALSE
721
+ ,.http_major= 1
722
+ ,.http_minor= 0
723
+ ,.method= HTTP_CONNECT
724
+ ,.query_string= ""
725
+ ,.fragment= ""
726
+ ,.request_path= ""
727
+ ,.request_url= "HOME0.NETSCAPE.COM:443"
728
+ ,.num_headers= 2
729
+ ,.upgrade=""
730
+ ,.headers= { { "User-agent", "Mozilla/1.1N" }
731
+ , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
732
+ }
733
+ ,.body= ""
734
+ }
735
+
736
+ #if !HTTP_PARSER_STRICT
737
+ #define UTF8_PATH_REQ 27
738
+ , {.name= "utf-8 path request"
739
+ ,.type= HTTP_REQUEST
740
+ ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
741
+ "Host: github.com\r\n"
742
+ "\r\n"
743
+ ,.should_keep_alive= TRUE
744
+ ,.message_complete_on_eof= FALSE
745
+ ,.http_major= 1
746
+ ,.http_minor= 1
747
+ ,.method= HTTP_GET
748
+ ,.query_string= "q=1"
749
+ ,.fragment= "narf"
750
+ ,.request_path= "/δ¶/δt/pope"
751
+ ,.request_url= "/δ¶/δt/pope?q=1#narf"
752
+ ,.num_headers= 1
753
+ ,.headers= { {"Host", "github.com" }
754
+ }
755
+ ,.body= ""
756
+ }
757
+
758
+ #define HOSTNAME_UNDERSCORE 28
759
+ , {.name = "hostname underscore"
760
+ ,.type= HTTP_REQUEST
761
+ ,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n"
762
+ "User-agent: Mozilla/1.1N\r\n"
763
+ "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
764
+ "\r\n"
765
+ ,.should_keep_alive= FALSE
766
+ ,.message_complete_on_eof= FALSE
767
+ ,.http_major= 1
768
+ ,.http_minor= 0
769
+ ,.method= HTTP_CONNECT
770
+ ,.query_string= ""
771
+ ,.fragment= ""
772
+ ,.request_path= ""
773
+ ,.request_url= "home_0.netscape.com:443"
774
+ ,.num_headers= 2
775
+ ,.upgrade=""
776
+ ,.headers= { { "User-agent", "Mozilla/1.1N" }
777
+ , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
778
+ }
779
+ ,.body= ""
780
+ }
781
+ #endif /* !HTTP_PARSER_STRICT */
782
+
783
+ /* see https://github.com/ry/http-parser/issues/47 */
784
+ #define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 29
785
+ , {.name = "eat CRLF between requests, no \"Connection: close\" header"
786
+ ,.raw= "POST / HTTP/1.1\r\n"
787
+ "Host: www.example.com\r\n"
788
+ "Content-Type: application/x-www-form-urlencoded\r\n"
789
+ "Content-Length: 4\r\n"
790
+ "\r\n"
791
+ "q=42\r\n" /* note the trailing CRLF */
792
+ ,.should_keep_alive= TRUE
793
+ ,.message_complete_on_eof= FALSE
794
+ ,.http_major= 1
795
+ ,.http_minor= 1
796
+ ,.method= HTTP_POST
797
+ ,.query_string= ""
798
+ ,.fragment= ""
799
+ ,.request_path= "/"
800
+ ,.request_url= "/"
801
+ ,.num_headers= 3
802
+ ,.upgrade= 0
803
+ ,.headers= { { "Host", "www.example.com" }
804
+ , { "Content-Type", "application/x-www-form-urlencoded" }
805
+ , { "Content-Length", "4" }
806
+ }
807
+ ,.body= "q=42"
808
+ }
809
+
810
+ /* see https://github.com/ry/http-parser/issues/47 */
811
+ #define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 30
812
+ , {.name = "eat CRLF between requests even if \"Connection: close\" is set"
813
+ ,.raw= "POST / HTTP/1.1\r\n"
814
+ "Host: www.example.com\r\n"
815
+ "Content-Type: application/x-www-form-urlencoded\r\n"
816
+ "Content-Length: 4\r\n"
817
+ "Connection: close\r\n"
818
+ "\r\n"
819
+ "q=42\r\n" /* note the trailing CRLF */
820
+ ,.should_keep_alive= FALSE
821
+ ,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */
822
+ ,.http_major= 1
823
+ ,.http_minor= 1
824
+ ,.method= HTTP_POST
825
+ ,.query_string= ""
826
+ ,.fragment= ""
827
+ ,.request_path= "/"
828
+ ,.request_url= "/"
829
+ ,.num_headers= 4
830
+ ,.upgrade= 0
831
+ ,.headers= { { "Host", "www.example.com" }
832
+ , { "Content-Type", "application/x-www-form-urlencoded" }
833
+ , { "Content-Length", "4" }
834
+ , { "Connection", "close" }
835
+ }
836
+ ,.body= "q=42"
837
+ }
838
+
839
+ #define PURGE_REQ 31
840
+ , {.name = "PURGE request"
841
+ ,.type= HTTP_REQUEST
842
+ ,.raw= "PURGE /file.txt HTTP/1.1\r\n"
843
+ "Host: www.example.com\r\n"
844
+ "\r\n"
845
+ ,.should_keep_alive= TRUE
846
+ ,.message_complete_on_eof= FALSE
847
+ ,.http_major= 1
848
+ ,.http_minor= 1
849
+ ,.method= HTTP_PURGE
850
+ ,.query_string= ""
851
+ ,.fragment= ""
852
+ ,.request_path= "/file.txt"
853
+ ,.request_url= "/file.txt"
854
+ ,.num_headers= 1
855
+ ,.headers= { { "Host", "www.example.com" } }
856
+ ,.body= ""
857
+ }
858
+
859
+ #define SEARCH_REQ 32
860
+ , {.name = "SEARCH request"
861
+ ,.type= HTTP_REQUEST
862
+ ,.raw= "SEARCH / HTTP/1.1\r\n"
863
+ "Host: www.example.com\r\n"
864
+ "\r\n"
865
+ ,.should_keep_alive= TRUE
866
+ ,.message_complete_on_eof= FALSE
867
+ ,.http_major= 1
868
+ ,.http_minor= 1
869
+ ,.method= HTTP_SEARCH
870
+ ,.query_string= ""
871
+ ,.fragment= ""
872
+ ,.request_path= "/"
873
+ ,.request_url= "/"
874
+ ,.num_headers= 1
875
+ ,.headers= { { "Host", "www.example.com" } }
876
+ ,.body= ""
877
+ }
878
+
879
+ #define PROXY_WITH_BASIC_AUTH 33
880
+ , {.name= "host:port and basic_auth"
881
+ ,.type= HTTP_REQUEST
882
+ ,.raw= "GET http://a%12:b!&*$@hypnotoad.org:1234/toto HTTP/1.1\r\n"
883
+ "\r\n"
884
+ ,.should_keep_alive= TRUE
885
+ ,.message_complete_on_eof= FALSE
886
+ ,.http_major= 1
887
+ ,.http_minor= 1
888
+ ,.method= HTTP_GET
889
+ ,.fragment= ""
890
+ ,.request_path= "/toto"
891
+ ,.request_url= "http://a%12:b!&*$@hypnotoad.org:1234/toto"
892
+ ,.host= "hypnotoad.org"
893
+ ,.userinfo= "a%12:b!&*$"
894
+ ,.port= 1234
895
+ ,.num_headers= 0
896
+ ,.headers= { }
897
+ ,.body= ""
898
+ }
899
+
900
+
901
+ , {.name= NULL } /* sentinel */
902
+ };
903
+
904
+ /* * R E S P O N S E S * */
905
+ const struct message responses[] =
906
+ #define GOOGLE_301 0
907
+ { {.name= "google 301"
908
+ ,.type= HTTP_RESPONSE
909
+ ,.raw= "HTTP/1.1 301 Moved Permanently\r\n"
910
+ "Location: http://www.google.com/\r\n"
911
+ "Content-Type: text/html; charset=UTF-8\r\n"
912
+ "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n"
913
+ "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n"
914
+ "X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */
915
+ "Cache-Control: public, max-age=2592000\r\n"
916
+ "Server: gws\r\n"
917
+ "Content-Length: 219 \r\n"
918
+ "\r\n"
919
+ "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
920
+ "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
921
+ "<H1>301 Moved</H1>\n"
922
+ "The document has moved\n"
923
+ "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
924
+ "</BODY></HTML>\r\n"
925
+ ,.should_keep_alive= TRUE
926
+ ,.message_complete_on_eof= FALSE
927
+ ,.http_major= 1
928
+ ,.http_minor= 1
929
+ ,.status_code= 301
930
+ ,.num_headers= 8
931
+ ,.headers=
932
+ { { "Location", "http://www.google.com/" }
933
+ , { "Content-Type", "text/html; charset=UTF-8" }
934
+ , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
935
+ , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" }
936
+ , { "X-$PrototypeBI-Version", "1.6.0.3" }
937
+ , { "Cache-Control", "public, max-age=2592000" }
938
+ , { "Server", "gws" }
939
+ , { "Content-Length", "219 " }
940
+ }
941
+ ,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
942
+ "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
943
+ "<H1>301 Moved</H1>\n"
944
+ "The document has moved\n"
945
+ "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
946
+ "</BODY></HTML>\r\n"
947
+ }
948
+
949
+ #define NO_CONTENT_LENGTH_RESPONSE 1
950
+ /* The client should wait for the server's EOF. That is, when content-length
951
+ * is not specified, and "Connection: close", the end of body is specified
952
+ * by the EOF.
953
+ * Compare with APACHEBENCH_GET
954
+ */
955
+ , {.name= "no content-length response"
956
+ ,.type= HTTP_RESPONSE
957
+ ,.raw= "HTTP/1.1 200 OK\r\n"
958
+ "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n"
959
+ "Server: Apache\r\n"
960
+ "X-Powered-By: Servlet/2.5 JSP/2.1\r\n"
961
+ "Content-Type: text/xml; charset=utf-8\r\n"
962
+ "Connection: close\r\n"
963
+ "\r\n"
964
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
965
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
966
+ " <SOAP-ENV:Body>\n"
967
+ " <SOAP-ENV:Fault>\n"
968
+ " <faultcode>SOAP-ENV:Client</faultcode>\n"
969
+ " <faultstring>Client Error</faultstring>\n"
970
+ " </SOAP-ENV:Fault>\n"
971
+ " </SOAP-ENV:Body>\n"
972
+ "</SOAP-ENV:Envelope>"
973
+ ,.should_keep_alive= FALSE
974
+ ,.message_complete_on_eof= TRUE
975
+ ,.http_major= 1
976
+ ,.http_minor= 1
977
+ ,.status_code= 200
978
+ ,.num_headers= 5
979
+ ,.headers=
980
+ { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" }
981
+ , { "Server", "Apache" }
982
+ , { "X-Powered-By", "Servlet/2.5 JSP/2.1" }
983
+ , { "Content-Type", "text/xml; charset=utf-8" }
984
+ , { "Connection", "close" }
985
+ }
986
+ ,.body= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
987
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
988
+ " <SOAP-ENV:Body>\n"
989
+ " <SOAP-ENV:Fault>\n"
990
+ " <faultcode>SOAP-ENV:Client</faultcode>\n"
991
+ " <faultstring>Client Error</faultstring>\n"
992
+ " </SOAP-ENV:Fault>\n"
993
+ " </SOAP-ENV:Body>\n"
994
+ "</SOAP-ENV:Envelope>"
995
+ }
996
+
997
+ #define NO_HEADERS_NO_BODY_404 2
998
+ , {.name= "404 no headers no body"
999
+ ,.type= HTTP_RESPONSE
1000
+ ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
1001
+ ,.should_keep_alive= FALSE
1002
+ ,.message_complete_on_eof= TRUE
1003
+ ,.http_major= 1
1004
+ ,.http_minor= 1
1005
+ ,.status_code= 404
1006
+ ,.num_headers= 0
1007
+ ,.headers= {}
1008
+ ,.body_size= 0
1009
+ ,.body= ""
1010
+ }
1011
+
1012
+ #define NO_REASON_PHRASE 3
1013
+ , {.name= "301 no response phrase"
1014
+ ,.type= HTTP_RESPONSE
1015
+ ,.raw= "HTTP/1.1 301\r\n\r\n"
1016
+ ,.should_keep_alive = FALSE
1017
+ ,.message_complete_on_eof= TRUE
1018
+ ,.http_major= 1
1019
+ ,.http_minor= 1
1020
+ ,.status_code= 301
1021
+ ,.num_headers= 0
1022
+ ,.headers= {}
1023
+ ,.body= ""
1024
+ }
1025
+
1026
+ #define TRAILING_SPACE_ON_CHUNKED_BODY 4
1027
+ , {.name="200 trailing space on chunked body"
1028
+ ,.type= HTTP_RESPONSE
1029
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1030
+ "Content-Type: text/plain\r\n"
1031
+ "Transfer-Encoding: chunked\r\n"
1032
+ "\r\n"
1033
+ "25 \r\n"
1034
+ "This is the data in the first chunk\r\n"
1035
+ "\r\n"
1036
+ "1C\r\n"
1037
+ "and this is the second one\r\n"
1038
+ "\r\n"
1039
+ "0 \r\n"
1040
+ "\r\n"
1041
+ ,.should_keep_alive= TRUE
1042
+ ,.message_complete_on_eof= FALSE
1043
+ ,.http_major= 1
1044
+ ,.http_minor= 1
1045
+ ,.status_code= 200
1046
+ ,.num_headers= 2
1047
+ ,.headers=
1048
+ { {"Content-Type", "text/plain" }
1049
+ , {"Transfer-Encoding", "chunked" }
1050
+ }
1051
+ ,.body_size = 37+28
1052
+ ,.body =
1053
+ "This is the data in the first chunk\r\n"
1054
+ "and this is the second one\r\n"
1055
+
1056
+ }
1057
+
1058
+ #define NO_CARRIAGE_RET 5
1059
+ , {.name="no carriage ret"
1060
+ ,.type= HTTP_RESPONSE
1061
+ ,.raw= "HTTP/1.1 200 OK\n"
1062
+ "Content-Type: text/html; charset=utf-8\n"
1063
+ "Connection: close\n"
1064
+ "\n"
1065
+ "these headers are from http://news.ycombinator.com/"
1066
+ ,.should_keep_alive= FALSE
1067
+ ,.message_complete_on_eof= TRUE
1068
+ ,.http_major= 1
1069
+ ,.http_minor= 1
1070
+ ,.status_code= 200
1071
+ ,.num_headers= 2
1072
+ ,.headers=
1073
+ { {"Content-Type", "text/html; charset=utf-8" }
1074
+ , {"Connection", "close" }
1075
+ }
1076
+ ,.body= "these headers are from http://news.ycombinator.com/"
1077
+ }
1078
+
1079
+ #define PROXY_CONNECTION 6
1080
+ , {.name="proxy connection"
1081
+ ,.type= HTTP_RESPONSE
1082
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1083
+ "Content-Type: text/html; charset=UTF-8\r\n"
1084
+ "Content-Length: 11\r\n"
1085
+ "Proxy-Connection: close\r\n"
1086
+ "Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n"
1087
+ "\r\n"
1088
+ "hello world"
1089
+ ,.should_keep_alive= FALSE
1090
+ ,.message_complete_on_eof= FALSE
1091
+ ,.http_major= 1
1092
+ ,.http_minor= 1
1093
+ ,.status_code= 200
1094
+ ,.num_headers= 4
1095
+ ,.headers=
1096
+ { {"Content-Type", "text/html; charset=UTF-8" }
1097
+ , {"Content-Length", "11" }
1098
+ , {"Proxy-Connection", "close" }
1099
+ , {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"}
1100
+ }
1101
+ ,.body= "hello world"
1102
+ }
1103
+
1104
+ #define UNDERSTORE_HEADER_KEY 7
1105
+ // shown by
1106
+ // curl -o /dev/null -v "http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;"
1107
+ , {.name="underscore header key"
1108
+ ,.type= HTTP_RESPONSE
1109
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1110
+ "Server: DCLK-AdSvr\r\n"
1111
+ "Content-Type: text/xml\r\n"
1112
+ "Content-Length: 0\r\n"
1113
+ "DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\r\n\r\n"
1114
+ ,.should_keep_alive= TRUE
1115
+ ,.message_complete_on_eof= FALSE
1116
+ ,.http_major= 1
1117
+ ,.http_minor= 1
1118
+ ,.status_code= 200
1119
+ ,.num_headers= 4
1120
+ ,.headers=
1121
+ { {"Server", "DCLK-AdSvr" }
1122
+ , {"Content-Type", "text/xml" }
1123
+ , {"Content-Length", "0" }
1124
+ , {"DCLK_imp", "v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o" }
1125
+ }
1126
+ ,.body= ""
1127
+ }
1128
+
1129
+ #define BONJOUR_MADAME_FR 8
1130
+ /* The client should not merge two headers fields when the first one doesn't
1131
+ * have a value.
1132
+ */
1133
+ , {.name= "bonjourmadame.fr"
1134
+ ,.type= HTTP_RESPONSE
1135
+ ,.raw= "HTTP/1.0 301 Moved Permanently\r\n"
1136
+ "Date: Thu, 03 Jun 2010 09:56:32 GMT\r\n"
1137
+ "Server: Apache/2.2.3 (Red Hat)\r\n"
1138
+ "Cache-Control: public\r\n"
1139
+ "Pragma: \r\n"
1140
+ "Location: http://www.bonjourmadame.fr/\r\n"
1141
+ "Vary: Accept-Encoding\r\n"
1142
+ "Content-Length: 0\r\n"
1143
+ "Content-Type: text/html; charset=UTF-8\r\n"
1144
+ "Connection: keep-alive\r\n"
1145
+ "\r\n"
1146
+ ,.should_keep_alive= TRUE
1147
+ ,.message_complete_on_eof= FALSE
1148
+ ,.http_major= 1
1149
+ ,.http_minor= 0
1150
+ ,.status_code= 301
1151
+ ,.num_headers= 9
1152
+ ,.headers=
1153
+ { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" }
1154
+ , { "Server", "Apache/2.2.3 (Red Hat)" }
1155
+ , { "Cache-Control", "public" }
1156
+ , { "Pragma", "" }
1157
+ , { "Location", "http://www.bonjourmadame.fr/" }
1158
+ , { "Vary", "Accept-Encoding" }
1159
+ , { "Content-Length", "0" }
1160
+ , { "Content-Type", "text/html; charset=UTF-8" }
1161
+ , { "Connection", "keep-alive" }
1162
+ }
1163
+ ,.body= ""
1164
+ }
1165
+
1166
+ #define RES_FIELD_UNDERSCORE 9
1167
+ /* Should handle spaces in header fields */
1168
+ , {.name= "field underscore"
1169
+ ,.type= HTTP_RESPONSE
1170
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1171
+ "Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n"
1172
+ "Server: Apache\r\n"
1173
+ "Cache-Control: no-cache, must-revalidate\r\n"
1174
+ "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
1175
+ ".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n"
1176
+ "Vary: Accept-Encoding\r\n"
1177
+ "_eep-Alive: timeout=45\r\n" /* semantic value ignored */
1178
+ "_onnection: Keep-Alive\r\n" /* semantic value ignored */
1179
+ "Transfer-Encoding: chunked\r\n"
1180
+ "Content-Type: text/html\r\n"
1181
+ "Connection: close\r\n"
1182
+ "\r\n"
1183
+ "0\r\n\r\n"
1184
+ ,.should_keep_alive= FALSE
1185
+ ,.message_complete_on_eof= FALSE
1186
+ ,.http_major= 1
1187
+ ,.http_minor= 1
1188
+ ,.status_code= 200
1189
+ ,.num_headers= 11
1190
+ ,.headers=
1191
+ { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" }
1192
+ , { "Server", "Apache" }
1193
+ , { "Cache-Control", "no-cache, must-revalidate" }
1194
+ , { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" }
1195
+ , { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" }
1196
+ , { "Vary", "Accept-Encoding" }
1197
+ , { "_eep-Alive", "timeout=45" }
1198
+ , { "_onnection", "Keep-Alive" }
1199
+ , { "Transfer-Encoding", "chunked" }
1200
+ , { "Content-Type", "text/html" }
1201
+ , { "Connection", "close" }
1202
+ }
1203
+ ,.body= ""
1204
+ }
1205
+
1206
+ #define NON_ASCII_IN_STATUS_LINE 10
1207
+ /* Should handle non-ASCII in status line */
1208
+ , {.name= "non-ASCII in status line"
1209
+ ,.type= HTTP_RESPONSE
1210
+ ,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n"
1211
+ "Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n"
1212
+ "Content-Length: 0\r\n"
1213
+ "Connection: close\r\n"
1214
+ "\r\n"
1215
+ ,.should_keep_alive= FALSE
1216
+ ,.message_complete_on_eof= FALSE
1217
+ ,.http_major= 1
1218
+ ,.http_minor= 1
1219
+ ,.status_code= 500
1220
+ ,.num_headers= 3
1221
+ ,.headers=
1222
+ { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" }
1223
+ , { "Content-Length", "0" }
1224
+ , { "Connection", "close" }
1225
+ }
1226
+ ,.body= ""
1227
+ }
1228
+
1229
+ #define HTTP_VERSION_0_9 11
1230
+ /* Should handle HTTP/0.9 */
1231
+ , {.name= "http version 0.9"
1232
+ ,.type= HTTP_RESPONSE
1233
+ ,.raw= "HTTP/0.9 200 OK\r\n"
1234
+ "\r\n"
1235
+ ,.should_keep_alive= FALSE
1236
+ ,.message_complete_on_eof= TRUE
1237
+ ,.http_major= 0
1238
+ ,.http_minor= 9
1239
+ ,.status_code= 200
1240
+ ,.num_headers= 0
1241
+ ,.headers=
1242
+ {}
1243
+ ,.body= ""
1244
+ }
1245
+
1246
+ #define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12
1247
+ /* The client should wait for the server's EOF. That is, when neither
1248
+ * content-length nor transfer-encoding is specified, the end of body
1249
+ * is specified by the EOF.
1250
+ */
1251
+ , {.name= "neither content-length nor transfer-encoding response"
1252
+ ,.type= HTTP_RESPONSE
1253
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1254
+ "Content-Type: text/plain\r\n"
1255
+ "\r\n"
1256
+ "hello world"
1257
+ ,.should_keep_alive= FALSE
1258
+ ,.message_complete_on_eof= TRUE
1259
+ ,.http_major= 1
1260
+ ,.http_minor= 1
1261
+ ,.status_code= 200
1262
+ ,.num_headers= 1
1263
+ ,.headers=
1264
+ { { "Content-Type", "text/plain" }
1265
+ }
1266
+ ,.body= "hello world"
1267
+ }
1268
+
1269
+ #define NO_BODY_HTTP10_KA_200 13
1270
+ , {.name= "HTTP/1.0 with keep-alive and EOF-terminated 200 status"
1271
+ ,.type= HTTP_RESPONSE
1272
+ ,.raw= "HTTP/1.0 200 OK\r\n"
1273
+ "Connection: keep-alive\r\n"
1274
+ "\r\n"
1275
+ ,.should_keep_alive= FALSE
1276
+ ,.message_complete_on_eof= TRUE
1277
+ ,.http_major= 1
1278
+ ,.http_minor= 0
1279
+ ,.status_code= 200
1280
+ ,.num_headers= 1
1281
+ ,.headers=
1282
+ { { "Connection", "keep-alive" }
1283
+ }
1284
+ ,.body_size= 0
1285
+ ,.body= ""
1286
+ }
1287
+
1288
+ #define NO_BODY_HTTP10_KA_204 14
1289
+ , {.name= "HTTP/1.0 with keep-alive and a 204 status"
1290
+ ,.type= HTTP_RESPONSE
1291
+ ,.raw= "HTTP/1.0 204 No content\r\n"
1292
+ "Connection: keep-alive\r\n"
1293
+ "\r\n"
1294
+ ,.should_keep_alive= TRUE
1295
+ ,.message_complete_on_eof= FALSE
1296
+ ,.http_major= 1
1297
+ ,.http_minor= 0
1298
+ ,.status_code= 204
1299
+ ,.num_headers= 1
1300
+ ,.headers=
1301
+ { { "Connection", "keep-alive" }
1302
+ }
1303
+ ,.body_size= 0
1304
+ ,.body= ""
1305
+ }
1306
+
1307
+ #define NO_BODY_HTTP11_KA_200 15
1308
+ , {.name= "HTTP/1.1 with an EOF-terminated 200 status"
1309
+ ,.type= HTTP_RESPONSE
1310
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1311
+ "\r\n"
1312
+ ,.should_keep_alive= FALSE
1313
+ ,.message_complete_on_eof= TRUE
1314
+ ,.http_major= 1
1315
+ ,.http_minor= 1
1316
+ ,.status_code= 200
1317
+ ,.num_headers= 0
1318
+ ,.headers={}
1319
+ ,.body_size= 0
1320
+ ,.body= ""
1321
+ }
1322
+
1323
+ #define NO_BODY_HTTP11_KA_204 16
1324
+ , {.name= "HTTP/1.1 with a 204 status"
1325
+ ,.type= HTTP_RESPONSE
1326
+ ,.raw= "HTTP/1.1 204 No content\r\n"
1327
+ "\r\n"
1328
+ ,.should_keep_alive= TRUE
1329
+ ,.message_complete_on_eof= FALSE
1330
+ ,.http_major= 1
1331
+ ,.http_minor= 1
1332
+ ,.status_code= 204
1333
+ ,.num_headers= 0
1334
+ ,.headers={}
1335
+ ,.body_size= 0
1336
+ ,.body= ""
1337
+ }
1338
+
1339
+ #define NO_BODY_HTTP11_NOKA_204 17
1340
+ , {.name= "HTTP/1.1 with a 204 status and keep-alive disabled"
1341
+ ,.type= HTTP_RESPONSE
1342
+ ,.raw= "HTTP/1.1 204 No content\r\n"
1343
+ "Connection: close\r\n"
1344
+ "\r\n"
1345
+ ,.should_keep_alive= FALSE
1346
+ ,.message_complete_on_eof= FALSE
1347
+ ,.http_major= 1
1348
+ ,.http_minor= 1
1349
+ ,.status_code= 204
1350
+ ,.num_headers= 1
1351
+ ,.headers=
1352
+ { { "Connection", "close" }
1353
+ }
1354
+ ,.body_size= 0
1355
+ ,.body= ""
1356
+ }
1357
+
1358
+ #define NO_BODY_HTTP11_KA_CHUNKED_200 18
1359
+ , {.name= "HTTP/1.1 with chunked endocing and a 200 response"
1360
+ ,.type= HTTP_RESPONSE
1361
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1362
+ "Transfer-Encoding: chunked\r\n"
1363
+ "\r\n"
1364
+ "0\r\n"
1365
+ "\r\n"
1366
+ ,.should_keep_alive= TRUE
1367
+ ,.message_complete_on_eof= FALSE
1368
+ ,.http_major= 1
1369
+ ,.http_minor= 1
1370
+ ,.status_code= 200
1371
+ ,.num_headers= 1
1372
+ ,.headers=
1373
+ { { "Transfer-Encoding", "chunked" }
1374
+ }
1375
+ ,.body_size= 0
1376
+ ,.body= ""
1377
+ }
1378
+
1379
+ #if !HTTP_PARSER_STRICT
1380
+ #define SPACE_IN_FIELD_RES 19
1381
+ /* Should handle spaces in header fields */
1382
+ , {.name= "field space"
1383
+ ,.type= HTTP_RESPONSE
1384
+ ,.raw= "HTTP/1.1 200 OK\r\n"
1385
+ "Server: Microsoft-IIS/6.0\r\n"
1386
+ "X-Powered-By: ASP.NET\r\n"
1387
+ "en-US Content-Type: text/xml\r\n" /* this is the problem */
1388
+ "Content-Type: text/xml\r\n"
1389
+ "Content-Length: 16\r\n"
1390
+ "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
1391
+ "Connection: keep-alive\r\n"
1392
+ "\r\n"
1393
+ "<xml>hello</xml>" /* fake body */
1394
+ ,.should_keep_alive= TRUE
1395
+ ,.message_complete_on_eof= FALSE
1396
+ ,.http_major= 1
1397
+ ,.http_minor= 1
1398
+ ,.status_code= 200
1399
+ ,.num_headers= 7
1400
+ ,.headers=
1401
+ { { "Server", "Microsoft-IIS/6.0" }
1402
+ , { "X-Powered-By", "ASP.NET" }
1403
+ , { "en-US Content-Type", "text/xml" }
1404
+ , { "Content-Type", "text/xml" }
1405
+ , { "Content-Length", "16" }
1406
+ , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
1407
+ , { "Connection", "keep-alive" }
1408
+ }
1409
+ ,.body= "<xml>hello</xml>"
1410
+ }
1411
+ #endif /* !HTTP_PARSER_STRICT */
1412
+
1413
+ , {.name= NULL } /* sentinel */
1414
+ };
1415
+
1416
+ /* strnlen() is a POSIX.2008 addition. Can't rely on it being available so
1417
+ * define it ourselves.
1418
+ */
1419
+ size_t
1420
+ strnlen(const char *s, size_t maxlen)
1421
+ {
1422
+ const char *p;
1423
+
1424
+ p = memchr(s, '\0', maxlen);
1425
+ if (p == NULL)
1426
+ return maxlen;
1427
+
1428
+ return p - s;
1429
+ }
1430
+
1431
+ size_t
1432
+ strlncat(char *dst, size_t len, const char *src, size_t n)
1433
+ {
1434
+ size_t slen;
1435
+ size_t dlen;
1436
+ size_t rlen;
1437
+ size_t ncpy;
1438
+
1439
+ slen = strnlen(src, n);
1440
+ dlen = strnlen(dst, len);
1441
+
1442
+ if (dlen < len) {
1443
+ rlen = len - dlen;
1444
+ ncpy = slen < rlen ? slen : (rlen - 1);
1445
+ memcpy(dst + dlen, src, ncpy);
1446
+ dst[dlen + ncpy] = '\0';
1447
+ }
1448
+
1449
+ assert(len > slen + dlen);
1450
+ return slen + dlen;
1451
+ }
1452
+
1453
+ size_t
1454
+ strlcat(char *dst, const char *src, size_t len)
1455
+ {
1456
+ return strlncat(dst, len, src, (size_t) -1);
1457
+ }
1458
+
1459
+ size_t
1460
+ strlncpy(char *dst, size_t len, const char *src, size_t n)
1461
+ {
1462
+ size_t slen;
1463
+ size_t ncpy;
1464
+
1465
+ slen = strnlen(src, n);
1466
+
1467
+ if (len > 0) {
1468
+ ncpy = slen < len ? slen : (len - 1);
1469
+ memcpy(dst, src, ncpy);
1470
+ dst[ncpy] = '\0';
1471
+ }
1472
+
1473
+ assert(len > slen);
1474
+ return slen;
1475
+ }
1476
+
1477
+ size_t
1478
+ strlcpy(char *dst, const char *src, size_t len)
1479
+ {
1480
+ return strlncpy(dst, len, src, (size_t) -1);
1481
+ }
1482
+
1483
+ int
1484
+ request_url_cb (http_parser *p, const char *buf, size_t len)
1485
+ {
1486
+ assert(p == parser);
1487
+ strlncat(messages[num_messages].request_url,
1488
+ sizeof(messages[num_messages].request_url),
1489
+ buf,
1490
+ len);
1491
+ return 0;
1492
+ }
1493
+
1494
+ int
1495
+ status_complete_cb (http_parser *p) {
1496
+ assert(p == parser);
1497
+ p->data++;
1498
+ return 0;
1499
+ }
1500
+
1501
+ int
1502
+ header_field_cb (http_parser *p, const char *buf, size_t len)
1503
+ {
1504
+ assert(p == parser);
1505
+ struct message *m = &messages[num_messages];
1506
+
1507
+ if (m->last_header_element != FIELD)
1508
+ m->num_headers++;
1509
+
1510
+ strlncat(m->headers[m->num_headers-1][0],
1511
+ sizeof(m->headers[m->num_headers-1][0]),
1512
+ buf,
1513
+ len);
1514
+
1515
+ m->last_header_element = FIELD;
1516
+
1517
+ return 0;
1518
+ }
1519
+
1520
+ int
1521
+ header_value_cb (http_parser *p, const char *buf, size_t len)
1522
+ {
1523
+ assert(p == parser);
1524
+ struct message *m = &messages[num_messages];
1525
+
1526
+ strlncat(m->headers[m->num_headers-1][1],
1527
+ sizeof(m->headers[m->num_headers-1][1]),
1528
+ buf,
1529
+ len);
1530
+
1531
+ m->last_header_element = VALUE;
1532
+
1533
+ return 0;
1534
+ }
1535
+
1536
+ void
1537
+ check_body_is_final (const http_parser *p)
1538
+ {
1539
+ if (messages[num_messages].body_is_final) {
1540
+ fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
1541
+ "on last on_body callback call "
1542
+ "but it doesn't! ***\n\n");
1543
+ assert(0);
1544
+ abort();
1545
+ }
1546
+ messages[num_messages].body_is_final = http_body_is_final(p);
1547
+ }
1548
+
1549
+ int
1550
+ body_cb (http_parser *p, const char *buf, size_t len)
1551
+ {
1552
+ assert(p == parser);
1553
+ strlncat(messages[num_messages].body,
1554
+ sizeof(messages[num_messages].body),
1555
+ buf,
1556
+ len);
1557
+ messages[num_messages].body_size += len;
1558
+ check_body_is_final(p);
1559
+ // printf("body_cb: '%s'\n", requests[num_messages].body);
1560
+ return 0;
1561
+ }
1562
+
1563
+ int
1564
+ count_body_cb (http_parser *p, const char *buf, size_t len)
1565
+ {
1566
+ assert(p == parser);
1567
+ assert(buf);
1568
+ messages[num_messages].body_size += len;
1569
+ check_body_is_final(p);
1570
+ return 0;
1571
+ }
1572
+
1573
+ int
1574
+ message_begin_cb (http_parser *p)
1575
+ {
1576
+ assert(p == parser);
1577
+ messages[num_messages].message_begin_cb_called = TRUE;
1578
+ return 0;
1579
+ }
1580
+
1581
+ int
1582
+ headers_complete_cb (http_parser *p)
1583
+ {
1584
+ assert(p == parser);
1585
+ messages[num_messages].method = parser->method;
1586
+ messages[num_messages].status_code = parser->status_code;
1587
+ messages[num_messages].http_major = parser->http_major;
1588
+ messages[num_messages].http_minor = parser->http_minor;
1589
+ messages[num_messages].headers_complete_cb_called = TRUE;
1590
+ messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
1591
+ return 0;
1592
+ }
1593
+
1594
+ int
1595
+ message_complete_cb (http_parser *p)
1596
+ {
1597
+ assert(p == parser);
1598
+ if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser))
1599
+ {
1600
+ fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
1601
+ "value in both on_message_complete and on_headers_complete "
1602
+ "but it doesn't! ***\n\n");
1603
+ assert(0);
1604
+ abort();
1605
+ }
1606
+
1607
+ if (messages[num_messages].body_size &&
1608
+ http_body_is_final(p) &&
1609
+ !messages[num_messages].body_is_final)
1610
+ {
1611
+ fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
1612
+ "on last on_body callback call "
1613
+ "but it doesn't! ***\n\n");
1614
+ assert(0);
1615
+ abort();
1616
+ }
1617
+
1618
+ messages[num_messages].message_complete_cb_called = TRUE;
1619
+
1620
+ messages[num_messages].message_complete_on_eof = currently_parsing_eof;
1621
+
1622
+ num_messages++;
1623
+ return 0;
1624
+ }
1625
+
1626
+ /* These dontcall_* callbacks exist so that we can verify that when we're
1627
+ * paused, no additional callbacks are invoked */
1628
+ int
1629
+ dontcall_message_begin_cb (http_parser *p)
1630
+ {
1631
+ if (p) { } // gcc
1632
+ fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n");
1633
+ abort();
1634
+ }
1635
+
1636
+ int
1637
+ dontcall_header_field_cb (http_parser *p, const char *buf, size_t len)
1638
+ {
1639
+ if (p || buf || len) { } // gcc
1640
+ fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n");
1641
+ abort();
1642
+ }
1643
+
1644
+ int
1645
+ dontcall_header_value_cb (http_parser *p, const char *buf, size_t len)
1646
+ {
1647
+ if (p || buf || len) { } // gcc
1648
+ fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n");
1649
+ abort();
1650
+ }
1651
+
1652
+ int
1653
+ dontcall_request_url_cb (http_parser *p, const char *buf, size_t len)
1654
+ {
1655
+ if (p || buf || len) { } // gcc
1656
+ fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n");
1657
+ abort();
1658
+ }
1659
+
1660
+ int
1661
+ dontcall_body_cb (http_parser *p, const char *buf, size_t len)
1662
+ {
1663
+ if (p || buf || len) { } // gcc
1664
+ fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n");
1665
+ abort();
1666
+ }
1667
+
1668
+ int
1669
+ dontcall_headers_complete_cb (http_parser *p)
1670
+ {
1671
+ if (p) { } // gcc
1672
+ fprintf(stderr, "\n\n*** on_headers_complete() called on paused "
1673
+ "parser ***\n\n");
1674
+ abort();
1675
+ }
1676
+
1677
+ int
1678
+ dontcall_message_complete_cb (http_parser *p)
1679
+ {
1680
+ if (p) { } // gcc
1681
+ fprintf(stderr, "\n\n*** on_message_complete() called on paused "
1682
+ "parser ***\n\n");
1683
+ abort();
1684
+ }
1685
+
1686
+ static http_parser_settings settings_dontcall =
1687
+ {.on_message_begin = dontcall_message_begin_cb
1688
+ ,.on_header_field = dontcall_header_field_cb
1689
+ ,.on_header_value = dontcall_header_value_cb
1690
+ ,.on_url = dontcall_request_url_cb
1691
+ ,.on_body = dontcall_body_cb
1692
+ ,.on_headers_complete = dontcall_headers_complete_cb
1693
+ ,.on_message_complete = dontcall_message_complete_cb
1694
+ };
1695
+
1696
+ /* These pause_* callbacks always pause the parser and just invoke the regular
1697
+ * callback that tracks content. Before returning, we overwrite the parser
1698
+ * settings to point to the _dontcall variety so that we can verify that
1699
+ * the pause actually did, you know, pause. */
1700
+ int
1701
+ pause_message_begin_cb (http_parser *p)
1702
+ {
1703
+ http_parser_pause(p, 1);
1704
+ *current_pause_parser = settings_dontcall;
1705
+ return message_begin_cb(p);
1706
+ }
1707
+
1708
+ int
1709
+ pause_header_field_cb (http_parser *p, const char *buf, size_t len)
1710
+ {
1711
+ http_parser_pause(p, 1);
1712
+ *current_pause_parser = settings_dontcall;
1713
+ return header_field_cb(p, buf, len);
1714
+ }
1715
+
1716
+ int
1717
+ pause_header_value_cb (http_parser *p, const char *buf, size_t len)
1718
+ {
1719
+ http_parser_pause(p, 1);
1720
+ *current_pause_parser = settings_dontcall;
1721
+ return header_value_cb(p, buf, len);
1722
+ }
1723
+
1724
+ int
1725
+ pause_request_url_cb (http_parser *p, const char *buf, size_t len)
1726
+ {
1727
+ http_parser_pause(p, 1);
1728
+ *current_pause_parser = settings_dontcall;
1729
+ return request_url_cb(p, buf, len);
1730
+ }
1731
+
1732
+ int
1733
+ pause_body_cb (http_parser *p, const char *buf, size_t len)
1734
+ {
1735
+ http_parser_pause(p, 1);
1736
+ *current_pause_parser = settings_dontcall;
1737
+ return body_cb(p, buf, len);
1738
+ }
1739
+
1740
+ int
1741
+ pause_headers_complete_cb (http_parser *p)
1742
+ {
1743
+ http_parser_pause(p, 1);
1744
+ *current_pause_parser = settings_dontcall;
1745
+ return headers_complete_cb(p);
1746
+ }
1747
+
1748
+ int
1749
+ pause_message_complete_cb (http_parser *p)
1750
+ {
1751
+ http_parser_pause(p, 1);
1752
+ *current_pause_parser = settings_dontcall;
1753
+ return message_complete_cb(p);
1754
+ }
1755
+
1756
+ static http_parser_settings settings_pause =
1757
+ {.on_message_begin = pause_message_begin_cb
1758
+ ,.on_header_field = pause_header_field_cb
1759
+ ,.on_header_value = pause_header_value_cb
1760
+ ,.on_url = pause_request_url_cb
1761
+ ,.on_body = pause_body_cb
1762
+ ,.on_headers_complete = pause_headers_complete_cb
1763
+ ,.on_message_complete = pause_message_complete_cb
1764
+ };
1765
+
1766
+ static http_parser_settings settings =
1767
+ {.on_message_begin = message_begin_cb
1768
+ ,.on_header_field = header_field_cb
1769
+ ,.on_header_value = header_value_cb
1770
+ ,.on_url = request_url_cb
1771
+ ,.on_body = body_cb
1772
+ ,.on_headers_complete = headers_complete_cb
1773
+ ,.on_message_complete = message_complete_cb
1774
+ };
1775
+
1776
+ static http_parser_settings settings_count_body =
1777
+ {.on_message_begin = message_begin_cb
1778
+ ,.on_header_field = header_field_cb
1779
+ ,.on_header_value = header_value_cb
1780
+ ,.on_url = request_url_cb
1781
+ ,.on_body = count_body_cb
1782
+ ,.on_headers_complete = headers_complete_cb
1783
+ ,.on_message_complete = message_complete_cb
1784
+ };
1785
+
1786
+ static http_parser_settings settings_null =
1787
+ {.on_message_begin = 0
1788
+ ,.on_header_field = 0
1789
+ ,.on_header_value = 0
1790
+ ,.on_url = 0
1791
+ ,.on_body = 0
1792
+ ,.on_headers_complete = 0
1793
+ ,.on_message_complete = 0
1794
+ };
1795
+
1796
+ void
1797
+ parser_init (enum http_parser_type type)
1798
+ {
1799
+ num_messages = 0;
1800
+
1801
+ assert(parser == NULL);
1802
+
1803
+ parser = malloc(sizeof(http_parser));
1804
+
1805
+ http_parser_init(parser, type);
1806
+
1807
+ memset(&messages, 0, sizeof messages);
1808
+
1809
+ }
1810
+
1811
+ void
1812
+ parser_free ()
1813
+ {
1814
+ assert(parser);
1815
+ free(parser);
1816
+ parser = NULL;
1817
+ }
1818
+
1819
+ size_t parse (const char *buf, size_t len)
1820
+ {
1821
+ size_t nparsed;
1822
+ currently_parsing_eof = (len == 0);
1823
+ nparsed = http_parser_execute(parser, &settings, buf, len);
1824
+ return nparsed;
1825
+ }
1826
+
1827
+ size_t parse_count_body (const char *buf, size_t len)
1828
+ {
1829
+ size_t nparsed;
1830
+ currently_parsing_eof = (len == 0);
1831
+ nparsed = http_parser_execute(parser, &settings_count_body, buf, len);
1832
+ return nparsed;
1833
+ }
1834
+
1835
+ size_t parse_pause (const char *buf, size_t len)
1836
+ {
1837
+ size_t nparsed;
1838
+ http_parser_settings s = settings_pause;
1839
+
1840
+ currently_parsing_eof = (len == 0);
1841
+ current_pause_parser = &s;
1842
+ nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
1843
+ return nparsed;
1844
+ }
1845
+
1846
+ static inline int
1847
+ check_str_eq (const struct message *m,
1848
+ const char *prop,
1849
+ const char *expected,
1850
+ const char *found) {
1851
+ if ((expected == NULL) != (found == NULL)) {
1852
+ printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1853
+ printf("expected %s\n", (expected == NULL) ? "NULL" : expected);
1854
+ printf(" found %s\n", (found == NULL) ? "NULL" : found);
1855
+ return 0;
1856
+ }
1857
+ if (expected != NULL && 0 != strcmp(expected, found)) {
1858
+ printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1859
+ printf("expected '%s'\n", expected);
1860
+ printf(" found '%s'\n", found);
1861
+ return 0;
1862
+ }
1863
+ return 1;
1864
+ }
1865
+
1866
+ static inline int
1867
+ check_num_eq (const struct message *m,
1868
+ const char *prop,
1869
+ int expected,
1870
+ int found) {
1871
+ if (expected != found) {
1872
+ printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1873
+ printf("expected %d\n", expected);
1874
+ printf(" found %d\n", found);
1875
+ return 0;
1876
+ }
1877
+ return 1;
1878
+ }
1879
+
1880
+ #define MESSAGE_CHECK_STR_EQ(expected, found, prop) \
1881
+ if (!check_str_eq(expected, #prop, expected->prop, found->prop)) return 0
1882
+
1883
+ #define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
1884
+ if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
1885
+
1886
+ #define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn) \
1887
+ do { \
1888
+ char ubuf[256]; \
1889
+ \
1890
+ if ((u)->field_set & (1 << (fn))) { \
1891
+ memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off, \
1892
+ (u)->field_data[(fn)].len); \
1893
+ ubuf[(u)->field_data[(fn)].len] = '\0'; \
1894
+ } else { \
1895
+ ubuf[0] = '\0'; \
1896
+ } \
1897
+ \
1898
+ check_str_eq(expected, #prop, expected->prop, ubuf); \
1899
+ } while(0)
1900
+
1901
+ int
1902
+ message_eq (int index, const struct message *expected)
1903
+ {
1904
+ int i;
1905
+ struct message *m = &messages[index];
1906
+
1907
+ MESSAGE_CHECK_NUM_EQ(expected, m, http_major);
1908
+ MESSAGE_CHECK_NUM_EQ(expected, m, http_minor);
1909
+
1910
+ if (expected->type == HTTP_REQUEST) {
1911
+ MESSAGE_CHECK_NUM_EQ(expected, m, method);
1912
+ } else {
1913
+ MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
1914
+ }
1915
+
1916
+ MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);
1917
+ MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof);
1918
+
1919
+ assert(m->message_begin_cb_called);
1920
+ assert(m->headers_complete_cb_called);
1921
+ assert(m->message_complete_cb_called);
1922
+
1923
+
1924
+ MESSAGE_CHECK_STR_EQ(expected, m, request_url);
1925
+
1926
+ /* Check URL components; we can't do this w/ CONNECT since it doesn't
1927
+ * send us a well-formed URL.
1928
+ */
1929
+ if (*m->request_url && m->method != HTTP_CONNECT) {
1930
+ struct http_parser_url u;
1931
+
1932
+ if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) {
1933
+ fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n",
1934
+ m->request_url);
1935
+ abort();
1936
+ }
1937
+
1938
+ if (expected->host) {
1939
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST);
1940
+ }
1941
+
1942
+ if (expected->userinfo) {
1943
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO);
1944
+ }
1945
+
1946
+ m->port = (u.field_set & (1 << UF_PORT)) ?
1947
+ u.port : 0;
1948
+
1949
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY);
1950
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT);
1951
+ MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH);
1952
+ MESSAGE_CHECK_NUM_EQ(expected, m, port);
1953
+ }
1954
+
1955
+ if (expected->body_size) {
1956
+ MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
1957
+ } else {
1958
+ MESSAGE_CHECK_STR_EQ(expected, m, body);
1959
+ }
1960
+
1961
+ MESSAGE_CHECK_NUM_EQ(expected, m, num_headers);
1962
+
1963
+ int r;
1964
+ for (i = 0; i < m->num_headers; i++) {
1965
+ r = check_str_eq(expected, "header field", expected->headers[i][0], m->headers[i][0]);
1966
+ if (!r) return 0;
1967
+ r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]);
1968
+ if (!r) return 0;
1969
+ }
1970
+
1971
+ MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
1972
+
1973
+ return 1;
1974
+ }
1975
+
1976
+ /* Given a sequence of varargs messages, return the number of them that the
1977
+ * parser should successfully parse, taking into account that upgraded
1978
+ * messages prevent all subsequent messages from being parsed.
1979
+ */
1980
+ size_t
1981
+ count_parsed_messages(const size_t nmsgs, ...) {
1982
+ size_t i;
1983
+ va_list ap;
1984
+
1985
+ va_start(ap, nmsgs);
1986
+
1987
+ for (i = 0; i < nmsgs; i++) {
1988
+ struct message *m = va_arg(ap, struct message *);
1989
+
1990
+ if (m->upgrade) {
1991
+ va_end(ap);
1992
+ return i + 1;
1993
+ }
1994
+ }
1995
+
1996
+ va_end(ap);
1997
+ return nmsgs;
1998
+ }
1999
+
2000
+ /* Given a sequence of bytes and the number of these that we were able to
2001
+ * parse, verify that upgrade bodies are correct.
2002
+ */
2003
+ void
2004
+ upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
2005
+ va_list ap;
2006
+ size_t i;
2007
+ size_t off = 0;
2008
+
2009
+ va_start(ap, nmsgs);
2010
+
2011
+ for (i = 0; i < nmsgs; i++) {
2012
+ struct message *m = va_arg(ap, struct message *);
2013
+
2014
+ off += strlen(m->raw);
2015
+
2016
+ if (m->upgrade) {
2017
+ off -= strlen(m->upgrade);
2018
+
2019
+ /* Check the portion of the response after its specified upgrade */
2020
+ if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
2021
+ abort();
2022
+ }
2023
+
2024
+ /* Fix up the response so that message_eq() will verify the beginning
2025
+ * of the upgrade */
2026
+ *(body + nread + strlen(m->upgrade)) = '\0';
2027
+ messages[num_messages -1 ].upgrade = body + nread;
2028
+
2029
+ va_end(ap);
2030
+ return;
2031
+ }
2032
+ }
2033
+
2034
+ va_end(ap);
2035
+ printf("\n\n*** Error: expected a message with upgrade ***\n");
2036
+
2037
+ abort();
2038
+ }
2039
+
2040
+ static void
2041
+ print_error (const char *raw, size_t error_location)
2042
+ {
2043
+ fprintf(stderr, "\n*** %s ***\n\n",
2044
+ http_errno_description(HTTP_PARSER_ERRNO(parser)));
2045
+
2046
+ int this_line = 0, char_len = 0;
2047
+ size_t i, j, len = strlen(raw), error_location_line = 0;
2048
+ for (i = 0; i < len; i++) {
2049
+ if (i == error_location) this_line = 1;
2050
+ switch (raw[i]) {
2051
+ case '\r':
2052
+ char_len = 2;
2053
+ fprintf(stderr, "\\r");
2054
+ break;
2055
+
2056
+ case '\n':
2057
+ char_len = 2;
2058
+ fprintf(stderr, "\\n\n");
2059
+
2060
+ if (this_line) goto print;
2061
+
2062
+ error_location_line = 0;
2063
+ continue;
2064
+
2065
+ default:
2066
+ char_len = 1;
2067
+ fputc(raw[i], stderr);
2068
+ break;
2069
+ }
2070
+ if (!this_line) error_location_line += char_len;
2071
+ }
2072
+
2073
+ fprintf(stderr, "[eof]\n");
2074
+
2075
+ print:
2076
+ for (j = 0; j < error_location_line; j++) {
2077
+ fputc(' ', stderr);
2078
+ }
2079
+ fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
2080
+ }
2081
+
2082
+ void
2083
+ test_preserve_data (void)
2084
+ {
2085
+ char my_data[] = "application-specific data";
2086
+ http_parser parser;
2087
+ parser.data = my_data;
2088
+ http_parser_init(&parser, HTTP_REQUEST);
2089
+ if (parser.data != my_data) {
2090
+ printf("\n*** parser.data not preserved accross http_parser_init ***\n\n");
2091
+ abort();
2092
+ }
2093
+ }
2094
+
2095
+ struct url_test {
2096
+ const char *name;
2097
+ const char *url;
2098
+ int is_connect;
2099
+ struct http_parser_url u;
2100
+ int rv;
2101
+ };
2102
+
2103
+ const struct url_test url_tests[] =
2104
+ { {.name="proxy request"
2105
+ ,.url="http://hostname/"
2106
+ ,.is_connect=0
2107
+ ,.u=
2108
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2109
+ ,.port=0
2110
+ ,.field_data=
2111
+ {{ 0, 4 } /* UF_SCHEMA */
2112
+ ,{ 7, 8 } /* UF_HOST */
2113
+ ,{ 0, 0 } /* UF_PORT */
2114
+ ,{ 15, 1 } /* UF_PATH */
2115
+ ,{ 0, 0 } /* UF_QUERY */
2116
+ ,{ 0, 0 } /* UF_FRAGMENT */
2117
+ ,{ 0, 0 } /* UF_USERINFO */
2118
+ }
2119
+ }
2120
+ ,.rv=0
2121
+ }
2122
+
2123
+ , {.name="proxy request with port"
2124
+ ,.url="http://hostname:444/"
2125
+ ,.is_connect=0
2126
+ ,.u=
2127
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
2128
+ ,.port=444
2129
+ ,.field_data=
2130
+ {{ 0, 4 } /* UF_SCHEMA */
2131
+ ,{ 7, 8 } /* UF_HOST */
2132
+ ,{ 16, 3 } /* UF_PORT */
2133
+ ,{ 19, 1 } /* UF_PATH */
2134
+ ,{ 0, 0 } /* UF_QUERY */
2135
+ ,{ 0, 0 } /* UF_FRAGMENT */
2136
+ ,{ 0, 0 } /* UF_USERINFO */
2137
+ }
2138
+ }
2139
+ ,.rv=0
2140
+ }
2141
+
2142
+ , {.name="CONNECT request"
2143
+ ,.url="hostname:443"
2144
+ ,.is_connect=1
2145
+ ,.u=
2146
+ {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
2147
+ ,.port=443
2148
+ ,.field_data=
2149
+ {{ 0, 0 } /* UF_SCHEMA */
2150
+ ,{ 0, 8 } /* UF_HOST */
2151
+ ,{ 9, 3 } /* UF_PORT */
2152
+ ,{ 0, 0 } /* UF_PATH */
2153
+ ,{ 0, 0 } /* UF_QUERY */
2154
+ ,{ 0, 0 } /* UF_FRAGMENT */
2155
+ ,{ 0, 0 } /* UF_USERINFO */
2156
+ }
2157
+ }
2158
+ ,.rv=0
2159
+ }
2160
+
2161
+ , {.name="CONNECT request but not connect"
2162
+ ,.url="hostname:443"
2163
+ ,.is_connect=0
2164
+ ,.rv=1
2165
+ }
2166
+
2167
+ , {.name="proxy ipv6 request"
2168
+ ,.url="http://[1:2::3:4]/"
2169
+ ,.is_connect=0
2170
+ ,.u=
2171
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2172
+ ,.port=0
2173
+ ,.field_data=
2174
+ {{ 0, 4 } /* UF_SCHEMA */
2175
+ ,{ 8, 8 } /* UF_HOST */
2176
+ ,{ 0, 0 } /* UF_PORT */
2177
+ ,{ 17, 1 } /* UF_PATH */
2178
+ ,{ 0, 0 } /* UF_QUERY */
2179
+ ,{ 0, 0 } /* UF_FRAGMENT */
2180
+ ,{ 0, 0 } /* UF_USERINFO */
2181
+ }
2182
+ }
2183
+ ,.rv=0
2184
+ }
2185
+
2186
+ , {.name="proxy ipv6 request with port"
2187
+ ,.url="http://[1:2::3:4]:67/"
2188
+ ,.is_connect=0
2189
+ ,.u=
2190
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
2191
+ ,.port=67
2192
+ ,.field_data=
2193
+ {{ 0, 4 } /* UF_SCHEMA */
2194
+ ,{ 8, 8 } /* UF_HOST */
2195
+ ,{ 18, 2 } /* UF_PORT */
2196
+ ,{ 20, 1 } /* UF_PATH */
2197
+ ,{ 0, 0 } /* UF_QUERY */
2198
+ ,{ 0, 0 } /* UF_FRAGMENT */
2199
+ ,{ 0, 0 } /* UF_USERINFO */
2200
+ }
2201
+ }
2202
+ ,.rv=0
2203
+ }
2204
+
2205
+ , {.name="CONNECT ipv6 address"
2206
+ ,.url="[1:2::3:4]:443"
2207
+ ,.is_connect=1
2208
+ ,.u=
2209
+ {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
2210
+ ,.port=443
2211
+ ,.field_data=
2212
+ {{ 0, 0 } /* UF_SCHEMA */
2213
+ ,{ 1, 8 } /* UF_HOST */
2214
+ ,{ 11, 3 } /* UF_PORT */
2215
+ ,{ 0, 0 } /* UF_PATH */
2216
+ ,{ 0, 0 } /* UF_QUERY */
2217
+ ,{ 0, 0 } /* UF_FRAGMENT */
2218
+ ,{ 0, 0 } /* UF_USERINFO */
2219
+ }
2220
+ }
2221
+ ,.rv=0
2222
+ }
2223
+
2224
+ , {.name="ipv4 in ipv6 address"
2225
+ ,.url="http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/"
2226
+ ,.is_connect=0
2227
+ ,.u=
2228
+ {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
2229
+ ,.port=0
2230
+ ,.field_data=
2231
+ {{ 0, 4 } /* UF_SCHEMA */
2232
+ ,{ 8, 37 } /* UF_HOST */
2233
+ ,{ 0, 0 } /* UF_PORT */
2234
+ ,{ 46, 1 } /* UF_PATH */
2235
+ ,{ 0, 0 } /* UF_QUERY */
2236
+ ,{ 0, 0 } /* UF_FRAGMENT */
2237
+ ,{ 0, 0 } /* UF_USERINFO */
2238
+ }
2239
+ }
2240
+ ,.rv=0
2241
+ }
2242
+
2243
+ , {.name="extra ? in query string"
2244
+ ,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css,"
2245
+ "fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css,"
2246
+ "fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css"
2247
+ ,.is_connect=0
2248
+ ,.u=
2249
+ {.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY)
2250
+ ,.port=0
2251
+ ,.field_data=
2252
+ {{ 0, 4 } /* UF_SCHEMA */
2253
+ ,{ 7, 10 } /* UF_HOST */
2254
+ ,{ 0, 0 } /* UF_PORT */
2255
+ ,{ 17, 12 } /* UF_PATH */
2256
+ ,{ 30,187 } /* UF_QUERY */
2257
+ ,{ 0, 0 } /* UF_FRAGMENT */
2258
+ ,{ 0, 0 } /* UF_USERINFO */
2259
+ }
2260
+ }
2261
+ ,.rv=0
2262
+ }
2263
+
2264
+ , {.name="space URL encoded"
2265
+ ,.url="/toto.html?toto=a%20b"
2266
+ ,.is_connect=0
2267
+ ,.u=
2268
+ {.field_set= (1<<UF_PATH) | (1<<UF_QUERY)
2269
+ ,.port=0
2270
+ ,.field_data=
2271
+ {{ 0, 0 } /* UF_SCHEMA */
2272
+ ,{ 0, 0 } /* UF_HOST */
2273
+ ,{ 0, 0 } /* UF_PORT */
2274
+ ,{ 0, 10 } /* UF_PATH */
2275
+ ,{ 11, 10 } /* UF_QUERY */
2276
+ ,{ 0, 0 } /* UF_FRAGMENT */
2277
+ ,{ 0, 0 } /* UF_USERINFO */
2278
+ }
2279
+ }
2280
+ ,.rv=0
2281
+ }
2282
+
2283
+
2284
+ , {.name="URL fragment"
2285
+ ,.url="/toto.html#titi"
2286
+ ,.is_connect=0
2287
+ ,.u=
2288
+ {.field_set= (1<<UF_PATH) | (1<<UF_FRAGMENT)
2289
+ ,.port=0
2290
+ ,.field_data=
2291
+ {{ 0, 0 } /* UF_SCHEMA */
2292
+ ,{ 0, 0 } /* UF_HOST */
2293
+ ,{ 0, 0 } /* UF_PORT */
2294
+ ,{ 0, 10 } /* UF_PATH */
2295
+ ,{ 0, 0 } /* UF_QUERY */
2296
+ ,{ 11, 4 } /* UF_FRAGMENT */
2297
+ ,{ 0, 0 } /* UF_USERINFO */
2298
+ }
2299
+ }
2300
+ ,.rv=0
2301
+ }
2302
+
2303
+ , {.name="complex URL fragment"
2304
+ ,.url="http://www.webmasterworld.com/r.cgi?f=21&d=8405&url="
2305
+ "http://www.example.com/index.html?foo=bar&hello=world#midpage"
2306
+ ,.is_connect=0
2307
+ ,.u=
2308
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY) |\
2309
+ (1<<UF_FRAGMENT)
2310
+ ,.port=0
2311
+ ,.field_data=
2312
+ {{ 0, 4 } /* UF_SCHEMA */
2313
+ ,{ 7, 22 } /* UF_HOST */
2314
+ ,{ 0, 0 } /* UF_PORT */
2315
+ ,{ 29, 6 } /* UF_PATH */
2316
+ ,{ 36, 69 } /* UF_QUERY */
2317
+ ,{106, 7 } /* UF_FRAGMENT */
2318
+ ,{ 0, 0 } /* UF_USERINFO */
2319
+ }
2320
+ }
2321
+ ,.rv=0
2322
+ }
2323
+
2324
+ , {.name="complex URL from node js url parser doc"
2325
+ ,.url="http://host.com:8080/p/a/t/h?query=string#hash"
2326
+ ,.is_connect=0
2327
+ ,.u=
2328
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
2329
+ (1<<UF_QUERY) | (1<<UF_FRAGMENT)
2330
+ ,.port=8080
2331
+ ,.field_data=
2332
+ {{ 0, 4 } /* UF_SCHEMA */
2333
+ ,{ 7, 8 } /* UF_HOST */
2334
+ ,{ 16, 4 } /* UF_PORT */
2335
+ ,{ 20, 8 } /* UF_PATH */
2336
+ ,{ 29, 12 } /* UF_QUERY */
2337
+ ,{ 42, 4 } /* UF_FRAGMENT */
2338
+ ,{ 0, 0 } /* UF_USERINFO */
2339
+ }
2340
+ }
2341
+ ,.rv=0
2342
+ }
2343
+
2344
+ , {.name="complex URL with basic auth from node js url parser doc"
2345
+ ,.url="http://a:b@host.com:8080/p/a/t/h?query=string#hash"
2346
+ ,.is_connect=0
2347
+ ,.u=
2348
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
2349
+ (1<<UF_QUERY) | (1<<UF_FRAGMENT) | (1<<UF_USERINFO)
2350
+ ,.port=8080
2351
+ ,.field_data=
2352
+ {{ 0, 4 } /* UF_SCHEMA */
2353
+ ,{ 11, 8 } /* UF_HOST */
2354
+ ,{ 20, 4 } /* UF_PORT */
2355
+ ,{ 24, 8 } /* UF_PATH */
2356
+ ,{ 33, 12 } /* UF_QUERY */
2357
+ ,{ 46, 4 } /* UF_FRAGMENT */
2358
+ ,{ 7, 3 } /* UF_USERINFO */
2359
+ }
2360
+ }
2361
+ ,.rv=0
2362
+ }
2363
+
2364
+ , {.name="double @"
2365
+ ,.url="http://a:b@@hostname:443/"
2366
+ ,.is_connect=0
2367
+ ,.rv=1
2368
+ }
2369
+
2370
+ , {.name="proxy empty host"
2371
+ ,.url="http://:443/"
2372
+ ,.is_connect=0
2373
+ ,.rv=1
2374
+ }
2375
+
2376
+ , {.name="proxy empty port"
2377
+ ,.url="http://hostname:/"
2378
+ ,.is_connect=0
2379
+ ,.rv=1
2380
+ }
2381
+
2382
+ , {.name="CONNECT with basic auth"
2383
+ ,.url="a:b@hostname:443"
2384
+ ,.is_connect=1
2385
+ ,.rv=1
2386
+ }
2387
+
2388
+ , {.name="CONNECT empty host"
2389
+ ,.url=":443"
2390
+ ,.is_connect=1
2391
+ ,.rv=1
2392
+ }
2393
+
2394
+ , {.name="CONNECT empty port"
2395
+ ,.url="hostname:"
2396
+ ,.is_connect=1
2397
+ ,.rv=1
2398
+ }
2399
+
2400
+ , {.name="CONNECT with extra bits"
2401
+ ,.url="hostname:443/"
2402
+ ,.is_connect=1
2403
+ ,.rv=1
2404
+ }
2405
+
2406
+ , {.name="space in URL"
2407
+ ,.url="/foo bar/"
2408
+ ,.rv=1 /* s_dead */
2409
+ }
2410
+
2411
+ , {.name="proxy basic auth with space url encoded"
2412
+ ,.url="http://a%20:b@host.com/"
2413
+ ,.is_connect=0
2414
+ ,.u=
2415
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2416
+ ,.port=0
2417
+ ,.field_data=
2418
+ {{ 0, 4 } /* UF_SCHEMA */
2419
+ ,{ 14, 8 } /* UF_HOST */
2420
+ ,{ 0, 0 } /* UF_PORT */
2421
+ ,{ 22, 1 } /* UF_PATH */
2422
+ ,{ 0, 0 } /* UF_QUERY */
2423
+ ,{ 0, 0 } /* UF_FRAGMENT */
2424
+ ,{ 7, 6 } /* UF_USERINFO */
2425
+ }
2426
+ }
2427
+ ,.rv=0
2428
+ }
2429
+
2430
+ , {.name="carriage return in URL"
2431
+ ,.url="/foo\rbar/"
2432
+ ,.rv=1 /* s_dead */
2433
+ }
2434
+
2435
+ , {.name="proxy double : in URL"
2436
+ ,.url="http://hostname::443/"
2437
+ ,.rv=1 /* s_dead */
2438
+ }
2439
+
2440
+ , {.name="proxy basic auth with double :"
2441
+ ,.url="http://a::b@host.com/"
2442
+ ,.is_connect=0
2443
+ ,.u=
2444
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2445
+ ,.port=0
2446
+ ,.field_data=
2447
+ {{ 0, 4 } /* UF_SCHEMA */
2448
+ ,{ 12, 8 } /* UF_HOST */
2449
+ ,{ 0, 0 } /* UF_PORT */
2450
+ ,{ 20, 1 } /* UF_PATH */
2451
+ ,{ 0, 0 } /* UF_QUERY */
2452
+ ,{ 0, 0 } /* UF_FRAGMENT */
2453
+ ,{ 7, 4 } /* UF_USERINFO */
2454
+ }
2455
+ }
2456
+ ,.rv=0
2457
+ }
2458
+
2459
+ , {.name="line feed in URL"
2460
+ ,.url="/foo\nbar/"
2461
+ ,.rv=1 /* s_dead */
2462
+ }
2463
+
2464
+ , {.name="proxy empty basic auth"
2465
+ ,.url="http://@hostname/fo"
2466
+ ,.u=
2467
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
2468
+ ,.port=0
2469
+ ,.field_data=
2470
+ {{ 0, 4 } /* UF_SCHEMA */
2471
+ ,{ 8, 8 } /* UF_HOST */
2472
+ ,{ 0, 0 } /* UF_PORT */
2473
+ ,{ 16, 3 } /* UF_PATH */
2474
+ ,{ 0, 0 } /* UF_QUERY */
2475
+ ,{ 0, 0 } /* UF_FRAGMENT */
2476
+ ,{ 0, 0 } /* UF_USERINFO */
2477
+ }
2478
+ }
2479
+ ,.rv=0
2480
+ }
2481
+ , {.name="proxy line feed in hostname"
2482
+ ,.url="http://host\name/fo"
2483
+ ,.rv=1 /* s_dead */
2484
+ }
2485
+
2486
+ , {.name="proxy % in hostname"
2487
+ ,.url="http://host%name/fo"
2488
+ ,.rv=1 /* s_dead */
2489
+ }
2490
+
2491
+ , {.name="proxy ; in hostname"
2492
+ ,.url="http://host;ame/fo"
2493
+ ,.rv=1 /* s_dead */
2494
+ }
2495
+
2496
+ , {.name="proxy basic auth with unreservedchars"
2497
+ ,.url="http://a!;-_!=+$@host.com/"
2498
+ ,.is_connect=0
2499
+ ,.u=
2500
+ {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
2501
+ ,.port=0
2502
+ ,.field_data=
2503
+ {{ 0, 4 } /* UF_SCHEMA */
2504
+ ,{ 17, 8 } /* UF_HOST */
2505
+ ,{ 0, 0 } /* UF_PORT */
2506
+ ,{ 25, 1 } /* UF_PATH */
2507
+ ,{ 0, 0 } /* UF_QUERY */
2508
+ ,{ 0, 0 } /* UF_FRAGMENT */
2509
+ ,{ 7, 9 } /* UF_USERINFO */
2510
+ }
2511
+ }
2512
+ ,.rv=0
2513
+ }
2514
+
2515
+ , {.name="proxy only empty basic auth"
2516
+ ,.url="http://@/fo"
2517
+ ,.rv=1 /* s_dead */
2518
+ }
2519
+
2520
+ , {.name="proxy only basic auth"
2521
+ ,.url="http://toto@/fo"
2522
+ ,.rv=1 /* s_dead */
2523
+ }
2524
+
2525
+ , {.name="proxy emtpy hostname"
2526
+ ,.url="http:///fo"
2527
+ ,.rv=1 /* s_dead */
2528
+ }
2529
+
2530
+ , {.name="proxy = in URL"
2531
+ ,.url="http://host=ame/fo"
2532
+ ,.rv=1 /* s_dead */
2533
+ }
2534
+
2535
+ #if HTTP_PARSER_STRICT
2536
+
2537
+ , {.name="tab in URL"
2538
+ ,.url="/foo\tbar/"
2539
+ ,.rv=1 /* s_dead */
2540
+ }
2541
+
2542
+ , {.name="form feed in URL"
2543
+ ,.url="/foo\fbar/"
2544
+ ,.rv=1 /* s_dead */
2545
+ }
2546
+
2547
+ #else /* !HTTP_PARSER_STRICT */
2548
+
2549
+ , {.name="tab in URL"
2550
+ ,.url="/foo\tbar/"
2551
+ ,.u=
2552
+ {.field_set=(1 << UF_PATH)
2553
+ ,.field_data=
2554
+ {{ 0, 0 } /* UF_SCHEMA */
2555
+ ,{ 0, 0 } /* UF_HOST */
2556
+ ,{ 0, 0 } /* UF_PORT */
2557
+ ,{ 0, 9 } /* UF_PATH */
2558
+ ,{ 0, 0 } /* UF_QUERY */
2559
+ ,{ 0, 0 } /* UF_FRAGMENT */
2560
+ ,{ 0, 0 } /* UF_USERINFO */
2561
+ }
2562
+ }
2563
+ ,.rv=0
2564
+ }
2565
+
2566
+ , {.name="form feed in URL"
2567
+ ,.url="/foo\fbar/"
2568
+ ,.u=
2569
+ {.field_set=(1 << UF_PATH)
2570
+ ,.field_data=
2571
+ {{ 0, 0 } /* UF_SCHEMA */
2572
+ ,{ 0, 0 } /* UF_HOST */
2573
+ ,{ 0, 0 } /* UF_PORT */
2574
+ ,{ 0, 9 } /* UF_PATH */
2575
+ ,{ 0, 0 } /* UF_QUERY */
2576
+ ,{ 0, 0 } /* UF_FRAGMENT */
2577
+ ,{ 0, 0 } /* UF_USERINFO */
2578
+ }
2579
+ }
2580
+ ,.rv=0
2581
+ }
2582
+ #endif
2583
+ };
2584
+
2585
+ void
2586
+ dump_url (const char *url, const struct http_parser_url *u)
2587
+ {
2588
+ unsigned int i;
2589
+
2590
+ printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
2591
+ for (i = 0; i < UF_MAX; i++) {
2592
+ if ((u->field_set & (1 << i)) == 0) {
2593
+ printf("\tfield_data[%u]: unset\n", i);
2594
+ continue;
2595
+ }
2596
+
2597
+ printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n\"",
2598
+ i,
2599
+ u->field_data[i].off,
2600
+ u->field_data[i].len,
2601
+ u->field_data[i].len,
2602
+ url + u->field_data[i].off);
2603
+ }
2604
+ }
2605
+
2606
+ void
2607
+ test_parse_url (void)
2608
+ {
2609
+ struct http_parser_url u;
2610
+ const struct url_test *test;
2611
+ unsigned int i;
2612
+ int rv;
2613
+
2614
+ for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) {
2615
+ test = &url_tests[i];
2616
+ memset(&u, 0, sizeof(u));
2617
+
2618
+ rv = http_parser_parse_url(test->url,
2619
+ strlen(test->url),
2620
+ test->is_connect,
2621
+ &u);
2622
+
2623
+ if (test->rv == 0) {
2624
+ if (rv != 0) {
2625
+ printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
2626
+ "unexpected rv %d ***\n\n", test->url, test->name, rv);
2627
+ abort();
2628
+ }
2629
+
2630
+ if (memcmp(&u, &test->u, sizeof(u)) != 0) {
2631
+ printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n",
2632
+ test->url, test->name);
2633
+
2634
+ printf("target http_parser_url:\n");
2635
+ dump_url(test->url, &test->u);
2636
+ printf("result http_parser_url:\n");
2637
+ dump_url(test->url, &u);
2638
+
2639
+ abort();
2640
+ }
2641
+ } else {
2642
+ /* test->rv != 0 */
2643
+ if (rv == 0) {
2644
+ printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
2645
+ "unexpected rv %d ***\n\n", test->url, test->name, rv);
2646
+ abort();
2647
+ }
2648
+ }
2649
+ }
2650
+ }
2651
+
2652
+ void
2653
+ test_method_str (void)
2654
+ {
2655
+ assert(0 == strcmp("GET", http_method_str(HTTP_GET)));
2656
+ assert(0 == strcmp("<unknown>", http_method_str(1337)));
2657
+ }
2658
+
2659
+ void
2660
+ test_message (const struct message *message)
2661
+ {
2662
+ size_t raw_len = strlen(message->raw);
2663
+ size_t msg1len;
2664
+ for (msg1len = 0; msg1len < raw_len; msg1len++) {
2665
+ parser_init(message->type);
2666
+
2667
+ size_t read;
2668
+ const char *msg1 = message->raw;
2669
+ const char *msg2 = msg1 + msg1len;
2670
+ size_t msg2len = raw_len - msg1len;
2671
+
2672
+ if (msg1len) {
2673
+ read = parse(msg1, msg1len);
2674
+
2675
+ if (message->upgrade && parser->upgrade) {
2676
+ messages[num_messages - 1].upgrade = msg1 + read;
2677
+ goto test;
2678
+ }
2679
+
2680
+ if (read != msg1len) {
2681
+ print_error(msg1, read);
2682
+ abort();
2683
+ }
2684
+ }
2685
+
2686
+
2687
+ read = parse(msg2, msg2len);
2688
+
2689
+ if (message->upgrade && parser->upgrade) {
2690
+ messages[num_messages - 1].upgrade = msg2 + read;
2691
+ goto test;
2692
+ }
2693
+
2694
+ if (read != msg2len) {
2695
+ print_error(msg2, read);
2696
+ abort();
2697
+ }
2698
+
2699
+ read = parse(NULL, 0);
2700
+
2701
+ if (read != 0) {
2702
+ print_error(message->raw, read);
2703
+ abort();
2704
+ }
2705
+
2706
+ test:
2707
+
2708
+ if (num_messages != 1) {
2709
+ printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
2710
+ abort();
2711
+ }
2712
+
2713
+ if(!message_eq(0, message)) abort();
2714
+
2715
+ parser_free();
2716
+ }
2717
+ }
2718
+
2719
+ void
2720
+ test_message_count_body (const struct message *message)
2721
+ {
2722
+ parser_init(message->type);
2723
+
2724
+ size_t read;
2725
+ size_t l = strlen(message->raw);
2726
+ size_t i, toread;
2727
+ size_t chunk = 4024;
2728
+
2729
+ for (i = 0; i < l; i+= chunk) {
2730
+ toread = MIN(l-i, chunk);
2731
+ read = parse_count_body(message->raw + i, toread);
2732
+ if (read != toread) {
2733
+ print_error(message->raw, read);
2734
+ abort();
2735
+ }
2736
+ }
2737
+
2738
+
2739
+ read = parse_count_body(NULL, 0);
2740
+ if (read != 0) {
2741
+ print_error(message->raw, read);
2742
+ abort();
2743
+ }
2744
+
2745
+ if (num_messages != 1) {
2746
+ printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
2747
+ abort();
2748
+ }
2749
+
2750
+ if(!message_eq(0, message)) abort();
2751
+
2752
+ parser_free();
2753
+ }
2754
+
2755
+ void
2756
+ test_simple (const char *buf, enum http_errno err_expected)
2757
+ {
2758
+ parser_init(HTTP_REQUEST);
2759
+
2760
+ size_t parsed;
2761
+ int pass;
2762
+ enum http_errno err;
2763
+
2764
+ parsed = parse(buf, strlen(buf));
2765
+ pass = (parsed == strlen(buf));
2766
+ err = HTTP_PARSER_ERRNO(parser);
2767
+ parsed = parse(NULL, 0);
2768
+ pass &= (parsed == 0);
2769
+
2770
+ parser_free();
2771
+
2772
+ /* In strict mode, allow us to pass with an unexpected HPE_STRICT as
2773
+ * long as the caller isn't expecting success.
2774
+ */
2775
+ #if HTTP_PARSER_STRICT
2776
+ if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
2777
+ #else
2778
+ if (err_expected != err) {
2779
+ #endif
2780
+ fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n",
2781
+ http_errno_name(err_expected), http_errno_name(err), buf);
2782
+ abort();
2783
+ }
2784
+ }
2785
+
2786
+ void
2787
+ test_header_overflow_error (int req)
2788
+ {
2789
+ http_parser parser;
2790
+ http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
2791
+ size_t parsed;
2792
+ const char *buf;
2793
+ buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.0 200 OK\r\n";
2794
+ parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
2795
+ assert(parsed == strlen(buf));
2796
+
2797
+ buf = "header-key: header-value\r\n";
2798
+ size_t buflen = strlen(buf);
2799
+
2800
+ int i;
2801
+ for (i = 0; i < 10000; i++) {
2802
+ parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
2803
+ if (parsed != buflen) {
2804
+ //fprintf(stderr, "error found on iter %d\n", i);
2805
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
2806
+ return;
2807
+ }
2808
+ }
2809
+
2810
+ fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n");
2811
+ abort();
2812
+ }
2813
+
2814
+ static void
2815
+ test_content_length_overflow (const char *buf, size_t buflen, int expect_ok)
2816
+ {
2817
+ http_parser parser;
2818
+ http_parser_init(&parser, HTTP_RESPONSE);
2819
+ http_parser_execute(&parser, &settings_null, buf, buflen);
2820
+
2821
+ if (expect_ok)
2822
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK);
2823
+ else
2824
+ assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH);
2825
+ }
2826
+
2827
+ void
2828
+ test_header_content_length_overflow_error (void)
2829
+ {
2830
+ #define X(size) \
2831
+ "HTTP/1.1 200 OK\r\n" \
2832
+ "Content-Length: " #size "\r\n" \
2833
+ "\r\n"
2834
+ const char a[] = X(18446744073709551614); /* 2^64-2 */
2835
+ const char b[] = X(18446744073709551615); /* 2^64-1 */
2836
+ const char c[] = X(18446744073709551616); /* 2^64 */
2837
+ #undef X
2838
+ test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
2839
+ test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
2840
+ test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
2841
+ }
2842
+
2843
+ void
2844
+ test_chunk_content_length_overflow_error (void)
2845
+ {
2846
+ #define X(size) \
2847
+ "HTTP/1.1 200 OK\r\n" \
2848
+ "Transfer-Encoding: chunked\r\n" \
2849
+ "\r\n" \
2850
+ #size "\r\n" \
2851
+ "..."
2852
+ const char a[] = X(FFFFFFFFFFFFFFFE); /* 2^64-2 */
2853
+ const char b[] = X(FFFFFFFFFFFFFFFF); /* 2^64-1 */
2854
+ const char c[] = X(10000000000000000); /* 2^64 */
2855
+ #undef X
2856
+ test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */
2857
+ test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
2858
+ test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
2859
+ }
2860
+
2861
+ void
2862
+ test_no_overflow_long_body (int req, size_t length)
2863
+ {
2864
+ http_parser parser;
2865
+ http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
2866
+ size_t parsed;
2867
+ size_t i;
2868
+ char buf1[3000];
2869
+ size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %lu\r\n\r\n",
2870
+ req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", (unsigned long)length);
2871
+ parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
2872
+ if (parsed != buf1len)
2873
+ goto err;
2874
+
2875
+ for (i = 0; i < length; i++) {
2876
+ char foo = 'a';
2877
+ parsed = http_parser_execute(&parser, &settings_null, &foo, 1);
2878
+ if (parsed != 1)
2879
+ goto err;
2880
+ }
2881
+
2882
+ parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
2883
+ if (parsed != buf1len) goto err;
2884
+ return;
2885
+
2886
+ err:
2887
+ fprintf(stderr,
2888
+ "\n*** error in test_no_overflow_long_body %s of length %lu ***\n",
2889
+ req ? "REQUEST" : "RESPONSE",
2890
+ (unsigned long)length);
2891
+ abort();
2892
+ }
2893
+
2894
+ void
2895
+ test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
2896
+ {
2897
+ int message_count = count_parsed_messages(3, r1, r2, r3);
2898
+
2899
+ char total[ strlen(r1->raw)
2900
+ + strlen(r2->raw)
2901
+ + strlen(r3->raw)
2902
+ + 1
2903
+ ];
2904
+ total[0] = '\0';
2905
+
2906
+ strcat(total, r1->raw);
2907
+ strcat(total, r2->raw);
2908
+ strcat(total, r3->raw);
2909
+
2910
+ parser_init(r1->type);
2911
+
2912
+ size_t read;
2913
+
2914
+ read = parse(total, strlen(total));
2915
+
2916
+ if (parser->upgrade) {
2917
+ upgrade_message_fix(total, read, 3, r1, r2, r3);
2918
+ goto test;
2919
+ }
2920
+
2921
+ if (read != strlen(total)) {
2922
+ print_error(total, read);
2923
+ abort();
2924
+ }
2925
+
2926
+ read = parse(NULL, 0);
2927
+
2928
+ if (read != 0) {
2929
+ print_error(total, read);
2930
+ abort();
2931
+ }
2932
+
2933
+ test:
2934
+
2935
+ if (message_count != num_messages) {
2936
+ fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
2937
+ abort();
2938
+ }
2939
+
2940
+ if (!message_eq(0, r1)) abort();
2941
+ if (message_count > 1 && !message_eq(1, r2)) abort();
2942
+ if (message_count > 2 && !message_eq(2, r3)) abort();
2943
+
2944
+ parser_free();
2945
+ }
2946
+
2947
+ /* SCAN through every possible breaking to make sure the
2948
+ * parser can handle getting the content in any chunks that
2949
+ * might come from the socket
2950
+ */
2951
+ void
2952
+ test_scan (const struct message *r1, const struct message *r2, const struct message *r3)
2953
+ {
2954
+ char total[80*1024] = "\0";
2955
+ char buf1[80*1024] = "\0";
2956
+ char buf2[80*1024] = "\0";
2957
+ char buf3[80*1024] = "\0";
2958
+
2959
+ strcat(total, r1->raw);
2960
+ strcat(total, r2->raw);
2961
+ strcat(total, r3->raw);
2962
+
2963
+ size_t read;
2964
+
2965
+ int total_len = strlen(total);
2966
+
2967
+ int total_ops = 2 * (total_len - 1) * (total_len - 2) / 2;
2968
+ int ops = 0 ;
2969
+
2970
+ size_t buf1_len, buf2_len, buf3_len;
2971
+ int message_count = count_parsed_messages(3, r1, r2, r3);
2972
+
2973
+ int i,j,type_both;
2974
+ for (type_both = 0; type_both < 2; type_both ++ ) {
2975
+ for (j = 2; j < total_len; j ++ ) {
2976
+ for (i = 1; i < j; i ++ ) {
2977
+
2978
+ if (ops % 1000 == 0) {
2979
+ printf("\b\b\b\b%3.0f%%", 100 * (float)ops /(float)total_ops);
2980
+ fflush(stdout);
2981
+ }
2982
+ ops += 1;
2983
+
2984
+ parser_init(type_both ? HTTP_BOTH : r1->type);
2985
+
2986
+ buf1_len = i;
2987
+ strlncpy(buf1, sizeof(buf1), total, buf1_len);
2988
+ buf1[buf1_len] = 0;
2989
+
2990
+ buf2_len = j - i;
2991
+ strlncpy(buf2, sizeof(buf1), total+i, buf2_len);
2992
+ buf2[buf2_len] = 0;
2993
+
2994
+ buf3_len = total_len - j;
2995
+ strlncpy(buf3, sizeof(buf1), total+j, buf3_len);
2996
+ buf3[buf3_len] = 0;
2997
+
2998
+ read = parse(buf1, buf1_len);
2999
+
3000
+ if (parser->upgrade) goto test;
3001
+
3002
+ if (read != buf1_len) {
3003
+ print_error(buf1, read);
3004
+ goto error;
3005
+ }
3006
+
3007
+ read += parse(buf2, buf2_len);
3008
+
3009
+ if (parser->upgrade) goto test;
3010
+
3011
+ if (read != buf1_len + buf2_len) {
3012
+ print_error(buf2, read);
3013
+ goto error;
3014
+ }
3015
+
3016
+ read += parse(buf3, buf3_len);
3017
+
3018
+ if (parser->upgrade) goto test;
3019
+
3020
+ if (read != buf1_len + buf2_len + buf3_len) {
3021
+ print_error(buf3, read);
3022
+ goto error;
3023
+ }
3024
+
3025
+ parse(NULL, 0);
3026
+
3027
+ test:
3028
+ if (parser->upgrade) {
3029
+ upgrade_message_fix(total, read, 3, r1, r2, r3);
3030
+ }
3031
+
3032
+ if (message_count != num_messages) {
3033
+ fprintf(stderr, "\n\nParser didn't see %d messages only %d\n",
3034
+ message_count, num_messages);
3035
+ goto error;
3036
+ }
3037
+
3038
+ if (!message_eq(0, r1)) {
3039
+ fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n");
3040
+ goto error;
3041
+ }
3042
+
3043
+ if (message_count > 1 && !message_eq(1, r2)) {
3044
+ fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
3045
+ goto error;
3046
+ }
3047
+
3048
+ if (message_count > 2 && !message_eq(2, r3)) {
3049
+ fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
3050
+ goto error;
3051
+ }
3052
+
3053
+ parser_free();
3054
+ }
3055
+ }
3056
+ }
3057
+ puts("\b\b\b\b100%");
3058
+ return;
3059
+
3060
+ error:
3061
+ fprintf(stderr, "i=%d j=%d\n", i, j);
3062
+ fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1);
3063
+ fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2);
3064
+ fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3);
3065
+ abort();
3066
+ }
3067
+
3068
+ // user required to free the result
3069
+ // string terminated by \0
3070
+ char *
3071
+ create_large_chunked_message (int body_size_in_kb, const char* headers)
3072
+ {
3073
+ int i;
3074
+ size_t wrote = 0;
3075
+ size_t headers_len = strlen(headers);
3076
+ size_t bufsize = headers_len + (5+1024+2)*body_size_in_kb + 6;
3077
+ char * buf = malloc(bufsize);
3078
+
3079
+ memcpy(buf, headers, headers_len);
3080
+ wrote += headers_len;
3081
+
3082
+ for (i = 0; i < body_size_in_kb; i++) {
3083
+ // write 1kb chunk into the body.
3084
+ memcpy(buf + wrote, "400\r\n", 5);
3085
+ wrote += 5;
3086
+ memset(buf + wrote, 'C', 1024);
3087
+ wrote += 1024;
3088
+ strcpy(buf + wrote, "\r\n");
3089
+ wrote += 2;
3090
+ }
3091
+
3092
+ memcpy(buf + wrote, "0\r\n\r\n", 6);
3093
+ wrote += 6;
3094
+ assert(wrote == bufsize);
3095
+
3096
+ return buf;
3097
+ }
3098
+
3099
+ void
3100
+ test_status_complete (void)
3101
+ {
3102
+ parser_init(HTTP_RESPONSE);
3103
+ parser->data = 0;
3104
+ http_parser_settings settings = settings_null;
3105
+ settings.on_status_complete = status_complete_cb;
3106
+
3107
+ char *response = "don't mind me, just a simple response";
3108
+ http_parser_execute(parser, &settings, response, strlen(response));
3109
+ assert(parser->data == (void*)0); // the status_complete callback was never called
3110
+ assert(parser->http_errno == HPE_INVALID_CONSTANT); // the errno for an invalid status line
3111
+ }
3112
+
3113
+ /* Verify that we can pause parsing at any of the bytes in the
3114
+ * message and still get the result that we're expecting. */
3115
+ void
3116
+ test_message_pause (const struct message *msg)
3117
+ {
3118
+ char *buf = (char*) msg->raw;
3119
+ size_t buflen = strlen(msg->raw);
3120
+ size_t nread;
3121
+
3122
+ parser_init(msg->type);
3123
+
3124
+ do {
3125
+ nread = parse_pause(buf, buflen);
3126
+
3127
+ // We can only set the upgrade buffer once we've gotten our message
3128
+ // completion callback.
3129
+ if (messages[0].message_complete_cb_called &&
3130
+ msg->upgrade &&
3131
+ parser->upgrade) {
3132
+ messages[0].upgrade = buf + nread;
3133
+ goto test;
3134
+ }
3135
+
3136
+ if (nread < buflen) {
3137
+
3138
+ // Not much do to if we failed a strict-mode check
3139
+ if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
3140
+ parser_free();
3141
+ return;
3142
+ }
3143
+
3144
+ assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
3145
+ }
3146
+
3147
+ buf += nread;
3148
+ buflen -= nread;
3149
+ http_parser_pause(parser, 0);
3150
+ } while (buflen > 0);
3151
+
3152
+ nread = parse_pause(NULL, 0);
3153
+ assert (nread == 0);
3154
+
3155
+ test:
3156
+ if (num_messages != 1) {
3157
+ printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
3158
+ abort();
3159
+ }
3160
+
3161
+ if(!message_eq(0, msg)) abort();
3162
+
3163
+ parser_free();
3164
+ }
3165
+
3166
+ int
3167
+ main (void)
3168
+ {
3169
+ parser = NULL;
3170
+ int i, j, k;
3171
+ int request_count;
3172
+ int response_count;
3173
+
3174
+ printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser));
3175
+
3176
+ for (request_count = 0; requests[request_count].name; request_count++);
3177
+ for (response_count = 0; responses[response_count].name; response_count++);
3178
+
3179
+ //// API
3180
+ test_preserve_data();
3181
+ test_parse_url();
3182
+ test_method_str();
3183
+
3184
+ //// OVERFLOW CONDITIONS
3185
+
3186
+ test_header_overflow_error(HTTP_REQUEST);
3187
+ test_no_overflow_long_body(HTTP_REQUEST, 1000);
3188
+ test_no_overflow_long_body(HTTP_REQUEST, 100000);
3189
+
3190
+ test_header_overflow_error(HTTP_RESPONSE);
3191
+ test_no_overflow_long_body(HTTP_RESPONSE, 1000);
3192
+ test_no_overflow_long_body(HTTP_RESPONSE, 100000);
3193
+
3194
+ test_header_content_length_overflow_error();
3195
+ test_chunk_content_length_overflow_error();
3196
+
3197
+ //// RESPONSES
3198
+
3199
+ for (i = 0; i < response_count; i++) {
3200
+ test_message(&responses[i]);
3201
+ }
3202
+
3203
+ for (i = 0; i < response_count; i++) {
3204
+ test_message_pause(&responses[i]);
3205
+ }
3206
+
3207
+ for (i = 0; i < response_count; i++) {
3208
+ if (!responses[i].should_keep_alive) continue;
3209
+ for (j = 0; j < response_count; j++) {
3210
+ if (!responses[j].should_keep_alive) continue;
3211
+ for (k = 0; k < response_count; k++) {
3212
+ test_multiple3(&responses[i], &responses[j], &responses[k]);
3213
+ }
3214
+ }
3215
+ }
3216
+
3217
+ test_message_count_body(&responses[NO_HEADERS_NO_BODY_404]);
3218
+ test_message_count_body(&responses[TRAILING_SPACE_ON_CHUNKED_BODY]);
3219
+
3220
+ // test very large chunked response
3221
+ {
3222
+ char * msg = create_large_chunked_message(31337,
3223
+ "HTTP/1.0 200 OK\r\n"
3224
+ "Transfer-Encoding: chunked\r\n"
3225
+ "Content-Type: text/plain\r\n"
3226
+ "\r\n");
3227
+ struct message large_chunked =
3228
+ {.name= "large chunked"
3229
+ ,.type= HTTP_RESPONSE
3230
+ ,.raw= msg
3231
+ ,.should_keep_alive= FALSE
3232
+ ,.message_complete_on_eof= FALSE
3233
+ ,.http_major= 1
3234
+ ,.http_minor= 0
3235
+ ,.status_code= 200
3236
+ ,.num_headers= 2
3237
+ ,.headers=
3238
+ { { "Transfer-Encoding", "chunked" }
3239
+ , { "Content-Type", "text/plain" }
3240
+ }
3241
+ ,.body_size= 31337*1024
3242
+ };
3243
+ test_message_count_body(&large_chunked);
3244
+ free(msg);
3245
+ }
3246
+
3247
+
3248
+
3249
+ printf("response scan 1/2 ");
3250
+ test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
3251
+ , &responses[NO_BODY_HTTP10_KA_204]
3252
+ , &responses[NO_REASON_PHRASE]
3253
+ );
3254
+
3255
+ printf("response scan 2/2 ");
3256
+ test_scan( &responses[BONJOUR_MADAME_FR]
3257
+ , &responses[UNDERSTORE_HEADER_KEY]
3258
+ , &responses[NO_CARRIAGE_RET]
3259
+ );
3260
+
3261
+ puts("responses okay");
3262
+
3263
+
3264
+ /// REQUESTS
3265
+
3266
+ test_simple("hello world", HPE_INVALID_METHOD);
3267
+ test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
3268
+
3269
+
3270
+ test_simple("ASDF / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
3271
+ test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
3272
+ test_simple("GETA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
3273
+
3274
+ // Well-formed but incomplete
3275
+ test_simple("GET / HTTP/1.1\r\n"
3276
+ "Content-Type: text/plain\r\n"
3277
+ "Content-Length: 6\r\n"
3278
+ "\r\n"
3279
+ "fooba",
3280
+ HPE_OK);
3281
+
3282
+ static const char *all_methods[] = {
3283
+ "DELETE",
3284
+ "GET",
3285
+ "HEAD",
3286
+ "POST",
3287
+ "PUT",
3288
+ //"CONNECT", //CONNECT can't be tested like other methods, it's a tunnel
3289
+ "OPTIONS",
3290
+ "TRACE",
3291
+ "COPY",
3292
+ "LOCK",
3293
+ "MKCOL",
3294
+ "MOVE",
3295
+ "PROPFIND",
3296
+ "PROPPATCH",
3297
+ "UNLOCK",
3298
+ "REPORT",
3299
+ "MKACTIVITY",
3300
+ "CHECKOUT",
3301
+ "MERGE",
3302
+ "M-SEARCH",
3303
+ "NOTIFY",
3304
+ "SUBSCRIBE",
3305
+ "UNSUBSCRIBE",
3306
+ "PATCH",
3307
+ 0 };
3308
+ const char **this_method;
3309
+ for (this_method = all_methods; *this_method; this_method++) {
3310
+ char buf[200];
3311
+ sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
3312
+ test_simple(buf, HPE_OK);
3313
+ }
3314
+
3315
+ static const char *bad_methods[] = {
3316
+ "C******",
3317
+ "M****",
3318
+ 0 };
3319
+ for (this_method = bad_methods; *this_method; this_method++) {
3320
+ char buf[200];
3321
+ sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
3322
+ test_simple(buf, HPE_UNKNOWN);
3323
+ }
3324
+
3325
+ const char *dumbfuck2 =
3326
+ "GET / HTTP/1.1\r\n"
3327
+ "X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n"
3328
+ "\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n"
3329
+ "\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n"
3330
+ "\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n"
3331
+ "\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n"
3332
+ "\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n"
3333
+ "\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n"
3334
+ "\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n"
3335
+ "\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n"
3336
+ "\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n"
3337
+ "\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n"
3338
+ "\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n"
3339
+ "\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n"
3340
+ "\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n"
3341
+ "\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n"
3342
+ "\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n"
3343
+ "\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n"
3344
+ "\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n"
3345
+ "\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n"
3346
+ "\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n"
3347
+ "\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n"
3348
+ "\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n"
3349
+ "\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n"
3350
+ "\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n"
3351
+ "\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n"
3352
+ "\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n"
3353
+ "\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n"
3354
+ "\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n"
3355
+ "\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n"
3356
+ "\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n"
3357
+ "\tRA==\r\n"
3358
+ "\t-----END CERTIFICATE-----\r\n"
3359
+ "\r\n";
3360
+ test_simple(dumbfuck2, HPE_OK);
3361
+
3362
+ #if 0
3363
+ // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
3364
+ // until EOF.
3365
+ //
3366
+ // no content-length
3367
+ // error if there is a body without content length
3368
+ const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\n"
3369
+ "Accept: */*\r\n"
3370
+ "\r\n"
3371
+ "HELLO";
3372
+ test_simple(bad_get_no_headers_no_body, 0);
3373
+ #endif
3374
+ /* TODO sending junk and large headers gets rejected */
3375
+
3376
+
3377
+ /* check to make sure our predefined requests are okay */
3378
+ for (i = 0; requests[i].name; i++) {
3379
+ test_message(&requests[i]);
3380
+ }
3381
+
3382
+ for (i = 0; i < request_count; i++) {
3383
+ test_message_pause(&requests[i]);
3384
+ }
3385
+
3386
+ for (i = 0; i < request_count; i++) {
3387
+ if (!requests[i].should_keep_alive) continue;
3388
+ for (j = 0; j < request_count; j++) {
3389
+ if (!requests[j].should_keep_alive) continue;
3390
+ for (k = 0; k < request_count; k++) {
3391
+ test_multiple3(&requests[i], &requests[j], &requests[k]);
3392
+ }
3393
+ }
3394
+ }
3395
+
3396
+ printf("request scan 1/4 ");
3397
+ test_scan( &requests[GET_NO_HEADERS_NO_BODY]
3398
+ , &requests[GET_ONE_HEADER_NO_BODY]
3399
+ , &requests[GET_NO_HEADERS_NO_BODY]
3400
+ );
3401
+
3402
+ printf("request scan 2/4 ");
3403
+ test_scan( &requests[POST_CHUNKED_ALL_YOUR_BASE]
3404
+ , &requests[POST_IDENTITY_BODY_WORLD]
3405
+ , &requests[GET_FUNKY_CONTENT_LENGTH]
3406
+ );
3407
+
3408
+ printf("request scan 3/4 ");
3409
+ test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END]
3410
+ , &requests[CHUNKED_W_TRAILING_HEADERS]
3411
+ , &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH]
3412
+ );
3413
+
3414
+ printf("request scan 4/4 ");
3415
+ test_scan( &requests[QUERY_URL_WITH_QUESTION_MARK_GET]
3416
+ , &requests[PREFIX_NEWLINE_GET ]
3417
+ , &requests[CONNECT_REQUEST]
3418
+ );
3419
+
3420
+ test_status_complete();
3421
+
3422
+ puts("requests okay");
3423
+
3424
+ return 0;
3425
+ }