http_parser.rb 0.6.0.beta.1 → 0.8.0
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/.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);
|