yakischloba-http-parser 0.0.2
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.
- data/Rakefile +30 -0
- data/http-parser.gemspec +30 -0
- data/lib/http-parser.rb +193 -0
- data/src/LICENSE +79 -0
- data/src/Makefile +27 -0
- data/src/README.md +129 -0
- data/src/http_parser.h +133 -0
- data/src/http_parser.rl +502 -0
- data/src/test.c +785 -0
- data/test/request.rb +82 -0
- data/test/response.rb +79 -0
- metadata +95 -0
data/src/test.c
ADDED
@@ -0,0 +1,785 @@
|
|
1
|
+
#include "http_parser.h"
|
2
|
+
#include <stdlib.h>
|
3
|
+
#include <assert.h>
|
4
|
+
#include <stdio.h>
|
5
|
+
#include <string.h>
|
6
|
+
#include <stdarg.h>
|
7
|
+
|
8
|
+
#undef TRUE
|
9
|
+
#define TRUE 1
|
10
|
+
#undef FALSE
|
11
|
+
#define FALSE 0
|
12
|
+
|
13
|
+
#define MAX_HEADERS 10
|
14
|
+
#define MAX_ELEMENT_SIZE 500
|
15
|
+
|
16
|
+
static http_parser parser;
|
17
|
+
struct message {
|
18
|
+
const char *name; // for debugging purposes
|
19
|
+
const char *raw;
|
20
|
+
enum http_parser_type type;
|
21
|
+
int method;
|
22
|
+
int status_code;
|
23
|
+
char request_path[MAX_ELEMENT_SIZE];
|
24
|
+
char request_uri[MAX_ELEMENT_SIZE];
|
25
|
+
char fragment[MAX_ELEMENT_SIZE];
|
26
|
+
char query_string[MAX_ELEMENT_SIZE];
|
27
|
+
char body[MAX_ELEMENT_SIZE];
|
28
|
+
int num_headers;
|
29
|
+
enum { NONE=0, FIELD, VALUE } last_header_element;
|
30
|
+
char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
|
31
|
+
int should_keep_alive;
|
32
|
+
|
33
|
+
int message_begin_cb_called;
|
34
|
+
int headers_complete_cb_called;
|
35
|
+
int message_complete_cb_called;
|
36
|
+
};
|
37
|
+
|
38
|
+
static struct message messages[5];
|
39
|
+
static int num_messages;
|
40
|
+
|
41
|
+
/* * R E Q U E S T S * */
|
42
|
+
const struct message requests[] =
|
43
|
+
#define CURL_GET 0
|
44
|
+
{ {.name= "curl get"
|
45
|
+
,.type= HTTP_REQUEST
|
46
|
+
,.raw= "GET /test HTTP/1.1\r\n"
|
47
|
+
"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"
|
48
|
+
"Host: 0.0.0.0=5000\r\n"
|
49
|
+
"Accept: */*\r\n"
|
50
|
+
"\r\n"
|
51
|
+
,.should_keep_alive= TRUE
|
52
|
+
,.method= HTTP_GET
|
53
|
+
,.query_string= ""
|
54
|
+
,.fragment= ""
|
55
|
+
,.request_path= "/test"
|
56
|
+
,.request_uri= "/test"
|
57
|
+
,.num_headers= 3
|
58
|
+
,.headers=
|
59
|
+
{ { "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" }
|
60
|
+
, { "Host", "0.0.0.0=5000" }
|
61
|
+
, { "Accept", "*/*" }
|
62
|
+
}
|
63
|
+
,.body= ""
|
64
|
+
}
|
65
|
+
|
66
|
+
#define FIREFOX_GET 1
|
67
|
+
, {.name= "firefox get"
|
68
|
+
,.type= HTTP_REQUEST
|
69
|
+
,.raw= "GET /favicon.ico HTTP/1.1\r\n"
|
70
|
+
"Host: 0.0.0.0=5000\r\n"
|
71
|
+
"User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n"
|
72
|
+
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
|
73
|
+
"Accept-Language: en-us,en;q=0.5\r\n"
|
74
|
+
"Accept-Encoding: gzip,deflate\r\n"
|
75
|
+
"Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
|
76
|
+
"Keep-Alive: 300\r\n"
|
77
|
+
"Connection: keep-alive\r\n"
|
78
|
+
"\r\n"
|
79
|
+
,.should_keep_alive= TRUE
|
80
|
+
,.method= HTTP_GET
|
81
|
+
,.query_string= ""
|
82
|
+
,.fragment= ""
|
83
|
+
,.request_path= "/favicon.ico"
|
84
|
+
,.request_uri= "/favicon.ico"
|
85
|
+
,.num_headers= 8
|
86
|
+
,.headers=
|
87
|
+
{ { "Host", "0.0.0.0=5000" }
|
88
|
+
, { "User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0" }
|
89
|
+
, { "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" }
|
90
|
+
, { "Accept-Language", "en-us,en;q=0.5" }
|
91
|
+
, { "Accept-Encoding", "gzip,deflate" }
|
92
|
+
, { "Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7" }
|
93
|
+
, { "Keep-Alive", "300" }
|
94
|
+
, { "Connection", "keep-alive" }
|
95
|
+
}
|
96
|
+
,.body= ""
|
97
|
+
}
|
98
|
+
|
99
|
+
#define DUMBFUCK 2
|
100
|
+
, {.name= "dumbfuck"
|
101
|
+
,.type= HTTP_REQUEST
|
102
|
+
,.raw= "GET /dumbfuck HTTP/1.1\r\n"
|
103
|
+
"aaaaaaaaaaaaa:++++++++++\r\n"
|
104
|
+
"\r\n"
|
105
|
+
,.should_keep_alive= TRUE
|
106
|
+
,.method= HTTP_GET
|
107
|
+
,.query_string= ""
|
108
|
+
,.fragment= ""
|
109
|
+
,.request_path= "/dumbfuck"
|
110
|
+
,.request_uri= "/dumbfuck"
|
111
|
+
,.num_headers= 1
|
112
|
+
,.headers=
|
113
|
+
{ { "aaaaaaaaaaaaa", "++++++++++" }
|
114
|
+
}
|
115
|
+
,.body= ""
|
116
|
+
}
|
117
|
+
|
118
|
+
#define FRAGMENT_IN_URI 3
|
119
|
+
, {.name= "fragment in uri"
|
120
|
+
,.type= HTTP_REQUEST
|
121
|
+
,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n"
|
122
|
+
"\r\n"
|
123
|
+
,.should_keep_alive= TRUE
|
124
|
+
,.method= HTTP_GET
|
125
|
+
,.query_string= "page=1"
|
126
|
+
,.fragment= "posts-17408"
|
127
|
+
,.request_path= "/forums/1/topics/2375"
|
128
|
+
/* XXX request uri does not include fragment? */
|
129
|
+
,.request_uri= "/forums/1/topics/2375?page=1"
|
130
|
+
,.num_headers= 0
|
131
|
+
,.body= ""
|
132
|
+
}
|
133
|
+
|
134
|
+
#define GET_NO_HEADERS_NO_BODY 4
|
135
|
+
, {.name= "get no headers no body"
|
136
|
+
,.type= HTTP_REQUEST
|
137
|
+
,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n"
|
138
|
+
"\r\n"
|
139
|
+
,.should_keep_alive= TRUE
|
140
|
+
,.method= HTTP_GET
|
141
|
+
,.query_string= ""
|
142
|
+
,.fragment= ""
|
143
|
+
,.request_path= "/get_no_headers_no_body/world"
|
144
|
+
,.request_uri= "/get_no_headers_no_body/world"
|
145
|
+
,.num_headers= 0
|
146
|
+
,.body= ""
|
147
|
+
}
|
148
|
+
|
149
|
+
#define GET_ONE_HEADER_NO_BODY 5
|
150
|
+
, {.name= "get one header no body"
|
151
|
+
,.type= HTTP_REQUEST
|
152
|
+
,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n"
|
153
|
+
"Accept: */*\r\n"
|
154
|
+
"\r\n"
|
155
|
+
,.should_keep_alive= TRUE
|
156
|
+
,.method= HTTP_GET
|
157
|
+
,.query_string= ""
|
158
|
+
,.fragment= ""
|
159
|
+
,.request_path= "/get_one_header_no_body"
|
160
|
+
,.request_uri= "/get_one_header_no_body"
|
161
|
+
,.num_headers= 1
|
162
|
+
,.headers=
|
163
|
+
{ { "Accept" , "*/*" }
|
164
|
+
}
|
165
|
+
,.body= ""
|
166
|
+
}
|
167
|
+
|
168
|
+
#define GET_FUNKY_CONTENT_LENGTH 6
|
169
|
+
, {.name= "get funky content length body hello"
|
170
|
+
,.type= HTTP_REQUEST
|
171
|
+
,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n"
|
172
|
+
"conTENT-Length: 5\r\n"
|
173
|
+
"\r\n"
|
174
|
+
"HELLO"
|
175
|
+
,.should_keep_alive= FALSE
|
176
|
+
,.method= HTTP_GET
|
177
|
+
,.query_string= ""
|
178
|
+
,.fragment= ""
|
179
|
+
,.request_path= "/get_funky_content_length_body_hello"
|
180
|
+
,.request_uri= "/get_funky_content_length_body_hello"
|
181
|
+
,.num_headers= 1
|
182
|
+
,.headers=
|
183
|
+
{ { "conTENT-Length" , "5" }
|
184
|
+
}
|
185
|
+
,.body= "HELLO"
|
186
|
+
}
|
187
|
+
|
188
|
+
#define POST_IDENTITY_BODY_WORLD 7
|
189
|
+
, {.name= "post identity body world"
|
190
|
+
,.type= HTTP_REQUEST
|
191
|
+
,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
|
192
|
+
"Accept: */*\r\n"
|
193
|
+
"Transfer-Encoding: identity\r\n"
|
194
|
+
"Content-Length: 5\r\n"
|
195
|
+
"\r\n"
|
196
|
+
"World"
|
197
|
+
,.should_keep_alive= TRUE
|
198
|
+
,.method= HTTP_POST
|
199
|
+
,.query_string= "q=search"
|
200
|
+
,.fragment= "hey"
|
201
|
+
,.request_path= "/post_identity_body_world"
|
202
|
+
,.request_uri= "/post_identity_body_world?q=search"
|
203
|
+
,.num_headers= 3
|
204
|
+
,.headers=
|
205
|
+
{ { "Accept", "*/*" }
|
206
|
+
, { "Transfer-Encoding", "identity" }
|
207
|
+
, { "Content-Length", "5" }
|
208
|
+
}
|
209
|
+
,.body= "World"
|
210
|
+
}
|
211
|
+
|
212
|
+
#define POST_CHUNKED_ALL_YOUR_BASE 8
|
213
|
+
, {.name= "post - chunked body: all your base are belong to us"
|
214
|
+
,.type= HTTP_REQUEST
|
215
|
+
,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n"
|
216
|
+
"Transfer-Encoding: chunked\r\n"
|
217
|
+
"\r\n"
|
218
|
+
"1e\r\nall your base are belong to us\r\n"
|
219
|
+
"0\r\n"
|
220
|
+
"\r\n"
|
221
|
+
,.should_keep_alive= TRUE
|
222
|
+
,.method= HTTP_POST
|
223
|
+
,.query_string= ""
|
224
|
+
,.fragment= ""
|
225
|
+
,.request_path= "/post_chunked_all_your_base"
|
226
|
+
,.request_uri= "/post_chunked_all_your_base"
|
227
|
+
,.num_headers= 1
|
228
|
+
,.headers=
|
229
|
+
{ { "Transfer-Encoding" , "chunked" }
|
230
|
+
}
|
231
|
+
,.body= "all your base are belong to us"
|
232
|
+
}
|
233
|
+
|
234
|
+
#define TWO_CHUNKS_MULT_ZERO_END 9
|
235
|
+
, {.name= "two chunks ; triple zero ending"
|
236
|
+
,.type= HTTP_REQUEST
|
237
|
+
,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n"
|
238
|
+
"Transfer-Encoding: chunked\r\n"
|
239
|
+
"\r\n"
|
240
|
+
"5\r\nhello\r\n"
|
241
|
+
"6\r\n world\r\n"
|
242
|
+
"000\r\n"
|
243
|
+
"\r\n"
|
244
|
+
,.should_keep_alive= TRUE
|
245
|
+
,.method= HTTP_POST
|
246
|
+
,.query_string= ""
|
247
|
+
,.fragment= ""
|
248
|
+
,.request_path= "/two_chunks_mult_zero_end"
|
249
|
+
,.request_uri= "/two_chunks_mult_zero_end"
|
250
|
+
,.num_headers= 1
|
251
|
+
,.headers=
|
252
|
+
{ { "Transfer-Encoding", "chunked" }
|
253
|
+
}
|
254
|
+
,.body= "hello world"
|
255
|
+
}
|
256
|
+
|
257
|
+
#define CHUNKED_W_TRAILING_HEADERS 10
|
258
|
+
, {.name= "chunked with trailing headers. blech."
|
259
|
+
,.type= HTTP_REQUEST
|
260
|
+
,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
|
261
|
+
"Transfer-Encoding: chunked\r\n"
|
262
|
+
"\r\n"
|
263
|
+
"5\r\nhello\r\n"
|
264
|
+
"6\r\n world\r\n"
|
265
|
+
"0\r\n"
|
266
|
+
"Vary: *\r\n"
|
267
|
+
"Content-Type: text/plain\r\n"
|
268
|
+
"\r\n"
|
269
|
+
,.should_keep_alive= TRUE
|
270
|
+
,.method= HTTP_POST
|
271
|
+
,.query_string= ""
|
272
|
+
,.fragment= ""
|
273
|
+
,.request_path= "/chunked_w_trailing_headers"
|
274
|
+
,.request_uri= "/chunked_w_trailing_headers"
|
275
|
+
,.num_headers= 1
|
276
|
+
,.headers=
|
277
|
+
{ { "Transfer-Encoding", "chunked" }
|
278
|
+
}
|
279
|
+
,.body= "hello world"
|
280
|
+
}
|
281
|
+
|
282
|
+
#define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
|
283
|
+
, {.name= "with bullshit after the length"
|
284
|
+
,.type= HTTP_REQUEST
|
285
|
+
,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
|
286
|
+
"Transfer-Encoding: chunked\r\n"
|
287
|
+
"\r\n"
|
288
|
+
"5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n"
|
289
|
+
"6; blahblah; blah\r\n world\r\n"
|
290
|
+
"0\r\n"
|
291
|
+
"\r\n"
|
292
|
+
,.should_keep_alive= TRUE
|
293
|
+
,.method= HTTP_POST
|
294
|
+
,.query_string= ""
|
295
|
+
,.fragment= ""
|
296
|
+
,.request_path= "/chunked_w_bullshit_after_length"
|
297
|
+
,.request_uri= "/chunked_w_bullshit_after_length"
|
298
|
+
,.num_headers= 1
|
299
|
+
,.headers=
|
300
|
+
{ { "Transfer-Encoding", "chunked" }
|
301
|
+
}
|
302
|
+
,.body= "hello world"
|
303
|
+
}
|
304
|
+
|
305
|
+
, {.name= NULL } /* sentinel */
|
306
|
+
};
|
307
|
+
|
308
|
+
/* * R E S P O N S E S * */
|
309
|
+
const struct message responses[] =
|
310
|
+
{ {.name= "google 301"
|
311
|
+
,.type= HTTP_RESPONSE
|
312
|
+
,.raw= "HTTP/1.1 301 Moved Permanently\r\n"
|
313
|
+
"Location: http://www.google.com/\r\n"
|
314
|
+
"Content-Type: text/html; charset=UTF-8\r\n"
|
315
|
+
"Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n"
|
316
|
+
"Expires: Tue, 26 May 2009 11:11:49 GMT\r\n"
|
317
|
+
"Cache-Control: public, max-age=2592000\r\n"
|
318
|
+
"Server: gws\r\n"
|
319
|
+
"Content-Length: 219\r\n"
|
320
|
+
"\r\n"
|
321
|
+
"<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
|
322
|
+
"<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
|
323
|
+
"<H1>301 Moved</H1>\n"
|
324
|
+
"The document has moved\n"
|
325
|
+
"<A HREF=\"http://www.google.com/\">here</A>.\r\n"
|
326
|
+
"</BODY></HTML>\r\n"
|
327
|
+
,.should_keep_alive= TRUE
|
328
|
+
,.status_code= 301
|
329
|
+
,.num_headers= 7
|
330
|
+
,.headers=
|
331
|
+
{ { "Location", "http://www.google.com/" }
|
332
|
+
, { "Content-Type", "text/html; charset=UTF-8" }
|
333
|
+
, { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
|
334
|
+
, { "Expires", "Tue, 26 May 2009 11:11:49 GMT" }
|
335
|
+
, { "Cache-Control", "public, max-age=2592000" }
|
336
|
+
, { "Server", "gws" }
|
337
|
+
, { "Content-Length", "219" }
|
338
|
+
}
|
339
|
+
,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
|
340
|
+
"<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
|
341
|
+
"<H1>301 Moved</H1>\n"
|
342
|
+
"The document has moved\n"
|
343
|
+
"<A HREF=\"http://www.google.com/\">here</A>.\r\n"
|
344
|
+
"</BODY></HTML>\r\n"
|
345
|
+
}
|
346
|
+
|
347
|
+
, {.name= "404 no headers no body"
|
348
|
+
,.type= HTTP_RESPONSE
|
349
|
+
,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
|
350
|
+
,.should_keep_alive= TRUE
|
351
|
+
,.status_code= 404
|
352
|
+
,.num_headers= 0
|
353
|
+
,.headers= {}
|
354
|
+
,.body= ""
|
355
|
+
}
|
356
|
+
|
357
|
+
, {.name= "301 no response phrase"
|
358
|
+
,.type= HTTP_RESPONSE
|
359
|
+
,.raw= "HTTP/1.1 301\r\n\r\n"
|
360
|
+
,.should_keep_alive = TRUE
|
361
|
+
,.status_code= 301
|
362
|
+
,.num_headers= 0
|
363
|
+
,.headers= {}
|
364
|
+
,.body= ""
|
365
|
+
}
|
366
|
+
|
367
|
+
, {.name= NULL } /* sentinel */
|
368
|
+
};
|
369
|
+
|
370
|
+
int
|
371
|
+
request_path_cb (http_parser *parser, const char *p, size_t len)
|
372
|
+
{
|
373
|
+
assert(parser);
|
374
|
+
strncat(messages[num_messages].request_path, p, len);
|
375
|
+
return 0;
|
376
|
+
}
|
377
|
+
|
378
|
+
int
|
379
|
+
request_uri_cb (http_parser *parser, const char *p, size_t len)
|
380
|
+
{
|
381
|
+
assert(parser);
|
382
|
+
strncat(messages[num_messages].request_uri, p, len);
|
383
|
+
return 0;
|
384
|
+
}
|
385
|
+
|
386
|
+
int
|
387
|
+
query_string_cb (http_parser *parser, const char *p, size_t len)
|
388
|
+
{
|
389
|
+
assert(parser);
|
390
|
+
strncat(messages[num_messages].query_string, p, len);
|
391
|
+
return 0;
|
392
|
+
}
|
393
|
+
|
394
|
+
int
|
395
|
+
fragment_cb (http_parser *parser, const char *p, size_t len)
|
396
|
+
{
|
397
|
+
assert(parser);
|
398
|
+
strncat(messages[num_messages].fragment, p, len);
|
399
|
+
return 0;
|
400
|
+
}
|
401
|
+
|
402
|
+
int
|
403
|
+
header_field_cb (http_parser *parser, const char *p, size_t len)
|
404
|
+
{
|
405
|
+
assert(parser);
|
406
|
+
struct message *m = &messages[num_messages];
|
407
|
+
|
408
|
+
if (m->last_header_element != FIELD)
|
409
|
+
m->num_headers++;
|
410
|
+
|
411
|
+
strncat(m->headers[m->num_headers-1][0], p, len);
|
412
|
+
|
413
|
+
m->last_header_element = FIELD;
|
414
|
+
|
415
|
+
return 0;
|
416
|
+
}
|
417
|
+
|
418
|
+
int
|
419
|
+
header_value_cb (http_parser *parser, const char *p, size_t len)
|
420
|
+
{
|
421
|
+
assert(parser);
|
422
|
+
struct message *m = &messages[num_messages];
|
423
|
+
|
424
|
+
strncat(m->headers[m->num_headers-1][1], p, len);
|
425
|
+
|
426
|
+
m->last_header_element = VALUE;
|
427
|
+
|
428
|
+
return 0;
|
429
|
+
}
|
430
|
+
|
431
|
+
int
|
432
|
+
body_cb (http_parser *parser, const char *p, size_t len)
|
433
|
+
{
|
434
|
+
assert(parser);
|
435
|
+
strncat(messages[num_messages].body, p, len);
|
436
|
+
// printf("body_cb: '%s'\n", requests[num_messages].body);
|
437
|
+
return 0;
|
438
|
+
}
|
439
|
+
|
440
|
+
int
|
441
|
+
message_complete_cb (http_parser *parser)
|
442
|
+
{
|
443
|
+
messages[num_messages].method = parser->method;
|
444
|
+
messages[num_messages].status_code = parser->status_code;
|
445
|
+
|
446
|
+
messages[num_messages].message_complete_cb_called = TRUE;
|
447
|
+
|
448
|
+
num_messages++;
|
449
|
+
return 0;
|
450
|
+
}
|
451
|
+
|
452
|
+
int
|
453
|
+
message_begin_cb (http_parser *parser)
|
454
|
+
{
|
455
|
+
assert(parser);
|
456
|
+
messages[num_messages].message_begin_cb_called = TRUE;
|
457
|
+
return 0;
|
458
|
+
}
|
459
|
+
|
460
|
+
int
|
461
|
+
headers_complete_cb (http_parser *parser)
|
462
|
+
{
|
463
|
+
assert(parser);
|
464
|
+
messages[num_messages].headers_complete_cb_called = TRUE;
|
465
|
+
return 0;
|
466
|
+
}
|
467
|
+
|
468
|
+
void
|
469
|
+
parser_init (enum http_parser_type type)
|
470
|
+
{
|
471
|
+
num_messages = 0;
|
472
|
+
|
473
|
+
http_parser_init(&parser, type);
|
474
|
+
|
475
|
+
memset(&messages, 0, sizeof messages);
|
476
|
+
|
477
|
+
parser.on_message_begin = message_begin_cb;
|
478
|
+
parser.on_header_field = header_field_cb;
|
479
|
+
parser.on_header_value = header_value_cb;
|
480
|
+
parser.on_path = request_path_cb;
|
481
|
+
parser.on_uri = request_uri_cb;
|
482
|
+
parser.on_fragment = fragment_cb;
|
483
|
+
parser.on_query_string = query_string_cb;
|
484
|
+
parser.on_body = body_cb;
|
485
|
+
parser.on_headers_complete = headers_complete_cb;
|
486
|
+
parser.on_message_complete = message_complete_cb;
|
487
|
+
}
|
488
|
+
|
489
|
+
void
|
490
|
+
message_eq (int index, const struct message *expected)
|
491
|
+
{
|
492
|
+
int i;
|
493
|
+
struct message *m = &messages[index];
|
494
|
+
|
495
|
+
assert(m->method == expected->method);
|
496
|
+
assert(m->status_code == expected->status_code);
|
497
|
+
|
498
|
+
assert(m->message_begin_cb_called);
|
499
|
+
assert(m->headers_complete_cb_called);
|
500
|
+
assert(m->message_complete_cb_called);
|
501
|
+
|
502
|
+
assert(0 == strcmp(m->body, expected->body));
|
503
|
+
assert(0 == strcmp(m->fragment, expected->fragment));
|
504
|
+
assert(0 == strcmp(m->query_string, expected->query_string));
|
505
|
+
assert(0 == strcmp(m->request_path, expected->request_path));
|
506
|
+
assert(0 == strcmp(m->request_uri, expected->request_uri));
|
507
|
+
assert(m->num_headers == expected->num_headers);
|
508
|
+
for (i = 0; i < m->num_headers; i++) {
|
509
|
+
assert(0 == strcmp(m->headers[i][0], expected->headers[i][0]));
|
510
|
+
assert(0 == strcmp(m->headers[i][1], expected->headers[i][1]));
|
511
|
+
}
|
512
|
+
}
|
513
|
+
|
514
|
+
void
|
515
|
+
parse_messages (int message_count, const struct message *input_messages[])
|
516
|
+
{
|
517
|
+
// Concat the input messages
|
518
|
+
size_t length = 0;
|
519
|
+
int i;
|
520
|
+
for (i = 0; i < message_count; i++) {
|
521
|
+
length += strlen(input_messages[i]->raw);
|
522
|
+
}
|
523
|
+
char total[length + 1];
|
524
|
+
total[0] = '\0';
|
525
|
+
|
526
|
+
for (i = 0; i < message_count; i++) {
|
527
|
+
strcat(total, input_messages[i]->raw);
|
528
|
+
}
|
529
|
+
|
530
|
+
// Parse the stream
|
531
|
+
size_t traversed = 0;
|
532
|
+
parser_init(HTTP_REQUEST);
|
533
|
+
|
534
|
+
traversed = http_parser_execute(&parser, total, length);
|
535
|
+
|
536
|
+
assert(!http_parser_has_error(&parser));
|
537
|
+
assert(num_messages == message_count);
|
538
|
+
|
539
|
+
for (i = 0; i < message_count; i++) {
|
540
|
+
message_eq(i, input_messages[i]);
|
541
|
+
}
|
542
|
+
}
|
543
|
+
|
544
|
+
|
545
|
+
void
|
546
|
+
test_message (const struct message *message)
|
547
|
+
{
|
548
|
+
size_t traversed = 0;
|
549
|
+
parser_init(message->type);
|
550
|
+
|
551
|
+
traversed = http_parser_execute(&parser, message->raw, strlen(message->raw));
|
552
|
+
assert(!http_parser_has_error(&parser));
|
553
|
+
assert(num_messages == 1);
|
554
|
+
|
555
|
+
message_eq(0, message);
|
556
|
+
}
|
557
|
+
|
558
|
+
void
|
559
|
+
test_error (const char *buf)
|
560
|
+
{
|
561
|
+
size_t traversed = 0;
|
562
|
+
parser_init(HTTP_REQUEST);
|
563
|
+
|
564
|
+
traversed = http_parser_execute(&parser, buf, strlen(buf));
|
565
|
+
|
566
|
+
assert(http_parser_has_error(&parser));
|
567
|
+
}
|
568
|
+
|
569
|
+
void
|
570
|
+
test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
|
571
|
+
{
|
572
|
+
char total[ strlen(r1->raw)
|
573
|
+
+ strlen(r2->raw)
|
574
|
+
+ strlen(r3->raw)
|
575
|
+
+ 1
|
576
|
+
];
|
577
|
+
total[0] = '\0';
|
578
|
+
|
579
|
+
strcat(total, r1->raw);
|
580
|
+
strcat(total, r2->raw);
|
581
|
+
strcat(total, r3->raw);
|
582
|
+
|
583
|
+
size_t traversed = 0;
|
584
|
+
parser_init(HTTP_REQUEST);
|
585
|
+
|
586
|
+
traversed = http_parser_execute(&parser, total, strlen(total));
|
587
|
+
|
588
|
+
assert(! http_parser_has_error(&parser) );
|
589
|
+
assert(num_messages == 3);
|
590
|
+
message_eq(0, r1);
|
591
|
+
message_eq(1, r2);
|
592
|
+
message_eq(2, r3);
|
593
|
+
}
|
594
|
+
|
595
|
+
/* SCAN through every possible breaking to make sure the
|
596
|
+
* parser can handle getting the content in any chunks that
|
597
|
+
* might come from the socket
|
598
|
+
*/
|
599
|
+
void
|
600
|
+
test_scan (const struct message *r1, const struct message *r2, const struct message *r3)
|
601
|
+
{
|
602
|
+
char total[80*1024] = "\0";
|
603
|
+
char buf1[80*1024] = "\0";
|
604
|
+
char buf2[80*1024] = "\0";
|
605
|
+
char buf3[80*1024] = "\0";
|
606
|
+
|
607
|
+
strcat(total, r1->raw);
|
608
|
+
strcat(total, r2->raw);
|
609
|
+
strcat(total, r3->raw);
|
610
|
+
|
611
|
+
int total_len = strlen(total);
|
612
|
+
|
613
|
+
int total_ops = (total_len - 1) * (total_len - 2) / 2;
|
614
|
+
int ops = 0 ;
|
615
|
+
|
616
|
+
int i,j;
|
617
|
+
for (j = 2; j < total_len; j ++ ) {
|
618
|
+
for (i = 1; i < j; i ++ ) {
|
619
|
+
|
620
|
+
if (ops % 1000 == 0) {
|
621
|
+
printf("\b\b\b\b%3.0f%%", 100 * (float)ops /(float)total_ops);
|
622
|
+
fflush(stdout);
|
623
|
+
}
|
624
|
+
ops += 1;
|
625
|
+
|
626
|
+
parser_init(HTTP_REQUEST);
|
627
|
+
|
628
|
+
int buf1_len = i;
|
629
|
+
strncpy(buf1, total, buf1_len);
|
630
|
+
buf1[buf1_len] = 0;
|
631
|
+
|
632
|
+
int buf2_len = j - i;
|
633
|
+
strncpy(buf2, total+i, buf2_len);
|
634
|
+
buf2[buf2_len] = 0;
|
635
|
+
|
636
|
+
int buf3_len = total_len - j;
|
637
|
+
strncpy(buf3, total+j, buf3_len);
|
638
|
+
buf3[buf3_len] = 0;
|
639
|
+
|
640
|
+
/*
|
641
|
+
printf("buf1: %s - %d\n", buf1, buf1_len);
|
642
|
+
printf("buf2: %s - %d \n", buf2, buf2_len );
|
643
|
+
printf("buf3: %s - %d\n\n", buf3, buf3_len);
|
644
|
+
*/
|
645
|
+
|
646
|
+
http_parser_execute(&parser, buf1, buf1_len);
|
647
|
+
|
648
|
+
assert(!http_parser_has_error(&parser));
|
649
|
+
|
650
|
+
http_parser_execute(&parser, buf2, buf2_len);
|
651
|
+
|
652
|
+
assert(!http_parser_has_error(&parser));
|
653
|
+
|
654
|
+
http_parser_execute(&parser, buf3, buf3_len);
|
655
|
+
|
656
|
+
assert(! http_parser_has_error(&parser));
|
657
|
+
|
658
|
+
assert(3 == num_messages);
|
659
|
+
|
660
|
+
message_eq(0, r1);
|
661
|
+
message_eq(1, r2);
|
662
|
+
message_eq(2, r3);
|
663
|
+
}
|
664
|
+
}
|
665
|
+
puts("\b\b\b\b100%");
|
666
|
+
}
|
667
|
+
|
668
|
+
int
|
669
|
+
main (void)
|
670
|
+
{
|
671
|
+
int i, j, k;
|
672
|
+
|
673
|
+
printf("sizeof(http_parser) = %d\n", sizeof(http_parser));
|
674
|
+
|
675
|
+
int request_count;
|
676
|
+
for (request_count = 0; requests[request_count].name; request_count++);
|
677
|
+
|
678
|
+
int response_count;
|
679
|
+
for (response_count = 0; responses[response_count].name; response_count++);
|
680
|
+
|
681
|
+
|
682
|
+
//// RESPONSES
|
683
|
+
|
684
|
+
for (i = 0; i < response_count; i++) {
|
685
|
+
test_message(&responses[i]);
|
686
|
+
}
|
687
|
+
|
688
|
+
|
689
|
+
|
690
|
+
puts("responses okay");
|
691
|
+
|
692
|
+
|
693
|
+
|
694
|
+
/// REQUESTS
|
695
|
+
|
696
|
+
|
697
|
+
test_error("hello world");
|
698
|
+
test_error("GET / HTP/1.1\r\n\r\n");
|
699
|
+
|
700
|
+
const char *dumbfuck2 =
|
701
|
+
"GET / HTTP/1.1\r\n"
|
702
|
+
"X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n"
|
703
|
+
"\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n"
|
704
|
+
"\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n"
|
705
|
+
"\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n"
|
706
|
+
"\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n"
|
707
|
+
"\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n"
|
708
|
+
"\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n"
|
709
|
+
"\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n"
|
710
|
+
"\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n"
|
711
|
+
"\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n"
|
712
|
+
"\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n"
|
713
|
+
"\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n"
|
714
|
+
"\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n"
|
715
|
+
"\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n"
|
716
|
+
"\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n"
|
717
|
+
"\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n"
|
718
|
+
"\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n"
|
719
|
+
"\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n"
|
720
|
+
"\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n"
|
721
|
+
"\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n"
|
722
|
+
"\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n"
|
723
|
+
"\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n"
|
724
|
+
"\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n"
|
725
|
+
"\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n"
|
726
|
+
"\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n"
|
727
|
+
"\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n"
|
728
|
+
"\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n"
|
729
|
+
"\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n"
|
730
|
+
"\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n"
|
731
|
+
"\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n"
|
732
|
+
"\tRA==\r\n"
|
733
|
+
"\t-----END CERTIFICATE-----\r\n"
|
734
|
+
"\r\n";
|
735
|
+
test_error(dumbfuck2);
|
736
|
+
|
737
|
+
// no content-length
|
738
|
+
// error if there is a body without content length
|
739
|
+
const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\n"
|
740
|
+
"Accept: */*\r\n"
|
741
|
+
"\r\n"
|
742
|
+
"HELLO";
|
743
|
+
test_error(bad_get_no_headers_no_body);
|
744
|
+
|
745
|
+
|
746
|
+
/* TODO sending junk and large headers gets rejected */
|
747
|
+
|
748
|
+
|
749
|
+
/* check to make sure our predefined requests are okay */
|
750
|
+
for (i = 0; requests[i].name; i++) {
|
751
|
+
test_message(&requests[i]);
|
752
|
+
}
|
753
|
+
|
754
|
+
for (i = 0; i < request_count; i++) {
|
755
|
+
for (j = 0; j < request_count; j++) {
|
756
|
+
for (k = 0; k < request_count; k++) {
|
757
|
+
//printf("%d %d %d\n", i, j, k);
|
758
|
+
test_multiple3(&requests[i], &requests[j], &requests[k]);
|
759
|
+
}
|
760
|
+
}
|
761
|
+
}
|
762
|
+
|
763
|
+
printf("request scan 1/3 ");
|
764
|
+
test_scan( &requests[GET_NO_HEADERS_NO_BODY]
|
765
|
+
, &requests[GET_ONE_HEADER_NO_BODY]
|
766
|
+
, &requests[GET_NO_HEADERS_NO_BODY]
|
767
|
+
);
|
768
|
+
|
769
|
+
printf("request scan 2/3 ");
|
770
|
+
test_scan( &requests[GET_FUNKY_CONTENT_LENGTH]
|
771
|
+
, &requests[POST_IDENTITY_BODY_WORLD]
|
772
|
+
, &requests[POST_CHUNKED_ALL_YOUR_BASE]
|
773
|
+
);
|
774
|
+
|
775
|
+
printf("request scan 3/3 ");
|
776
|
+
test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END]
|
777
|
+
, &requests[CHUNKED_W_TRAILING_HEADERS]
|
778
|
+
, &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH]
|
779
|
+
);
|
780
|
+
|
781
|
+
puts("requests okay");
|
782
|
+
|
783
|
+
|
784
|
+
return 0;
|
785
|
+
}
|