nyara 0.0.1.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/example/design.rb +62 -0
  3. data/example/fib.rb +15 -0
  4. data/example/hello.rb +5 -0
  5. data/example/stream.rb +10 -0
  6. data/ext/accept.c +133 -0
  7. data/ext/event.c +89 -0
  8. data/ext/extconf.rb +34 -0
  9. data/ext/hashes.c +130 -0
  10. data/ext/http-parser/AUTHORS +41 -0
  11. data/ext/http-parser/CONTRIBUTIONS +4 -0
  12. data/ext/http-parser/LICENSE-MIT +23 -0
  13. data/ext/http-parser/contrib/parsertrace.c +156 -0
  14. data/ext/http-parser/contrib/url_parser.c +44 -0
  15. data/ext/http-parser/http_parser.c +2175 -0
  16. data/ext/http-parser/http_parser.h +304 -0
  17. data/ext/http-parser/test.c +3425 -0
  18. data/ext/http_parser.c +1 -0
  19. data/ext/inc/epoll.h +60 -0
  20. data/ext/inc/kqueue.h +77 -0
  21. data/ext/inc/status_codes.inc +64 -0
  22. data/ext/inc/str_intern.h +66 -0
  23. data/ext/inc/version.inc +1 -0
  24. data/ext/mime.c +107 -0
  25. data/ext/multipart-parser-c/README.md +18 -0
  26. data/ext/multipart-parser-c/multipart_parser.c +309 -0
  27. data/ext/multipart-parser-c/multipart_parser.h +48 -0
  28. data/ext/multipart_parser.c +1 -0
  29. data/ext/nyara.c +56 -0
  30. data/ext/nyara.h +59 -0
  31. data/ext/request.c +474 -0
  32. data/ext/route.cc +325 -0
  33. data/ext/url_encoded.c +304 -0
  34. data/hello.rb +5 -0
  35. data/lib/nyara/config.rb +64 -0
  36. data/lib/nyara/config_hash.rb +51 -0
  37. data/lib/nyara/controller.rb +336 -0
  38. data/lib/nyara/cookie.rb +31 -0
  39. data/lib/nyara/cpu_counter.rb +65 -0
  40. data/lib/nyara/header_hash.rb +18 -0
  41. data/lib/nyara/mime_types.rb +612 -0
  42. data/lib/nyara/nyara.rb +82 -0
  43. data/lib/nyara/param_hash.rb +5 -0
  44. data/lib/nyara/request.rb +144 -0
  45. data/lib/nyara/route.rb +138 -0
  46. data/lib/nyara/route_entry.rb +43 -0
  47. data/lib/nyara/session.rb +104 -0
  48. data/lib/nyara/view.rb +317 -0
  49. data/lib/nyara.rb +25 -0
  50. data/nyara.gemspec +20 -0
  51. data/rakefile +91 -0
  52. data/readme.md +35 -0
  53. data/spec/ext_mime_match_spec.rb +27 -0
  54. data/spec/ext_parse_accept_value_spec.rb +29 -0
  55. data/spec/ext_parse_spec.rb +138 -0
  56. data/spec/ext_route_spec.rb +70 -0
  57. data/spec/hashes_spec.rb +71 -0
  58. data/spec/path_helper_spec.rb +77 -0
  59. data/spec/request_delegate_spec.rb +67 -0
  60. data/spec/request_spec.rb +56 -0
  61. data/spec/route_entry_spec.rb +12 -0
  62. data/spec/route_spec.rb +84 -0
  63. data/spec/session_spec.rb +66 -0
  64. data/spec/spec_helper.rb +52 -0
  65. data/spec/view_spec.rb +87 -0
  66. data/tools/bench-cookie.rb +22 -0
  67. metadata +111 -0
@@ -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
+ }