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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +27 -0
- data/README.md +182 -0
- data/Rakefile +12 -0
- data/ext/multipart_parser/ext_help.h +18 -0
- data/ext/multipart_parser/extconf.rb +3 -0
- data/ext/multipart_parser/multipart_parser.c +581 -0
- data/ext/multipart_parser/multipart_parser_c.c +302 -0
- data/ext/multipart_parser/multipart_parser_c.h +67 -0
- data/lib/multipart_parser/version.rb +3 -0
- data/lib/multipart_parser.rb +21 -0
- data/multipart_parser.gemspec +25 -0
- data/spec/multipart_parser_spec.rb +157 -0
- data/spec/spec_helper.rb +2 -0
- metadata +119 -0
@@ -0,0 +1,302 @@
|
|
1
|
+
/* Based on node-formidable by Felix Geisendörfer
|
2
|
+
* Igor Afonov - afonov@gmail.com - 2012
|
3
|
+
* MIT License - http://www.opensource.org/licenses/mit-license.php
|
4
|
+
* Modified Benjamin Bryant - https://github.com/bhbryant/multipart_parser - 2015
|
5
|
+
*/
|
6
|
+
|
7
|
+
#include "multipart_parser_c.h"
|
8
|
+
|
9
|
+
#include <stdio.h>
|
10
|
+
#include <stdarg.h>
|
11
|
+
#include <string.h>
|
12
|
+
|
13
|
+
static void multipart_log(const char * format, ...)
|
14
|
+
{
|
15
|
+
#ifdef DEBUG_MULTIPART
|
16
|
+
va_list args;
|
17
|
+
va_start(args, format);
|
18
|
+
|
19
|
+
fprintf(stderr, "[HTTP_MULTIPART_PARSER] %s:%d: ", __FILE__, __LINE__);
|
20
|
+
vfprintf(stderr, format, args);
|
21
|
+
fprintf(stderr, "\n");
|
22
|
+
#endif
|
23
|
+
}
|
24
|
+
|
25
|
+
#define NOTIFY_CB(FOR) \
|
26
|
+
do { \
|
27
|
+
if (settings->on_##FOR) { \
|
28
|
+
if (settings->on_##FOR(p) != 0) { \
|
29
|
+
return i; \
|
30
|
+
} \
|
31
|
+
} \
|
32
|
+
} while (0)
|
33
|
+
|
34
|
+
#define EMIT_DATA_CB(FOR, ptr, len) \
|
35
|
+
do { \
|
36
|
+
if (settings->on_##FOR) { \
|
37
|
+
if (settings->on_##FOR(p, ptr, len) != 0) { \
|
38
|
+
return i; \
|
39
|
+
} \
|
40
|
+
} \
|
41
|
+
} while (0)
|
42
|
+
|
43
|
+
|
44
|
+
#define LF 10
|
45
|
+
#define CR 13
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
enum state {
|
50
|
+
s_uninitialized = 1,
|
51
|
+
s_start,
|
52
|
+
s_start_boundary,
|
53
|
+
s_header_field_start,
|
54
|
+
s_header_field,
|
55
|
+
s_headers_almost_done,
|
56
|
+
s_header_value_start,
|
57
|
+
s_header_value,
|
58
|
+
s_header_value_almost_done,
|
59
|
+
s_part_data_start,
|
60
|
+
s_part_data,
|
61
|
+
s_part_data_almost_boundary,
|
62
|
+
s_part_data_boundary,
|
63
|
+
s_part_data_almost_end,
|
64
|
+
s_part_complete,
|
65
|
+
s_part_data_final_hyphen,
|
66
|
+
s_end
|
67
|
+
};
|
68
|
+
|
69
|
+
multipart_parser_c* multipart_parser_c_init
|
70
|
+
(const char *boundary, size_t boundary_length) {
|
71
|
+
|
72
|
+
|
73
|
+
multipart_parser_c* p = malloc(sizeof(multipart_parser_c) +
|
74
|
+
(2 * sizeof(char)) + boundary_length +
|
75
|
+
(2 * sizeof(char)) + boundary_length + 9 );
|
76
|
+
|
77
|
+
|
78
|
+
p->multipart_boundary[0] = '-';
|
79
|
+
p->multipart_boundary[1] = '-';
|
80
|
+
strcpy(&(p->multipart_boundary[2]), boundary);
|
81
|
+
|
82
|
+
|
83
|
+
p->boundary_length = (2 * sizeof(char)) + boundary_length;
|
84
|
+
|
85
|
+
p->lookbehind = (p->multipart_boundary + p->boundary_length + 1);
|
86
|
+
|
87
|
+
p->index = 0;
|
88
|
+
p->state = s_start;
|
89
|
+
|
90
|
+
|
91
|
+
return p;
|
92
|
+
}
|
93
|
+
|
94
|
+
void multipart_parser_c_free(multipart_parser_c* p) {
|
95
|
+
free(p);
|
96
|
+
}
|
97
|
+
|
98
|
+
void multipart_parser_c_set_data(multipart_parser_c *p, void *data) {
|
99
|
+
p->data = data;
|
100
|
+
}
|
101
|
+
|
102
|
+
void *multipart_parser_c_get_data(multipart_parser_c *p) {
|
103
|
+
return p->data;
|
104
|
+
}
|
105
|
+
|
106
|
+
size_t multipart_parser_c_execute(multipart_parser_c* p, const multipart_parser_c_settings* settings, const char *buf, size_t len) {
|
107
|
+
size_t i = 0;
|
108
|
+
size_t mark = 0;
|
109
|
+
char c, cl;
|
110
|
+
int is_last = 0;
|
111
|
+
|
112
|
+
while(i < len) {
|
113
|
+
c = buf[i];
|
114
|
+
is_last = (i == (len - 1));
|
115
|
+
switch (p->state) {
|
116
|
+
case s_start:
|
117
|
+
multipart_log("s_start");
|
118
|
+
p->index = 0;
|
119
|
+
NOTIFY_CB(message_begin);
|
120
|
+
p->state = s_start_boundary;
|
121
|
+
|
122
|
+
/* fallthrough */
|
123
|
+
case s_start_boundary:
|
124
|
+
multipart_log("s_start_boundary");
|
125
|
+
if (p->index == p->boundary_length) {
|
126
|
+
if (c != CR) {
|
127
|
+
return i;
|
128
|
+
}
|
129
|
+
p->index++;
|
130
|
+
break;
|
131
|
+
} else if (p->index == (p->boundary_length + 1)) {
|
132
|
+
if (c != LF) {
|
133
|
+
return i;
|
134
|
+
}
|
135
|
+
p->index = 0;
|
136
|
+
NOTIFY_CB(part_begin);
|
137
|
+
p->state = s_header_field_start;
|
138
|
+
break;
|
139
|
+
}
|
140
|
+
if (c != p->multipart_boundary[p->index]) {
|
141
|
+
return i;
|
142
|
+
}
|
143
|
+
p->index++;
|
144
|
+
break;
|
145
|
+
|
146
|
+
case s_header_field_start:
|
147
|
+
multipart_log("s_header_field_start");
|
148
|
+
mark = i;
|
149
|
+
p->state = s_header_field;
|
150
|
+
|
151
|
+
/* fallthrough */
|
152
|
+
case s_header_field:
|
153
|
+
multipart_log("s_header_field");
|
154
|
+
if (c == CR) {
|
155
|
+
p->state = s_headers_almost_done;
|
156
|
+
break;
|
157
|
+
}
|
158
|
+
|
159
|
+
if (c == ':') {
|
160
|
+
EMIT_DATA_CB(header_field, buf + mark, i - mark);
|
161
|
+
p->state = s_header_value_start;
|
162
|
+
break;
|
163
|
+
}
|
164
|
+
|
165
|
+
cl = tolower(c);
|
166
|
+
if ((c != '-') && (cl < 'a' || cl > 'z')) {
|
167
|
+
multipart_log("invalid character in header name");
|
168
|
+
return i;
|
169
|
+
}
|
170
|
+
if (is_last)
|
171
|
+
EMIT_DATA_CB(header_field, buf + mark, (i - mark) + 1);
|
172
|
+
break;
|
173
|
+
|
174
|
+
case s_headers_almost_done:
|
175
|
+
multipart_log("s_headers_almost_done");
|
176
|
+
if (c != LF) {
|
177
|
+
return i;
|
178
|
+
}
|
179
|
+
|
180
|
+
p->state = s_part_data_start;
|
181
|
+
break;
|
182
|
+
|
183
|
+
case s_header_value_start:
|
184
|
+
multipart_log("s_header_value_start");
|
185
|
+
if (c == ' ') {
|
186
|
+
break;
|
187
|
+
}
|
188
|
+
|
189
|
+
mark = i;
|
190
|
+
p->state = s_header_value;
|
191
|
+
|
192
|
+
/* fallthrough */
|
193
|
+
case s_header_value:
|
194
|
+
multipart_log("s_header_value");
|
195
|
+
if (c == CR) {
|
196
|
+
EMIT_DATA_CB(header_value, buf + mark, i - mark);
|
197
|
+
p->state = s_header_value_almost_done;
|
198
|
+
break;
|
199
|
+
}
|
200
|
+
if (is_last)
|
201
|
+
EMIT_DATA_CB(header_value, buf + mark, (i - mark) + 1);
|
202
|
+
break;
|
203
|
+
|
204
|
+
case s_header_value_almost_done:
|
205
|
+
multipart_log("s_header_value_almost_done");
|
206
|
+
if (c != LF) {
|
207
|
+
return i;
|
208
|
+
}
|
209
|
+
p->state = s_header_field_start;
|
210
|
+
break;
|
211
|
+
|
212
|
+
case s_part_data_start:
|
213
|
+
multipart_log("s_part_data_start");
|
214
|
+
NOTIFY_CB(headers_complete);
|
215
|
+
mark = i;
|
216
|
+
p->state = s_part_data;
|
217
|
+
|
218
|
+
/* fallthrough */
|
219
|
+
case s_part_data:
|
220
|
+
multipart_log("s_part_data");
|
221
|
+
if (c == CR) {
|
222
|
+
EMIT_DATA_CB(part_data, buf + mark, i - mark);
|
223
|
+
mark = i;
|
224
|
+
p->state = s_part_data_almost_boundary;
|
225
|
+
p->lookbehind[0] = CR;
|
226
|
+
break;
|
227
|
+
}
|
228
|
+
if (is_last)
|
229
|
+
EMIT_DATA_CB(part_data, buf + mark, (i - mark) + 1);
|
230
|
+
break;
|
231
|
+
|
232
|
+
case s_part_data_almost_boundary:
|
233
|
+
multipart_log("s_part_data_almost_boundary");
|
234
|
+
if (c == LF) {
|
235
|
+
p->state = s_part_data_boundary;
|
236
|
+
p->lookbehind[1] = LF;
|
237
|
+
p->index = 0;
|
238
|
+
break;
|
239
|
+
}
|
240
|
+
EMIT_DATA_CB(part_data, p->lookbehind, 1);
|
241
|
+
p->state = s_part_data;
|
242
|
+
mark = i --;
|
243
|
+
break;
|
244
|
+
|
245
|
+
case s_part_data_boundary:
|
246
|
+
multipart_log("s_part_data_boundary");
|
247
|
+
if (p->multipart_boundary[p->index] != c) {
|
248
|
+
EMIT_DATA_CB(part_data, p->lookbehind, 2 + p->index);
|
249
|
+
p->state = s_part_data;
|
250
|
+
mark = i --;
|
251
|
+
break;
|
252
|
+
}
|
253
|
+
p->lookbehind[2 + p->index] = c;
|
254
|
+
if ((++ p->index) == p->boundary_length) {
|
255
|
+
NOTIFY_CB(part_complete);
|
256
|
+
p->state = s_part_data_almost_end;
|
257
|
+
}
|
258
|
+
break;
|
259
|
+
|
260
|
+
case s_part_data_almost_end:
|
261
|
+
multipart_log("s_part_data_almost_end");
|
262
|
+
if (c == '-') {
|
263
|
+
p->state = s_part_data_final_hyphen;
|
264
|
+
break;
|
265
|
+
}
|
266
|
+
if (c == CR) {
|
267
|
+
p->state = s_part_complete;
|
268
|
+
break;
|
269
|
+
}
|
270
|
+
return i;
|
271
|
+
|
272
|
+
case s_part_data_final_hyphen:
|
273
|
+
multipart_log("s_part_data_final_hyphen");
|
274
|
+
if (c == '-') {
|
275
|
+
NOTIFY_CB(message_complete);
|
276
|
+
p->state = s_end;
|
277
|
+
break;
|
278
|
+
}
|
279
|
+
return i;
|
280
|
+
|
281
|
+
case s_part_complete:
|
282
|
+
multipart_log("s_part_complete");
|
283
|
+
if (c == LF) {
|
284
|
+
p->state = s_header_field_start;
|
285
|
+
NOTIFY_CB(part_begin);
|
286
|
+
break;
|
287
|
+
}
|
288
|
+
return i;
|
289
|
+
|
290
|
+
case s_end:
|
291
|
+
multipart_log("s_end: %02X", (int) c);
|
292
|
+
break;
|
293
|
+
|
294
|
+
default:
|
295
|
+
multipart_log("Multipart parser unrecoverable error");
|
296
|
+
return 0;
|
297
|
+
}
|
298
|
+
++ i;
|
299
|
+
}
|
300
|
+
|
301
|
+
return len;
|
302
|
+
}
|
@@ -0,0 +1,67 @@
|
|
1
|
+
/* Based on node-formidable by Felix Geisendörfer
|
2
|
+
* Igor Afonov - afonov@gmail.com - 2012
|
3
|
+
* MIT License - http://www.opensource.org/licenses/mit-license.php
|
4
|
+
* Modified Benjamin Bryant - https://github.com/bhbryant/multipart_parser - 2015
|
5
|
+
*/
|
6
|
+
#ifndef _multipart_parser_c_h
|
7
|
+
#define _multipart_parser_c_h
|
8
|
+
|
9
|
+
#ifdef __cplusplus
|
10
|
+
extern "C"
|
11
|
+
{
|
12
|
+
#endif
|
13
|
+
|
14
|
+
#include <stdlib.h>
|
15
|
+
#include <ctype.h>
|
16
|
+
|
17
|
+
typedef struct multipart_parser_c multipart_parser_c;
|
18
|
+
typedef struct multipart_parser_c_settings multipart_parser_c_settings;
|
19
|
+
typedef struct multipart_parser_c_state multipart_parser_c_state;
|
20
|
+
|
21
|
+
typedef int (*multipart_data_cb) (multipart_parser_c*, const char *at, size_t length);
|
22
|
+
typedef int (*multipart_notify_cb) (multipart_parser_c*);
|
23
|
+
|
24
|
+
struct multipart_parser_c_settings {
|
25
|
+
multipart_data_cb on_header_field;
|
26
|
+
multipart_data_cb on_header_value;
|
27
|
+
multipart_data_cb on_part_data;
|
28
|
+
|
29
|
+
multipart_notify_cb on_message_begin;
|
30
|
+
multipart_notify_cb on_part_begin;
|
31
|
+
multipart_notify_cb on_headers_complete;
|
32
|
+
multipart_notify_cb on_part_complete;
|
33
|
+
multipart_notify_cb on_message_complete;
|
34
|
+
};
|
35
|
+
|
36
|
+
struct multipart_parser_c {
|
37
|
+
void * data;
|
38
|
+
|
39
|
+
size_t index;
|
40
|
+
size_t boundary_length;
|
41
|
+
|
42
|
+
unsigned char state;
|
43
|
+
|
44
|
+
|
45
|
+
void * context; /* modified from original code to allow pointer wrapper to be passed to callbacks */
|
46
|
+
|
47
|
+
char* lookbehind;
|
48
|
+
char multipart_boundary[1];
|
49
|
+
|
50
|
+
};
|
51
|
+
|
52
|
+
/*modified from original code to move settings as an agument to execute fuction */
|
53
|
+
multipart_parser_c* multipart_parser_c_init(const char *boundary, size_t boundary_length);
|
54
|
+
|
55
|
+
void multipart_parser_c_free(multipart_parser_c* p);
|
56
|
+
|
57
|
+
/* modified from original code to allow take settings as argument */
|
58
|
+
size_t multipart_parser_c_execute(multipart_parser_c* p, const multipart_parser_c_settings* settings, const char *buf, size_t len);
|
59
|
+
|
60
|
+
void multipart_parser_c_set_data(multipart_parser_c* p, void* data);
|
61
|
+
void * multipart_parser_c_get_data(multipart_parser_c* p);
|
62
|
+
|
63
|
+
#ifdef __cplusplus
|
64
|
+
} /* extern "C" */
|
65
|
+
#endif
|
66
|
+
|
67
|
+
#endif
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "multipart_parser/version"
|
2
|
+
require "multipart_parser/multipart_parser"
|
3
|
+
|
4
|
+
class MultipartParser
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
#Multiple headers are listed as arrays
|
9
|
+
attr_reader :default_header_value_type
|
10
|
+
|
11
|
+
def default_header_value_type=(val)
|
12
|
+
if (val != :mixed && val != :strings && val != :arrays)
|
13
|
+
raise ArgumentError, "Invalid header value type"
|
14
|
+
end
|
15
|
+
@default_header_value_type = val
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
MultipartParser.default_header_value_type = :mixed
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'multipart_parser/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "multipart_parser"
|
8
|
+
spec.version = MultipartParser::VERSION
|
9
|
+
spec.authors = ["Benjamin Bryant"]
|
10
|
+
spec.extensions = ["ext/multipart_parser/extconf.rb"]
|
11
|
+
spec.summary = %q{multipart/form-data parser}
|
12
|
+
spec.description = %q{Ruby bindings for https://github.com/iafonov/multipart-parser-c}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rake-compiler"
|
24
|
+
spec.add_development_dependency "rspec"
|
25
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MultipartParser do
|
4
|
+
|
5
|
+
let(:handler) { proc {} }
|
6
|
+
|
7
|
+
def execute(parser)
|
8
|
+
parser << "--Boundary+17376C87E2579930\r\nContent-Disposition: form-data; name=ID; paramName=TEST_PARAM_1\r\nContent-Transfer-Encoding: binary\r\nContent-Type: text/plain\r\n\r\nfirst frame\r\n";
|
9
|
+
parser << "--Boundary+17376C87E2579930\r\nContent-Disposition: form-data; name=ID; paramName=TEST_PARAM_2\r\nContent-Transfer-Encoding: binary\r\nContent-Type: text/plain\r\n\r\nsecond frame\r\n";
|
10
|
+
parser << "--Boundary+17376C87E2579930--\r\n"
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
context "with assigned callbacks" do
|
16
|
+
let(:parser) { MultipartParser.new "Boundary+17376C87E2579930" }
|
17
|
+
|
18
|
+
it "emits message_begin" do
|
19
|
+
parser.on_message_begin = handler
|
20
|
+
|
21
|
+
expect(handler).to receive(:call).once
|
22
|
+
|
23
|
+
execute(parser)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "emits part_begin" do
|
27
|
+
parser.on_part_begin = handler
|
28
|
+
|
29
|
+
expect(handler).to receive(:call).twice
|
30
|
+
|
31
|
+
execute(parser)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "parse multipart headers" do
|
35
|
+
parser.on_headers_complete = handler
|
36
|
+
|
37
|
+
|
38
|
+
expect(handler).to receive(:call).with({"Content-Disposition"=>"form-data; name=ID; paramName=TEST_PARAM_1", "Content-Transfer-Encoding"=>"binary", "Content-Type"=>"text/plain"})
|
39
|
+
expect(handler).to receive(:call).with({"Content-Disposition"=>"form-data; name=ID; paramName=TEST_PARAM_2", "Content-Transfer-Encoding"=>"binary", "Content-Type"=>"text/plain"})
|
40
|
+
|
41
|
+
execute(parser)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "parses multipart data" do
|
45
|
+
|
46
|
+
parser.on_data = handler
|
47
|
+
|
48
|
+
expect(handler).to receive(:call).with("first frame")
|
49
|
+
expect(handler).to receive(:call).with("second frame")
|
50
|
+
|
51
|
+
execute(parser)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "emits part_complete" do
|
55
|
+
parser.on_part_complete = handler
|
56
|
+
|
57
|
+
expect(handler).to receive(:call).twice
|
58
|
+
|
59
|
+
execute(parser)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "emits message_complete" do
|
63
|
+
parser.on_message_complete = handler
|
64
|
+
|
65
|
+
expect(handler).to receive(:call).once
|
66
|
+
|
67
|
+
execute(parser)
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
context "with callback object" do
|
74
|
+
let(:callback_obj) { double("Callback")}
|
75
|
+
let(:parser) { MultipartParser.new "Boundary+17376C87E2579930", callback_obj }
|
76
|
+
|
77
|
+
|
78
|
+
it "emits message_begin" do
|
79
|
+
expect(callback_obj).to receive(:on_message_begin).once
|
80
|
+
|
81
|
+
execute(parser)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "emits part_begin" do
|
85
|
+
expect(callback_obj).to receive(:on_part_begin).twice
|
86
|
+
|
87
|
+
execute(parser)
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
it "parse multipart headers" do
|
92
|
+
expect(callback_obj).to receive(:on_headers_complete).with({"Content-Disposition"=>"form-data; name=ID; paramName=TEST_PARAM_1", "Content-Transfer-Encoding"=>"binary", "Content-Type"=>"text/plain"})
|
93
|
+
expect(callback_obj).to receive(:on_headers_complete).with({"Content-Disposition"=>"form-data; name=ID; paramName=TEST_PARAM_2", "Content-Transfer-Encoding"=>"binary", "Content-Type"=>"text/plain"})
|
94
|
+
|
95
|
+
execute(parser)
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
it "parses multipart data" do
|
100
|
+
|
101
|
+
expect(callback_obj).to receive(:on_data).with("first frame")
|
102
|
+
expect(callback_obj).to receive(:on_data).with("second frame")
|
103
|
+
|
104
|
+
execute(parser)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "emits part_complete" do
|
108
|
+
expect(callback_obj).to receive(:on_part_complete).twice
|
109
|
+
|
110
|
+
execute(parser)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "emits message_complete" do
|
114
|
+
expect(callback_obj).to receive(:on_message_complete).once
|
115
|
+
|
116
|
+
execute(parser)
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
it "allows the type of the header values to be configured" do
|
122
|
+
|
123
|
+
parser = MultipartParser.new("Boundary+17376C87E2579930", nil, :arrays)
|
124
|
+
parser.on_headers_complete = handler
|
125
|
+
|
126
|
+
expect(handler).to receive(:call).with({"Content-Disposition"=>["form-data; name=ID_A", "form-data; name=ID_B"], "Content-Type"=>["text/plain"]})
|
127
|
+
|
128
|
+
parser << "--Boundary+17376C87E2579930\r\nContent-Disposition: form-data; name=ID_A\r\nContent-Disposition: form-data; name=ID_B\r\nContent-Type: text/plain\r\n\r\ntext\r\n"
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
it "allows the default header value type to be set" do
|
133
|
+
value_type = MultipartParser.default_header_value_type
|
134
|
+
|
135
|
+
MultipartParser.default_header_value_type = :arrays
|
136
|
+
|
137
|
+
parser = MultipartParser.new("Boundary+17376C87E2579930")
|
138
|
+
|
139
|
+
|
140
|
+
parser.on_headers_complete = handler
|
141
|
+
|
142
|
+
expect(handler).to receive(:call).with({"Content-Disposition"=>["form-data; name=ID_A", "form-data; name=ID_B"], "Content-Type"=>["text/plain"]})
|
143
|
+
|
144
|
+
parser << "--Boundary+17376C87E2579930\r\nContent-Disposition: form-data; name=ID_A\r\nContent-Disposition: form-data; name=ID_B\r\nContent-Type: text/plain\r\n\r\ntext\r\n"
|
145
|
+
|
146
|
+
## reset
|
147
|
+
MultipartParser.default_header_value_type = value_type
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'has a version number' do
|
151
|
+
expect(MultipartParser::VERSION).not_to be nil
|
152
|
+
end
|
153
|
+
|
154
|
+
specify { expect { MultipartParser.new }.to raise_error(ArgumentError) }
|
155
|
+
|
156
|
+
end
|
157
|
+
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: multipart_parser
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Benjamin Bryant
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake-compiler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Ruby bindings for https://github.com/iafonov/multipart-parser-c
|
70
|
+
email:
|
71
|
+
executables: []
|
72
|
+
extensions:
|
73
|
+
- ext/multipart_parser/extconf.rb
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".rspec"
|
78
|
+
- ".travis.yml"
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- ext/multipart_parser/ext_help.h
|
84
|
+
- ext/multipart_parser/extconf.rb
|
85
|
+
- ext/multipart_parser/multipart_parser.c
|
86
|
+
- ext/multipart_parser/multipart_parser_c.c
|
87
|
+
- ext/multipart_parser/multipart_parser_c.h
|
88
|
+
- lib/multipart_parser.rb
|
89
|
+
- lib/multipart_parser/version.rb
|
90
|
+
- multipart_parser.gemspec
|
91
|
+
- spec/multipart_parser_spec.rb
|
92
|
+
- spec/spec_helper.rb
|
93
|
+
homepage: ''
|
94
|
+
licenses:
|
95
|
+
- MIT
|
96
|
+
metadata: {}
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
requirements: []
|
112
|
+
rubyforge_project:
|
113
|
+
rubygems_version: 2.4.3
|
114
|
+
signing_key:
|
115
|
+
specification_version: 4
|
116
|
+
summary: multipart/form-data parser
|
117
|
+
test_files:
|
118
|
+
- spec/multipart_parser_spec.rb
|
119
|
+
- spec/spec_helper.rb
|