jls-http_parser.rb 0.5.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,546 @@
1
+ #include "ruby.h"
2
+ #include "ext_help.h"
3
+ #include "ryah_http_parser.h"
4
+
5
+ #define GET_WRAPPER(N, from) ParserWrapper *N = (ParserWrapper *)(from)->data;
6
+ #define HASH_CAT(h, k, ptr, len) \
7
+ do { \
8
+ VALUE __v = rb_hash_aref(h, k); \
9
+ if (__v != Qnil) { \
10
+ rb_str_cat(__v, ptr, len); \
11
+ } else { \
12
+ rb_hash_aset(h, k, rb_str_new(ptr, len)); \
13
+ } \
14
+ } while(0)
15
+
16
+ typedef struct ParserWrapper {
17
+ ryah_http_parser parser;
18
+
19
+ VALUE request_url;
20
+ VALUE request_path;
21
+ VALUE query_string;
22
+ VALUE fragment;
23
+
24
+ VALUE headers;
25
+
26
+ VALUE upgrade_data;
27
+
28
+ VALUE on_message_begin;
29
+ VALUE on_headers_complete;
30
+ VALUE on_body;
31
+ VALUE on_message_complete;
32
+
33
+ VALUE callback_object;
34
+ VALUE stopped;
35
+ VALUE completed;
36
+
37
+ VALUE header_value_type;
38
+
39
+ VALUE last_field_name;
40
+ VALUE curr_field_name;
41
+
42
+ enum ryah_http_parser_type type;
43
+ } ParserWrapper;
44
+
45
+ void ParserWrapper_init(ParserWrapper *wrapper) {
46
+ ryah_http_parser_init(&wrapper->parser, wrapper->type);
47
+ wrapper->parser.status_code = 0;
48
+ wrapper->parser.http_major = 0;
49
+ wrapper->parser.http_minor = 0;
50
+
51
+ wrapper->request_url = Qnil;
52
+ wrapper->request_path = Qnil;
53
+ wrapper->query_string = Qnil;
54
+ wrapper->fragment = Qnil;
55
+
56
+ wrapper->upgrade_data = Qnil;
57
+
58
+ wrapper->headers = Qnil;
59
+ wrapper->completed = Qfalse;
60
+
61
+ wrapper->last_field_name = Qnil;
62
+ wrapper->curr_field_name = Qnil;
63
+ }
64
+
65
+ void ParserWrapper_mark(void *data) {
66
+ if(data) {
67
+ ParserWrapper *wrapper = (ParserWrapper *) data;
68
+ rb_gc_mark_maybe(wrapper->request_url);
69
+ rb_gc_mark_maybe(wrapper->request_path);
70
+ rb_gc_mark_maybe(wrapper->query_string);
71
+ rb_gc_mark_maybe(wrapper->fragment);
72
+ rb_gc_mark_maybe(wrapper->upgrade_data);
73
+ rb_gc_mark_maybe(wrapper->headers);
74
+ rb_gc_mark_maybe(wrapper->on_message_begin);
75
+ rb_gc_mark_maybe(wrapper->on_headers_complete);
76
+ rb_gc_mark_maybe(wrapper->on_body);
77
+ rb_gc_mark_maybe(wrapper->on_message_complete);
78
+ rb_gc_mark_maybe(wrapper->callback_object);
79
+ rb_gc_mark_maybe(wrapper->last_field_name);
80
+ rb_gc_mark_maybe(wrapper->curr_field_name);
81
+ }
82
+ }
83
+
84
+ void ParserWrapper_free(void *data) {
85
+ if(data) {
86
+ free(data);
87
+ }
88
+ }
89
+
90
+ static VALUE cParser;
91
+ static VALUE cRequestParser;
92
+ static VALUE cResponseParser;
93
+
94
+ static VALUE eParserError;
95
+
96
+ static ID Icall;
97
+ static ID Ion_message_begin;
98
+ static ID Ion_headers_complete;
99
+ static ID Ion_body;
100
+ static ID Ion_message_complete;
101
+
102
+ static VALUE Sstop;
103
+ static VALUE Sreset;
104
+ static VALUE Sarrays;
105
+ static VALUE Sstrings;
106
+ static VALUE Smixed;
107
+
108
+ /** Callbacks **/
109
+
110
+ int on_message_begin(ryah_http_parser *parser) {
111
+ GET_WRAPPER(wrapper, parser);
112
+
113
+ wrapper->request_url = rb_str_new2("");
114
+ wrapper->request_path = rb_str_new2("");
115
+ wrapper->query_string = rb_str_new2("");
116
+ wrapper->fragment = rb_str_new2("");
117
+ wrapper->headers = rb_hash_new();
118
+ wrapper->upgrade_data = rb_str_new2("");
119
+
120
+ VALUE ret = Qnil;
121
+
122
+ if (wrapper->callback_object != Qnil && rb_respond_to(wrapper->callback_object, Ion_message_begin)) {
123
+ ret = rb_funcall(wrapper->callback_object, Ion_message_begin, 0);
124
+ } else if (wrapper->on_message_begin != Qnil) {
125
+ ret = rb_funcall(wrapper->on_message_begin, Icall, 0);
126
+ }
127
+
128
+ if (ret == Sstop) {
129
+ wrapper->stopped = Qtrue;
130
+ return -1;
131
+ } else {
132
+ return 0;
133
+ }
134
+ }
135
+
136
+ int on_url(ryah_http_parser *parser, const char *at, size_t length) {
137
+ GET_WRAPPER(wrapper, parser);
138
+ rb_str_cat(wrapper->request_url, at, length);
139
+ return 0;
140
+ }
141
+
142
+ int on_path(ryah_http_parser *parser, const char *at, size_t length) {
143
+ GET_WRAPPER(wrapper, parser);
144
+ rb_str_cat(wrapper->request_path, at, length);
145
+ return 0;
146
+ }
147
+
148
+ int on_query_string(ryah_http_parser *parser, const char *at, size_t length) {
149
+ GET_WRAPPER(wrapper, parser);
150
+ rb_str_cat(wrapper->query_string, at, length);
151
+ return 0;
152
+ }
153
+
154
+ int on_fragment(ryah_http_parser *parser, const char *at, size_t length) {
155
+ GET_WRAPPER(wrapper, parser);
156
+ rb_str_cat(wrapper->fragment, at, length);
157
+ return 0;
158
+ }
159
+
160
+ int on_header_field(ryah_http_parser *parser, const char *at, size_t length) {
161
+ GET_WRAPPER(wrapper, parser);
162
+
163
+ if (wrapper->curr_field_name == Qnil) {
164
+ wrapper->last_field_name = Qnil;
165
+ wrapper->curr_field_name = rb_str_new(at, length);
166
+ } else {
167
+ rb_str_cat(wrapper->curr_field_name, at, length);
168
+ }
169
+
170
+ return 0;
171
+ }
172
+
173
+ int on_header_value(ryah_http_parser *parser, const char *at, size_t length) {
174
+ GET_WRAPPER(wrapper, parser);
175
+
176
+ int new_field = 0;
177
+ VALUE current_value;
178
+
179
+ if (wrapper->last_field_name == Qnil) {
180
+ new_field = 1;
181
+ wrapper->last_field_name = wrapper->curr_field_name;
182
+ wrapper->curr_field_name = Qnil;
183
+ }
184
+
185
+ current_value = rb_hash_aref(wrapper->headers, wrapper->last_field_name);
186
+
187
+ if (new_field == 1) {
188
+ if (current_value == Qnil) {
189
+ if (wrapper->header_value_type == Sarrays) {
190
+ rb_hash_aset(wrapper->headers, wrapper->last_field_name, rb_ary_new3(1, rb_str_new2("")));
191
+ } else {
192
+ rb_hash_aset(wrapper->headers, wrapper->last_field_name, rb_str_new2(""));
193
+ }
194
+ } else {
195
+ if (wrapper->header_value_type == Smixed) {
196
+ if (TYPE(current_value) == T_STRING) {
197
+ rb_hash_aset(wrapper->headers, wrapper->last_field_name, rb_ary_new3(2, current_value, rb_str_new2("")));
198
+ } else {
199
+ rb_ary_push(current_value, rb_str_new2(""));
200
+ }
201
+ } else if (wrapper->header_value_type == Sarrays) {
202
+ rb_ary_push(current_value, rb_str_new2(""));
203
+ } else {
204
+ rb_str_cat(current_value, ", ", 2);
205
+ }
206
+ }
207
+ current_value = rb_hash_aref(wrapper->headers, wrapper->last_field_name);
208
+ }
209
+
210
+ if (TYPE(current_value) == T_ARRAY) {
211
+ current_value = rb_ary_entry(current_value, -1);
212
+ }
213
+
214
+ rb_str_cat(current_value, at, length);
215
+
216
+ return 0;
217
+ }
218
+
219
+ int on_headers_complete(ryah_http_parser *parser) {
220
+ GET_WRAPPER(wrapper, parser);
221
+
222
+ VALUE ret = Qnil;
223
+
224
+ if (wrapper->callback_object != Qnil && rb_respond_to(wrapper->callback_object, Ion_headers_complete)) {
225
+ ret = rb_funcall(wrapper->callback_object, Ion_headers_complete, 1, wrapper->headers);
226
+ } else if (wrapper->on_headers_complete != Qnil) {
227
+ ret = rb_funcall(wrapper->on_headers_complete, Icall, 1, wrapper->headers);
228
+ }
229
+
230
+ if (ret == Sstop) {
231
+ wrapper->stopped = Qtrue;
232
+ return -1;
233
+ } else if (ret == Sreset){
234
+ return 1;
235
+ } else {
236
+ return 0;
237
+ }
238
+ }
239
+
240
+ int on_body(ryah_http_parser *parser, const char *at, size_t length) {
241
+ GET_WRAPPER(wrapper, parser);
242
+
243
+ VALUE ret = Qnil;
244
+
245
+ if (wrapper->callback_object != Qnil && rb_respond_to(wrapper->callback_object, Ion_body)) {
246
+ ret = rb_funcall(wrapper->callback_object, Ion_body, 1, rb_str_new(at, length));
247
+ } else if (wrapper->on_body != Qnil) {
248
+ ret = rb_funcall(wrapper->on_body, Icall, 1, rb_str_new(at, length));
249
+ }
250
+
251
+ if (ret == Sstop) {
252
+ wrapper->stopped = Qtrue;
253
+ return -1;
254
+ } else {
255
+ return 0;
256
+ }
257
+ }
258
+
259
+ int on_message_complete(ryah_http_parser *parser) {
260
+ GET_WRAPPER(wrapper, parser);
261
+
262
+ VALUE ret = Qnil;
263
+ wrapper->completed = Qtrue;
264
+
265
+ if (wrapper->callback_object != Qnil && rb_respond_to(wrapper->callback_object, Ion_message_complete)) {
266
+ ret = rb_funcall(wrapper->callback_object, Ion_message_complete, 0);
267
+ } else if (wrapper->on_message_complete != Qnil) {
268
+ ret = rb_funcall(wrapper->on_message_complete, Icall, 0);
269
+ }
270
+
271
+ if (ret == Sstop) {
272
+ wrapper->stopped = Qtrue;
273
+ return -1;
274
+ } else {
275
+ return 0;
276
+ }
277
+ }
278
+
279
+ static ryah_http_parser_settings settings = {
280
+ .on_message_begin = on_message_begin,
281
+ .on_path = on_path,
282
+ .on_query_string = on_query_string,
283
+ .on_url = on_url,
284
+ .on_fragment = on_fragment,
285
+ .on_header_field = on_header_field,
286
+ .on_header_value = on_header_value,
287
+ .on_headers_complete = on_headers_complete,
288
+ .on_body = on_body,
289
+ .on_message_complete = on_message_complete
290
+ };
291
+
292
+ VALUE Parser_alloc_by_type(VALUE klass, enum ryah_http_parser_type type) {
293
+ ParserWrapper *wrapper = ALLOC_N(ParserWrapper, 1);
294
+ wrapper->type = type;
295
+ wrapper->parser.data = wrapper;
296
+
297
+ wrapper->on_message_begin = Qnil;
298
+ wrapper->on_headers_complete = Qnil;
299
+ wrapper->on_body = Qnil;
300
+ wrapper->on_message_complete = Qnil;
301
+
302
+ wrapper->callback_object = Qnil;
303
+
304
+ ParserWrapper_init(wrapper);
305
+
306
+ return Data_Wrap_Struct(klass, ParserWrapper_mark, ParserWrapper_free, wrapper);
307
+ }
308
+
309
+ VALUE Parser_alloc(VALUE klass) {
310
+ return Parser_alloc_by_type(klass, HTTP_BOTH);
311
+ }
312
+
313
+ VALUE RequestParser_alloc(VALUE klass) {
314
+ return Parser_alloc_by_type(klass, HTTP_REQUEST);
315
+ }
316
+
317
+ VALUE ResponseParser_alloc(VALUE klass) {
318
+ return Parser_alloc_by_type(klass, HTTP_RESPONSE);
319
+ }
320
+
321
+ VALUE Parser_initialize(int argc, VALUE *argv, VALUE self) {
322
+ ParserWrapper *wrapper = NULL;
323
+ DATA_GET(self, ParserWrapper, wrapper);
324
+
325
+ wrapper->header_value_type = rb_iv_get(CLASS_OF(self), "@default_header_value_type");
326
+
327
+ if (argc == 1) {
328
+ wrapper->callback_object = argv[0];
329
+ }
330
+
331
+ if (argc == 2) {
332
+ wrapper->callback_object = argv[0];
333
+ wrapper->header_value_type = argv[1];
334
+ }
335
+
336
+ return self;
337
+ }
338
+
339
+ VALUE Parser_execute(VALUE self, VALUE data) {
340
+ ParserWrapper *wrapper = NULL;
341
+
342
+ Check_Type(data, T_STRING);
343
+ char *ptr = RSTRING_PTR(data);
344
+ long len = RSTRING_LEN(data);
345
+
346
+ DATA_GET(self, ParserWrapper, wrapper);
347
+
348
+ wrapper->stopped = Qfalse;
349
+ size_t nparsed = ryah_http_parser_execute(&wrapper->parser, &settings, ptr, len);
350
+
351
+ if (wrapper->parser.upgrade) {
352
+ rb_str_cat(wrapper->upgrade_data, ptr + nparsed + 1, len - nparsed - 1);
353
+
354
+ } else if (nparsed != (size_t)len) {
355
+ if (!RTEST(wrapper->stopped) && !RTEST(wrapper->completed))
356
+ rb_raise(eParserError, "Could not parse data entirely");
357
+ else
358
+ nparsed += 1; // error states fail on the current character
359
+ }
360
+
361
+ return INT2FIX(nparsed);
362
+ }
363
+
364
+ VALUE Parser_set_on_message_begin(VALUE self, VALUE callback) {
365
+ ParserWrapper *wrapper = NULL;
366
+ DATA_GET(self, ParserWrapper, wrapper);
367
+
368
+ wrapper->on_message_begin = callback;
369
+ return callback;
370
+ }
371
+
372
+ VALUE Parser_set_on_headers_complete(VALUE self, VALUE callback) {
373
+ ParserWrapper *wrapper = NULL;
374
+ DATA_GET(self, ParserWrapper, wrapper);
375
+
376
+ wrapper->on_headers_complete = callback;
377
+ return callback;
378
+ }
379
+
380
+ VALUE Parser_set_on_body(VALUE self, VALUE callback) {
381
+ ParserWrapper *wrapper = NULL;
382
+ DATA_GET(self, ParserWrapper, wrapper);
383
+
384
+ wrapper->on_body = callback;
385
+ return callback;
386
+ }
387
+
388
+ VALUE Parser_set_on_message_complete(VALUE self, VALUE callback) {
389
+ ParserWrapper *wrapper = NULL;
390
+ DATA_GET(self, ParserWrapper, wrapper);
391
+
392
+ wrapper->on_message_complete = callback;
393
+ return callback;
394
+ }
395
+
396
+ VALUE Parser_keep_alive_p(VALUE self) {
397
+ ParserWrapper *wrapper = NULL;
398
+ DATA_GET(self, ParserWrapper, wrapper);
399
+
400
+ return http_should_keep_alive(&wrapper->parser) == 1 ? Qtrue : Qfalse;
401
+ }
402
+
403
+ VALUE Parser_upgrade_p(VALUE self) {
404
+ ParserWrapper *wrapper = NULL;
405
+ DATA_GET(self, ParserWrapper, wrapper);
406
+
407
+ return wrapper->parser.upgrade ? Qtrue : Qfalse;
408
+ }
409
+
410
+ VALUE Parser_http_version(VALUE self) {
411
+ ParserWrapper *wrapper = NULL;
412
+ DATA_GET(self, ParserWrapper, wrapper);
413
+
414
+ if (wrapper->parser.http_major == 0 && wrapper->parser.http_minor == 0)
415
+ return Qnil;
416
+ else
417
+ return rb_ary_new3(2, INT2FIX(wrapper->parser.http_major), INT2FIX(wrapper->parser.http_minor));
418
+ }
419
+
420
+ VALUE Parser_http_major(VALUE self) {
421
+ ParserWrapper *wrapper = NULL;
422
+ DATA_GET(self, ParserWrapper, wrapper);
423
+
424
+ if (wrapper->parser.http_major == 0 && wrapper->parser.http_minor == 0)
425
+ return Qnil;
426
+ else
427
+ return INT2FIX(wrapper->parser.http_major);
428
+ }
429
+
430
+ VALUE Parser_http_minor(VALUE self) {
431
+ ParserWrapper *wrapper = NULL;
432
+ DATA_GET(self, ParserWrapper, wrapper);
433
+
434
+ if (wrapper->parser.http_major == 0 && wrapper->parser.http_minor == 0)
435
+ return Qnil;
436
+ else
437
+ return INT2FIX(wrapper->parser.http_minor);
438
+ }
439
+
440
+ VALUE Parser_http_method(VALUE self) {
441
+ ParserWrapper *wrapper = NULL;
442
+ DATA_GET(self, ParserWrapper, wrapper);
443
+
444
+ if (wrapper->parser.type == HTTP_REQUEST)
445
+ return rb_str_new2(http_method_str(wrapper->parser.method));
446
+ else
447
+ return Qnil;
448
+ }
449
+
450
+ VALUE Parser_status_code(VALUE self) {
451
+ ParserWrapper *wrapper = NULL;
452
+ DATA_GET(self, ParserWrapper, wrapper);
453
+
454
+ if (wrapper->parser.status_code)
455
+ return INT2FIX(wrapper->parser.status_code);
456
+ else
457
+ return Qnil;
458
+ }
459
+
460
+ #define DEFINE_GETTER(name) \
461
+ VALUE Parser_##name(VALUE self) { \
462
+ ParserWrapper *wrapper = NULL; \
463
+ DATA_GET(self, ParserWrapper, wrapper); \
464
+ return wrapper->name; \
465
+ }
466
+
467
+ DEFINE_GETTER(request_url);
468
+ DEFINE_GETTER(request_path);
469
+ DEFINE_GETTER(query_string);
470
+ DEFINE_GETTER(fragment);
471
+ DEFINE_GETTER(headers);
472
+ DEFINE_GETTER(upgrade_data);
473
+ DEFINE_GETTER(header_value_type);
474
+
475
+ VALUE Parser_set_header_value_type(VALUE self, VALUE val) {
476
+ if (val != Sarrays && val != Sstrings && val != Smixed) {
477
+ rb_raise(rb_eArgError, "Invalid header value type");
478
+ }
479
+
480
+ ParserWrapper *wrapper = NULL;
481
+ DATA_GET(self, ParserWrapper, wrapper);
482
+ wrapper->header_value_type = val;
483
+ return wrapper->header_value_type;
484
+ }
485
+
486
+ VALUE Parser_reset(VALUE self) {
487
+ ParserWrapper *wrapper = NULL;
488
+ DATA_GET(self, ParserWrapper, wrapper);
489
+
490
+ ParserWrapper_init(wrapper);
491
+
492
+ return Qtrue;
493
+ }
494
+
495
+ void Init_ruby_http_parser() {
496
+ VALUE mHTTP = rb_define_module("HTTP");
497
+ cParser = rb_define_class_under(mHTTP, "Parser", rb_cObject);
498
+ cRequestParser = rb_define_class_under(mHTTP, "RequestParser", cParser);
499
+ cResponseParser = rb_define_class_under(mHTTP, "ResponseParser", cParser);
500
+
501
+ eParserError = rb_define_class_under(cParser, "Error", rb_eIOError);
502
+ Icall = rb_intern("call");
503
+ Ion_message_begin = rb_intern("on_message_begin");
504
+ Ion_headers_complete = rb_intern("on_headers_complete");
505
+ Ion_body = rb_intern("on_body");
506
+ Ion_message_complete = rb_intern("on_message_complete");
507
+ Sstop = ID2SYM(rb_intern("stop"));
508
+ Sreset = ID2SYM(rb_intern("reset"));
509
+
510
+ Sarrays = ID2SYM(rb_intern("arrays"));
511
+ Sstrings = ID2SYM(rb_intern("strings"));
512
+ Smixed = ID2SYM(rb_intern("mixed"));
513
+
514
+ rb_define_alloc_func(cParser, Parser_alloc);
515
+ rb_define_alloc_func(cRequestParser, RequestParser_alloc);
516
+ rb_define_alloc_func(cResponseParser, ResponseParser_alloc);
517
+
518
+ rb_define_method(cParser, "initialize", Parser_initialize, -1);
519
+
520
+ rb_define_method(cParser, "on_message_begin=", Parser_set_on_message_begin, 1);
521
+ rb_define_method(cParser, "on_headers_complete=", Parser_set_on_headers_complete, 1);
522
+ rb_define_method(cParser, "on_body=", Parser_set_on_body, 1);
523
+ rb_define_method(cParser, "on_message_complete=", Parser_set_on_message_complete, 1);
524
+ rb_define_method(cParser, "<<", Parser_execute, 1);
525
+
526
+ rb_define_method(cParser, "keep_alive?", Parser_keep_alive_p, 0);
527
+ rb_define_method(cParser, "upgrade?", Parser_upgrade_p, 0);
528
+
529
+ rb_define_method(cParser, "http_version", Parser_http_version, 0);
530
+ rb_define_method(cParser, "http_major", Parser_http_major, 0);
531
+ rb_define_method(cParser, "http_minor", Parser_http_minor, 0);
532
+
533
+ rb_define_method(cParser, "http_method", Parser_http_method, 0);
534
+ rb_define_method(cParser, "status_code", Parser_status_code, 0);
535
+
536
+ rb_define_method(cParser, "request_url", Parser_request_url, 0);
537
+ rb_define_method(cParser, "request_path", Parser_request_path, 0);
538
+ rb_define_method(cParser, "query_string", Parser_query_string, 0);
539
+ rb_define_method(cParser, "fragment", Parser_fragment, 0);
540
+ rb_define_method(cParser, "headers", Parser_headers, 0);
541
+ rb_define_method(cParser, "upgrade_data", Parser_upgrade_data, 0);
542
+ rb_define_method(cParser, "header_value_type", Parser_header_value_type, 0);
543
+ rb_define_method(cParser, "header_value_type=", Parser_set_header_value_type, 1);
544
+
545
+ rb_define_method(cParser, "reset!", Parser_reset, 0);
546
+ }