http_parser.rb 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.gitignore +11 -0
  2. data/.gitmodules +6 -0
  3. data/README.md +45 -0
  4. data/Rakefile +6 -0
  5. data/bench/thin.rb +57 -0
  6. data/ext/ruby_http_parser/.gitignore +1 -0
  7. data/ext/ruby_http_parser/RubyHttpParserService.java +18 -0
  8. data/ext/ruby_http_parser/ext_help.h +18 -0
  9. data/ext/ruby_http_parser/extconf.rb +16 -0
  10. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +403 -0
  11. data/ext/ruby_http_parser/ruby_http_parser.c +474 -0
  12. data/ext/ruby_http_parser/vendor/.gitkeep +0 -0
  13. data/ext/ruby_http_parser/vendor/http-parser-java/CONTRIBUTIONS +4 -0
  14. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +19 -0
  15. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +171 -0
  16. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +19 -0
  17. data/ext/ruby_http_parser/vendor/http-parser-java/compile +1 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1590 -0
  19. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +167 -0
  20. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +7 -0
  21. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +90 -0
  22. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +31 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserType.java +13 -0
  24. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPCallback.java +5 -0
  25. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPDataCallback.java +25 -0
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPErrorCallback.java +7 -0
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +1894 -0
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +78 -0
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/Util.java +112 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +487 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +115 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1865 -0
  33. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +1 -0
  34. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +1 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +539 -0
  36. data/ext/ruby_http_parser/vendor/http-parser-java/tools/byte_constants.rb +6 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/tools/const_char.rb +13 -0
  38. data/ext/ruby_http_parser/vendor/http-parser-java/tools/lowcase.rb +15 -0
  39. data/ext/ruby_http_parser/vendor/http-parser-java/tools/parse_tests.rb +33 -0
  40. data/ext/ruby_http_parser/vendor/http-parser/CONTRIBUTIONS +4 -0
  41. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +19 -0
  42. data/ext/ruby_http_parser/vendor/http-parser/README.md +171 -0
  43. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1590 -0
  44. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +167 -0
  45. data/ext/ruby_http_parser/vendor/http-parser/test.c +1755 -0
  46. data/http_parser.rb.gemspec +15 -0
  47. data/lib/http/parser.rb +1 -0
  48. data/lib/http_parser.rb +4 -0
  49. data/spec/parser_spec.rb +187 -0
  50. data/spec/spec_helper.rb +2 -0
  51. data/spec/support/requests.json +381 -0
  52. data/spec/support/responses.json +186 -0
  53. data/tasks/compile.rake +39 -0
  54. data/tasks/spec.rake +5 -0
  55. data/tasks/submodules.rake +7 -0
  56. metadata +121 -0
