http_parser.rb 0.6.0.beta.1 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/linux.yml +23 -0
- data/.github/workflows/windows.yml +23 -0
- data/.gitignore +5 -4
- data/.gitmodules +2 -2
- data/README.md +2 -2
- data/Rakefile +4 -2
- data/ext/ruby_http_parser/extconf.rb +1 -1
- data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +86 -52
- data/ext/ruby_http_parser/ruby_http_parser.c +53 -7
- data/ext/ruby_http_parser/vendor/http-parser/AUTHORS +37 -1
- data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +1 -5
- data/ext/ruby_http_parser/vendor/http-parser/Makefile +110 -8
- data/ext/ruby_http_parser/vendor/http-parser/README.md +105 -37
- data/ext/ruby_http_parser/vendor/http-parser/bench.c +128 -0
- data/ext/ruby_http_parser/vendor/http-parser/contrib/parsertrace.c +157 -0
- data/ext/ruby_http_parser/vendor/http-parser/contrib/url_parser.c +47 -0
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +892 -510
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.gyp +34 -2
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +198 -77
- data/ext/ruby_http_parser/vendor/http-parser/test.c +1781 -201
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +271 -154
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +48 -61
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +5 -3
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +37 -104
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +116 -101
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +9 -5
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +1 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test.c +579 -153
- data/http_parser.rb.gemspec +14 -9
- data/spec/parser_spec.rb +177 -99
- data/spec/support/requests.json +2 -2
- data/spec/support/responses.json +20 -0
- data/tasks/spec.rake +1 -1
- metadata +131 -162
- data/Gemfile.lock +0 -39
- data/ext/ruby_http_parser/vendor/http-parser/CONTRIBUTIONS +0 -4
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPHeadersCompleteCallback.java +0 -13
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPHeadersCompleteCallback.java +0 -12
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: cb02853976523252e0d5f36af3ba83b363010d4c163d9a4951b419054e7486ae
|
4
|
+
data.tar.gz: 3b7ac3ea5df5866582e0f68653d205764498e60e01835d2e19661cf4e1089d1b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 67516e3f1024e4bbf8dd00ae441646d53ca279313683b6f8da24668901c59b364608ecd9c69dab63709d9f89245ddc5363e0865f294a5af9d8e0b39d4712328f
|
7
|
+
data.tar.gz: 505a790706bc8f84404c2c6dd7e2b0025d591ca30311ca93d4eda4cf58a274c105acf68ed029ce96e38b04b040b86d4e953cf944a80a1e66a72f2a737d8cbfba
|
@@ -0,0 +1,23 @@
|
|
1
|
+
name: Testing on Ubuntu
|
2
|
+
on:
|
3
|
+
- push
|
4
|
+
- pull_request
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ${{ matrix.os }}
|
8
|
+
strategy:
|
9
|
+
fail-fast: false
|
10
|
+
matrix:
|
11
|
+
ruby: [ '2.6', '2.7', '3.0' ]
|
12
|
+
os: [ 'ubuntu-latest' ]
|
13
|
+
name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }}
|
14
|
+
steps:
|
15
|
+
- uses: actions/checkout@v2
|
16
|
+
- uses: ruby/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: ${{ matrix.ruby }}
|
19
|
+
- name: unit testing
|
20
|
+
run: |
|
21
|
+
gem install bundler rake
|
22
|
+
bundle install --jobs 4 --retry 3
|
23
|
+
bundle exec rake
|
@@ -0,0 +1,23 @@
|
|
1
|
+
name: Testing on Windows
|
2
|
+
on:
|
3
|
+
- push
|
4
|
+
- pull_request
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ${{ matrix.os }}
|
8
|
+
strategy:
|
9
|
+
fail-fast: false
|
10
|
+
matrix:
|
11
|
+
ruby: [ '2.6', '2.7', '3.0' ]
|
12
|
+
os: [ 'windows-latest' ]
|
13
|
+
name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }}
|
14
|
+
steps:
|
15
|
+
- uses: actions/checkout@v2
|
16
|
+
- uses: ruby/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: ${{ matrix.ruby }}
|
19
|
+
- name: unit testing
|
20
|
+
run: |
|
21
|
+
gem install bundler rake
|
22
|
+
bundle install --jobs 4 --retry 3
|
23
|
+
bundle exec rake
|
data/.gitignore
CHANGED
data/.gitmodules
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
[submodule "http-parser"]
|
2
2
|
path = ext/ruby_http_parser/vendor/http-parser
|
3
|
-
url =
|
3
|
+
url = https://github.com/nodejs/http-parser.git
|
4
4
|
[submodule "http-parser-java"]
|
5
5
|
path = ext/ruby_http_parser/vendor/http-parser-java
|
6
|
-
url =
|
6
|
+
url = https://github.com/tmm1/http-parser.java
|
data/README.md
CHANGED
@@ -3,13 +3,13 @@
|
|
3
3
|
A simple callback-based HTTP request/response parser for writing http
|
4
4
|
servers, clients and proxies.
|
5
5
|
|
6
|
-
This gem is built on top of [joyent/http-parser](
|
6
|
+
This gem is built on top of [joyent/http-parser](https://github.com/joyent/http-parser) and its java port [http-parser/http-parser.java](https://github.com/http-parser/http-parser.java).
|
7
7
|
|
8
8
|
## Supported Platforms
|
9
9
|
|
10
10
|
This gem aims to work on all major Ruby platforms, including:
|
11
11
|
|
12
|
-
- MRI 1.8 and
|
12
|
+
- MRI 1.8, 1.9 and 2.0; should work on MRI 2.4+
|
13
13
|
- Rubinius
|
14
14
|
- JRuby
|
15
15
|
- win32
|
data/Rakefile
CHANGED
@@ -1,32 +1,34 @@
|
|
1
1
|
package org.ruby_http_parser;
|
2
2
|
|
3
|
+
import http_parser.HTTPException;
|
4
|
+
import http_parser.HTTPMethod;
|
5
|
+
import http_parser.HTTPParser;
|
6
|
+
import http_parser.lolevel.HTTPCallback;
|
7
|
+
import http_parser.lolevel.HTTPDataCallback;
|
8
|
+
import http_parser.lolevel.ParserSettings;
|
9
|
+
|
10
|
+
import java.nio.ByteBuffer;
|
11
|
+
import java.util.Arrays;
|
12
|
+
import java.util.ArrayList;
|
13
|
+
import java.util.List;
|
14
|
+
|
15
|
+
import org.jcodings.Encoding;
|
16
|
+
import org.jcodings.specific.UTF8Encoding;
|
3
17
|
import org.jruby.Ruby;
|
4
18
|
import org.jruby.RubyArray;
|
5
19
|
import org.jruby.RubyClass;
|
6
20
|
import org.jruby.RubyHash;
|
7
|
-
import org.jruby.RubyModule;
|
8
21
|
import org.jruby.RubyNumeric;
|
9
22
|
import org.jruby.RubyObject;
|
10
23
|
import org.jruby.RubyString;
|
11
24
|
import org.jruby.RubySymbol;
|
12
|
-
|
25
|
+
import org.jruby.anno.JRubyMethod;
|
26
|
+
import org.jruby.exceptions.RaiseException;
|
13
27
|
import org.jruby.runtime.ObjectAllocator;
|
14
28
|
import org.jruby.runtime.ThreadContext;
|
15
29
|
import org.jruby.runtime.builtin.IRubyObject;
|
16
|
-
|
17
|
-
import org.jruby.anno.JRubyMethod;
|
18
|
-
import org.jruby.exceptions.RaiseException;
|
19
|
-
|
20
30
|
import org.jruby.util.ByteList;
|
21
31
|
|
22
|
-
import org.jcodings.specific.UTF8Encoding;
|
23
|
-
|
24
|
-
import java.nio.ByteBuffer;
|
25
|
-
import http_parser.*;
|
26
|
-
import http_parser.lolevel.ParserSettings;
|
27
|
-
import http_parser.lolevel.HTTPCallback;
|
28
|
-
import http_parser.lolevel.HTTPDataCallback;
|
29
|
-
|
30
32
|
public class RubyHttpParser extends RubyObject {
|
31
33
|
|
32
34
|
@JRubyMethod(name = "strict?", module = true)
|
@@ -40,7 +42,7 @@ public class RubyHttpParser extends RubyObject {
|
|
40
42
|
}
|
41
43
|
};
|
42
44
|
|
43
|
-
byte[] fetchBytes
|
45
|
+
byte[] fetchBytes(ByteBuffer b, int pos, int len) {
|
44
46
|
byte[] by = new byte[len];
|
45
47
|
int saved = b.position();
|
46
48
|
b.position(pos);
|
@@ -80,11 +82,17 @@ public class RubyHttpParser extends RubyObject {
|
|
80
82
|
private byte[] _current_header;
|
81
83
|
private byte[] _last_header;
|
82
84
|
|
85
|
+
private static final Encoding UTF8 = UTF8Encoding.INSTANCE;
|
86
|
+
|
87
|
+
private static final List<String> VALUE_TYPES = new ArrayList<String>(
|
88
|
+
Arrays.asList("mixed", "arrays", "strings")
|
89
|
+
);
|
90
|
+
|
83
91
|
public RubyHttpParser(final Ruby runtime, RubyClass clazz) {
|
84
|
-
super(runtime,clazz);
|
92
|
+
super(runtime, clazz);
|
85
93
|
|
86
94
|
this.runtime = runtime;
|
87
|
-
this.eParserError = (RubyClass)runtime.getModule("HTTP").getClass("Parser").getConstant("Error");
|
95
|
+
this.eParserError = (RubyClass) runtime.getModule("HTTP").getClass("Parser").getConstant("Error");
|
88
96
|
|
89
97
|
this.on_message_begin = null;
|
90
98
|
this.on_headers_complete = null;
|
@@ -95,7 +103,8 @@ public class RubyHttpParser extends RubyObject {
|
|
95
103
|
|
96
104
|
this.completed = false;
|
97
105
|
|
98
|
-
this.header_value_type = runtime.getModule("HTTP").getClass("Parser")
|
106
|
+
this.header_value_type = runtime.getModule("HTTP").getClass("Parser")
|
107
|
+
.getInstanceVariable("@default_header_value_type");
|
99
108
|
|
100
109
|
initSettings();
|
101
110
|
init();
|
@@ -105,15 +114,19 @@ public class RubyHttpParser extends RubyObject {
|
|
105
114
|
this.settings = new ParserSettings();
|
106
115
|
|
107
116
|
this.settings.on_url = new HTTPDataCallback() {
|
108
|
-
public int cb
|
117
|
+
public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
|
109
118
|
byte[] data = fetchBytes(buf, pos, len);
|
110
|
-
((
|
119
|
+
if (runtime.is1_9() || runtime.is2_0()) {
|
120
|
+
((RubyString) requestUrl).cat(data, 0, data.length, UTF8);
|
121
|
+
} else {
|
122
|
+
((RubyString) requestUrl).cat(data);
|
123
|
+
}
|
111
124
|
return 0;
|
112
125
|
}
|
113
126
|
};
|
114
127
|
|
115
128
|
this.settings.on_header_field = new HTTPDataCallback() {
|
116
|
-
public int cb
|
129
|
+
public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
|
117
130
|
byte[] data = fetchBytes(buf, pos, len);
|
118
131
|
|
119
132
|
if (_current_header == null)
|
@@ -123,7 +136,7 @@ public class RubyHttpParser extends RubyObject {
|
|
123
136
|
System.arraycopy(_current_header, 0, tmp, 0, _current_header.length);
|
124
137
|
System.arraycopy(data, 0, tmp, _current_header.length, data.length);
|
125
138
|
_current_header = tmp;
|
126
|
-
|
139
|
+
}
|
127
140
|
|
128
141
|
return 0;
|
129
142
|
}
|
@@ -133,7 +146,7 @@ public class RubyHttpParser extends RubyObject {
|
|
133
146
|
final RubySymbol stopSym = runtime.newSymbol("stop");
|
134
147
|
final RubySymbol resetSym = runtime.newSymbol("reset");
|
135
148
|
this.settings.on_header_value = new HTTPDataCallback() {
|
136
|
-
public int cb
|
149
|
+
public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
|
137
150
|
byte[] data = fetchBytes(buf, pos, len);
|
138
151
|
ThreadContext context = headers.getRuntime().getCurrentContext();
|
139
152
|
IRubyObject key, val;
|
@@ -145,57 +158,74 @@ public class RubyHttpParser extends RubyObject {
|
|
145
158
|
_current_header = null;
|
146
159
|
}
|
147
160
|
|
148
|
-
key = RubyString.newString(runtime, new ByteList(_last_header,
|
161
|
+
key = RubyString.newString(runtime, new ByteList(_last_header, UTF8, false));
|
149
162
|
val = headers.op_aref(context, key);
|
150
163
|
|
151
164
|
if (new_field == 1) {
|
152
165
|
if (val.isNil()) {
|
153
166
|
if (header_value_type == arraysSym) {
|
154
|
-
headers.op_aset(context, key,
|
167
|
+
headers.op_aset(context, key,
|
168
|
+
RubyArray.newArrayLight(runtime, RubyString.newStringLight(runtime, 10, UTF8)));
|
155
169
|
} else {
|
156
|
-
headers.op_aset(context, key, RubyString.newStringLight(runtime, 10));
|
170
|
+
headers.op_aset(context, key, RubyString.newStringLight(runtime, 10, UTF8));
|
157
171
|
}
|
158
172
|
} else {
|
159
173
|
if (header_value_type == mixedSym) {
|
160
174
|
if (val instanceof RubyString) {
|
161
|
-
headers.op_aset(context, key,
|
175
|
+
headers.op_aset(context, key,
|
176
|
+
RubyArray.newArrayLight(runtime, val, RubyString.newStringLight(runtime, 10, UTF8)));
|
162
177
|
} else {
|
163
|
-
((RubyArray)val).add(RubyString.newStringLight(runtime, 10));
|
178
|
+
((RubyArray) val).add(RubyString.newStringLight(runtime, 10, UTF8));
|
164
179
|
}
|
165
180
|
} else if (header_value_type == arraysSym) {
|
166
|
-
((RubyArray)val).add(RubyString.newStringLight(runtime, 10));
|
181
|
+
((RubyArray) val).add(RubyString.newStringLight(runtime, 10, UTF8));
|
167
182
|
} else {
|
168
|
-
((
|
183
|
+
if (runtime.is1_9() || runtime.is2_0()) {
|
184
|
+
((RubyString) val).cat(',', UTF8).cat(' ', UTF8);
|
185
|
+
} else {
|
186
|
+
((RubyString) val).cat(',').cat(' ');
|
187
|
+
}
|
169
188
|
}
|
170
189
|
}
|
171
190
|
val = headers.op_aref(context, key);
|
172
191
|
}
|
173
192
|
|
174
193
|
if (val instanceof RubyArray) {
|
175
|
-
val = ((RubyArray)val).entry(-1);
|
194
|
+
val = ((RubyArray) val).entry(-1);
|
176
195
|
}
|
177
196
|
|
178
|
-
((
|
197
|
+
if (runtime.is1_9() || runtime.is2_0()) {
|
198
|
+
((RubyString) val).cat(data, 0, data.length, UTF8);
|
199
|
+
} else {
|
200
|
+
((RubyString) val).cat(data);
|
201
|
+
}
|
179
202
|
|
180
203
|
return 0;
|
181
204
|
}
|
182
205
|
};
|
183
206
|
|
184
207
|
this.settings.on_message_begin = new HTTPCallback() {
|
185
|
-
public int cb
|
208
|
+
public int cb(http_parser.lolevel.HTTPParser p) {
|
186
209
|
headers = new RubyHash(runtime);
|
187
210
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
211
|
+
if (runtime.is1_9() || runtime.is2_0()) {
|
212
|
+
requestUrl = RubyString.newEmptyString(runtime, UTF8);
|
213
|
+
requestPath = RubyString.newEmptyString(runtime, UTF8);
|
214
|
+
queryString = RubyString.newEmptyString(runtime, UTF8);
|
215
|
+
fragment = RubyString.newEmptyString(runtime, UTF8);
|
216
|
+
upgradeData = RubyString.newEmptyString(runtime, UTF8);
|
217
|
+
} else {
|
218
|
+
requestUrl = RubyString.newEmptyString(runtime);
|
219
|
+
requestPath = RubyString.newEmptyString(runtime);
|
220
|
+
queryString = RubyString.newEmptyString(runtime);
|
221
|
+
fragment = RubyString.newEmptyString(runtime);
|
222
|
+
upgradeData = RubyString.newEmptyString(runtime);
|
223
|
+
}
|
194
224
|
|
195
225
|
IRubyObject ret = runtime.getNil();
|
196
226
|
|
197
227
|
if (callback_object != null) {
|
198
|
-
if (((RubyObject)callback_object).respondsTo("on_message_begin")) {
|
228
|
+
if (((RubyObject) callback_object).respondsTo("on_message_begin")) {
|
199
229
|
ThreadContext context = callback_object.getRuntime().getCurrentContext();
|
200
230
|
ret = callback_object.callMethod(context, "on_message_begin");
|
201
231
|
}
|
@@ -212,13 +242,13 @@ public class RubyHttpParser extends RubyObject {
|
|
212
242
|
}
|
213
243
|
};
|
214
244
|
this.settings.on_message_complete = new HTTPCallback() {
|
215
|
-
public int cb
|
245
|
+
public int cb(http_parser.lolevel.HTTPParser p) {
|
216
246
|
IRubyObject ret = runtime.getNil();
|
217
247
|
|
218
248
|
completed = true;
|
219
249
|
|
220
250
|
if (callback_object != null) {
|
221
|
-
if (((RubyObject)callback_object).respondsTo("on_message_complete")) {
|
251
|
+
if (((RubyObject) callback_object).respondsTo("on_message_complete")) {
|
222
252
|
ThreadContext context = callback_object.getRuntime().getCurrentContext();
|
223
253
|
ret = callback_object.callMethod(context, "on_message_complete");
|
224
254
|
}
|
@@ -235,11 +265,11 @@ public class RubyHttpParser extends RubyObject {
|
|
235
265
|
}
|
236
266
|
};
|
237
267
|
this.settings.on_headers_complete = new HTTPCallback() {
|
238
|
-
public int cb
|
268
|
+
public int cb(http_parser.lolevel.HTTPParser p) {
|
239
269
|
IRubyObject ret = runtime.getNil();
|
240
270
|
|
241
271
|
if (callback_object != null) {
|
242
|
-
if (((RubyObject)callback_object).respondsTo("on_headers_complete")) {
|
272
|
+
if (((RubyObject) callback_object).respondsTo("on_headers_complete")) {
|
243
273
|
ThreadContext context = callback_object.getRuntime().getCurrentContext();
|
244
274
|
ret = callback_object.callMethod(context, "on_headers_complete", headers);
|
245
275
|
}
|
@@ -258,18 +288,19 @@ public class RubyHttpParser extends RubyObject {
|
|
258
288
|
}
|
259
289
|
};
|
260
290
|
this.settings.on_body = new HTTPDataCallback() {
|
261
|
-
public int cb
|
291
|
+
public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
|
262
292
|
IRubyObject ret = runtime.getNil();
|
263
293
|
byte[] data = fetchBytes(buf, pos, len);
|
264
294
|
|
265
295
|
if (callback_object != null) {
|
266
|
-
if (((RubyObject)callback_object).respondsTo("on_body")) {
|
296
|
+
if (((RubyObject) callback_object).respondsTo("on_body")) {
|
267
297
|
ThreadContext context = callback_object.getRuntime().getCurrentContext();
|
268
|
-
ret = callback_object.callMethod(context, "on_body",
|
298
|
+
ret = callback_object.callMethod(context, "on_body",
|
299
|
+
RubyString.newString(runtime, new ByteList(data, UTF8, false)));
|
269
300
|
}
|
270
301
|
} else if (on_body != null) {
|
271
302
|
ThreadContext context = on_body.getRuntime().getCurrentContext();
|
272
|
-
ret = on_body.callMethod(context, "call", RubyString.newString(runtime, new ByteList(data,
|
303
|
+
ret = on_body.callMethod(context, "call", RubyString.newString(runtime, new ByteList(data, UTF8, false)));
|
273
304
|
}
|
274
305
|
|
275
306
|
if (ret == stopSym) {
|
@@ -337,7 +368,7 @@ public class RubyHttpParser extends RubyObject {
|
|
337
368
|
|
338
369
|
@JRubyMethod(name = "<<")
|
339
370
|
public IRubyObject execute(IRubyObject data) {
|
340
|
-
RubyString str = (RubyString)data;
|
371
|
+
RubyString str = (RubyString) data;
|
341
372
|
ByteList byteList = str.getByteList();
|
342
373
|
ByteBuffer buf = ByteBuffer.wrap(byteList.getUnsafeBytes(), byteList.getBegin(), byteList.getRealSize());
|
343
374
|
boolean stopped = false;
|
@@ -352,8 +383,11 @@ public class RubyHttpParser extends RubyObject {
|
|
352
383
|
|
353
384
|
if (parser.getUpgrade()) {
|
354
385
|
byte[] upData = fetchBytes(buf, buf.position(), buf.limit() - buf.position());
|
355
|
-
((
|
356
|
-
|
386
|
+
if (runtime.is1_9() || runtime.is2_0()) {
|
387
|
+
((RubyString) upgradeData).cat(upData, 0, upData.length, UTF8);
|
388
|
+
} else {
|
389
|
+
((RubyString) upgradeData).cat(upData);
|
390
|
+
}
|
357
391
|
} else if (buf.hasRemaining() && !completed) {
|
358
392
|
if (!stopped)
|
359
393
|
throw new RaiseException(runtime, eParserError, "Could not parse data entirely", true);
|
@@ -447,7 +481,7 @@ public class RubyHttpParser extends RubyObject {
|
|
447
481
|
@JRubyMethod(name = "header_value_type=")
|
448
482
|
public IRubyObject set_header_value_type(IRubyObject val) {
|
449
483
|
String valString = val.toString();
|
450
|
-
if (valString
|
484
|
+
if (!VALUE_TYPES.contains(valString)) {
|
451
485
|
throw runtime.newArgumentError("Invalid header value type");
|
452
486
|
}
|
453
487
|
header_value_type = val;
|
@@ -16,6 +16,7 @@
|
|
16
16
|
typedef struct ParserWrapper {
|
17
17
|
ryah_http_parser parser;
|
18
18
|
|
19
|
+
VALUE status;
|
19
20
|
VALUE request_url;
|
20
21
|
|
21
22
|
VALUE headers;
|
@@ -45,6 +46,7 @@ void ParserWrapper_init(ParserWrapper *wrapper) {
|
|
45
46
|
wrapper->parser.http_major = 0;
|
46
47
|
wrapper->parser.http_minor = 0;
|
47
48
|
|
49
|
+
wrapper->status = Qnil;
|
48
50
|
wrapper->request_url = Qnil;
|
49
51
|
|
50
52
|
wrapper->upgrade_data = Qnil;
|
@@ -59,6 +61,7 @@ void ParserWrapper_init(ParserWrapper *wrapper) {
|
|
59
61
|
void ParserWrapper_mark(void *data) {
|
60
62
|
if(data) {
|
61
63
|
ParserWrapper *wrapper = (ParserWrapper *) data;
|
64
|
+
rb_gc_mark_maybe(wrapper->status);
|
62
65
|
rb_gc_mark_maybe(wrapper->request_url);
|
63
66
|
rb_gc_mark_maybe(wrapper->upgrade_data);
|
64
67
|
rb_gc_mark_maybe(wrapper->headers);
|
@@ -101,6 +104,7 @@ static VALUE Smixed;
|
|
101
104
|
int on_message_begin(ryah_http_parser *parser) {
|
102
105
|
GET_WRAPPER(wrapper, parser);
|
103
106
|
|
107
|
+
wrapper->status = rb_str_new2("");
|
104
108
|
wrapper->request_url = rb_str_new2("");
|
105
109
|
wrapper->headers = rb_hash_new();
|
106
110
|
wrapper->upgrade_data = rb_str_new2("");
|
@@ -121,9 +125,28 @@ int on_message_begin(ryah_http_parser *parser) {
|
|
121
125
|
}
|
122
126
|
}
|
123
127
|
|
128
|
+
int on_status(ryah_http_parser *parser, const char *at, size_t length) {
|
129
|
+
GET_WRAPPER(wrapper, parser);
|
130
|
+
|
131
|
+
if (at && length) {
|
132
|
+
if (wrapper->status == Qnil) {
|
133
|
+
wrapper->status = rb_str_new(at, length);
|
134
|
+
} else {
|
135
|
+
rb_str_cat(wrapper->status, at, length);
|
136
|
+
}
|
137
|
+
}
|
138
|
+
return 0;
|
139
|
+
}
|
140
|
+
|
124
141
|
int on_url(ryah_http_parser *parser, const char *at, size_t length) {
|
125
142
|
GET_WRAPPER(wrapper, parser);
|
126
|
-
|
143
|
+
if (at && length) {
|
144
|
+
if (wrapper->request_url == Qnil) {
|
145
|
+
wrapper->request_url = rb_str_new(at, length);
|
146
|
+
} else {
|
147
|
+
rb_str_cat(wrapper->request_url, at, length);
|
148
|
+
}
|
149
|
+
}
|
127
150
|
return 0;
|
128
151
|
}
|
129
152
|
|
@@ -136,7 +159,6 @@ int on_header_field(ryah_http_parser *parser, const char *at, size_t length) {
|
|
136
159
|
} else {
|
137
160
|
rb_str_cat(wrapper->curr_field_name, at, length);
|
138
161
|
}
|
139
|
-
|
140
162
|
return 0;
|
141
163
|
}
|
142
164
|
|
@@ -248,6 +270,7 @@ int on_message_complete(ryah_http_parser *parser) {
|
|
248
270
|
|
249
271
|
static ryah_http_parser_settings settings = {
|
250
272
|
.on_message_begin = on_message_begin,
|
273
|
+
.on_status = on_status,
|
251
274
|
.on_url = on_url,
|
252
275
|
.on_header_field = on_header_field,
|
253
276
|
.on_header_value = on_header_value,
|
@@ -293,7 +316,17 @@ VALUE Parser_initialize(int argc, VALUE *argv, VALUE self) {
|
|
293
316
|
ParserWrapper *wrapper = NULL;
|
294
317
|
DATA_GET(self, ParserWrapper, wrapper);
|
295
318
|
|
296
|
-
|
319
|
+
VALUE default_header_value_type = Qnil;
|
320
|
+
|
321
|
+
if (argc > 0 && RB_TYPE_P(argv[argc-1], T_HASH)) {
|
322
|
+
ID keyword_ids[1];
|
323
|
+
keyword_ids[0] = rb_intern("default_header_value_type");
|
324
|
+
rb_get_kwargs(argv[argc-1], keyword_ids, 0, 1, &default_header_value_type);
|
325
|
+
if (default_header_value_type == Qundef) {
|
326
|
+
default_header_value_type = Qnil;
|
327
|
+
}
|
328
|
+
--argc;
|
329
|
+
}
|
297
330
|
|
298
331
|
if (argc == 1) {
|
299
332
|
wrapper->callback_object = argv[0];
|
@@ -301,7 +334,13 @@ VALUE Parser_initialize(int argc, VALUE *argv, VALUE self) {
|
|
301
334
|
|
302
335
|
if (argc == 2) {
|
303
336
|
wrapper->callback_object = argv[0];
|
304
|
-
|
337
|
+
default_header_value_type = argv[1];
|
338
|
+
}
|
339
|
+
|
340
|
+
if (default_header_value_type == Qnil) {
|
341
|
+
wrapper->header_value_type = rb_iv_get(CLASS_OF(self), "@default_header_value_type");
|
342
|
+
} else {
|
343
|
+
wrapper->header_value_type = default_header_value_type;
|
305
344
|
}
|
306
345
|
|
307
346
|
return self;
|
@@ -320,11 +359,12 @@ VALUE Parser_execute(VALUE self, VALUE data) {
|
|
320
359
|
size_t nparsed = ryah_http_parser_execute(&wrapper->parser, &settings, ptr, len);
|
321
360
|
|
322
361
|
if (wrapper->parser.upgrade) {
|
323
|
-
if (RTEST(wrapper->stopped))
|
362
|
+
if (RTEST(wrapper->stopped) && !RTEST(wrapper->completed))
|
324
363
|
nparsed += 1;
|
325
364
|
|
326
|
-
|
327
|
-
|
365
|
+
if (nparsed < len)
|
366
|
+
rb_str_cat(wrapper->upgrade_data, ptr + nparsed, len - nparsed);
|
367
|
+
|
328
368
|
} else if (nparsed != (size_t)len) {
|
329
369
|
if (!RTEST(wrapper->stopped) && !RTEST(wrapper->completed))
|
330
370
|
rb_raise(eParserError, "Could not parse data entirely (%zu != %zu)", nparsed, len);
|
@@ -438,6 +478,7 @@ VALUE Parser_status_code(VALUE self) {
|
|
438
478
|
return wrapper->name; \
|
439
479
|
}
|
440
480
|
|
481
|
+
DEFINE_GETTER(status);
|
441
482
|
DEFINE_GETTER(request_url);
|
442
483
|
DEFINE_GETTER(headers);
|
443
484
|
DEFINE_GETTER(upgrade_data);
|
@@ -464,6 +505,10 @@ VALUE Parser_reset(VALUE self) {
|
|
464
505
|
}
|
465
506
|
|
466
507
|
void Init_ruby_http_parser() {
|
508
|
+
#ifdef HAVE_RB_EXT_RACTOR_SAFE
|
509
|
+
rb_ext_ractor_safe(true);
|
510
|
+
#endif
|
511
|
+
|
467
512
|
VALUE mHTTP = rb_define_module("HTTP");
|
468
513
|
cParser = rb_define_class_under(mHTTP, "Parser", rb_cObject);
|
469
514
|
cRequestParser = rb_define_class_under(mHTTP, "RequestParser", cParser);
|
@@ -505,6 +550,7 @@ void Init_ruby_http_parser() {
|
|
505
550
|
rb_define_method(cParser, "http_method", Parser_http_method, 0);
|
506
551
|
rb_define_method(cParser, "status_code", Parser_status_code, 0);
|
507
552
|
|
553
|
+
rb_define_method(cParser, "status", Parser_status, 0);
|
508
554
|
rb_define_method(cParser, "request_url", Parser_request_url, 0);
|
509
555
|
rb_define_method(cParser, "headers", Parser_headers, 0);
|
510
556
|
rb_define_method(cParser, "upgrade_data", Parser_upgrade_data, 0);
|