ebb 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,97 @@
1
+ /* HTTP/1.1 Parser
2
+ * Copyright 2008 ryah dahl, ry at tiny clouds punkt org
3
+ *
4
+ * Based on Zed Shaw's parser for Mongrel.
5
+ * Copyright (c) 2005 Zed A. Shaw
6
+ *
7
+ * This software may be distributed under the "MIT" license included in the
8
+ * README
9
+ */
10
+ #ifndef ebb_request_parser_h
11
+ #define ebb_request_parser_h
12
+
13
+ #include <sys/types.h>
14
+
15
+ typedef struct ebb_request ebb_request;
16
+ typedef struct ebb_request_parser ebb_request_parser;
17
+ typedef void (*ebb_header_cb)(ebb_request*, const char *at, size_t length, int header_index);
18
+ typedef void (*ebb_element_cb)(ebb_request*, const char *at, size_t length);
19
+
20
+ #define EBB_RAGEL_STACK_SIZE 10
21
+ #define EBB_MAX_MULTIPART_BOUNDARY_LEN 20
22
+
23
+ struct ebb_request {
24
+ enum { EBB_COPY
25
+ , EBB_DELETE
26
+ , EBB_GET
27
+ , EBB_HEAD
28
+ , EBB_LOCK
29
+ , EBB_MKCOL
30
+ , EBB_MOVE
31
+ , EBB_OPTIONS
32
+ , EBB_POST
33
+ , EBB_PROPFIND
34
+ , EBB_PROPPATCH
35
+ , EBB_PUT
36
+ , EBB_TRACE
37
+ , EBB_UNLOCK
38
+ } method;
39
+
40
+ enum { EBB_IDENTITY
41
+ , EBB_CHUNKED
42
+ } transfer_encoding; /* ro */
43
+
44
+ size_t content_length; /* ro - 0 if unknown */
45
+ size_t body_read; /* ro */
46
+ int eating_body; /* ro */
47
+ int expect_continue; /* ro */
48
+ unsigned int version_major; /* ro */
49
+ unsigned int version_minor; /* ro */
50
+ int number_of_headers; /* ro */
51
+ int keep_alive; /* private - use ebb_request_should_keep_alive */
52
+
53
+ char multipart_boundary[EBB_MAX_MULTIPART_BOUNDARY_LEN]; /* ro */
54
+ unsigned int multipart_boundary_len; /* ro */
55
+
56
+ /* Public - ordered list of callbacks */
57
+ ebb_element_cb on_path;
58
+ ebb_element_cb on_query_string;
59
+ ebb_element_cb on_uri;
60
+ ebb_element_cb on_fragment;
61
+ ebb_header_cb on_header_field;
62
+ ebb_header_cb on_header_value;
63
+ void (*on_headers_complete)(ebb_request *);
64
+ ebb_element_cb on_body;
65
+ void (*on_complete)(ebb_request *);
66
+ void *data;
67
+ };
68
+
69
+ struct ebb_request_parser {
70
+ int cs; /* private */
71
+ int stack[EBB_RAGEL_STACK_SIZE]; /* private */
72
+ int top; /* private */
73
+ size_t chunk_size; /* private */
74
+ unsigned eating:1; /* private */
75
+ ebb_request *current_request; /* ro */
76
+ const char *header_field_mark;
77
+ const char *header_value_mark;
78
+ const char *query_string_mark;
79
+ const char *path_mark;
80
+ const char *uri_mark;
81
+ const char *fragment_mark;
82
+
83
+ /* Public */
84
+ ebb_request* (*new_request)(void*);
85
+ void *data;
86
+ };
87
+
88
+ void ebb_request_parser_init(ebb_request_parser *parser);
89
+ size_t ebb_request_parser_execute(ebb_request_parser *parser, const char *data, size_t len);
90
+ int ebb_request_parser_has_error(ebb_request_parser *parser);
91
+ int ebb_request_parser_is_finished(ebb_request_parser *parser);
92
+ void ebb_request_init(ebb_request *);
93
+ int ebb_request_should_keep_alive(ebb_request *request);
94
+ #define ebb_request_has_body(request) \
95
+ (request->transfer_encoding == EBB_CHUNKED || request->content_length > 0 )
96
+
97
+ #endif
@@ -0,0 +1,513 @@
1
+ /* HTTP/1.1 Parser
2
+ * Copyright 2008 ryah dahl, ry at tiny clouds punkt org
3
+ *
4
+ * Based on Zed Shaw's parser for Mongrel.
5
+ * Copyright (c) 2005 Zed A. Shaw
6
+ *
7
+ * This software may be distributed under the "MIT" license included in the
8
+ * README
9
+ */
10
+ #include "ebb_request_parser.h"
11
+
12
+ #include <stdio.h>
13
+ #include <assert.h>
14
+
15
+ #define TRUE 1
16
+ #define FALSE 0
17
+ #define MIN(a,b) (a < b ? a : b)
18
+
19
+ #define REMAINING (pe - p)
20
+ #define CURRENT (parser->current_request)
21
+ #define CONTENT_LENGTH (parser->current_request->content_length)
22
+
23
+ #define LEN(FROM) (p - parser->FROM##_mark)
24
+ #define CALLBACK(FOR) \
25
+ if(parser->FOR##_mark && CURRENT->on_##FOR) { \
26
+ CURRENT->on_##FOR( CURRENT \
27
+ , parser->FOR##_mark \
28
+ , p - parser->FOR##_mark \
29
+ ); \
30
+ }
31
+ #define HEADER_CALLBACK(FOR) \
32
+ if(parser->FOR##_mark && CURRENT->on_##FOR) { \
33
+ CURRENT->on_##FOR( CURRENT \
34
+ , parser->FOR##_mark \
35
+ , p - parser->FOR##_mark \
36
+ , CURRENT->number_of_headers \
37
+ ); \
38
+ }
39
+ #define END_REQUEST \
40
+ if(CURRENT->on_complete) \
41
+ CURRENT->on_complete(CURRENT); \
42
+ CURRENT = NULL;
43
+
44
+ %%{
45
+ machine ebb_request_parser;
46
+
47
+ action mark_header_field { parser->header_field_mark = p; }
48
+ action mark_header_value { parser->header_value_mark = p; }
49
+ action mark_fragment { parser->fragment_mark = p; }
50
+ action mark_query_string { parser->query_string_mark = p; }
51
+ action mark_request_path { parser->path_mark = p; }
52
+ action mark_request_uri { parser->uri_mark = p; }
53
+
54
+ action method_copy { CURRENT->method = EBB_COPY; }
55
+ action method_delete { CURRENT->method = EBB_DELETE; }
56
+ action method_get { CURRENT->method = EBB_GET; }
57
+ action method_head { CURRENT->method = EBB_HEAD; }
58
+ action method_lock { CURRENT->method = EBB_LOCK; }
59
+ action method_mkcol { CURRENT->method = EBB_MKCOL; }
60
+ action method_move { CURRENT->method = EBB_MOVE; }
61
+ action method_options { CURRENT->method = EBB_OPTIONS; }
62
+ action method_post { CURRENT->method = EBB_POST; }
63
+ action method_propfind { CURRENT->method = EBB_PROPFIND; }
64
+ action method_proppatch { CURRENT->method = EBB_PROPPATCH; }
65
+ action method_put { CURRENT->method = EBB_PUT; }
66
+ action method_trace { CURRENT->method = EBB_TRACE; }
67
+ action method_unlock { CURRENT->method = EBB_UNLOCK; }
68
+
69
+ action write_field {
70
+ //printf("write_field!\n");
71
+ HEADER_CALLBACK(header_field);
72
+ parser->header_field_mark = NULL;
73
+ }
74
+
75
+ action write_value {
76
+ //printf("write_value!\n");
77
+ HEADER_CALLBACK(header_value);
78
+ parser->header_value_mark = NULL;
79
+ }
80
+
81
+ action request_uri {
82
+ //printf("request uri\n");
83
+ CALLBACK(uri);
84
+ parser->uri_mark = NULL;
85
+ }
86
+
87
+ action fragment {
88
+ //printf("fragment\n");
89
+ CALLBACK(fragment);
90
+ parser->fragment_mark = NULL;
91
+ }
92
+
93
+ action query_string {
94
+ //printf("query string\n");
95
+ CALLBACK(query_string);
96
+ parser->query_string_mark = NULL;
97
+ }
98
+
99
+ action request_path {
100
+ //printf("request path\n");
101
+ CALLBACK(path);
102
+ parser->path_mark = NULL;
103
+ }
104
+
105
+ action content_length {
106
+ //printf("content_length!\n");
107
+ CURRENT->content_length *= 10;
108
+ CURRENT->content_length += *p - '0';
109
+ }
110
+
111
+ action use_identity_encoding { CURRENT->transfer_encoding = EBB_IDENTITY; }
112
+ action use_chunked_encoding { CURRENT->transfer_encoding = EBB_CHUNKED; }
113
+
114
+ action set_keep_alive { CURRENT->keep_alive = TRUE; }
115
+ action set_not_keep_alive { CURRENT->keep_alive = FALSE; }
116
+
117
+ action multipart_boundary {
118
+ if(CURRENT->multipart_boundary_len == EBB_MAX_MULTIPART_BOUNDARY_LEN) {
119
+ cs = -1;
120
+ fbreak;
121
+ }
122
+ CURRENT->multipart_boundary[CURRENT->multipart_boundary_len++] = *p;
123
+ }
124
+
125
+ action expect_continue {
126
+ CURRENT->expect_continue = TRUE;
127
+ }
128
+
129
+ action trailer {
130
+ //printf("trailer\n");
131
+ /* not implemenetd yet. (do requests even have trailing headers?) */
132
+ }
133
+
134
+ action version_major {
135
+ CURRENT->version_major *= 10;
136
+ CURRENT->version_major += *p - '0';
137
+ }
138
+
139
+ action version_minor {
140
+ CURRENT->version_minor *= 10;
141
+ CURRENT->version_minor += *p - '0';
142
+ }
143
+
144
+ action end_header_line {
145
+ CURRENT->number_of_headers++;
146
+ }
147
+
148
+ action end_headers {
149
+ if(CURRENT->on_headers_complete)
150
+ CURRENT->on_headers_complete(CURRENT);
151
+ }
152
+
153
+ action add_to_chunk_size {
154
+ //printf("add to chunk size\n");
155
+ parser->chunk_size *= 16;
156
+ /* XXX: this can be optimized slightly */
157
+ if( 'A' <= *p && *p <= 'F')
158
+ parser->chunk_size += *p - 'A' + 10;
159
+ else if( 'a' <= *p && *p <= 'f')
160
+ parser->chunk_size += *p - 'a' + 10;
161
+ else if( '0' <= *p && *p <= '9')
162
+ parser->chunk_size += *p - '0';
163
+ else
164
+ assert(0 && "bad hex char");
165
+ }
166
+
167
+ action skip_chunk_data {
168
+ //printf("skip chunk data\n");
169
+ //printf("chunk_size: %d\n", parser->chunk_size);
170
+ if(parser->chunk_size > REMAINING) {
171
+ parser->eating = TRUE;
172
+ CURRENT->on_body(CURRENT, p, REMAINING);
173
+ parser->chunk_size -= REMAINING;
174
+ fhold;
175
+ fbreak;
176
+ } else {
177
+ CURRENT->on_body(CURRENT, p, parser->chunk_size);
178
+ p += parser->chunk_size;
179
+ parser->chunk_size = 0;
180
+ parser->eating = FALSE;
181
+ fhold;
182
+ fgoto chunk_end;
183
+ }
184
+ }
185
+
186
+ action end_chunked_body {
187
+ //printf("end chunked body\n");
188
+ END_REQUEST;
189
+ fret; // goto Request;
190
+ }
191
+
192
+ action start_req {
193
+ assert(CURRENT == NULL);
194
+ CURRENT = parser->new_request(parser->data);
195
+ }
196
+
197
+ action body_logic {
198
+ if(CURRENT->transfer_encoding == EBB_CHUNKED) {
199
+ fcall ChunkedBody;
200
+ } else {
201
+ /*
202
+ * EAT BODY
203
+ * this is very ugly. sorry.
204
+ *
205
+ */
206
+ if( CURRENT->content_length == 0) {
207
+
208
+ END_REQUEST;
209
+
210
+ } else if( CURRENT->content_length < REMAINING ) {
211
+ /*
212
+ *
213
+ * FINISH EATING THE BODY. there is still more
214
+ * on the buffer - so we just let it continue
215
+ * parsing after we're done
216
+ *
217
+ */
218
+ p += 1;
219
+ if( CURRENT->on_body )
220
+ CURRENT->on_body(CURRENT, p, CURRENT->content_length);
221
+
222
+ p += CURRENT->content_length;
223
+ CURRENT->body_read = CURRENT->content_length;
224
+
225
+ assert(0 <= REMAINING);
226
+
227
+ END_REQUEST;
228
+
229
+ fhold;
230
+
231
+ } else {
232
+ /*
233
+ * The body is larger than the buffer
234
+ * EAT REST OF BUFFER
235
+ * there is still more to read though. this will
236
+ * be handled on the next invokion of ebb_request_parser_execute
237
+ * right before we enter the state machine.
238
+ *
239
+ */
240
+ p += 1;
241
+ size_t eat = REMAINING;
242
+
243
+ if( CURRENT->on_body && eat > 0)
244
+ CURRENT->on_body(CURRENT, p, eat);
245
+
246
+ p += eat;
247
+ CURRENT->body_read += eat;
248
+ CURRENT->eating_body = TRUE;
249
+ //printf("eating body!\n");
250
+
251
+ assert(CURRENT->body_read < CURRENT->content_length);
252
+ assert(REMAINING == 0);
253
+
254
+ fhold; fbreak;
255
+ }
256
+ }
257
+ }
258
+
259
+ #
260
+ ##
261
+ ###
262
+ #### HTTP/1.1 STATE MACHINE
263
+ ###
264
+ ## RequestHeaders and character types are from
265
+ # Zed Shaw's beautiful Mongrel parser.
266
+
267
+ CRLF = "\r\n";
268
+
269
+ # character types
270
+ CTL = (cntrl | 127);
271
+ safe = ("$" | "-" | "_" | ".");
272
+ extra = ("!" | "*" | "'" | "(" | ")" | ",");
273
+ reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
274
+ unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">");
275
+ national = any -- (alpha | digit | reserved | extra | safe | unsafe);
276
+ unreserved = (alpha | digit | safe | extra | national);
277
+ escape = ("%" xdigit xdigit);
278
+ uchar = (unreserved | escape);
279
+ pchar = (uchar | ":" | "@" | "&" | "=" | "+");
280
+ tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
281
+
282
+ # elements
283
+ token = (ascii -- (CTL | tspecials));
284
+ quote = "\"";
285
+ # qdtext = token -- "\"";
286
+ # quoted_pair = "\" ascii;
287
+ # quoted_string = "\"" (qdtext | quoted_pair )* "\"";
288
+
289
+ # headers
290
+
291
+ Method = ( "COPY" %method_copy
292
+ | "DELETE" %method_delete
293
+ | "GET" %method_get
294
+ | "HEAD" %method_head
295
+ | "LOCK" %method_lock
296
+ | "MKCOL" %method_mkcol
297
+ | "MOVE" %method_move
298
+ | "OPTIONS" %method_options
299
+ | "POST" %method_post
300
+ | "PROPFIND" %method_propfind
301
+ | "PROPPATCH" %method_proppatch
302
+ | "PUT" %method_put
303
+ | "TRACE" %method_trace
304
+ | "UNLOCK" %method_unlock
305
+ ); # Not allowing extension methods
306
+
307
+ HTTP_Version = "HTTP/" digit+ $version_major "." digit+ $version_minor;
308
+
309
+ scheme = ( alpha | digit | "+" | "-" | "." )* ;
310
+ absolute_uri = (scheme ":" (uchar | reserved )*);
311
+ path = ( pchar+ ( "/" pchar* )* ) ;
312
+ query = ( uchar | reserved )* >mark_query_string %query_string ;
313
+ param = ( pchar | "/" )* ;
314
+ params = ( param ( ";" param )* ) ;
315
+ rel_path = ( path? (";" params)? ) ;
316
+ absolute_path = ( "/"+ rel_path ) >mark_request_path %request_path ("?" query)?;
317
+ Request_URI = ( "*" | absolute_uri | absolute_path ) >mark_request_uri %request_uri;
318
+ Fragment = ( uchar | reserved )* >mark_fragment %fragment;
319
+
320
+ field_name = ( token -- ":" )+;
321
+ Field_Name = field_name >mark_header_field %write_field;
322
+
323
+ field_value = ((any - " ") any*)?;
324
+ Field_Value = field_value >mark_header_value %write_value;
325
+
326
+ hsep = ":" " "*;
327
+ header = (field_name hsep field_value) :> CRLF;
328
+ Header = ( ("Content-Length"i hsep digit+ $content_length)
329
+ | ("Connection"i hsep
330
+ ( "Keep-Alive"i %set_keep_alive
331
+ | "close"i %set_not_keep_alive
332
+ )
333
+ )
334
+ | ("Content-Type"i hsep
335
+ "multipart/form-data" any*
336
+ "boundary=" quote token+ $multipart_boundary quote
337
+ )
338
+ | ("Transfer-Encoding"i %use_chunked_encoding hsep "identity" %use_identity_encoding)
339
+ | ("Expect"i hsep "100-continue"i %expect_continue)
340
+ | ("Trailer"i hsep field_value %trailer)
341
+ | (Field_Name hsep Field_Value)
342
+ ) :> CRLF;
343
+
344
+ Request_Line = ( Method " " Request_URI ("#" Fragment)? " " HTTP_Version CRLF ) ;
345
+ RequestHeader = Request_Line (Header %end_header_line)* :> CRLF @end_headers;
346
+
347
+ # chunked message
348
+ trailing_headers = header*;
349
+ #chunk_ext_val = token | quoted_string;
350
+ chunk_ext_val = token*;
351
+ chunk_ext_name = token*;
352
+ chunk_extension = ( ";" " "* chunk_ext_name ("=" chunk_ext_val)? )*;
353
+ last_chunk = "0"+ chunk_extension CRLF;
354
+ chunk_size = (xdigit* [1-9a-fA-F] xdigit*) $add_to_chunk_size;
355
+ chunk_end = CRLF;
356
+ chunk_body = any >skip_chunk_data;
357
+ chunk_begin = chunk_size chunk_extension CRLF;
358
+ chunk = chunk_begin chunk_body chunk_end;
359
+ ChunkedBody := chunk* last_chunk trailing_headers CRLF @end_chunked_body;
360
+
361
+ Request = RequestHeader >start_req @body_logic;
362
+
363
+ main := Request+; # sequence of requests (for keep-alive)
364
+ }%%
365
+
366
+ %% write data;
367
+
368
+ #define COPYSTACK(dest, src) for(i = 0; i < EBB_RAGEL_STACK_SIZE; i++) { dest[i] = src[i]; }
369
+
370
+ void ebb_request_parser_init(ebb_request_parser *parser)
371
+ {
372
+ int i;
373
+
374
+ int cs = 0;
375
+ int top = 0;
376
+ int stack[EBB_RAGEL_STACK_SIZE];
377
+ %% write init;
378
+ parser->cs = cs;
379
+ parser->top = top;
380
+ COPYSTACK(parser->stack, stack);
381
+
382
+ parser->chunk_size = 0;
383
+ parser->eating = 0;
384
+
385
+ parser->current_request = NULL;
386
+
387
+ parser->header_field_mark = parser->header_value_mark =
388
+ parser->query_string_mark = parser->path_mark =
389
+ parser->uri_mark = parser->fragment_mark = NULL;
390
+
391
+ parser->new_request = NULL;
392
+ }
393
+
394
+
395
+ /** exec **/
396
+ size_t ebb_request_parser_execute(ebb_request_parser *parser, const char *buffer, size_t len)
397
+ {
398
+ const char *p, *pe;
399
+ int i, cs = parser->cs;
400
+
401
+ int top = parser->top;
402
+ int stack[EBB_RAGEL_STACK_SIZE];
403
+ COPYSTACK(stack, parser->stack);
404
+
405
+ assert(parser->new_request && "undefined callback");
406
+
407
+ p = buffer;
408
+ pe = buffer+len;
409
+
410
+ if(0 < parser->chunk_size && parser->eating) {
411
+ /*
412
+ *
413
+ * eat chunked body
414
+ *
415
+ */
416
+ //printf("eat chunk body (before parse)\n");
417
+ size_t eat = MIN(len, parser->chunk_size);
418
+ if(eat == parser->chunk_size) {
419
+ parser->eating = FALSE;
420
+ }
421
+ CURRENT->on_body(CURRENT, p, eat);
422
+ p += eat;
423
+ parser->chunk_size -= eat;
424
+ //printf("eat: %d\n", eat);
425
+ } else if( parser->current_request && CURRENT->eating_body ) {
426
+ /*
427
+ *
428
+ * eat normal body
429
+ *
430
+ */
431
+ //printf("eat normal body (before parse)\n");
432
+ size_t eat = MIN(len, CURRENT->content_length - CURRENT->body_read);
433
+
434
+ CURRENT->on_body(CURRENT, p, eat);
435
+ p += eat;
436
+ CURRENT->body_read += eat;
437
+
438
+ if(CURRENT->body_read == CURRENT->content_length) {
439
+ END_REQUEST;
440
+ }
441
+ }
442
+
443
+ if(parser->header_field_mark) parser->header_field_mark = buffer;
444
+ if(parser->header_value_mark) parser->header_value_mark = buffer;
445
+ if(parser->fragment_mark) parser->fragment_mark = buffer;
446
+ if(parser->query_string_mark) parser->query_string_mark = buffer;
447
+ if(parser->path_mark) parser->path_mark = buffer;
448
+ if(parser->uri_mark) parser->uri_mark = buffer;
449
+
450
+ %% write exec;
451
+
452
+ parser->cs = cs;
453
+ parser->top = top;
454
+ COPYSTACK(parser->stack, stack);
455
+
456
+ HEADER_CALLBACK(header_field);
457
+ HEADER_CALLBACK(header_value);
458
+ CALLBACK(fragment);
459
+ CALLBACK(query_string);
460
+ CALLBACK(path);
461
+ CALLBACK(uri);
462
+
463
+ assert(p <= pe && "buffer overflow after parsing execute");
464
+
465
+ return(p - buffer);
466
+ }
467
+
468
+ int ebb_request_parser_has_error(ebb_request_parser *parser)
469
+ {
470
+ return parser->cs == ebb_request_parser_error;
471
+ }
472
+
473
+ int ebb_request_parser_is_finished(ebb_request_parser *parser)
474
+ {
475
+ return parser->cs == ebb_request_parser_first_final;
476
+ }
477
+
478
+ void ebb_request_init(ebb_request *request)
479
+ {
480
+ request->expect_continue = FALSE;
481
+ request->eating_body = 0;
482
+ request->body_read = 0;
483
+ request->content_length = 0;
484
+ request->version_major = 0;
485
+ request->version_minor = 0;
486
+ request->number_of_headers = 0;
487
+ request->transfer_encoding = EBB_IDENTITY;
488
+ request->multipart_boundary_len = 0;
489
+ request->keep_alive = -1;
490
+
491
+ request->on_complete = NULL;
492
+ request->on_headers_complete = NULL;
493
+ request->on_body = NULL;
494
+ request->on_header_field = NULL;
495
+ request->on_header_value = NULL;
496
+ request->on_uri = NULL;
497
+ request->on_fragment = NULL;
498
+ request->on_path = NULL;
499
+ request->on_query_string = NULL;
500
+ }
501
+
502
+ int ebb_request_should_keep_alive(ebb_request *request)
503
+ {
504
+ if(request->keep_alive == -1)
505
+ if(request->version_major == 1)
506
+ return (request->version_minor != 0);
507
+ else if(request->version_major == 0)
508
+ return FALSE;
509
+ else
510
+ return TRUE;
511
+ else
512
+ return request->keep_alive;
513
+ }