@@ -0,0 +1,115 @@
1
+ package http_parser.lolevel;
2
+
3
+ import java.nio.ByteBuffer;
4
+ import http_parser.HTTPException;
5
+
6
+ public class UnitTest {
7
+
8
+ static void p(Object o) {System.out.println(o);}
9
+
10
+ public static void testErrorFormat() {
11
+ String bla = "This has an error in position 10 (the n in 'an')";
12
+ ByteBuffer buf = ByteBuffer.wrap(bla.getBytes());
13
+ buf.position(10);
14
+
15
+ String mes =
16
+ "This has an error in position 10 (the n in 'an')\n" +
17
+ "..........^";
18
+
19
+ check_equals(mes, Util.error ("test error", buf, 0));
20
+
21
+
22
+ bla = "123456789A123456789B123456789C123456789D123456789E123456789F123456789G123456789H123456789I123456789J";
23
+ buf = ByteBuffer.wrap(bla.getBytes());
24
+ buf.position(50);
25
+ mes =
26
+ "56789B123456789C123456789D123456789E123456789F123456789G123456789H123456\n"+
27
+ "....................................^";
28
+ check_equals(mes, Util.error("test trim right and left", buf, 0));
29
+
30
+
31
+ buf.position(5);
32
+ mes =
33
+ "123456789A123456789B123456789C123456789D123456789E123456789F123456789G12\n"+
34
+ ".....^";
35
+ check_equals(mes, Util.error("test trim right", buf, 0));
36
+
37
+
38
+ int limit = buf.limit();
39
+ buf.limit(10);
40
+ mes =
41
+ "123456789A\n"+
42
+ ".....^";
43
+ check_equals(mes, Util.error("all before, not enough after", buf, 0));
44
+
45
+
46
+
47
+ buf.limit(limit);
48
+ buf.position(90);
49
+ mes =
50
+ "9C123456789D123456789E123456789F123456789G123456789H123456789I123456789J\n"+
51
+ "..............................................................^";
52
+ check_equals(mes, Util.error("test trim left", buf, 10));
53
+ }
54
+
55
+
56
+ // Test that the error callbacks are properly called.
57
+ public static void testErrorCallback () {
58
+ String nothttp = "THis is certainly not valid HTTP";
59
+ ByteBuffer buf = ByteBuffer.wrap(nothttp.getBytes());
60
+
61
+ ParserSettings s = new ParserSettings();
62
+ s.on_error = new HTTPErrorCallback() {
63
+ public void cb (HTTPParser p, String mes, ByteBuffer buf, int pos) {
64
+ throw new HTTPException(mes);
65
+ }
66
+ }; // err callback
67
+
68
+
69
+ HTTPParser p = new HTTPParser();
70
+ try {
71
+ p.execute(s, buf);
72
+ } catch (HTTPException e) {
73
+ check_equals("Invalid HTTP method", e.getMessage());
74
+ }
75
+
76
+ buf = ByteBuffer.wrap("GET / HTTP 1.10000".getBytes());
77
+ p = new HTTPParser();
78
+ try {
79
+ p.execute(s, buf);
80
+ } catch (HTTPException e) {
81
+ check_equals("ridiculous http minor", e.getMessage());
82
+ }
83
+
84
+ // if no error handler is defined, behave just like the above...
85
+ ParserSettings s0 = new ParserSettings();
86
+
87
+ buf = ByteBuffer.wrap("THis is certainly not valid HTTP".getBytes());
88
+ p = new HTTPParser();
89
+ try {
90
+ p.execute(s0, buf);
91
+ } catch (HTTPException e) {
92
+ check_equals("Invalid HTTP method", e.getMessage());
93
+ }
94
+
95
+ buf = ByteBuffer.wrap("GET / HTTP 1.10000".getBytes());
96
+ p = new HTTPParser();
97
+ try {
98
+ p.execute(s0, buf);
99
+ } catch (HTTPException e) {
100
+ check_equals("ridiculous http minor", e.getMessage());
101
+ }
102
+ }
103
+
104
+ static void check_equals(Object supposed2be, Object is) {
105
+ if (!supposed2be.equals(is)) {
106
+ throw new RuntimeException(is + " is supposed to be "+supposed2be);
107
+ }
108
+ }
109
+
110
+
111
+ public static void main (String [] args) {
112
+ testErrorFormat();
113
+ testErrorCallback();
114
+ }
115
+ }
@@ -0,0 +1,1865 @@
1
+ /* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
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 10
35
+ #define MAX_ELEMENT_SIZE 500
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
+ int num_headers;
54
+ enum { NONE=0, FIELD, VALUE } last_header_element;
55
+ char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
56
+ int should_keep_alive;
57
+
58
+ int upgrade;
59
+
60
+ unsigned short http_major;
61
+ unsigned short http_minor;
62
+
63
+ int message_begin_cb_called;
64
+ int headers_complete_cb_called;
65
+ int message_complete_cb_called;
66
+ int message_complete_on_eof;
67
+ };
68
+
69
+ static int currently_parsing_eof;
70
+
71
+ static struct message messages[5];
72
+ static int num_messages;
73
+
74
+ /* * R E Q U E S T S * */
75
+ const struct message requests[] =
76
+ #define CURL_GET 0
77
+ { {.name= "curl get"
78
+ ,.type= HTTP_REQUEST
79
+ ,.raw= "GET /test HTTP/1.1\r\n"
80
+ "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"
81
+ "Host: 0.0.0.0=5000\r\n"
82
+ "Accept: */*\r\n"
83
+ "\r\n"
84
+ ,.should_keep_alive= TRUE
85
+ ,.message_complete_on_eof= FALSE
86
+ ,.http_major= 1
87
+ ,.http_minor= 1
88
+ ,.method= HTTP_GET
89
+ ,.query_string= ""
90
+ ,.fragment= ""
91
+ ,.request_path= "/test"
92
+ ,.request_url= "/test"
93
+ ,.num_headers= 3
94
+ ,.headers=
95
+ { { "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" }
96
+ , { "Host", "0.0.0.0=5000" }
97
+ , { "Accept", "*/*" }
98
+ }
99
+ ,.body= ""
100
+ }
101
+
102
+ #define FIREFOX_GET 1
103
+ , {.name= "firefox get"
104
+ ,.type= HTTP_REQUEST
105
+ ,.raw= "GET /favicon.ico HTTP/1.1\r\n"
106
+ "Host: 0.0.0.0=5000\r\n"
107
+ "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n"
108
+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
109
+ "Accept-Language: en-us,en;q=0.5\r\n"
110
+ "Accept-Encoding: gzip,deflate\r\n"
111
+ "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
112
+ "Keep-Alive: 300\r\n"
113
+ "Connection: keep-alive\r\n"
114
+ "\r\n"
115
+ ,.should_keep_alive= TRUE
116
+ ,.message_complete_on_eof= FALSE
117
+ ,.http_major= 1
118
+ ,.http_minor= 1
119
+ ,.method= HTTP_GET
120
+ ,.query_string= ""
121
+ ,.fragment= ""
122
+ ,.request_path= "/favicon.ico"
123
+ ,.request_url= "/favicon.ico"
124
+ ,.num_headers= 8
125
+ ,.headers=
126
+ { { "Host", "0.0.0.0=5000" }
127
+ , { "User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0" }
128
+ , { "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" }
129
+ , { "Accept-Language", "en-us,en;q=0.5" }
130
+ , { "Accept-Encoding", "gzip,deflate" }
131
+ , { "Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7" }
132
+ , { "Keep-Alive", "300" }
133
+ , { "Connection", "keep-alive" }
134
+ }
135
+ ,.body= ""
136
+ }
137
+
138
+ #define DUMBFUCK 2
139
+ , {.name= "dumbfuck"
140
+ ,.type= HTTP_REQUEST
141
+ ,.raw= "GET /dumbfuck HTTP/1.1\r\n"
142
+ "aaaaaaaaaaaaa:++++++++++\r\n"
143
+ "\r\n"
144
+ ,.should_keep_alive= TRUE
145
+ ,.message_complete_on_eof= FALSE
146
+ ,.http_major= 1
147
+ ,.http_minor= 1
148
+ ,.method= HTTP_GET
149
+ ,.query_string= ""
150
+ ,.fragment= ""
151
+ ,.request_path= "/dumbfuck"
152
+ ,.request_url= "/dumbfuck"
153
+ ,.num_headers= 1
154
+ ,.headers=
155
+ { { "aaaaaaaaaaaaa", "++++++++++" }
156
+ }
157
+ ,.body= ""
158
+ }
159
+
160
+ #define FRAGMENT_IN_URI 3
161
+ , {.name= "fragment in url"
162
+ ,.type= HTTP_REQUEST
163
+ ,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n"
164
+ "\r\n"
165
+ ,.should_keep_alive= TRUE
166
+ ,.message_complete_on_eof= FALSE
167
+ ,.http_major= 1
168
+ ,.http_minor= 1
169
+ ,.method= HTTP_GET
170
+ ,.query_string= "page=1"
171
+ ,.fragment= "posts-17408"
172
+ ,.request_path= "/forums/1/topics/2375"
173
+ /* XXX request url does include fragment? */
174
+ ,.request_url= "/forums/1/topics/2375?page=1#posts-17408"
175
+ ,.num_headers= 0
176
+ ,.body= ""
177
+ }
178
+
179
+ #define GET_NO_HEADERS_NO_BODY 4
180
+ , {.name= "get no headers no body"
181
+ ,.type= HTTP_REQUEST
182
+ ,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n"
183
+ "\r\n"
184
+ ,.should_keep_alive= TRUE
185
+ ,.message_complete_on_eof= FALSE /* would need Connection: close */
186
+ ,.http_major= 1
187
+ ,.http_minor= 1
188
+ ,.method= HTTP_GET
189
+ ,.query_string= ""
190
+ ,.fragment= ""
191
+ ,.request_path= "/get_no_headers_no_body/world"
192
+ ,.request_url= "/get_no_headers_no_body/world"
193
+ ,.num_headers= 0
194
+ ,.body= ""
195
+ }
196
+
197
+ #define GET_ONE_HEADER_NO_BODY 5
198
+ , {.name= "get one header no body"
199
+ ,.type= HTTP_REQUEST
200
+ ,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n"
201
+ "Accept: */*\r\n"
202
+ "\r\n"
203
+ ,.should_keep_alive= TRUE
204
+ ,.message_complete_on_eof= FALSE /* would need Connection: close */
205
+ ,.http_major= 1
206
+ ,.http_minor= 1
207
+ ,.method= HTTP_GET
208
+ ,.query_string= ""
209
+ ,.fragment= ""
210
+ ,.request_path= "/get_one_header_no_body"
211
+ ,.request_url= "/get_one_header_no_body"
212
+ ,.num_headers= 1
213
+ ,.headers=
214
+ { { "Accept" , "*/*" }
215
+ }
216
+ ,.body= ""
217
+ }
218
+
219
+ #define GET_FUNKY_CONTENT_LENGTH 6
220
+ , {.name= "get funky content length body hello"
221
+ ,.type= HTTP_REQUEST
222
+ ,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n"
223
+ "conTENT-Length: 5\r\n"
224
+ "\r\n"
225
+ "HELLO"
226
+ ,.should_keep_alive= FALSE
227
+ ,.message_complete_on_eof= FALSE
228
+ ,.http_major= 1
229
+ ,.http_minor= 0
230
+ ,.method= HTTP_GET
231
+ ,.query_string= ""
232
+ ,.fragment= ""
233
+ ,.request_path= "/get_funky_content_length_body_hello"
234
+ ,.request_url= "/get_funky_content_length_body_hello"
235
+ ,.num_headers= 1
236
+ ,.headers=
237
+ { { "conTENT-Length" , "5" }
238
+ }
239
+ ,.body= "HELLO"
240
+ }
241
+
242
+ #define POST_IDENTITY_BODY_WORLD 7
243
+ , {.name= "post identity body world"
244
+ ,.type= HTTP_REQUEST
245
+ ,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
246
+ "Accept: */*\r\n"
247
+ "Transfer-Encoding: identity\r\n"
248
+ "Content-Length: 5\r\n"
249
+ "\r\n"
250
+ "World"
251
+ ,.should_keep_alive= TRUE
252
+ ,.message_complete_on_eof= FALSE
253
+ ,.http_major= 1
254
+ ,.http_minor= 1
255
+ ,.method= HTTP_POST
256
+ ,.query_string= "q=search"
257
+ ,.fragment= "hey"
258
+ ,.request_path= "/post_identity_body_world"
259
+ ,.request_url= "/post_identity_body_world?q=search#hey"
260
+ ,.num_headers= 3
261
+ ,.headers=
262
+ { { "Accept", "*/*" }
263
+ , { "Transfer-Encoding", "identity" }
264
+ , { "Content-Length", "5" }
265
+ }
266
+ ,.body= "World"
267
+ }
268
+
269
+ #define POST_CHUNKED_ALL_YOUR_BASE 8
270
+ , {.name= "post - chunked body: all your base are belong to us"
271
+ ,.type= HTTP_REQUEST
272
+ ,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n"
273
+ "Transfer-Encoding: chunked\r\n"
274
+ "\r\n"
275
+ "1e\r\nall your base are belong to us\r\n"
276
+ "0\r\n"
277
+ "\r\n"
278
+ ,.should_keep_alive= TRUE
279
+ ,.message_complete_on_eof= FALSE
280
+ ,.http_major= 1
281
+ ,.http_minor= 1
282
+ ,.method= HTTP_POST
283
+ ,.query_string= ""
284
+ ,.fragment= ""
285
+ ,.request_path= "/post_chunked_all_your_base"
286
+ ,.request_url= "/post_chunked_all_your_base"
287
+ ,.num_headers= 1
288
+ ,.headers=
289
+ { { "Transfer-Encoding" , "chunked" }
290
+ }
291
+ ,.body= "all your base are belong to us"
292
+ }
293
+
294
+ #define TWO_CHUNKS_MULT_ZERO_END 9
295
+ , {.name= "two chunks ; triple zero ending"
296
+ ,.type= HTTP_REQUEST
297
+ ,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n"
298
+ "Transfer-Encoding: chunked\r\n"
299
+ "\r\n"
300
+ "5\r\nhello\r\n"
301
+ "6\r\n world\r\n"
302
+ "000\r\n"
303
+ "\r\n"
304
+ ,.should_keep_alive= TRUE
305
+ ,.message_complete_on_eof= FALSE
306
+ ,.http_major= 1
307
+ ,.http_minor= 1
308
+ ,.method= HTTP_POST
309
+ ,.query_string= ""
310
+ ,.fragment= ""
311
+ ,.request_path= "/two_chunks_mult_zero_end"
312
+ ,.request_url= "/two_chunks_mult_zero_end"
313
+ ,.num_headers= 1
314
+ ,.headers=
315
+ { { "Transfer-Encoding", "chunked" }
316
+ }
317
+ ,.body= "hello world"
318
+ }
319
+
320
+ #define CHUNKED_W_TRAILING_HEADERS 10
321
+ , {.name= "chunked with trailing headers. blech."
322
+ ,.type= HTTP_REQUEST
323
+ ,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
324
+ "Transfer-Encoding: chunked\r\n"
325
+ "\r\n"
326
+ "5\r\nhello\r\n"
327
+ "6\r\n world\r\n"
328
+ "0\r\n"
329
+ "Vary: *\r\n"
330
+ "Content-Type: text/plain\r\n"
331
+ "\r\n"
332
+ ,.should_keep_alive= TRUE
333
+ ,.message_complete_on_eof= FALSE
334
+ ,.http_major= 1
335
+ ,.http_minor= 1
336
+ ,.method= HTTP_POST
337
+ ,.query_string= ""
338
+ ,.fragment= ""
339
+ ,.request_path= "/chunked_w_trailing_headers"
340
+ ,.request_url= "/chunked_w_trailing_headers"
341
+ ,.num_headers= 3
342
+ ,.headers=
343
+ { { "Transfer-Encoding", "chunked" }
344
+ , { "Vary", "*" }
345
+ , { "Content-Type", "text/plain" }
346
+ }
347
+ ,.body= "hello world"
348
+ }
349
+
350
+ #define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
351
+ , {.name= "with bullshit after the length"
352
+ ,.type= HTTP_REQUEST
353
+ ,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
354
+ "Transfer-Encoding: chunked\r\n"
355
+ "\r\n"
356
+ "5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n"
357
+ "6; blahblah; blah\r\n world\r\n"
358
+ "0\r\n"
359
+ "\r\n"
360
+ ,.should_keep_alive= TRUE
361
+ ,.message_complete_on_eof= FALSE
362
+ ,.http_major= 1
363
+ ,.http_minor= 1
364
+ ,.method= HTTP_POST
365
+ ,.query_string= ""
366
+ ,.fragment= ""
367
+ ,.request_path= "/chunked_w_bullshit_after_length"
368
+ ,.request_url= "/chunked_w_bullshit_after_length"
369
+ ,.num_headers= 1
370
+ ,.headers=
371
+ { { "Transfer-Encoding", "chunked" }
372
+ }
373
+ ,.body= "hello world"
374
+ }
375
+
376
+ #define WITH_QUOTES 12
377
+ , {.name= "with quotes"
378
+ ,.type= HTTP_REQUEST
379
+ ,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n"
380
+ ,.should_keep_alive= TRUE
381
+ ,.message_complete_on_eof= FALSE
382
+ ,.http_major= 1
383
+ ,.http_minor= 1
384
+ ,.method= HTTP_GET
385
+ ,.query_string= "foo=\"bar\""
386
+ ,.fragment= ""
387
+ ,.request_path= "/with_\"stupid\"_quotes"
388
+ ,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\""
389
+ ,.num_headers= 0
390
+ ,.headers= { }
391
+ ,.body= ""
392
+ }
393
+
394
+ #define APACHEBENCH_GET 13
395
+ /* The server receiving this request SHOULD NOT wait for EOF
396
+ * to know that content-length == 0.
397
+ * How to represent this in a unit test? message_complete_on_eof
398
+ * Compare with NO_CONTENT_LENGTH_RESPONSE.
399
+ */
400
+ , {.name = "apachebench get"
401
+ ,.type= HTTP_REQUEST
402
+ ,.raw= "GET /test HTTP/1.0\r\n"
403
+ "Host: 0.0.0.0:5000\r\n"
404
+ "User-Agent: ApacheBench/2.3\r\n"
405
+ "Accept: */*\r\n\r\n"
406
+ ,.should_keep_alive= FALSE
407
+ ,.message_complete_on_eof= FALSE
408
+ ,.http_major= 1
409
+ ,.http_minor= 0
410
+ ,.method= HTTP_GET
411
+ ,.query_string= ""
412
+ ,.fragment= ""
413
+ ,.request_path= "/test"
414
+ ,.request_url= "/test"
415
+ ,.num_headers= 3
416
+ ,.headers= { { "Host", "0.0.0.0:5000" }
417
+ , { "User-Agent", "ApacheBench/2.3" }
418
+ , { "Accept", "*/*" }
419
+ }
420
+ ,.body= ""
421
+ }
422
+
423
+ #define QUERY_URL_WITH_QUESTION_MARK_GET 14
424
+ /* Some clients include '?' characters in query strings.
425
+ */
426
+ , {.name = "query url with question mark"
427
+ ,.type= HTTP_REQUEST
428
+ ,.raw= "GET /test.cgi?foo=bar?baz HTTP/1.1\r\n\r\n"
429
+ ,.should_keep_alive= TRUE
430
+ ,.message_complete_on_eof= FALSE
431
+ ,.http_major= 1
432
+ ,.http_minor= 1
433
+ ,.method= HTTP_GET
434
+ ,.query_string= "foo=bar?baz"
435
+ ,.fragment= ""
436
+ ,.request_path= "/test.cgi"
437
+ ,.request_url= "/test.cgi?foo=bar?baz"
438
+ ,.num_headers= 0
439
+ ,.headers= {}
440
+ ,.body= ""
441
+ }
442
+
443
+ #define PREFIX_NEWLINE_GET 15
444
+ /* Some clients, especially after a POST in a keep-alive connection,
445
+ * will send an extra CRLF before the next request
446
+ */
447
+ , {.name = "newline prefix get"
448
+ ,.type= HTTP_REQUEST
449
+ ,.raw= "\r\nGET /test HTTP/1.1\r\n\r\n"
450
+ ,.should_keep_alive= TRUE
451
+ ,.message_complete_on_eof= FALSE
452
+ ,.http_major= 1
453
+ ,.http_minor= 1
454
+ ,.method= HTTP_GET
455
+ ,.query_string= ""
456
+ ,.fragment= ""
457
+ ,.request_path= "/test"
458
+ ,.request_url= "/test"
459
+ ,.num_headers= 0
460
+ ,.headers= { }
461
+ ,.body= ""
462
+ }
463
+
464
+ #define UPGRADE_REQUEST 16
465
+ , {.name = "upgrade request"
466
+ ,.type= HTTP_REQUEST
467
+ ,.raw= "GET /demo HTTP/1.1\r\n"
468
+ "Host: example.com\r\n"
469
+ "Connection: Upgrade\r\n"
470
+ "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
471
+ "Sec-WebSocket-Protocol: sample\r\n"
472
+ "Upgrade: WebSocket\r\n"
473
+ "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
474
+ "Origin: http://example.com\r\n"
475
+ "\r\n"
476
+ ,.should_keep_alive= TRUE
477
+ ,.message_complete_on_eof= FALSE
478
+ ,.http_major= 1
479
+ ,.http_minor= 1
480
+ ,.method= HTTP_GET
481
+ ,.query_string= ""
482
+ ,.fragment= ""
483
+ ,.request_path= "/demo"
484
+ ,.request_url= "/demo"
485
+ ,.num_headers= 7
486
+ ,.upgrade=1
487
+ ,.headers= { { "Host", "example.com" }
488
+ , { "Connection", "Upgrade" }
489
+ , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
490
+ , { "Sec-WebSocket-Protocol", "sample" }
491
+ , { "Upgrade", "WebSocket" }
492
+ , { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" }
493
+ , { "Origin", "http://example.com" }
494
+ }
495
+ ,.body= ""
496
+ }
497
+
498
+ #define CONNECT_REQUEST 17
499
+ , {.name = "connect request"
500
+ ,.type= HTTP_REQUEST
501
+ ,.raw= "CONNECT home.netscape.com:443 HTTP/1.0\r\n"
502
+ "User-agent: Mozilla/1.1N\r\n"
503
+ "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
504
+ "\r\n"
505
+ ,.should_keep_alive= FALSE
506
+ ,.message_complete_on_eof= FALSE
507
+ ,.http_major= 1
508
+ ,.http_minor= 0
509
+ ,.method= HTTP_CONNECT
510
+ ,.query_string= ""
511
+ ,.fragment= ""
512
+ ,.request_path= ""
513
+ ,.request_url= "home.netscape.com:443"
514
+ ,.num_headers= 2
515
+ ,.upgrade=1
516
+ ,.headers= { { "User-agent", "Mozilla/1.1N" }
517
+ , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
518
+ }
519
+ ,.body= ""
520
+ }
521
+
522
+ #define REPORT_REQ 18
523
+ , {.name= "report request"
524
+ ,.type= HTTP_REQUEST
525
+ ,.raw= "REPORT /test HTTP/1.1\r\n"
526
+ "\r\n"
527
+ ,.should_keep_alive= TRUE
528
+ ,.message_complete_on_eof= FALSE
529
+ ,.http_major= 1
530
+ ,.http_minor= 1
531
+ ,.method= HTTP_REPORT
532
+ ,.query_string= ""
533
+ ,.fragment= ""
534
+ ,.request_path= "/test"
535
+ ,.request_url= "/test"
536
+ ,.num_headers= 0
537
+ ,.headers= {}
538
+ ,.body= ""
539
+ }
540
+
541
+ , {.name= NULL } /* sentinel */
542
+ };
543
+
544
+ /* * R E S P O N S E S * */
545
+ const struct message responses[] =
546
+ #define GOOGLE_301 0
547
+ { {.name= "google 301"
548
+ ,.type= HTTP_RESPONSE
549
+ ,.raw= "HTTP/1.1 301 Moved Permanently\r\n"
550
+ "Location: http://www.google.com/\r\n"
551
+ "Content-Type: text/html; charset=UTF-8\r\n"
552
+ "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n"
553
+ "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n"
554
+ "Cache-Control: public, max-age=2592000\r\n"
555
+ "Server: gws\r\n"
556
+ "Content-Length: 219\r\n"
557
+ "\r\n"
558
+ "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
559
+ "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
560
+ "<H1>301 Moved</H1>\n"
561
+ "The document has moved\n"
562
+ "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
563
+ "</BODY></HTML>\r\n"
564
+ ,.should_keep_alive= TRUE
565
+ ,.message_complete_on_eof= FALSE
566
+ ,.http_major= 1
567
+ ,.http_minor= 1
568
+ ,.status_code= 301
569
+ ,.num_headers= 7
570
+ ,.headers=
571
+ { { "Location", "http://www.google.com/" }
572
+ , { "Content-Type", "text/html; charset=UTF-8" }
573
+ , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
574
+ , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" }
575
+ , { "Cache-Control", "public, max-age=2592000" }
576
+ , { "Server", "gws" }
577
+ , { "Content-Length", "219" }
578
+ }
579
+ ,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
580
+ "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
581
+ "<H1>301 Moved</H1>\n"
582
+ "The document has moved\n"
583
+ "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
584
+ "</BODY></HTML>\r\n"
585
+ }
586
+
587
+ #define NO_CONTENT_LENGTH_RESPONSE 1
588
+ /* The client should wait for the server's EOF. That is, when content-length
589
+ * is not specified, and "Connection: close", the end of body is specified
590
+ * by the EOF.
591
+ * Compare with APACHEBENCH_GET
592
+ */
593
+ , {.name= "no content-length response"
594
+ ,.type= HTTP_RESPONSE
595
+ ,.raw= "HTTP/1.1 200 OK\r\n"
596
+ "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n"
597
+ "Server: Apache\r\n"
598
+ "X-Powered-By: Servlet/2.5 JSP/2.1\r\n"
599
+ "Content-Type: text/xml; charset=utf-8\r\n"
600
+ "Connection: close\r\n"
601
+ "\r\n"
602
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
603
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
604
+ " <SOAP-ENV:Body>\n"
605
+ " <SOAP-ENV:Fault>\n"
606
+ " <faultcode>SOAP-ENV:Client</faultcode>\n"
607
+ " <faultstring>Client Error</faultstring>\n"
608
+ " </SOAP-ENV:Fault>\n"
609
+ " </SOAP-ENV:Body>\n"
610
+ "</SOAP-ENV:Envelope>"
611
+ ,.should_keep_alive= FALSE
612
+ ,.message_complete_on_eof= TRUE
613
+ ,.http_major= 1
614
+ ,.http_minor= 1
615
+ ,.status_code= 200
616
+ ,.num_headers= 5
617
+ ,.headers=
618
+ { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" }
619
+ , { "Server", "Apache" }
620
+ , { "X-Powered-By", "Servlet/2.5 JSP/2.1" }
621
+ , { "Content-Type", "text/xml; charset=utf-8" }
622
+ , { "Connection", "close" }
623
+ }
624
+ ,.body= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
625
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
626
+ " <SOAP-ENV:Body>\n"
627
+ " <SOAP-ENV:Fault>\n"
628
+ " <faultcode>SOAP-ENV:Client</faultcode>\n"
629
+ " <faultstring>Client Error</faultstring>\n"
630
+ " </SOAP-ENV:Fault>\n"
631
+ " </SOAP-ENV:Body>\n"
632
+ "</SOAP-ENV:Envelope>"
633
+ }
634
+
635
+ #define NO_HEADERS_NO_BODY_404 2
636
+ , {.name= "404 no headers no body"
637
+ ,.type= HTTP_RESPONSE
638
+ ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
639
+ ,.should_keep_alive= TRUE
640
+ ,.message_complete_on_eof= FALSE
641
+ ,.http_major= 1
642
+ ,.http_minor= 1
643
+ ,.status_code= 404
644
+ ,.num_headers= 0
645
+ ,.headers= {}
646
+ ,.body_size= 0
647
+ ,.body= ""
648
+ }
649
+
650
+ #define NO_REASON_PHRASE 3
651
+ , {.name= "301 no response phrase"
652
+ ,.type= HTTP_RESPONSE
653
+ ,.raw= "HTTP/1.1 301\r\n\r\n"
654
+ ,.should_keep_alive = TRUE
655
+ ,.message_complete_on_eof= FALSE
656
+ ,.http_major= 1
657
+ ,.http_minor= 1
658
+ ,.status_code= 301
659
+ ,.num_headers= 0
660
+ ,.headers= {}
661
+ ,.body= ""
662
+ }
663
+
664
+ #define TRAILING_SPACE_ON_CHUNKED_BODY 4
665
+ , {.name="200 trailing space on chunked body"
666
+ ,.type= HTTP_RESPONSE
667
+ ,.raw= "HTTP/1.1 200 OK\r\n"
668
+ "Content-Type: text/plain\r\n"
669
+ "Transfer-Encoding: chunked\r\n"
670
+ "\r\n"
671
+ "25 \r\n"
672
+ "This is the data in the first chunk\r\n"
673
+ "\r\n"
674
+ "1C\r\n"
675
+ "and this is the second one\r\n"
676
+ "\r\n"
677
+ "0 \r\n"
678
+ "\r\n"
679
+ ,.should_keep_alive= TRUE
680
+ ,.message_complete_on_eof= FALSE
681
+ ,.http_major= 1
682
+ ,.http_minor= 1
683
+ ,.status_code= 200
684
+ ,.num_headers= 2
685
+ ,.headers=
686
+ { {"Content-Type", "text/plain" }
687
+ , {"Transfer-Encoding", "chunked" }
688
+ }
689
+ ,.body_size = 37+28
690
+ ,.body =
691
+ "This is the data in the first chunk\r\n"
692
+ "and this is the second one\r\n"
693
+
694
+ }
695
+
696
+ #define NO_CARRIAGE_RET 5
697
+ , {.name="no carriage ret"
698
+ ,.type= HTTP_RESPONSE
699
+ ,.raw= "HTTP/1.1 200 OK\n"
700
+ "Content-Type: text/html; charset=utf-8\n"
701
+ "Connection: close\n"
702
+ "\n"
703
+ "these headers are from http://news.ycombinator.com/"
704
+ ,.should_keep_alive= FALSE
705
+ ,.message_complete_on_eof= TRUE
706
+ ,.http_major= 1
707
+ ,.http_minor= 1
708
+ ,.status_code= 200
709
+ ,.num_headers= 2
710
+ ,.headers=
711
+ { {"Content-Type", "text/html; charset=utf-8" }
712
+ , {"Connection", "close" }
713
+ }
714
+ ,.body= "these headers are from http://news.ycombinator.com/"
715
+ }
716
+
717
+ #define PROXY_CONNECTION 6
718
+ , {.name="proxy connection"
719
+ ,.type= HTTP_RESPONSE
720
+ ,.raw= "HTTP/1.1 200 OK\r\n"
721
+ "Content-Type: text/html; charset=UTF-8\r\n"
722
+ "Content-Length: 11\r\n"
723
+ "Proxy-Connection: close\r\n"
724
+ "Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n"
725
+ "\r\n"
726
+ "hello world"
727
+ ,.should_keep_alive= FALSE
728
+ ,.message_complete_on_eof= FALSE
729
+ ,.http_major= 1
730
+ ,.http_minor= 1
731
+ ,.status_code= 200
732
+ ,.num_headers= 4
733
+ ,.headers=
734
+ { {"Content-Type", "text/html; charset=UTF-8" }
735
+ , {"Content-Length", "11" }
736
+ , {"Proxy-Connection", "close" }
737
+ , {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"}
738
+ }
739
+ ,.body= "hello world"
740
+ }
741
+
742
+ #define UNDERSTORE_HEADER_KEY 7
743
+ // shown by
744
+ // curl -o /dev/null -v "http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;"
745
+ , {.name="underscore header key"
746
+ ,.type= HTTP_RESPONSE
747
+ ,.raw= "HTTP/1.1 200 OK\r\n"
748
+ "Server: DCLK-AdSvr\r\n"
749
+ "Content-Type: text/xml\r\n"
750
+ "Content-Length: 0\r\n"
751
+ "DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\r\n\r\n"
752
+ ,.should_keep_alive= TRUE
753
+ ,.message_complete_on_eof= FALSE
754
+ ,.http_major= 1
755
+ ,.http_minor= 1
756
+ ,.status_code= 200
757
+ ,.num_headers= 4
758
+ ,.headers=
759
+ { {"Server", "DCLK-AdSvr" }
760
+ , {"Content-Type", "text/xml" }
761
+ , {"Content-Length", "0" }
762
+ , {"DCLK_imp", "v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o" }
763
+ }
764
+ ,.body= ""
765
+ }
766
+
767
+ #define BONJOUR_MADAME_FR 8
768
+ /* The client should not merge two headers fields when the first one doesn't
769
+ * have a value.
770
+ */
771
+ , {.name= "bonjourmadame.fr"
772
+ ,.type= HTTP_RESPONSE
773
+ ,.raw= "HTTP/1.0 301 Moved Permanently\r\n"
774
+ "Date: Thu, 03 Jun 2010 09:56:32 GMT\r\n"
775
+ "Server: Apache/2.2.3 (Red Hat)\r\n"
776
+ "Cache-Control: public\r\n"
777
+ "Pragma: \r\n"
778
+ "Location: http://www.bonjourmadame.fr/\r\n"
779
+ "Vary: Accept-Encoding\r\n"
780
+ "Content-Length: 0\r\n"
781
+ "Content-Type: text/html; charset=UTF-8\r\n"
782
+ "Connection: keep-alive\r\n"
783
+ "\r\n"
784
+ ,.should_keep_alive= TRUE
785
+ ,.message_complete_on_eof= FALSE
786
+ ,.http_major= 1
787
+ ,.http_minor= 0
788
+ ,.status_code= 301
789
+ ,.num_headers= 9
790
+ ,.headers=
791
+ { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" }
792
+ , { "Server", "Apache/2.2.3 (Red Hat)" }
793
+ , { "Cache-Control", "public" }
794
+ , { "Pragma", "" }
795
+ , { "Location", "http://www.bonjourmadame.fr/" }
796
+ , { "Vary", "Accept-Encoding" }
797
+ , { "Content-Length", "0" }
798
+ , { "Content-Type", "text/html; charset=UTF-8" }
799
+ , { "Connection", "keep-alive" }
800
+ }
801
+ ,.body= ""
802
+ }
803
+
804
+ #define SPACE_IN_FIELD_RES 9
805
+ /* Should handle spaces in header fields */
806
+ , {.name= "field space"
807
+ ,.type= HTTP_RESPONSE
808
+ ,.raw= "HTTP/1.1 200 OK\r\n"
809
+ "Server: Microsoft-IIS/6.0\r\n"
810
+ "X-Powered-By: ASP.NET\r\n"
811
+ "en-US Content-Type: text/xml\r\n" /* this is the problem */
812
+ "Content-Type: text/xml\r\n"
813
+ "Content-Length: 16\r\n"
814
+ "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
815
+ "Connection: keep-alive\r\n"
816
+ "\r\n"
817
+ "<xml>hello</xml>" /* fake body */
818
+ ,.should_keep_alive= TRUE
819
+ ,.message_complete_on_eof= FALSE
820
+ ,.http_major= 1
821
+ ,.http_minor= 1
822
+ ,.status_code= 200
823
+ ,.num_headers= 7
824
+ ,.headers=
825
+ { { "Server", "Microsoft-IIS/6.0" }
826
+ , { "X-Powered-By", "ASP.NET" }
827
+ , { "en-US Content-Type", "text/xml" }
828
+ , { "Content-Type", "text/xml" }
829
+ , { "Content-Length", "16" }
830
+ , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
831
+ , { "Connection", "keep-alive" }
832
+ }
833
+ ,.body= "<xml>hello</xml>"
834
+ }
835
+
836
+ , {.name= NULL } /* sentinel */
837
+ };
838
+
839
+ int
840
+ request_path_cb (http_parser *p, const char *buf, size_t len)
841
+ {
842
+ assert(p == parser);
843
+ strncat(messages[num_messages].request_path, buf, len);
844
+ return 0;
845
+ }
846
+
847
+ int
848
+ request_url_cb (http_parser *p, const char *buf, size_t len)
849
+ {
850
+ assert(p == parser);
851
+ strncat(messages[num_messages].request_url, buf, len);
852
+ return 0;
853
+ }
854
+
855
+ int
856
+ query_string_cb (http_parser *p, const char *buf, size_t len)
857
+ {
858
+ assert(p == parser);
859
+ strncat(messages[num_messages].query_string, buf, len);
860
+ return 0;
861
+ }
862
+
863
+ int
864
+ fragment_cb (http_parser *p, const char *buf, size_t len)
865
+ {
866
+ assert(p == parser);
867
+ strncat(messages[num_messages].fragment, buf, len);
868
+ return 0;
869
+ }
870
+
871
+ int
872
+ header_field_cb (http_parser *p, const char *buf, size_t len)
873
+ {
874
+ assert(p == parser);
875
+ struct message *m = &messages[num_messages];
876
+
877
+ if (m->last_header_element != FIELD)
878
+ m->num_headers++;
879
+
880
+ strncat(m->headers[m->num_headers-1][0], buf, len);
881
+
882
+ m->last_header_element = FIELD;
883
+
884
+ return 0;
885
+ }
886
+
887
+ int
888
+ header_value_cb (http_parser *p, const char *buf, size_t len)
889
+ {
890
+ assert(p == parser);
891
+ struct message *m = &messages[num_messages];
892
+
893
+ strncat(m->headers[m->num_headers-1][1], buf, len);
894
+
895
+ m->last_header_element = VALUE;
896
+
897
+ return 0;
898
+ }
899
+
900
+ int
901
+ body_cb (http_parser *p, const char *buf, size_t len)
902
+ {
903
+ assert(p == parser);
904
+ strncat(messages[num_messages].body, buf, len);
905
+ messages[num_messages].body_size += len;
906
+ // printf("body_cb: '%s'\n", requests[num_messages].body);
907
+ return 0;
908
+ }
909
+
910
+ int
911
+ count_body_cb (http_parser *p, const char *buf, size_t len)
912
+ {
913
+ assert(p == parser);
914
+ assert(buf);
915
+ messages[num_messages].body_size += len;
916
+ return 0;
917
+ }
918
+
919
+ int
920
+ message_begin_cb (http_parser *p)
921
+ {
922
+ assert(p == parser);
923
+ messages[num_messages].message_begin_cb_called = TRUE;
924
+ return 0;
925
+ }
926
+
927
+ int
928
+ headers_complete_cb (http_parser *p)
929
+ {
930
+ assert(p == parser);
931
+ messages[num_messages].method = parser->method;
932
+ messages[num_messages].status_code = parser->status_code;
933
+ messages[num_messages].http_major = parser->http_major;
934
+ messages[num_messages].http_minor = parser->http_minor;
935
+ messages[num_messages].headers_complete_cb_called = TRUE;
936
+ messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
937
+ return 0;
938
+ }
939
+
940
+ int
941
+ message_complete_cb (http_parser *p)
942
+ {
943
+ assert(p == parser);
944
+ if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser))
945
+ {
946
+ fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
947
+ "value in both on_message_complete and on_headers_complete "
948
+ "but it doesn't! ***\n\n");
949
+ assert(0);
950
+ exit(1);
951
+ }
952
+ messages[num_messages].message_complete_cb_called = TRUE;
953
+
954
+ messages[num_messages].message_complete_on_eof = currently_parsing_eof;
955
+
956
+ num_messages++;
957
+ return 0;
958
+ }
959
+
960
+ static http_parser_settings settings =
961
+ {.on_message_begin = message_begin_cb
962
+ ,.on_header_field = header_field_cb
963
+ ,.on_header_value = header_value_cb
964
+ ,.on_path = request_path_cb
965
+ ,.on_url = request_url_cb
966
+ ,.on_fragment = fragment_cb
967
+ ,.on_query_string = query_string_cb
968
+ ,.on_body = body_cb
969
+ ,.on_headers_complete = headers_complete_cb
970
+ ,.on_message_complete = message_complete_cb
971
+ };
972
+
973
+ static http_parser_settings settings_count_body =
974
+ {.on_message_begin = message_begin_cb
975
+ ,.on_header_field = header_field_cb
976
+ ,.on_header_value = header_value_cb
977
+ ,.on_path = request_path_cb
978
+ ,.on_url = request_url_cb
979
+ ,.on_fragment = fragment_cb
980
+ ,.on_query_string = query_string_cb
981
+ ,.on_body = count_body_cb
982
+ ,.on_headers_complete = headers_complete_cb
983
+ ,.on_message_complete = message_complete_cb
984
+ };
985
+
986
+ static http_parser_settings settings_null =
987
+ {.on_message_begin = 0
988
+ ,.on_header_field = 0
989
+ ,.on_header_value = 0
990
+ ,.on_path = 0
991
+ ,.on_url = 0
992
+ ,.on_fragment = 0
993
+ ,.on_query_string = 0
994
+ ,.on_body = 0
995
+ ,.on_headers_complete = 0
996
+ ,.on_message_complete = 0
997
+ };
998
+
999
+ void
1000
+ parser_init (enum http_parser_type type)
1001
+ {
1002
+ num_messages = 0;
1003
+
1004
+ assert(parser == NULL);
1005
+
1006
+ parser = malloc(sizeof(http_parser));
1007
+
1008
+ http_parser_init(parser, type);
1009
+
1010
+ memset(&messages, 0, sizeof messages);
1011
+
1012
+ }
1013
+
1014
+ void
1015
+ parser_free ()
1016
+ {
1017
+ assert(parser);
1018
+ free(parser);
1019
+ parser = NULL;
1020
+ }
1021
+
1022
+ size_t parse (const char *buf, size_t len)
1023
+ {
1024
+ size_t nparsed;
1025
+ currently_parsing_eof = (len == 0);
1026
+ nparsed = http_parser_execute(parser, &settings, buf, len);
1027
+ return nparsed;
1028
+ }
1029
+
1030
+ size_t parse_count_body (const char *buf, size_t len)
1031
+ {
1032
+ size_t nparsed;
1033
+ currently_parsing_eof = (len == 0);
1034
+ nparsed = http_parser_execute(parser, &settings_count_body, buf, len);
1035
+ return nparsed;
1036
+ }
1037
+
1038
+ static inline int
1039
+ check_str_eq (const struct message *m,
1040
+ const char *prop,
1041
+ const char *expected,
1042
+ const char *found) {
1043
+ if (0 != strcmp(expected, found)) {
1044
+ printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1045
+ printf("expected '%s'\n", expected);
1046
+ printf(" found '%s'\n", found);
1047
+ return 0;
1048
+ }
1049
+ return 1;
1050
+ }
1051
+
1052
+ static inline int
1053
+ check_num_eq (const struct message *m,
1054
+ const char *prop,
1055
+ int expected,
1056
+ int found) {
1057
+ if (expected != found) {
1058
+ printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
1059
+ printf("expected %d\n", expected);
1060
+ printf(" found %d\n", found);
1061
+ return 0;
1062
+ }
1063
+ return 1;
1064
+ }
1065
+
1066
+ #define MESSAGE_CHECK_STR_EQ(expected, found, prop) \
1067
+ if (!check_str_eq(expected, #prop, expected->prop, found->prop)) return 0
1068
+
1069
+ #define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
1070
+ if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
1071
+
1072
+
1073
+ int
1074
+ message_eq (int index, const struct message *expected)
1075
+ {
1076
+ int i;
1077
+ struct message *m = &messages[index];
1078
+
1079
+ MESSAGE_CHECK_NUM_EQ(expected, m, http_major);
1080
+ MESSAGE_CHECK_NUM_EQ(expected, m, http_minor);
1081
+
1082
+ if (expected->type == HTTP_REQUEST) {
1083
+ MESSAGE_CHECK_NUM_EQ(expected, m, method);
1084
+ } else {
1085
+ MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
1086
+ }
1087
+
1088
+ MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);
1089
+ MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof);
1090
+
1091
+ assert(m->message_begin_cb_called);
1092
+ assert(m->headers_complete_cb_called);
1093
+ assert(m->message_complete_cb_called);
1094
+
1095
+
1096
+ MESSAGE_CHECK_STR_EQ(expected, m, request_path);
1097
+ MESSAGE_CHECK_STR_EQ(expected, m, query_string);
1098
+ MESSAGE_CHECK_STR_EQ(expected, m, fragment);
1099
+ MESSAGE_CHECK_STR_EQ(expected, m, request_url);
1100
+ if (expected->body_size) {
1101
+ MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
1102
+ } else {
1103
+ MESSAGE_CHECK_STR_EQ(expected, m, body);
1104
+ }
1105
+
1106
+ MESSAGE_CHECK_NUM_EQ(expected, m, num_headers);
1107
+
1108
+ int r;
1109
+ for (i = 0; i < m->num_headers; i++) {
1110
+ r = check_str_eq(expected, "header field", expected->headers[i][0], m->headers[i][0]);
1111
+ if (!r) return 0;
1112
+ r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]);
1113
+ if (!r) return 0;
1114
+ }
1115
+
1116
+ return 1;
1117
+ }
1118
+
1119
+ static void
1120
+ print_error (const char *raw, size_t error_location)
1121
+ {
1122
+ fprintf(stderr, "\n*** parse error ***\n\n");
1123
+
1124
+ int this_line = 0, char_len = 0;
1125
+ size_t i, j, len = strlen(raw), error_location_line = 0;
1126
+ for (i = 0; i < len; i++) {
1127
+ if (i == error_location) this_line = 1;
1128
+ switch (raw[i]) {
1129
+ case '\r':
1130
+ char_len = 2;
1131
+ fprintf(stderr, "\\r");
1132
+ break;
1133
+
1134
+ case '\n':
1135
+ char_len = 2;
1136
+ fprintf(stderr, "\\n\n");
1137
+
1138
+ if (this_line) goto print;
1139
+
1140
+ error_location_line = 0;
1141
+ continue;
1142
+
1143
+ default:
1144
+ char_len = 1;
1145
+ fputc(raw[i], stderr);
1146
+ break;
1147
+ }
1148
+ if (!this_line) error_location_line += char_len;
1149
+ }
1150
+
1151
+ fprintf(stderr, "[eof]\n");
1152
+
1153
+ print:
1154
+ for (j = 0; j < error_location_line; j++) {
1155
+ fputc(' ', stderr);
1156
+ }
1157
+ fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
1158
+ }
1159
+
1160
+
1161
+ void
1162
+ test_message (const struct message *message)
1163
+ {
1164
+ size_t raw_len = strlen(message->raw);
1165
+ size_t msg1len;
1166
+ for (msg1len = 0; msg1len < raw_len; msg1len++) {
1167
+ parser_init(message->type);
1168
+
1169
+ size_t read;
1170
+ const char *msg1 = message->raw;
1171
+ const char *msg2 = msg1 + msg1len;
1172
+ size_t msg2len = raw_len - msg1len;
1173
+
1174
+ if (msg1len) {
1175
+ read = parse(msg1, msg1len);
1176
+
1177
+ if (message->upgrade && parser->upgrade) goto test;
1178
+
1179
+ if (read != msg1len) {
1180
+ print_error(msg1, read);
1181
+ exit(1);
1182
+ }
1183
+ }
1184
+
1185
+
1186
+ read = parse(msg2, msg2len);
1187
+
1188
+ if (message->upgrade && parser->upgrade) goto test;
1189
+
1190
+ if (read != msg2len) {
1191
+ print_error(msg2, read);
1192
+ exit(1);
1193
+ }
1194
+
1195
+ read = parse(NULL, 0);
1196
+
1197
+ if (message->upgrade && parser->upgrade) goto test;
1198
+
1199
+ if (read != 0) {
1200
+ print_error(message->raw, read);
1201
+ exit(1);
1202
+ }
1203
+
1204
+ test:
1205
+
1206
+ if (num_messages != 1) {
1207
+ printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
1208
+ exit(1);
1209
+ }
1210
+
1211
+ if(!message_eq(0, message)) exit(1);
1212
+
1213
+ parser_free();
1214
+ }
1215
+ }
1216
+
1217
+ void
1218
+ test_message_count_body (const struct message *message)
1219
+ {
1220
+ parser_init(message->type);
1221
+
1222
+ size_t read;
1223
+ size_t l = strlen(message->raw);
1224
+ size_t i, toread;
1225
+ size_t chunk = 4024;
1226
+
1227
+ for (i = 0; i < l; i+= chunk) {
1228
+ toread = MIN(l-i, chunk);
1229
+ read = parse_count_body(message->raw + i, toread);
1230
+ if (read != toread) {
1231
+ print_error(message->raw, read);
1232
+ exit(1);
1233
+ }
1234
+ }
1235
+
1236
+
1237
+ read = parse_count_body(NULL, 0);
1238
+ if (read != 0) {
1239
+ print_error(message->raw, read);
1240
+ exit(1);
1241
+ }
1242
+
1243
+ if (num_messages != 1) {
1244
+ printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
1245
+ exit(1);
1246
+ }
1247
+
1248
+ if(!message_eq(0, message)) exit(1);
1249
+
1250
+ parser_free();
1251
+ }
1252
+
1253
+ void
1254
+ test_simple (const char *buf, int should_pass)
1255
+ {
1256
+ parser_init(HTTP_REQUEST);
1257
+
1258
+ size_t parsed;
1259
+ int pass;
1260
+ parsed = parse(buf, strlen(buf));
1261
+ pass = (parsed == strlen(buf));
1262
+ parsed = parse(NULL, 0);
1263
+ pass &= (parsed == 0);
1264
+
1265
+ parser_free();
1266
+
1267
+ if (pass != should_pass) {
1268
+ fprintf(stderr, "\n*** test_simple expected %s ***\n\n%s", should_pass ? "success" : "error", buf);
1269
+ exit(1);
1270
+ }
1271
+ }
1272
+
1273
+ void
1274
+ test_header_overflow_error (int req)
1275
+ {
1276
+ http_parser parser;
1277
+ http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
1278
+ size_t parsed;
1279
+ const char *buf;
1280
+ buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.0 200 OK\r\n";
1281
+ parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
1282
+ assert(parsed == strlen(buf));
1283
+
1284
+ buf = "header-key: header-value\r\n";
1285
+ int i;
1286
+ for (i = 0; i < 10000; i++) {
1287
+ if (http_parser_execute(&parser, &settings_null, buf, strlen(buf)) != strlen(buf)) {
1288
+ //fprintf(stderr, "error found on iter %d\n", i);
1289
+ return;
1290
+ }
1291
+ }
1292
+
1293
+ fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n");
1294
+ exit(1);
1295
+ }
1296
+
1297
+ void
1298
+ test_no_overflow_long_body (int req, size_t length)
1299
+ {
1300
+ http_parser parser;
1301
+ http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
1302
+ size_t parsed;
1303
+ size_t i;
1304
+ char buf1[3000];
1305
+ size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %zu\r\n\r\n",
1306
+ req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", length);
1307
+ parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
1308
+ if (parsed != buf1len)
1309
+ goto err;
1310
+
1311
+ for (i = 0; i < length; i++) {
1312
+ char foo = 'a';
1313
+ parsed = http_parser_execute(&parser, &settings_null, &foo, 1);
1314
+ if (parsed != 1)
1315
+ goto err;
1316
+ }
1317
+
1318
+ parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
1319
+ if (parsed != buf1len) goto err;
1320
+ return;
1321
+
1322
+ err:
1323
+ fprintf(stderr,
1324
+ "\n*** error in test_no_overflow_long_body %s of length %zu ***\n",
1325
+ req ? "REQUEST" : "RESPONSE",
1326
+ length);
1327
+ exit(1);
1328
+ }
1329
+
1330
+ void
1331
+ test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
1332
+ {
1333
+ int message_count = 1;
1334
+ if (!r1->upgrade) {
1335
+ message_count++;
1336
+ if (!r2->upgrade) message_count++;
1337
+ }
1338
+ int has_upgrade = (message_count < 3 || r3->upgrade);
1339
+
1340
+ char total[ strlen(r1->raw)
1341
+ + strlen(r2->raw)
1342
+ + strlen(r3->raw)
1343
+ + 1
1344
+ ];
1345
+ total[0] = '\0';
1346
+
1347
+ strcat(total, r1->raw);
1348
+ strcat(total, r2->raw);
1349
+ strcat(total, r3->raw);
1350
+
1351
+ parser_init(r1->type);
1352
+
1353
+ size_t read;
1354
+
1355
+ read = parse(total, strlen(total));
1356
+
1357
+ if (has_upgrade && parser->upgrade) goto test;
1358
+
1359
+ if (read != strlen(total)) {
1360
+ print_error(total, read);
1361
+ exit(1);
1362
+ }
1363
+
1364
+ read = parse(NULL, 0);
1365
+
1366
+ if (has_upgrade && parser->upgrade) goto test;
1367
+
1368
+ if (read != 0) {
1369
+ print_error(total, read);
1370
+ exit(1);
1371
+ }
1372
+
1373
+ test:
1374
+
1375
+ if (message_count != num_messages) {
1376
+ fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
1377
+ exit(1);
1378
+ }
1379
+
1380
+ if (!message_eq(0, r1)) exit(1);
1381
+ if (message_count > 1) {
1382
+ if (!message_eq(1, r2)) exit(1);
1383
+ if (message_count > 2) {
1384
+ if (!message_eq(2, r3)) exit(1);
1385
+ }
1386
+ }
1387
+
1388
+ parser_free();
1389
+ }
1390
+
1391
+ /* SCAN through every possible breaking to make sure the
1392
+ * parser can handle getting the content in any chunks that
1393
+ * might come from the socket
1394
+ */
1395
+ void
1396
+ test_scan (const struct message *r1, const struct message *r2, const struct message *r3)
1397
+ {
1398
+ char total[80*1024] = "\0";
1399
+ char buf1[80*1024] = "\0";
1400
+ char buf2[80*1024] = "\0";
1401
+ char buf3[80*1024] = "\0";
1402
+
1403
+ strcat(total, r1->raw);
1404
+ strcat(total, r2->raw);
1405
+ strcat(total, r3->raw);
1406
+
1407
+ size_t read;
1408
+
1409
+ int total_len = strlen(total);
1410
+
1411
+ int total_ops = 2 * (total_len - 1) * (total_len - 2) / 2;
1412
+ int ops = 0 ;
1413
+
1414
+ size_t buf1_len, buf2_len, buf3_len;
1415
+
1416
+ int i,j,type_both;
1417
+ for (type_both = 0; type_both < 2; type_both ++ ) {
1418
+ for (j = 2; j < total_len; j ++ ) {
1419
+ for (i = 1; i < j; i ++ ) {
1420
+
1421
+ if (ops % 1000 == 0) {
1422
+ printf("\b\b\b\b%3.0f%%", 100 * (float)ops /(float)total_ops);
1423
+ fflush(stdout);
1424
+ }
1425
+ ops += 1;
1426
+
1427
+ parser_init(type_both ? HTTP_BOTH : r1->type);
1428
+
1429
+ buf1_len = i;
1430
+ strncpy(buf1, total, buf1_len);
1431
+ buf1[buf1_len] = 0;
1432
+
1433
+ buf2_len = j - i;
1434
+ strncpy(buf2, total+i, buf2_len);
1435
+ buf2[buf2_len] = 0;
1436
+
1437
+ buf3_len = total_len - j;
1438
+ strncpy(buf3, total+j, buf3_len);
1439
+ buf3[buf3_len] = 0;
1440
+
1441
+ read = parse(buf1, buf1_len);
1442
+
1443
+ if (r3->upgrade && parser->upgrade) goto test;
1444
+
1445
+ if (read != buf1_len) {
1446
+ print_error(buf1, read);
1447
+ goto error;
1448
+ }
1449
+
1450
+ read = parse(buf2, buf2_len);
1451
+
1452
+ if (r3->upgrade && parser->upgrade) goto test;
1453
+
1454
+ if (read != buf2_len) {
1455
+ print_error(buf2, read);
1456
+ goto error;
1457
+ }
1458
+
1459
+ read = parse(buf3, buf3_len);
1460
+
1461
+ if (r3->upgrade && parser->upgrade) goto test;
1462
+
1463
+ if (read != buf3_len) {
1464
+ print_error(buf3, read);
1465
+ goto error;
1466
+ }
1467
+
1468
+ parse(NULL, 0);
1469
+
1470
+ test:
1471
+
1472
+ if (3 != num_messages) {
1473
+ fprintf(stderr, "\n\nParser didn't see 3 messages only %d\n", num_messages);
1474
+ goto error;
1475
+ }
1476
+
1477
+ if (!message_eq(0, r1)) {
1478
+ fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n");
1479
+ goto error;
1480
+ }
1481
+
1482
+ if (!message_eq(1, r2)) {
1483
+ fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
1484
+ goto error;
1485
+ }
1486
+
1487
+ if (!message_eq(2, r3)) {
1488
+ fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
1489
+ goto error;
1490
+ }
1491
+
1492
+ parser_free();
1493
+ }
1494
+ }
1495
+ }
1496
+ puts("\b\b\b\b100%");
1497
+ return;
1498
+
1499
+ error:
1500
+ fprintf(stderr, "i=%d j=%d\n", i, j);
1501
+ fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1);
1502
+ fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2);
1503
+ fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3);
1504
+ exit(1);
1505
+ }
1506
+
1507
+ // user required to free the result
1508
+ // string terminated by \0
1509
+ char *
1510
+ create_large_chunked_message (int body_size_in_kb, const char* headers)
1511
+ {
1512
+ int i;
1513
+ size_t wrote = 0;
1514
+ size_t headers_len = strlen(headers);
1515
+ size_t bufsize = headers_len + (5+1024+2)*body_size_in_kb + 6;
1516
+ char * buf = malloc(bufsize);
1517
+
1518
+ memcpy(buf, headers, headers_len);
1519
+ wrote += headers_len;
1520
+
1521
+ for (i = 0; i < body_size_in_kb; i++) {
1522
+ // write 1kb chunk into the body.
1523
+ memcpy(buf + wrote, "400\r\n", 5);
1524
+ wrote += 5;
1525
+ memset(buf + wrote, 'C', 1024);
1526
+ wrote += 1024;
1527
+ strcpy(buf + wrote, "\r\n");
1528
+ wrote += 2;
1529
+ }
1530
+
1531
+ memcpy(buf + wrote, "0\r\n\r\n", 6);
1532
+ wrote += 6;
1533
+ assert(wrote == bufsize);
1534
+
1535
+ return buf;
1536
+ }
1537
+
1538
+ char *
1539
+ quote(const char * orig) {
1540
+ size_t j, i, len = strlen(orig);
1541
+ char * quoted = malloc(len*2); // hm..
1542
+ bzero(quoted, len*2);
1543
+ for (i=0, j=0; i!=len; ++i) {
1544
+ switch (orig[i]){
1545
+ case '\n':
1546
+ quoted[j++] = '\\';
1547
+ quoted[j++] = 'n';
1548
+ break;
1549
+ case '\r':
1550
+ quoted[j++] = '\\';
1551
+ quoted[j++] = 'r';
1552
+ break;
1553
+ case '"':
1554
+ quoted[j++] = '\\';
1555
+ quoted[j++] = '"';
1556
+ break;
1557
+ default :
1558
+ quoted[j++] = orig[i];
1559
+ }
1560
+ }
1561
+ return quoted;
1562
+ }
1563
+
1564
+ void
1565
+ dump_message(const struct message * m)
1566
+ {
1567
+ int i;
1568
+ printf("name :%s\n", m->name);
1569
+ char * bla = quote(m->raw);
1570
+ printf("raw :\"%s\"\n", bla);
1571
+ free(bla);
1572
+ switch (m->type){
1573
+ case HTTP_REQUEST:
1574
+ printf("type :HTTP_REQUEST\n");break;
1575
+ case HTTP_RESPONSE:
1576
+ printf("type :HTTP_RESPONSE\n"); break;
1577
+ case HTTP_BOTH:
1578
+ printf("type :HTTP_BOTH\n");
1579
+ }
1580
+ switch (m->method) {
1581
+ case HTTP_DELETE: printf("method: HTTP_DELETE\n");break;
1582
+ case HTTP_GET: printf("method: HTTP_GET\n");break;
1583
+ case HTTP_HEAD: printf("method: HTTP_HEAD\n");break;
1584
+ case HTTP_POST: printf("method: HTTP_POST\n");break;
1585
+ case HTTP_PUT: printf("method: HTTP_PUT\n");break;
1586
+ case HTTP_CONNECT: printf("method: HTTP_CONNECT\n");break;
1587
+ case HTTP_OPTIONS: printf("method: HTTP_OPTIONS\n");break;
1588
+ case HTTP_TRACE: printf("method: HTTP_TRACE\n");break;
1589
+ case HTTP_COPY: printf("method: HTTP_COPY\n");break;
1590
+ case HTTP_LOCK: printf("method: HTTP_LOCK\n");break;
1591
+ case HTTP_MKCOL: printf("method: HTTP_MKCOL\n");break;
1592
+ case HTTP_MOVE: printf("method: HTTP_MOVE\n");break;
1593
+ case HTTP_PROPFIND: printf("method: HTTP_PROPFIND\n");break;
1594
+ case HTTP_PROPPATCH: printf("method: HTTP_PROPPATCH\n");break;
1595
+ case HTTP_UNLOCK: printf("method: HTTP_UNLOCK\n");break;
1596
+ /* subversion */
1597
+ case HTTP_REPORT: printf("method: REPORT\n"); break;
1598
+ case HTTP_MKACTIVITY: printf("method: MKACTIVITY\n"); break;
1599
+ case HTTP_CHECKOUT: printf("method: CHECKOUT\n"); break;
1600
+ case HTTP_MERGE: printf("method: MERGE\n"); break;
1601
+ default:
1602
+ break;
1603
+ }
1604
+ printf("status_code :%d\n", m->status_code);
1605
+ printf("request_path:%s\n", m->request_path);
1606
+ printf("request_url :%s\n", m->request_url);
1607
+ printf("fragment :%s\n", m->fragment);
1608
+ printf("query_string:%s\n", m->query_string);
1609
+
1610
+ bla = quote(m->body);
1611
+ printf("body :\"%s\"\n", bla);
1612
+ free(bla);
1613
+ printf("body_size :%zu\n", m->body_size);
1614
+
1615
+ for (i=0; i!=m->num_headers; ++i){
1616
+ printf("header_%d :{ \"%s\": \"%s\"}\n", i, m->headers[i][0], m->headers[i][1]);
1617
+ }
1618
+
1619
+ printf("should_keep_alive :%d\n", m->should_keep_alive);
1620
+ printf("upgrade :%d\n", m->upgrade);
1621
+ printf("http_major :%d\n", m->http_major);
1622
+ printf("http_minor :%d\n", m->http_minor);
1623
+ // printf("message_begin_cb_called :%d\n", m->message_begin_cb_called);
1624
+ // printf("headers_complete_cb_called:%d\n", m->headers_complete_cb_called);
1625
+ // printf("message_complete_cb_called:%d\n", m->message_complete_cb_called);
1626
+ // printf("message_complete_on_eof :%d\n", m->message_complete_on_eof);
1627
+ printf("\n");
1628
+ }
1629
+
1630
+ void
1631
+ dump_messages(void)
1632
+ {
1633
+ int request_count;
1634
+ for (request_count = 0; requests[request_count].name; request_count++){
1635
+ dump_message(&requests[request_count]);
1636
+ }
1637
+ for (request_count = 0; responses[request_count].name; request_count++){
1638
+ dump_message(&responses[request_count]);
1639
+ }
1640
+ }
1641
+
1642
+ int
1643
+ main (int argc, char * argv[])
1644
+ {
1645
+ parser = NULL;
1646
+ int i, j, k;
1647
+ int request_count;
1648
+ int response_count;
1649
+
1650
+ if (1 != argc) {
1651
+ if (0 == (strncmp("-dump", argv[1], sizeof("-dump")))) {
1652
+ dump_messages();
1653
+ exit(0);
1654
+ }
1655
+ }
1656
+
1657
+ printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser));
1658
+
1659
+ for (request_count = 0; requests[request_count].name; request_count++);
1660
+ for (response_count = 0; responses[response_count].name; response_count++);
1661
+
1662
+ //// OVERFLOW CONDITIONS
1663
+
1664
+ test_header_overflow_error(HTTP_REQUEST);
1665
+ test_no_overflow_long_body(HTTP_REQUEST, 1000);
1666
+ test_no_overflow_long_body(HTTP_REQUEST, 100000);
1667
+
1668
+ test_header_overflow_error(HTTP_RESPONSE);
1669
+ test_no_overflow_long_body(HTTP_RESPONSE, 1000);
1670
+ test_no_overflow_long_body(HTTP_RESPONSE, 100000);
1671
+
1672
+ //// RESPONSES
1673
+
1674
+ for (i = 0; i < response_count; i++) {
1675
+ test_message(&responses[i]);
1676
+ }
1677
+
1678
+ for (i = 0; i < response_count; i++) {
1679
+ if (!responses[i].should_keep_alive) continue;
1680
+ for (j = 0; j < response_count; j++) {
1681
+ if (!responses[j].should_keep_alive) continue;
1682
+ for (k = 0; k < response_count; k++) {
1683
+ test_multiple3(&responses[i], &responses[j], &responses[k]);
1684
+ }
1685
+ }
1686
+ }
1687
+
1688
+ test_message_count_body(&responses[NO_HEADERS_NO_BODY_404]);
1689
+ test_message_count_body(&responses[TRAILING_SPACE_ON_CHUNKED_BODY]);
1690
+
1691
+ // test very large chunked response
1692
+ {
1693
+ char * msg = create_large_chunked_message(31337,
1694
+ "HTTP/1.0 200 OK\r\n"
1695
+ "Transfer-Encoding: chunked\r\n"
1696
+ "Content-Type: text/plain\r\n"
1697
+ "\r\n");
1698
+ struct message large_chunked =
1699
+ {.name= "large chunked"
1700
+ ,.type= HTTP_RESPONSE
1701
+ ,.raw= msg
1702
+ ,.should_keep_alive= FALSE
1703
+ ,.message_complete_on_eof= FALSE
1704
+ ,.http_major= 1
1705
+ ,.http_minor= 0
1706
+ ,.status_code= 200
1707
+ ,.num_headers= 2
1708
+ ,.headers=
1709
+ { { "Transfer-Encoding", "chunked" }
1710
+ , { "Content-Type", "text/plain" }
1711
+ }
1712
+ ,.body_size= 31337*1024
1713
+ };
1714
+ test_message_count_body(&large_chunked);
1715
+ free(msg);
1716
+ }
1717
+
1718
+
1719
+
1720
+ printf("response scan 1/2 ");
1721
+ test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
1722
+ , &responses[NO_HEADERS_NO_BODY_404]
1723
+ , &responses[NO_REASON_PHRASE]
1724
+ );
1725
+
1726
+ printf("response scan 2/2 ");
1727
+ test_scan( &responses[BONJOUR_MADAME_FR]
1728
+ , &responses[UNDERSTORE_HEADER_KEY]
1729
+ , &responses[NO_CARRIAGE_RET]
1730
+ );
1731
+
1732
+ puts("responses okay");
1733
+
1734
+
1735
+ /// REQUESTS
1736
+
1737
+
1738
+ test_simple("hello world", 0);
1739
+ test_simple("GET / HTP/1.1\r\n\r\n", 0);
1740
+
1741
+ test_simple("ASDF / HTTP/1.1\r\n\r\n", 0);
1742
+ test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", 0);
1743
+ test_simple("GETA / HTTP/1.1\r\n\r\n", 0);
1744
+
1745
+ static const char *all_methods[] = {
1746
+ "DELETE",
1747
+ "GET",
1748
+ "HEAD",
1749
+ "POST",
1750
+ "PUT",
1751
+ //"CONNECT", //CONNECT can't be tested like other methods, it's a tunnel
1752
+ "OPTIONS",
1753
+ "TRACE",
1754
+ "COPY",
1755
+ "LOCK",
1756
+ "MKCOL",
1757
+ "MOVE",
1758
+ "PROPFIND",
1759
+ "PROPPATCH",
1760
+ "UNLOCK",
1761
+ 0 };
1762
+ const char **this_method;
1763
+ for (this_method = all_methods; *this_method; this_method++) {
1764
+ char buf[200];
1765
+ sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
1766
+ test_simple(buf, 1);
1767
+ }
1768
+
1769
+ const char *dumbfuck2 =
1770
+ "GET / HTTP/1.1\r\n"
1771
+ "X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n"
1772
+ "\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n"
1773
+ "\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n"
1774
+ "\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n"
1775
+ "\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n"
1776
+ "\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n"
1777
+ "\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n"
1778
+ "\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n"
1779
+ "\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n"
1780
+ "\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n"
1781
+ "\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n"
1782
+ "\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n"
1783
+ "\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n"
1784
+ "\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n"
1785
+ "\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n"
1786
+ "\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n"
1787
+ "\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n"
1788
+ "\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n"
1789
+ "\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n"
1790
+ "\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n"
1791
+ "\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n"
1792
+ "\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n"
1793
+ "\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n"
1794
+ "\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n"
1795
+ "\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n"
1796
+ "\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n"
1797
+ "\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n"
1798
+ "\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n"
1799
+ "\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n"
1800
+ "\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n"
1801
+ "\tRA==\r\n"
1802
+ "\t-----END CERTIFICATE-----\r\n"
1803
+ "\r\n";
1804
+ test_simple(dumbfuck2, 0);
1805
+
1806
+ #if 0
1807
+ // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
1808
+ // until EOF.
1809
+ //
1810
+ // no content-length
1811
+ // error if there is a body without content length
1812
+ const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\n"
1813
+ "Accept: */*\r\n"
1814
+ "\r\n"
1815
+ "HELLO";
1816
+ test_simple(bad_get_no_headers_no_body, 0);
1817
+ #endif
1818
+ /* TODO sending junk and large headers gets rejected */
1819
+
1820
+
1821
+ /* check to make sure our predefined requests are okay */
1822
+ for (i = 0; requests[i].name; i++) {
1823
+ test_message(&requests[i]);
1824
+ }
1825
+
1826
+
1827
+
1828
+ for (i = 0; i < request_count; i++) {
1829
+ if (!requests[i].should_keep_alive) continue;
1830
+ for (j = 0; j < request_count; j++) {
1831
+ if (!requests[j].should_keep_alive) continue;
1832
+ for (k = 0; k < request_count; k++) {
1833
+ test_multiple3(&requests[i], &requests[j], &requests[k]);
1834
+ }
1835
+ }
1836
+ }
1837
+
1838
+ printf("request scan 1/4 ");
1839
+ test_scan( &requests[GET_NO_HEADERS_NO_BODY]
1840
+ , &requests[GET_ONE_HEADER_NO_BODY]
1841
+ , &requests[GET_NO_HEADERS_NO_BODY]
1842
+ );
1843
+
1844
+ printf("request scan 2/4 ");
1845
+ test_scan( &requests[POST_CHUNKED_ALL_YOUR_BASE]
1846
+ , &requests[POST_IDENTITY_BODY_WORLD]
1847
+ , &requests[GET_FUNKY_CONTENT_LENGTH]
1848
+ );
1849
+
1850
+ printf("request scan 3/4 ");
1851
+ test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END]
1852
+ , &requests[CHUNKED_W_TRAILING_HEADERS]
1853
+ , &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH]
1854
+ );
1855
+
1856
+ printf("request scan 4/4 ");
1857
+ test_scan( &requests[QUERY_URL_WITH_QUESTION_MARK_GET]
1858
+ , &requests[PREFIX_NEWLINE_GET ]
1859
+ , &requests[CONNECT_REQUEST]
1860
+ );
1861
+
1862
+ puts("requests okay");
1863
+
1864
+ return 0;
1865
+ }