multipart_parser 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,581 @@
1
+ /* Based on http_parser.rb by Aman Gupta
2
+ * Benjamin Bryant - https://github.com/bhbryant/multipart_parser - 2015
3
+ * MIT License - http://www.opensource.org/licenses/mit-license.php
4
+ */
5
+
6
+ #include "ruby.h"
7
+ #include "ext_help.h"
8
+
9
+ #include <stdio.h>
10
+ #include <string.h>
11
+ #include "multipart_parser_c.h"
12
+
13
+ #define GET_WRAPPER(N, from) MultipartParserWrapper *N = (MultipartParserWrapper *)(from)->context;
14
+
15
+
16
+
17
+
18
+
19
+ /* structure to hold all the state for calling the parser */
20
+ typedef struct MultipartParserWrapper {
21
+
22
+ multipart_parser_c* parser;
23
+
24
+ VALUE headers;
25
+
26
+ /* callbacks */
27
+ VALUE on_message_begin;
28
+ VALUE on_part_begin;
29
+ VALUE on_headers_complete;
30
+ VALUE on_data;
31
+ VALUE on_part_complete;
32
+ VALUE on_message_complete;
33
+
34
+ VALUE callback_object;
35
+
36
+ VALUE header_value_type;
37
+
38
+
39
+ VALUE curr_field_name;
40
+ VALUE last_field_name;
41
+
42
+ } MultipartParserWrapper;
43
+
44
+
45
+
46
+ static VALUE cMultipartParser;
47
+
48
+
49
+ static ID Icall;
50
+
51
+ static ID Ion_message_begin;
52
+ static ID Ion_part_begin;
53
+ static ID Ion_headers_complete;
54
+ static ID Ion_data;
55
+ static ID Ion_part_complete;
56
+ static ID Ion_message_complete;
57
+
58
+
59
+ static VALUE Sarrays;
60
+ static VALUE Sstrings;
61
+ static VALUE Smixed;
62
+
63
+ /* CALLBACKS from multipart_parser */
64
+
65
+
66
+ int on_message_begin(multipart_parser_c *parser) {
67
+ GET_WRAPPER(wrapper, parser);
68
+
69
+ VALUE ret = Qnil;
70
+
71
+ if (wrapper->callback_object != Qnil && rb_respond_to(wrapper->callback_object, Ion_message_begin)) {
72
+ ret = rb_funcall(wrapper->callback_object, Ion_message_begin, 0);
73
+ } else if (wrapper->on_message_begin != Qnil) {
74
+ ret = rb_funcall(wrapper->on_message_begin, Icall, 0);
75
+ }
76
+
77
+ return 0;
78
+ }
79
+
80
+ int on_part_begin(multipart_parser_c *parser) {
81
+ GET_WRAPPER(wrapper, parser);
82
+
83
+ /* new part, reset headers hash */
84
+ wrapper->headers = rb_hash_new();
85
+
86
+ VALUE ret = Qnil;
87
+
88
+ if (wrapper->callback_object != Qnil && rb_respond_to(wrapper->callback_object, Ion_part_begin)) {
89
+ ret = rb_funcall(wrapper->callback_object, Ion_part_begin, 0);
90
+ } else if (wrapper->on_part_begin != Qnil) {
91
+ ret = rb_funcall(wrapper->on_part_begin, Icall, 0);
92
+ }
93
+
94
+ return 0;
95
+ }
96
+
97
+
98
+ /* @internal */
99
+ int on_header_field(multipart_parser_c *parser , const char *at, size_t length) {
100
+
101
+
102
+ GET_WRAPPER(wrapper, parser);
103
+
104
+ if (wrapper->curr_field_name == Qnil) {
105
+ wrapper->last_field_name = Qnil;
106
+ wrapper->curr_field_name = rb_str_new(at, length);
107
+ } else {
108
+ rb_str_cat(wrapper->curr_field_name, at, length);
109
+ }
110
+
111
+
112
+ return 0;
113
+ }
114
+
115
+
116
+ /* @internal */
117
+ int on_header_value(multipart_parser_c *parser , const char *at, size_t length) {
118
+
119
+
120
+ GET_WRAPPER(wrapper, parser);
121
+
122
+ int new_field = 0;
123
+ VALUE current_value;
124
+
125
+ if (wrapper->last_field_name == Qnil) {
126
+ new_field = 1;
127
+ wrapper->last_field_name = wrapper->curr_field_name;
128
+ wrapper->curr_field_name = Qnil;
129
+ }
130
+
131
+ current_value = rb_hash_aref(wrapper->headers, wrapper->last_field_name);
132
+
133
+ if (new_field == 1) {
134
+ if (current_value == Qnil) {
135
+ if (wrapper->header_value_type == Sarrays) {
136
+ rb_hash_aset(wrapper->headers, wrapper->last_field_name, rb_ary_new3(1, rb_str_new2("")));
137
+ } else {
138
+ rb_hash_aset(wrapper->headers, wrapper->last_field_name, rb_str_new2(""));
139
+ }
140
+ } else {
141
+ if (wrapper->header_value_type == Smixed) {
142
+ if (TYPE(current_value) == T_STRING) {
143
+ rb_hash_aset(wrapper->headers, wrapper->last_field_name, rb_ary_new3(2, current_value, rb_str_new2("")));
144
+ } else {
145
+ rb_ary_push(current_value, rb_str_new2(""));
146
+ }
147
+ } else if (wrapper->header_value_type == Sarrays) {
148
+ rb_ary_push(current_value, rb_str_new2(""));
149
+ } else {
150
+ rb_str_cat(current_value, ", ", 2);
151
+ }
152
+ }
153
+ current_value = rb_hash_aref(wrapper->headers, wrapper->last_field_name);
154
+ }
155
+
156
+ if (TYPE(current_value) == T_ARRAY) {
157
+ current_value = rb_ary_entry(current_value, -1);
158
+ }
159
+
160
+ rb_str_cat(current_value, at, length);
161
+
162
+ return 0;
163
+ }
164
+
165
+
166
+ int on_headers_complete(multipart_parser_c *parser) {
167
+ GET_WRAPPER(wrapper, parser);
168
+
169
+ VALUE ret = Qnil;
170
+
171
+ if (wrapper->callback_object != Qnil && rb_respond_to(wrapper->callback_object, Ion_headers_complete)) {
172
+ ret = rb_funcall(wrapper->callback_object, Ion_headers_complete, 1, wrapper->headers);
173
+ } else if (wrapper->on_headers_complete != Qnil) {
174
+ ret = rb_funcall(wrapper->on_headers_complete, Icall, 1, wrapper->headers);
175
+ }
176
+
177
+ return 0;
178
+ }
179
+
180
+
181
+ int on_data(multipart_parser_c *parser , const char *at, size_t length) {
182
+
183
+
184
+ GET_WRAPPER(wrapper, parser);
185
+
186
+
187
+ VALUE ret = Qnil;
188
+
189
+ if (wrapper->callback_object != Qnil && rb_respond_to(wrapper->callback_object, Ion_data)) {
190
+ ret = rb_funcall(wrapper->callback_object, Ion_data, 1, rb_str_new(at, length));
191
+ } else if (wrapper->on_data != Qnil) {
192
+ ret = rb_funcall(wrapper->on_data, Icall, 1, rb_str_new(at, length));
193
+ }
194
+
195
+
196
+ return 0;
197
+ }
198
+
199
+
200
+
201
+
202
+
203
+ int on_part_complete(multipart_parser_c *parser) {
204
+
205
+ GET_WRAPPER(wrapper, parser);
206
+
207
+ VALUE ret = Qnil;
208
+
209
+ if (wrapper->callback_object != Qnil && rb_respond_to(wrapper->callback_object, Ion_part_complete)) {
210
+ ret = rb_funcall(wrapper->callback_object, Ion_part_complete, 0);
211
+ } else if (wrapper->on_part_complete != Qnil) {
212
+ ret = rb_funcall(wrapper->on_part_complete, Icall, 0);
213
+ }
214
+
215
+ return 0;
216
+ }
217
+
218
+ int on_message_complete(multipart_parser_c *parser) {
219
+
220
+ GET_WRAPPER(wrapper, parser);
221
+
222
+ VALUE ret = Qnil;
223
+
224
+ if (wrapper->callback_object != Qnil && rb_respond_to(wrapper->callback_object, Ion_message_complete)) {
225
+ ret = rb_funcall(wrapper->callback_object, Ion_message_complete, 0);
226
+ } else if (wrapper->on_message_complete != Qnil) {
227
+ ret = rb_funcall(wrapper->on_message_complete, Icall, 0);
228
+ }
229
+
230
+ return 0;
231
+ }
232
+
233
+
234
+
235
+
236
+ static multipart_parser_c_settings settings = {
237
+ .on_header_field = on_header_field,
238
+ .on_header_value = on_header_value,
239
+
240
+ .on_message_begin = on_message_begin,
241
+ .on_part_begin = on_part_begin,
242
+ .on_headers_complete = on_headers_complete,
243
+ .on_part_data = on_data,
244
+ .on_part_complete = on_part_complete,
245
+ .on_message_complete = on_message_complete
246
+ };
247
+
248
+
249
+
250
+ /* EXPOSED METHODS */
251
+
252
+ /*
253
+ MultipartParser#new(boundary)
254
+ MultipartParser#new(boundary, callback_object)
255
+ MultipartParser#new(boundary, callback_object, default_header_value_type) */
256
+ VALUE MultipartParser_initialize(int argc, VALUE *argv, VALUE self) {
257
+
258
+ MultipartParserWrapper *wrapper = NULL;
259
+ DATA_GET(self, MultipartParserWrapper, wrapper);
260
+
261
+ VALUE boundary;
262
+
263
+ wrapper->header_value_type = rb_iv_get(CLASS_OF(self), "@default_header_value_type");
264
+
265
+
266
+ if (argc == 0 ) {
267
+ rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)");
268
+ }
269
+
270
+ if (argc == 1) {
271
+ boundary = argv[0];
272
+ }
273
+ if (argc == 2) {
274
+ boundary = argv[0];
275
+ wrapper->callback_object = argv[1];
276
+ }
277
+
278
+ if (argc == 3) {
279
+ boundary = argv[0];
280
+ wrapper->callback_object = argv[1];
281
+ wrapper->header_value_type = argv[2];
282
+ }
283
+
284
+ Check_Type(boundary, T_STRING);
285
+ char *ptr = RSTRING_PTR(boundary);
286
+ long len = RSTRING_LEN(boundary);
287
+
288
+
289
+
290
+ wrapper->parser = multipart_parser_c_init(ptr,len);
291
+ wrapper->parser->context = wrapper;
292
+
293
+
294
+
295
+ return self;
296
+ }
297
+
298
+
299
+ /* MultipartParser# << (data) */
300
+ VALUE MultipartParser_execute(VALUE self, VALUE data) {
301
+ MultipartParserWrapper *wrapper = NULL;
302
+
303
+ Check_Type(data, T_STRING);
304
+ char *ptr = RSTRING_PTR(data);
305
+ long len = RSTRING_LEN(data);
306
+
307
+ DATA_GET(self, MultipartParserWrapper, wrapper);
308
+
309
+
310
+ size_t nparsed = multipart_parser_c_execute(wrapper->parser, &settings, ptr, len);
311
+
312
+
313
+ return INT2FIX(nparsed);
314
+ }
315
+
316
+
317
+ /* MultipartParser#on_message_begin= */
318
+ VALUE MultipartParser_set_on_message_begin(VALUE self, VALUE callback) {
319
+ MultipartParserWrapper *wrapper = NULL;
320
+ DATA_GET(self, MultipartParserWrapper, wrapper);
321
+
322
+
323
+ if (rb_class_of(callback) != rb_cProc) {
324
+ rb_raise(rb_eTypeError, "Expected Proc callback");
325
+ }
326
+
327
+ wrapper->on_message_begin = callback;
328
+
329
+ return callback;
330
+ }
331
+
332
+
333
+
334
+ /* MultipartParser#on_part_begin= */
335
+ VALUE MultipartParser_set_on_part_begin(VALUE self, VALUE callback) {
336
+ MultipartParserWrapper *wrapper = NULL;
337
+ DATA_GET(self, MultipartParserWrapper, wrapper);
338
+
339
+
340
+ if (rb_class_of(callback) != rb_cProc) {
341
+ rb_raise(rb_eTypeError, "Expected Proc callback");
342
+ }
343
+
344
+ wrapper->on_part_begin = callback;
345
+
346
+ return callback;
347
+ }
348
+
349
+
350
+
351
+ /* MultipartParser#on_headers_complete= */
352
+ VALUE MultipartParser_set_on_headers_complete(VALUE self, VALUE callback) {
353
+ MultipartParserWrapper *wrapper = NULL;
354
+ DATA_GET(self, MultipartParserWrapper, wrapper);
355
+
356
+
357
+ if (rb_class_of(callback) != rb_cProc) {
358
+ rb_raise(rb_eTypeError, "Expected Proc callback");
359
+ }
360
+
361
+ wrapper->on_headers_complete = callback;
362
+
363
+ return callback;
364
+ }
365
+
366
+
367
+ /* MultipartParser#on_data= */
368
+ VALUE MultipartParser_set_on_data(VALUE self, VALUE callback) {
369
+ MultipartParserWrapper *wrapper = NULL;
370
+ DATA_GET(self, MultipartParserWrapper, wrapper);
371
+
372
+
373
+ if (rb_class_of(callback) != rb_cProc) {
374
+ rb_raise(rb_eTypeError, "Expected Proc callback");
375
+ }
376
+
377
+ wrapper->on_data = callback;
378
+
379
+ return callback;
380
+ }
381
+
382
+
383
+ /* MultipartParser#on_part_complete= */
384
+ VALUE MultipartParser_set_on_part_complete(VALUE self, VALUE callback) {
385
+ MultipartParserWrapper *wrapper = NULL;
386
+ DATA_GET(self, MultipartParserWrapper, wrapper);
387
+
388
+
389
+ if (rb_class_of(callback) != rb_cProc) {
390
+ rb_raise(rb_eTypeError, "Expected Proc callback");
391
+ }
392
+
393
+ wrapper->on_part_complete = callback;
394
+
395
+ return callback;
396
+ }
397
+
398
+ /* MultipartParser#on_message_complete= */
399
+ VALUE MultipartParser_set_on_message_complete(VALUE self, VALUE callback) {
400
+ MultipartParserWrapper *wrapper = NULL;
401
+ DATA_GET(self, MultipartParserWrapper, wrapper);
402
+
403
+
404
+ if (rb_class_of(callback) != rb_cProc) {
405
+ rb_raise(rb_eTypeError, "Expected Proc callback");
406
+ }
407
+
408
+ wrapper->on_message_complete = callback;
409
+
410
+ return callback;
411
+ }
412
+
413
+
414
+
415
+
416
+ /* MultipartParser#headers */
417
+ VALUE MultipartParser_headers(VALUE self) {
418
+ MultipartParserWrapper *wrapper = NULL;
419
+ DATA_GET(self, MultipartParserWrapper, wrapper);
420
+
421
+ return wrapper->headers;
422
+ }
423
+
424
+
425
+
426
+ /* MultipartParser#header_value_type */
427
+ VALUE MultipartParser_header_value_type(VALUE self) {
428
+ MultipartParserWrapper *wrapper = NULL;
429
+ DATA_GET(self, MultipartParserWrapper, wrapper);
430
+
431
+ return wrapper->header_value_type;
432
+ }
433
+
434
+
435
+ /* MultipartParser#header_value_type= */
436
+ VALUE MultipartParser_set_header_value_type(VALUE self, VALUE val) {
437
+ if (val != Sarrays && val != Sstrings && val != Smixed) {
438
+ rb_raise(rb_eArgError, "Invalid header value type");
439
+ }
440
+
441
+ MultipartParserWrapper *wrapper = NULL;
442
+ DATA_GET(self, MultipartParserWrapper, wrapper);
443
+ wrapper->header_value_type = val;
444
+ return wrapper->header_value_type;
445
+ }
446
+
447
+
448
+
449
+
450
+ /* EXTENSION ALLOCATION AND MEMORY MANAGEMENT */
451
+
452
+
453
+
454
+
455
+ /* marks objects that are used by this function so GC doesn't remove them */
456
+ void MultipartParserWrapper_mark(void *data) {
457
+ if(data) {
458
+ MultipartParserWrapper *wrapper = (MultipartParserWrapper *) data;
459
+
460
+ rb_gc_mark_maybe(wrapper->on_message_begin);
461
+ rb_gc_mark_maybe(wrapper->on_part_begin);
462
+ rb_gc_mark_maybe(wrapper->on_headers_complete);
463
+ rb_gc_mark_maybe(wrapper->on_data);
464
+ rb_gc_mark_maybe(wrapper->on_part_complete);
465
+ rb_gc_mark_maybe(wrapper->on_message_complete);
466
+
467
+
468
+ rb_gc_mark_maybe(wrapper->headers);
469
+
470
+
471
+ rb_gc_mark_maybe(wrapper->callback_object);
472
+
473
+ rb_gc_mark_maybe(wrapper->curr_field_name);
474
+ rb_gc_mark_maybe(wrapper->last_field_name);
475
+
476
+ }
477
+ }
478
+
479
+ /* release memory */
480
+ void MultipartParserWrapper_free(void *data) {
481
+ if(data) {
482
+
483
+ MultipartParserWrapper *wrapper = (MultipartParserWrapper *) data;
484
+
485
+ if(wrapper->parser) {
486
+ multipart_parser_c_free(wrapper->parser);
487
+ }
488
+ free(data);
489
+ }
490
+ }
491
+
492
+
493
+
494
+ VALUE MultipartParser_alloc(VALUE klass) {
495
+
496
+ MultipartParserWrapper *wrapper = ALLOC_N(MultipartParserWrapper,1);
497
+
498
+ wrapper->parser = NULL;
499
+
500
+ wrapper->headers = Qnil;
501
+
502
+ wrapper->on_message_begin = Qnil;
503
+ wrapper->on_part_begin = Qnil;
504
+ wrapper->on_headers_complete = Qnil;
505
+ wrapper->on_data = Qnil;
506
+ wrapper->on_part_complete= Qnil;
507
+ wrapper->on_message_complete= Qnil;
508
+
509
+
510
+ wrapper->callback_object = Qnil;
511
+
512
+ wrapper->curr_field_name = Qnil;
513
+ wrapper->last_field_name = Qnil;
514
+
515
+
516
+
517
+ /* Data_Wrap_Struct(VALUE class, void (*mark)(), void (*free)(), void *ptr") */
518
+ return Data_Wrap_Struct(klass, MultipartParserWrapper_mark, MultipartParserWrapper_free, wrapper);
519
+
520
+ }
521
+
522
+
523
+
524
+ /* The initialization method for this module */
525
+ void Init_multipart_parser() {
526
+
527
+
528
+ cMultipartParser = rb_define_class("MultipartParser", rb_cObject);
529
+
530
+ rb_define_alloc_func(cMultipartParser, MultipartParser_alloc);
531
+
532
+ Icall = rb_intern("call");
533
+
534
+ Ion_message_begin = rb_intern("on_message_begin");
535
+ Ion_part_begin = rb_intern("on_part_begin");
536
+ Ion_headers_complete = rb_intern("on_headers_complete");
537
+ Ion_data = rb_intern("on_data");
538
+ Ion_part_complete = rb_intern("on_part_complete");
539
+ Ion_message_complete = rb_intern("on_message_complete");
540
+
541
+
542
+
543
+ Sarrays = ID2SYM(rb_intern("arrays"));
544
+ Sstrings = ID2SYM(rb_intern("strings"));
545
+ Smixed = ID2SYM(rb_intern("mixed"));
546
+
547
+
548
+ rb_define_method(cMultipartParser, "initialize", MultipartParser_initialize, -1);
549
+
550
+ rb_define_method(cMultipartParser, "on_message_begin=", MultipartParser_set_on_message_begin, 1);
551
+ rb_define_method(cMultipartParser, "on_part_begin=", MultipartParser_set_on_part_begin, 1);
552
+ rb_define_method(cMultipartParser, "on_headers_complete=", MultipartParser_set_on_headers_complete, 1);
553
+ rb_define_method(cMultipartParser, "on_data=", MultipartParser_set_on_data, 1);
554
+ rb_define_method(cMultipartParser, "on_part_complete=", MultipartParser_set_on_part_complete, 1);
555
+ rb_define_method(cMultipartParser, "on_message_complete=", MultipartParser_set_on_message_complete, 1);
556
+
557
+
558
+
559
+ rb_define_method(cMultipartParser, "<<", MultipartParser_execute, 1);
560
+
561
+
562
+ rb_define_method(cMultipartParser, "headers", MultipartParser_headers, 0);
563
+
564
+
565
+ rb_define_method(cMultipartParser, "header_value_type", MultipartParser_header_value_type, 0);
566
+ rb_define_method(cMultipartParser, "header_value_type=", MultipartParser_set_header_value_type, 1);
567
+
568
+ }
569
+
570
+
571
+
572
+
573
+
574
+
575
+
576
+
577
+
578
+
579
+
580
+
581
+