http_parser.rb 0.5.1-x86-mingw32 → 0.5.2-x86-mingw32
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.
- data/Gemfile.lock +16 -16
- data/LICENSE-MIT +20 -0
- data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +68 -11
- data/ext/ruby_http_parser/ruby_http_parser.c +74 -6
- data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +1 -1
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +52 -10
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +3 -1
- data/ext/ruby_http_parser/vendor/http-parser/test.c +89 -3
- data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +26 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/README.md +23 -143
- data/ext/ruby_http_parser/vendor/http-parser-java/TODO +3 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/build.xml +74 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/ext/primitives.jar +0 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +115 -61
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +19 -3
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPCallback.java +8 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPDataCallback.java +34 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPErrorCallback.java +12 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +4 -2
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +64 -52
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +5 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +323 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/{lolevel/Util.java → Util.java} +27 -28
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +259 -85
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +324 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +69 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +51 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +15 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +47 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +183 -447
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +61 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +2 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +26 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +165 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +58 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/test.c +232 -29
- data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +1 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +1 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test_utf8 +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +154 -7
- data/ext/ruby_http_parser/vendor/http-parser-java/tests.utf8 +17 -0
- data/http_parser.rb.gemspec +8 -2
- data/lib/1.8/ruby_http_parser.so +0 -0
- data/lib/1.9/ruby_http_parser.so +0 -0
- data/lib/http_parser.rb +17 -0
- data/spec/parser_spec.rb +97 -6
- data/tasks/compile.rake +3 -1
- metadata +83 -20
- data/ext/ruby_http_parser/vendor/http-parser-java/CONTRIBUTIONS +0 -4
data/Gemfile.lock
CHANGED
@@ -1,26 +1,25 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
http_parser.rb (0.5.
|
4
|
+
http_parser.rb (0.5.2)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: http://rubygems.org/
|
8
8
|
specs:
|
9
9
|
diff-lcs (1.1.2)
|
10
|
-
json (1.
|
11
|
-
rake (0.
|
12
|
-
rake-compiler (0.7.
|
13
|
-
rake
|
14
|
-
rspec (2.0
|
15
|
-
rspec-core (~> 2.0
|
16
|
-
rspec-expectations (~> 2.0
|
17
|
-
rspec-mocks (~> 2.0
|
18
|
-
rspec-core (2.0
|
19
|
-
rspec-expectations (2.0
|
20
|
-
diff-lcs (
|
21
|
-
rspec-mocks (2.0
|
22
|
-
|
23
|
-
rspec-expectations (~> 2.0.1)
|
10
|
+
json (1.5.1)
|
11
|
+
rake (0.9.2)
|
12
|
+
rake-compiler (0.7.9)
|
13
|
+
rake
|
14
|
+
rspec (2.4.0)
|
15
|
+
rspec-core (~> 2.4.0)
|
16
|
+
rspec-expectations (~> 2.4.0)
|
17
|
+
rspec-mocks (~> 2.4.0)
|
18
|
+
rspec-core (2.4.0)
|
19
|
+
rspec-expectations (2.4.0)
|
20
|
+
diff-lcs (~> 1.1.2)
|
21
|
+
rspec-mocks (2.4.0)
|
22
|
+
yajl-ruby (0.8.2)
|
24
23
|
|
25
24
|
PLATFORMS
|
26
25
|
ruby
|
@@ -28,5 +27,6 @@ PLATFORMS
|
|
28
27
|
DEPENDENCIES
|
29
28
|
http_parser.rb!
|
30
29
|
json (>= 1.4.6)
|
31
|
-
rake-compiler (>= 0.7.
|
30
|
+
rake-compiler (>= 0.7.9)
|
32
31
|
rspec (>= 2.0.1)
|
32
|
+
yajl-ruby (>= 0.8.1)
|
data/LICENSE-MIT
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2009,2010 Marc-André Cournoyer <macournoyer@gmail.com>
|
2
|
+
Copyright 2010,2011 Aman Gupta <aman@tmm1.net>
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
of this software and associated documentation files (the "Software"), to
|
6
|
+
deal in the Software without restriction, including without limitation the
|
7
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
8
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
9
|
+
furnished to do so, subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be included in
|
12
|
+
all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
19
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
20
|
+
IN THE SOFTWARE.
|
@@ -1,6 +1,7 @@
|
|
1
1
|
package org.ruby_http_parser;
|
2
2
|
|
3
3
|
import org.jruby.Ruby;
|
4
|
+
import org.jruby.RubyArray;
|
4
5
|
import org.jruby.RubyClass;
|
5
6
|
import org.jruby.RubyHash;
|
6
7
|
import org.jruby.RubyModule;
|
@@ -59,6 +60,9 @@ public class RubyHttpParser extends RubyObject {
|
|
59
60
|
private IRubyObject queryString;
|
60
61
|
private IRubyObject fragment;
|
61
62
|
|
63
|
+
private IRubyObject header_value_type;
|
64
|
+
private IRubyObject upgradeData;
|
65
|
+
|
62
66
|
private IRubyObject callback_object;
|
63
67
|
|
64
68
|
private String _current_header;
|
@@ -77,6 +81,8 @@ public class RubyHttpParser extends RubyObject {
|
|
77
81
|
|
78
82
|
this.callback_object = null;
|
79
83
|
|
84
|
+
this.header_value_type = runtime.getModule("HTTP").getClass("Parser").getInstanceVariable("@default_header_value_type");
|
85
|
+
|
80
86
|
initSettings();
|
81
87
|
init();
|
82
88
|
}
|
@@ -130,25 +136,45 @@ public class RubyHttpParser extends RubyObject {
|
|
130
136
|
byte[] data = fetchBytes(buf, pos, len);
|
131
137
|
ThreadContext context = headers.getRuntime().getCurrentContext();
|
132
138
|
IRubyObject key, val;
|
139
|
+
int new_field = 0;
|
133
140
|
|
134
141
|
if (_current_header != null) {
|
142
|
+
new_field = 1;
|
135
143
|
_last_header = _current_header;
|
136
144
|
_current_header = null;
|
145
|
+
}
|
137
146
|
|
138
|
-
|
147
|
+
key = (IRubyObject)runtime.newString(_last_header);
|
148
|
+
val = headers.op_aref(context, key);
|
149
|
+
|
150
|
+
if (new_field == 1) {
|
151
|
+
if (val.isNil()) {
|
152
|
+
if (header_value_type == runtime.newSymbol("arrays")) {
|
153
|
+
headers.op_aset(context, key, RubyArray.newArrayLight(runtime, runtime.newString("")));
|
154
|
+
} else {
|
155
|
+
headers.op_aset(context, key, runtime.newString(""));
|
156
|
+
}
|
157
|
+
} else {
|
158
|
+
if (header_value_type == runtime.newSymbol("mixed")) {
|
159
|
+
if (val instanceof RubyString) {
|
160
|
+
headers.op_aset(context, key, RubyArray.newArrayLight(runtime, val, runtime.newString("")));
|
161
|
+
} else {
|
162
|
+
((RubyArray)val).add(runtime.newString(""));
|
163
|
+
}
|
164
|
+
} else if (header_value_type == runtime.newSymbol("arrays")) {
|
165
|
+
((RubyArray)val).add(runtime.newString(""));
|
166
|
+
} else {
|
167
|
+
((RubyString)val).cat(", ".getBytes());
|
168
|
+
}
|
169
|
+
}
|
139
170
|
val = headers.op_aref(context, key);
|
171
|
+
}
|
140
172
|
|
141
|
-
|
142
|
-
|
143
|
-
} else {
|
144
|
-
key = (IRubyObject)runtime.newString(_last_header);
|
145
|
-
val = headers.op_aref(context, key);
|
173
|
+
if (val instanceof RubyArray) {
|
174
|
+
val = ((RubyArray)val).entry(-1);
|
146
175
|
}
|
147
176
|
|
148
|
-
|
149
|
-
headers.op_aset(context, key, runtime.newString(new String(data)));
|
150
|
-
else
|
151
|
-
((RubyString)val).cat(data);
|
177
|
+
((RubyString)val).cat(data);
|
152
178
|
|
153
179
|
return 0;
|
154
180
|
}
|
@@ -163,6 +189,8 @@ public class RubyHttpParser extends RubyObject {
|
|
163
189
|
queryString = runtime.newString("");
|
164
190
|
fragment = runtime.newString("");
|
165
191
|
|
192
|
+
upgradeData = runtime.newString("");
|
193
|
+
|
166
194
|
IRubyObject ret = runtime.getNil();
|
167
195
|
|
168
196
|
if (callback_object != null) {
|
@@ -256,6 +284,8 @@ public class RubyHttpParser extends RubyObject {
|
|
256
284
|
this.requestPath = runtime.getNil();
|
257
285
|
this.queryString = runtime.getNil();
|
258
286
|
this.fragment = runtime.getNil();
|
287
|
+
|
288
|
+
this.upgradeData = runtime.getNil();
|
259
289
|
}
|
260
290
|
|
261
291
|
@JRubyMethod(name = "initialize")
|
@@ -269,6 +299,12 @@ public class RubyHttpParser extends RubyObject {
|
|
269
299
|
return initialize();
|
270
300
|
}
|
271
301
|
|
302
|
+
@JRubyMethod(name = "initialize")
|
303
|
+
public IRubyObject initialize(IRubyObject arg, IRubyObject arg2) {
|
304
|
+
header_value_type = arg2;
|
305
|
+
return initialize(arg);
|
306
|
+
}
|
307
|
+
|
272
308
|
@JRubyMethod(name = "on_message_begin=")
|
273
309
|
public IRubyObject set_on_message_begin(IRubyObject cb) {
|
274
310
|
on_message_begin = cb;
|
@@ -308,7 +344,9 @@ public class RubyHttpParser extends RubyObject {
|
|
308
344
|
}
|
309
345
|
|
310
346
|
if (parser.getUpgrade()) {
|
311
|
-
|
347
|
+
byte[] upData = fetchBytes(buf, buf.position(), buf.limit() - buf.position());
|
348
|
+
((RubyString)upgradeData).concat(runtime.newString(new String(upData)));
|
349
|
+
|
312
350
|
} else if (buf.hasRemaining()) {
|
313
351
|
if (!stopped)
|
314
352
|
throw new RaiseException(runtime, eParserError, "Could not parse data entirely", true);
|
@@ -394,6 +432,25 @@ public class RubyHttpParser extends RubyObject {
|
|
394
432
|
return fragment == null ? runtime.getNil() : fragment;
|
395
433
|
}
|
396
434
|
|
435
|
+
@JRubyMethod(name = "header_value_type")
|
436
|
+
public IRubyObject getHeaderValueType() {
|
437
|
+
return header_value_type == null ? runtime.getNil() : header_value_type;
|
438
|
+
}
|
439
|
+
|
440
|
+
@JRubyMethod(name = "header_value_type=")
|
441
|
+
public IRubyObject set_header_value_type(IRubyObject val) {
|
442
|
+
if (val != runtime.newSymbol("mixed") && val != runtime.newSymbol("arrays") && val != runtime.newSymbol("strings")) {
|
443
|
+
throw runtime.newArgumentError("Invalid header value type");
|
444
|
+
}
|
445
|
+
header_value_type = val;
|
446
|
+
return val;
|
447
|
+
}
|
448
|
+
|
449
|
+
@JRubyMethod(name = "upgrade_data")
|
450
|
+
public IRubyObject upgradeData() {
|
451
|
+
return upgradeData == null ? runtime.getNil() : upgradeData;
|
452
|
+
}
|
453
|
+
|
397
454
|
@JRubyMethod(name = "reset!")
|
398
455
|
public IRubyObject reset() {
|
399
456
|
init();
|
@@ -23,6 +23,8 @@ typedef struct ParserWrapper {
|
|
23
23
|
|
24
24
|
VALUE headers;
|
25
25
|
|
26
|
+
VALUE upgrade_data;
|
27
|
+
|
26
28
|
VALUE on_message_begin;
|
27
29
|
VALUE on_headers_complete;
|
28
30
|
VALUE on_body;
|
@@ -32,6 +34,8 @@ typedef struct ParserWrapper {
|
|
32
34
|
VALUE stopped;
|
33
35
|
VALUE completed;
|
34
36
|
|
37
|
+
VALUE header_value_type;
|
38
|
+
|
35
39
|
VALUE last_field_name;
|
36
40
|
VALUE curr_field_name;
|
37
41
|
|
@@ -49,6 +53,8 @@ void ParserWrapper_init(ParserWrapper *wrapper) {
|
|
49
53
|
wrapper->query_string = Qnil;
|
50
54
|
wrapper->fragment = Qnil;
|
51
55
|
|
56
|
+
wrapper->upgrade_data = Qnil;
|
57
|
+
|
52
58
|
wrapper->headers = Qnil;
|
53
59
|
wrapper->completed = Qfalse;
|
54
60
|
|
@@ -63,6 +69,7 @@ void ParserWrapper_mark(void *data) {
|
|
63
69
|
rb_gc_mark_maybe(wrapper->request_path);
|
64
70
|
rb_gc_mark_maybe(wrapper->query_string);
|
65
71
|
rb_gc_mark_maybe(wrapper->fragment);
|
72
|
+
rb_gc_mark_maybe(wrapper->upgrade_data);
|
66
73
|
rb_gc_mark_maybe(wrapper->headers);
|
67
74
|
rb_gc_mark_maybe(wrapper->on_message_begin);
|
68
75
|
rb_gc_mark_maybe(wrapper->on_headers_complete);
|
@@ -93,6 +100,9 @@ static ID Ion_body;
|
|
93
100
|
static ID Ion_message_complete;
|
94
101
|
|
95
102
|
static VALUE Sstop;
|
103
|
+
static VALUE Sarrays;
|
104
|
+
static VALUE Sstrings;
|
105
|
+
static VALUE Smixed;
|
96
106
|
|
97
107
|
/** Callbacks **/
|
98
108
|
|
@@ -104,6 +114,7 @@ int on_message_begin(ryah_http_parser *parser) {
|
|
104
114
|
wrapper->query_string = rb_str_new2("");
|
105
115
|
wrapper->fragment = rb_str_new2("");
|
106
116
|
wrapper->headers = rb_hash_new();
|
117
|
+
wrapper->upgrade_data = rb_str_new2("");
|
107
118
|
|
108
119
|
VALUE ret = Qnil;
|
109
120
|
|
@@ -161,17 +172,45 @@ int on_header_field(ryah_http_parser *parser, const char *at, size_t length) {
|
|
161
172
|
int on_header_value(ryah_http_parser *parser, const char *at, size_t length) {
|
162
173
|
GET_WRAPPER(wrapper, parser);
|
163
174
|
|
175
|
+
int new_field = 0;
|
176
|
+
VALUE current_value;
|
177
|
+
|
164
178
|
if (wrapper->last_field_name == Qnil) {
|
179
|
+
new_field = 1;
|
165
180
|
wrapper->last_field_name = wrapper->curr_field_name;
|
166
181
|
wrapper->curr_field_name = Qnil;
|
182
|
+
}
|
167
183
|
|
168
|
-
|
169
|
-
|
170
|
-
|
184
|
+
current_value = rb_hash_aref(wrapper->headers, wrapper->last_field_name);
|
185
|
+
|
186
|
+
if (new_field == 1) {
|
187
|
+
if (current_value == Qnil) {
|
188
|
+
if (wrapper->header_value_type == Sarrays) {
|
189
|
+
rb_hash_aset(wrapper->headers, wrapper->last_field_name, rb_ary_new3(1, rb_str_new2("")));
|
190
|
+
} else {
|
191
|
+
rb_hash_aset(wrapper->headers, wrapper->last_field_name, rb_str_new2(""));
|
192
|
+
}
|
193
|
+
} else {
|
194
|
+
if (wrapper->header_value_type == Smixed) {
|
195
|
+
if (TYPE(current_value) == T_STRING) {
|
196
|
+
rb_hash_aset(wrapper->headers, wrapper->last_field_name, rb_ary_new3(2, current_value, rb_str_new2("")));
|
197
|
+
} else {
|
198
|
+
rb_ary_push(current_value, rb_str_new2(""));
|
199
|
+
}
|
200
|
+
} else if (wrapper->header_value_type == Sarrays) {
|
201
|
+
rb_ary_push(current_value, rb_str_new2(""));
|
202
|
+
} else {
|
203
|
+
rb_str_cat(current_value, ", ", 2);
|
204
|
+
}
|
171
205
|
}
|
206
|
+
current_value = rb_hash_aref(wrapper->headers, wrapper->last_field_name);
|
172
207
|
}
|
173
208
|
|
174
|
-
|
209
|
+
if (TYPE(current_value) == T_ARRAY) {
|
210
|
+
current_value = rb_ary_entry(current_value, -1);
|
211
|
+
}
|
212
|
+
|
213
|
+
rb_str_cat(current_value, at, length);
|
175
214
|
|
176
215
|
return 0;
|
177
216
|
}
|
@@ -280,8 +319,16 @@ VALUE Parser_initialize(int argc, VALUE *argv, VALUE self) {
|
|
280
319
|
ParserWrapper *wrapper = NULL;
|
281
320
|
DATA_GET(self, ParserWrapper, wrapper);
|
282
321
|
|
283
|
-
|
322
|
+
wrapper->header_value_type = rb_iv_get(CLASS_OF(self), "@default_header_value_type");
|
323
|
+
|
324
|
+
if (argc == 1) {
|
325
|
+
wrapper->callback_object = argv[0];
|
326
|
+
}
|
327
|
+
|
328
|
+
if (argc == 2) {
|
284
329
|
wrapper->callback_object = argv[0];
|
330
|
+
wrapper->header_value_type = argv[1];
|
331
|
+
}
|
285
332
|
|
286
333
|
return self;
|
287
334
|
}
|
@@ -299,7 +346,8 @@ VALUE Parser_execute(VALUE self, VALUE data) {
|
|
299
346
|
size_t nparsed = ryah_http_parser_execute(&wrapper->parser, &settings, ptr, len);
|
300
347
|
|
301
348
|
if (wrapper->parser.upgrade) {
|
302
|
-
|
349
|
+
rb_str_cat(wrapper->upgrade_data, ptr + nparsed + 1, len - nparsed - 1);
|
350
|
+
|
303
351
|
} else if (nparsed != len) {
|
304
352
|
if (!RTEST(wrapper->stopped) && !RTEST(wrapper->completed))
|
305
353
|
rb_raise(eParserError, "Could not parse data entirely");
|
@@ -418,6 +466,19 @@ DEFINE_GETTER(request_path);
|
|
418
466
|
DEFINE_GETTER(query_string);
|
419
467
|
DEFINE_GETTER(fragment);
|
420
468
|
DEFINE_GETTER(headers);
|
469
|
+
DEFINE_GETTER(upgrade_data);
|
470
|
+
DEFINE_GETTER(header_value_type);
|
471
|
+
|
472
|
+
VALUE Parser_set_header_value_type(VALUE self, VALUE val) {
|
473
|
+
if (val != Sarrays && val != Sstrings && val != Smixed) {
|
474
|
+
rb_raise(rb_eArgError, "Invalid header value type");
|
475
|
+
}
|
476
|
+
|
477
|
+
ParserWrapper *wrapper = NULL;
|
478
|
+
DATA_GET(self, ParserWrapper, wrapper);
|
479
|
+
wrapper->header_value_type = val;
|
480
|
+
return wrapper->header_value_type;
|
481
|
+
}
|
421
482
|
|
422
483
|
VALUE Parser_reset(VALUE self) {
|
423
484
|
ParserWrapper *wrapper = NULL;
|
@@ -442,6 +503,10 @@ void Init_ruby_http_parser() {
|
|
442
503
|
Ion_message_complete = rb_intern("on_message_complete");
|
443
504
|
Sstop = ID2SYM(rb_intern("stop"));
|
444
505
|
|
506
|
+
Sarrays = ID2SYM(rb_intern("arrays"));
|
507
|
+
Sstrings = ID2SYM(rb_intern("strings"));
|
508
|
+
Smixed = ID2SYM(rb_intern("mixed"));
|
509
|
+
|
445
510
|
rb_define_alloc_func(cParser, Parser_alloc);
|
446
511
|
rb_define_alloc_func(cRequestParser, RequestParser_alloc);
|
447
512
|
rb_define_alloc_func(cResponseParser, ResponseParser_alloc);
|
@@ -469,6 +534,9 @@ void Init_ruby_http_parser() {
|
|
469
534
|
rb_define_method(cParser, "query_string", Parser_query_string, 0);
|
470
535
|
rb_define_method(cParser, "fragment", Parser_fragment, 0);
|
471
536
|
rb_define_method(cParser, "headers", Parser_headers, 0);
|
537
|
+
rb_define_method(cParser, "upgrade_data", Parser_upgrade_data, 0);
|
538
|
+
rb_define_method(cParser, "header_value_type", Parser_header_value_type, 0);
|
539
|
+
rb_define_method(cParser, "header_value_type=", Parser_set_header_value_type, 1);
|
472
540
|
|
473
541
|
rb_define_method(cParser, "reset!", Parser_reset, 0);
|
474
542
|
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright
|
1
|
+
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
of this software and associated documentation files (the "Software"), to
|
@@ -1,4 +1,4 @@
|
|
1
|
-
/* Copyright
|
1
|
+
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
2
2
|
*
|
3
3
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
* of this software and associated documentation files (the "Software"), to
|
@@ -186,7 +186,28 @@ static const uint8_t normal_url_char[256] = {
|
|
186
186
|
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
|
187
187
|
1, 1, 1, 1, 1, 1, 1, 1,
|
188
188
|
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
|
189
|
-
1, 1, 1, 1, 1, 1, 1, 0
|
189
|
+
1, 1, 1, 1, 1, 1, 1, 0,
|
190
|
+
|
191
|
+
/* Remainder of non-ASCII range are accepted as-is to support implicitly UTF-8
|
192
|
+
encoded paths. This is out of spec, but clients generate this and most other
|
193
|
+
HTTP servers support it. We should, too. */
|
194
|
+
|
195
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
196
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
197
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
198
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
199
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
200
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
201
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
202
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
203
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
204
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
205
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
206
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
207
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
208
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
209
|
+
1, 1, 1, 1, 1, 1, 1, 1,
|
210
|
+
1, 1, 1, 1, 1, 1, 1, 1 };
|
190
211
|
|
191
212
|
|
192
213
|
enum state
|
@@ -240,15 +261,17 @@ enum state
|
|
240
261
|
|
241
262
|
, s_header_almost_done
|
242
263
|
|
264
|
+
, s_chunk_size_start
|
265
|
+
, s_chunk_size
|
266
|
+
, s_chunk_parameters
|
267
|
+
, s_chunk_size_almost_done
|
268
|
+
|
243
269
|
, s_headers_almost_done
|
244
270
|
/* Important: 's_headers_almost_done' must be the last 'header' state. All
|
245
271
|
* states beyond this must be 'body' states. It is used for overflow
|
246
272
|
* checking. See the PARSING_HEADER() macro.
|
247
273
|
*/
|
248
|
-
|
249
|
-
, s_chunk_size
|
250
|
-
, s_chunk_size_almost_done
|
251
|
-
, s_chunk_parameters
|
274
|
+
|
252
275
|
, s_chunk_data
|
253
276
|
, s_chunk_data_almost_done
|
254
277
|
, s_chunk_data_done
|
@@ -258,7 +281,7 @@ enum state
|
|
258
281
|
};
|
259
282
|
|
260
283
|
|
261
|
-
#define PARSING_HEADER(state) (state <= s_headers_almost_done
|
284
|
+
#define PARSING_HEADER(state) (state <= s_headers_almost_done)
|
262
285
|
|
263
286
|
|
264
287
|
enum header_states
|
@@ -331,10 +354,20 @@ size_t http_parser_execute (http_parser *parser,
|
|
331
354
|
uint64_t nread = parser->nread;
|
332
355
|
|
333
356
|
if (len == 0) {
|
334
|
-
|
335
|
-
|
357
|
+
switch (state) {
|
358
|
+
case s_body_identity_eof:
|
359
|
+
CALLBACK2(message_complete);
|
360
|
+
return 0;
|
361
|
+
|
362
|
+
case s_dead:
|
363
|
+
case s_start_req_or_res:
|
364
|
+
case s_start_res:
|
365
|
+
case s_start_req:
|
366
|
+
return 0;
|
367
|
+
|
368
|
+
default:
|
369
|
+
return 1; // error
|
336
370
|
}
|
337
|
-
return 0;
|
338
371
|
}
|
339
372
|
|
340
373
|
/* technically we could combine all of these (except for url_mark) into one
|
@@ -707,6 +740,9 @@ size_t http_parser_execute (http_parser *parser,
|
|
707
740
|
CALLBACK(url);
|
708
741
|
state = s_req_http_start;
|
709
742
|
break;
|
743
|
+
case '?':
|
744
|
+
state = s_req_query_string_start;
|
745
|
+
break;
|
710
746
|
default:
|
711
747
|
goto error;
|
712
748
|
}
|
@@ -729,6 +765,9 @@ size_t http_parser_execute (http_parser *parser,
|
|
729
765
|
CALLBACK(url);
|
730
766
|
state = s_req_http_start;
|
731
767
|
break;
|
768
|
+
case '?':
|
769
|
+
state = s_req_query_string_start;
|
770
|
+
break;
|
732
771
|
default:
|
733
772
|
goto error;
|
734
773
|
}
|
@@ -1448,6 +1487,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1448
1487
|
|
1449
1488
|
case s_chunk_size_start:
|
1450
1489
|
{
|
1490
|
+
assert(nread == 1);
|
1451
1491
|
assert(parser->flags & F_CHUNKED);
|
1452
1492
|
|
1453
1493
|
c = unhex[(unsigned char)ch];
|
@@ -1497,6 +1537,8 @@ size_t http_parser_execute (http_parser *parser,
|
|
1497
1537
|
assert(parser->flags & F_CHUNKED);
|
1498
1538
|
STRICT_CHECK(ch != LF);
|
1499
1539
|
|
1540
|
+
nread = 0;
|
1541
|
+
|
1500
1542
|
if (parser->content_length == 0) {
|
1501
1543
|
parser->flags |= F_TRAILING;
|
1502
1544
|
state = s_header_field_start;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
/* Copyright
|
1
|
+
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
2
2
|
*
|
3
3
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
* of this software and associated documentation files (the "Software"), to
|
@@ -24,6 +24,8 @@
|
|
24
24
|
extern "C" {
|
25
25
|
#endif
|
26
26
|
|
27
|
+
#define HTTP_PARSER_VERSION_MAJOR 1
|
28
|
+
#define HTTP_PARSER_VERSION_MINOR 0
|
27
29
|
|
28
30
|
#include <sys/types.h>
|
29
31
|
#if defined(_WIN32) && !defined(__MINGW32__)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
/* Copyright
|
1
|
+
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
2
2
|
*
|
3
3
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
* of this software and associated documentation files (the "Software"), to
|
@@ -557,7 +557,7 @@ const struct message requests[] =
|
|
557
557
|
,.body= ""
|
558
558
|
}
|
559
559
|
|
560
|
-
#define MSEARCH_REQ
|
560
|
+
#define MSEARCH_REQ 20
|
561
561
|
, {.name= "m-search request"
|
562
562
|
,.type= HTTP_REQUEST
|
563
563
|
,.raw= "M-SEARCH * HTTP/1.1\r\n"
|
@@ -582,6 +582,84 @@ const struct message requests[] =
|
|
582
582
|
,.body= ""
|
583
583
|
}
|
584
584
|
|
585
|
+
#define UTF8_PATH_REQ 21
|
586
|
+
, {.name= "utf-8 path request"
|
587
|
+
,.type= HTTP_REQUEST
|
588
|
+
,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
|
589
|
+
"Host: github.com\r\n"
|
590
|
+
"\r\n"
|
591
|
+
,.should_keep_alive= TRUE
|
592
|
+
,.message_complete_on_eof= FALSE
|
593
|
+
,.http_major= 1
|
594
|
+
,.http_minor= 1
|
595
|
+
,.method= HTTP_GET
|
596
|
+
,.query_string= "q=1"
|
597
|
+
,.fragment= "narf"
|
598
|
+
,.request_path= "/δ¶/δt/pope"
|
599
|
+
,.request_url= "/δ¶/δt/pope?q=1#narf"
|
600
|
+
,.num_headers= 1
|
601
|
+
,.headers= { {"Host", "github.com" }
|
602
|
+
}
|
603
|
+
,.body= ""
|
604
|
+
}
|
605
|
+
|
606
|
+
#define QUERY_TERMINATED_HOST 22
|
607
|
+
, {.name= "host terminated by a query string"
|
608
|
+
,.type= HTTP_REQUEST
|
609
|
+
,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
|
610
|
+
"\r\n"
|
611
|
+
,.should_keep_alive= TRUE
|
612
|
+
,.message_complete_on_eof= FALSE
|
613
|
+
,.http_major= 1
|
614
|
+
,.http_minor= 1
|
615
|
+
,.method= HTTP_GET
|
616
|
+
,.query_string= "hail=all"
|
617
|
+
,.fragment= ""
|
618
|
+
,.request_path= ""
|
619
|
+
,.request_url= "http://hypnotoad.org?hail=all"
|
620
|
+
,.num_headers= 0
|
621
|
+
,.headers= { }
|
622
|
+
,.body= ""
|
623
|
+
}
|
624
|
+
|
625
|
+
#define QUERY_TERMINATED_HOSTPORT 23
|
626
|
+
, {.name= "host:port terminated by a query string"
|
627
|
+
,.type= HTTP_REQUEST
|
628
|
+
,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
|
629
|
+
"\r\n"
|
630
|
+
,.should_keep_alive= TRUE
|
631
|
+
,.message_complete_on_eof= FALSE
|
632
|
+
,.http_major= 1
|
633
|
+
,.http_minor= 1
|
634
|
+
,.method= HTTP_GET
|
635
|
+
,.query_string= "hail=all"
|
636
|
+
,.fragment= ""
|
637
|
+
,.request_path= ""
|
638
|
+
,.request_url= "http://hypnotoad.org:1234?hail=all"
|
639
|
+
,.num_headers= 0
|
640
|
+
,.headers= { }
|
641
|
+
,.body= ""
|
642
|
+
}
|
643
|
+
|
644
|
+
#define SPACE_TERMINATED_HOSTPORT 24
|
645
|
+
, {.name= "host:port terminated by a space"
|
646
|
+
,.type= HTTP_REQUEST
|
647
|
+
,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
|
648
|
+
"\r\n"
|
649
|
+
,.should_keep_alive= TRUE
|
650
|
+
,.message_complete_on_eof= FALSE
|
651
|
+
,.http_major= 1
|
652
|
+
,.http_minor= 1
|
653
|
+
,.method= HTTP_GET
|
654
|
+
,.query_string= ""
|
655
|
+
,.fragment= ""
|
656
|
+
,.request_path= ""
|
657
|
+
,.request_url= "http://hypnotoad.org:1234"
|
658
|
+
,.num_headers= 0
|
659
|
+
,.headers= { }
|
660
|
+
,.body= ""
|
661
|
+
}
|
662
|
+
|
585
663
|
, {.name= NULL } /* sentinel */
|
586
664
|
};
|
587
665
|
|
@@ -1735,14 +1813,22 @@ main (void)
|
|
1735
1813
|
|
1736
1814
|
/// REQUESTS
|
1737
1815
|
|
1738
|
-
|
1739
1816
|
test_simple("hello world", 0);
|
1740
1817
|
test_simple("GET / HTP/1.1\r\n\r\n", 0);
|
1741
1818
|
|
1819
|
+
|
1742
1820
|
test_simple("ASDF / HTTP/1.1\r\n\r\n", 0);
|
1743
1821
|
test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", 0);
|
1744
1822
|
test_simple("GETA / HTTP/1.1\r\n\r\n", 0);
|
1745
1823
|
|
1824
|
+
// Well-formed but incomplete
|
1825
|
+
test_simple("GET / HTTP/1.1\r\n"
|
1826
|
+
"Content-Type: text/plain\r\n"
|
1827
|
+
"Content-Length: 6\r\n"
|
1828
|
+
"\r\n"
|
1829
|
+
"fooba",
|
1830
|
+
0);
|
1831
|
+
|
1746
1832
|
static const char *all_methods[] = {
|
1747
1833
|
"DELETE",
|
1748
1834
|
"GET",
|
@@ -1,4 +1,29 @@
|
|
1
|
-
Copyright
|
1
|
+
Copyright 2010 Tim Becker <tim.becker@kuriositaet.de>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
5
|
+
deal in the Software without restriction, including without limitation the
|
6
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
7
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
18
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
19
|
+
IN THE SOFTWARE.
|
20
|
+
|
21
|
+
--- END OF LICENSE
|
22
|
+
|
23
|
+
This code mainly based on code with the following license:
|
24
|
+
|
25
|
+
|
26
|
+
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
2
27
|
|
3
28
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
29
|
of this software and associated documentation files (the "Software"), to
|