http_parser.rb 0.5.1-x86-mswin32-60 → 0.5.2-x86-mswin32-60
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-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/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/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/http_parser.rb.gemspec +8 -2
- 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,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
|
@@ -1,20 +1,20 @@
|
|
1
1
|
HTTP Parser
|
2
2
|
===========
|
3
3
|
|
4
|
-
This is a parser for HTTP
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
This is a parser for HTTP written in Java, based quite heavily on
|
5
|
+
the Ryan Dahl's C Version: `http-parser` available here:
|
6
|
+
|
7
|
+
http://github.com/ry/http-parser
|
8
|
+
|
9
|
+
It parses both requests and responses. The parser is designed to be used
|
10
|
+
in performance HTTP applications.
|
10
11
|
|
11
12
|
Features:
|
12
13
|
|
13
|
-
* No dependencies
|
14
|
+
* No dependencies (probably won't be able to keep it up)
|
14
15
|
* Handles persistent streams (keep-alive).
|
15
16
|
* Decodes chunked encoding.
|
16
17
|
* Upgrade support
|
17
|
-
* Defends against buffer overflow attacks.
|
18
18
|
|
19
19
|
The parser extracts the following information from HTTP messages:
|
20
20
|
|
@@ -27,145 +27,25 @@ The parser extracts the following information from HTTP messages:
|
|
27
27
|
* Request path, query string, fragment
|
28
28
|
* Message body
|
29
29
|
|
30
|
+
Building
|
31
|
+
--------
|
32
|
+
|
33
|
+
use `ant compile|test|jar`
|
30
34
|
|
31
35
|
Usage
|
32
36
|
-----
|
33
37
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
http_parser_settings settings;
|
39
|
-
settings.on_path = my_path_callback;
|
40
|
-
settings.on_header_field = my_header_field_callback;
|
41
|
-
/* ... */
|
42
|
-
settings.data = my_socket;
|
43
|
-
|
44
|
-
http_parser *parser = malloc(sizeof(http_parser));
|
45
|
-
http_parser_init(parser, HTTP_REQUEST);
|
46
|
-
|
47
|
-
When data is received on the socket execute the parser and check for errors.
|
48
|
-
|
49
|
-
size_t len = 80*1024, nparsed;
|
50
|
-
char buf[len];
|
51
|
-
ssize_t recved;
|
52
|
-
|
53
|
-
recved = recv(fd, buf, len, 0);
|
54
|
-
|
55
|
-
if (recved < 0) {
|
56
|
-
/* Handle error. */
|
57
|
-
}
|
58
|
-
|
59
|
-
/* Start up / continue the parser.
|
60
|
-
* Note we pass recved==0 to signal that EOF has been recieved.
|
61
|
-
*/
|
62
|
-
nparsed = http_parser_execute(parser, &settings, buf, recved);
|
63
|
-
|
64
|
-
if (parser->upgrade) {
|
65
|
-
/* handle new protocol */
|
66
|
-
} else if (nparsed != recved) {
|
67
|
-
/* Handle error. Usually just close the connection. */
|
68
|
-
}
|
69
|
-
|
70
|
-
HTTP needs to know where the end of the stream is. For example, sometimes
|
71
|
-
servers send responses without Content-Length and expect the client to
|
72
|
-
consume input (for the body) until EOF. To tell http_parser about EOF, give
|
73
|
-
`0` as the forth parameter to `http_parser_execute()`. Callbacks and errors
|
74
|
-
can still be encountered during an EOF, so one must still be prepared
|
75
|
-
to receive them.
|
76
|
-
|
77
|
-
Scalar valued message information such as `status_code`, `method`, and the
|
78
|
-
HTTP version are stored in the parser structure. This data is only
|
79
|
-
temporally stored in `http_parser` and gets reset on each new message. If
|
80
|
-
this information is needed later, copy it out of the structure during the
|
81
|
-
`headers_complete` callback.
|
82
|
-
|
83
|
-
The parser decodes the transfer-encoding for both requests and responses
|
84
|
-
transparently. That is, a chunked encoding is decoded before being sent to
|
85
|
-
the on_body callback.
|
86
|
-
|
87
|
-
|
88
|
-
The Special Problem of Upgrade
|
89
|
-
------------------------------
|
90
|
-
|
91
|
-
HTTP supports upgrading the connection to a different protocol. An
|
92
|
-
increasingly common example of this is the Web Socket protocol which sends
|
93
|
-
a request like
|
94
|
-
|
95
|
-
GET /demo HTTP/1.1
|
96
|
-
Upgrade: WebSocket
|
97
|
-
Connection: Upgrade
|
98
|
-
Host: example.com
|
99
|
-
Origin: http://example.com
|
100
|
-
WebSocket-Protocol: sample
|
101
|
-
|
102
|
-
followed by non-HTTP data.
|
103
|
-
|
104
|
-
(See http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more
|
105
|
-
information the Web Socket protocol.)
|
106
|
-
|
107
|
-
To support this, the parser will treat this as a normal HTTP message without a
|
108
|
-
body. Issuing both on_headers_complete and on_message_complete callbacks. However
|
109
|
-
http_parser_execute() will stop parsing at the end of the headers and return.
|
110
|
-
|
111
|
-
The user is expected to check if `parser->upgrade` has been set to 1 after
|
112
|
-
`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied
|
113
|
-
offset by the return value of `http_parser_execute()`.
|
114
|
-
|
115
|
-
|
116
|
-
Callbacks
|
117
|
-
---------
|
118
|
-
|
119
|
-
During the `http_parser_execute()` call, the callbacks set in
|
120
|
-
`http_parser_settings` will be executed. The parser maintains state and
|
121
|
-
never looks behind, so buffering the data is not necessary. If you need to
|
122
|
-
save certain data for later usage, you can do that from the callbacks.
|
123
|
-
|
124
|
-
There are two types of callbacks:
|
125
|
-
|
126
|
-
* notification `typedef int (*http_cb) (http_parser*);`
|
127
|
-
Callbacks: on_message_begin, on_headers_complete, on_message_complete.
|
128
|
-
* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
|
129
|
-
Callbacks: (requests only) on_path, on_query_string, on_uri, on_fragment,
|
130
|
-
(common) on_header_field, on_header_value, on_body;
|
131
|
-
|
132
|
-
Callbacks must return 0 on success. Returning a non-zero value indicates
|
133
|
-
error to the parser, making it exit immediately.
|
134
|
-
|
135
|
-
In case you parse HTTP message in chunks (i.e. `read()` request line
|
136
|
-
from socket, parse, read half headers, parse, etc) your data callbacks
|
137
|
-
may be called more than once. Http-parser guarantees that data pointer is only
|
138
|
-
valid for the lifetime of callback. You can also `read()` into a heap allocated
|
139
|
-
buffer to avoid copying memory around if this fits your application.
|
140
|
-
|
141
|
-
Reading headers may be a tricky task if you read/parse headers partially.
|
142
|
-
Basically, you need to remember whether last header callback was field or value
|
143
|
-
and apply following logic:
|
144
|
-
|
145
|
-
(on_header_field and on_header_value shortened to on_h_*)
|
146
|
-
------------------------ ------------ --------------------------------------------
|
147
|
-
| State (prev. callback) | Callback | Description/action |
|
148
|
-
------------------------ ------------ --------------------------------------------
|
149
|
-
| nothing (first call) | on_h_field | Allocate new buffer and copy callback data |
|
150
|
-
| | | into it |
|
151
|
-
------------------------ ------------ --------------------------------------------
|
152
|
-
| value | on_h_field | New header started. |
|
153
|
-
| | | Copy current name,value buffers to headers |
|
154
|
-
| | | list and allocate new buffer for new name |
|
155
|
-
------------------------ ------------ --------------------------------------------
|
156
|
-
| field | on_h_field | Previous name continues. Reallocate name |
|
157
|
-
| | | buffer and append callback data to it |
|
158
|
-
------------------------ ------------ --------------------------------------------
|
159
|
-
| field | on_h_value | Value for current header started. Allocate |
|
160
|
-
| | | new buffer and copy callback data to it |
|
161
|
-
------------------------ ------------ --------------------------------------------
|
162
|
-
| value | on_h_value | Value continues. Reallocate value buffer |
|
163
|
-
| | | and append callback data to it |
|
164
|
-
------------------------ ------------ --------------------------------------------
|
38
|
+
TODO: in the present form, usage of the Java version of the parser
|
39
|
+
shouldn't be too difficult to figure out for someone familiar with the
|
40
|
+
C version.
|
165
41
|
|
42
|
+
More documentation will follow shortly, in case you're looking for an
|
43
|
+
easy to use http library, this lib is probably not what you are
|
44
|
+
looking for anyway ...
|
166
45
|
|
167
|
-
|
46
|
+
All text after this paragraph (and most of the text above it) are from
|
47
|
+
the original C version of the README and are currently only here for
|
48
|
+
reference. In case you encounter any difficulties, find bugs, need
|
49
|
+
help or have suggestions, feel free to contact me at
|
50
|
+
(tim.becker@kuriositaet.de).
|
168
51
|
|
169
|
-
* [partial example](http://gist.github.com/155877) in C
|
170
|
-
* [from http-parser tests](http://github.com/ry/http-parser/blob/37a0ff8928fb0d83cec0d0d8909c5a4abcd221af/test.c#L403) in C
|
171
|
-
* [from Node library](http://github.com/ry/node/blob/842eaf446d2fdcb33b296c67c911c32a0dabc747/src/http.js#L284) in Javascript
|