multipart_parser 0.0.1

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.
@@ -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
+