http_parser.rb 0.5.2 → 0.7.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 +4 -4
- data/Gemfile +1 -1
- data/README.md +52 -47
- data/Rakefile +1 -0
- data/bench/standalone.rb +23 -0
- data/bench/thin.rb +1 -0
- data/ext/ruby_http_parser/extconf.rb +1 -1
- data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +139 -83
- data/ext/ruby_http_parser/ruby_http_parser.c +40 -41
- data/ext/ruby_http_parser/vendor/http-parser-java/AUTHORS +32 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +5 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/README.md +133 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/TODO +6 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1202 -671
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.gyp +79 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +172 -51
- data/ext/ruby_http_parser/vendor/http-parser-java/src/Http-parser.java.iml +22 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/FieldData.java +41 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +8 -3
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParserUrl.java +76 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +35 -102
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/Util.java +6 -6
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +775 -682
- data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +8 -4
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +70 -20
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/ParseUrl.java +51 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +1 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +2 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +6 -17
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +1 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Url.java +127 -0
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +80 -9
- data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +2 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1637 -280
- data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +230 -71
- data/ext/ruby_http_parser/vendor/http-parser/AUTHORS +68 -0
- data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +1 -1
- data/ext/ruby_http_parser/vendor/http-parser/README.md +113 -38
- 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 +1576 -780
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.gyp +111 -0
- data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +308 -58
- data/ext/ruby_http_parser/vendor/http-parser/test.c +2964 -460
- data/http_parser.rb.gemspec +14 -7
- data/spec/parser_spec.rb +196 -102
- data/spec/support/requests.json +236 -24
- data/spec/support/responses.json +202 -36
- data/tasks/compile.rake +2 -2
- data/tasks/fixtures.rake +8 -2
- data/tasks/spec.rake +1 -1
- metadata +141 -134
- data/Gemfile.lock +0 -32
- data/ext/ruby_http_parser/vendor/http-parser-java/compile +0 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +0 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +0 -1
- data/ext/ruby_http_parser/vendor/http-parser-java/test_utf8 +0 -1
- data/ext/ruby_http_parser/vendor/http-parser/CONTRIBUTIONS +0 -4
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: afdb28464ccb0f753a1d66be02b667683d6aadfe31fe03a88f889252b457761d
|
4
|
+
data.tar.gz: 6041b3dc3f212160ae00b72b193e4620c5facd59096a28b122c0ef5a901dc80b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 41249c628f931cd9721e32bd378a44b8e49b25706b491ae3761d641e55bac1205afe9d3d02d88d5b4f3161adee4a65416c5eec2a51fcacad1b050d6f6d2637fc
|
7
|
+
data.tar.gz: ae5718390cce25d2ee7fcbaf923fac015189079b64ea2f37759c41ee2e7f2714dafaf6bf37ed6434f540e82e87c6a13723e226ac27000d1f0e2d7b2681643514
|
@@ -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
|
-
[submodule "
|
1
|
+
[submodule "http-parser"]
|
2
2
|
path = ext/ruby_http_parser/vendor/http-parser
|
3
|
-
url =
|
4
|
-
[submodule "
|
3
|
+
url = https://github.com/nodejs/http-parser.git
|
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/Gemfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
gemspec
|
data/README.md
CHANGED
@@ -3,83 +3,88 @@
|
|
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 [
|
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
|
16
16
|
|
17
17
|
## Usage
|
18
18
|
|
19
|
-
|
19
|
+
```ruby
|
20
|
+
require "http/parser"
|
20
21
|
|
21
|
-
|
22
|
+
parser = Http::Parser.new
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
parser.on_headers_complete = proc do
|
25
|
+
p parser.http_version
|
25
26
|
|
26
|
-
|
27
|
-
|
27
|
+
p parser.http_method # for requests
|
28
|
+
p parser.request_url
|
28
29
|
|
29
|
-
|
30
|
+
p parser.status_code # for responses
|
30
31
|
|
31
|
-
|
32
|
-
|
32
|
+
p parser.headers
|
33
|
+
end
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
parser.on_body = proc do |chunk|
|
36
|
+
# One chunk of the body
|
37
|
+
p chunk
|
38
|
+
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
parser.on_message_complete = proc do |env|
|
41
|
+
# Headers and body is all parsed
|
42
|
+
puts "Done!"
|
43
|
+
end
|
44
|
+
```
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
+
# Feed raw data from the socket to the parser
|
47
|
+
`parser << raw_data`
|
46
48
|
|
47
49
|
## Advanced Usage
|
48
50
|
|
49
51
|
### Accept callbacks on an object
|
50
52
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
```ruby
|
54
|
+
module MyHttpConnection
|
55
|
+
def connection_completed
|
56
|
+
@parser = Http::Parser.new(self)
|
57
|
+
end
|
55
58
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
+
def receive_data(data)
|
60
|
+
@parser << data
|
61
|
+
end
|
59
62
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
63
|
+
def on_message_begin
|
64
|
+
@headers = nil
|
65
|
+
@body = ''
|
66
|
+
end
|
64
67
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
+
def on_headers_complete(headers)
|
69
|
+
@headers = headers
|
70
|
+
end
|
68
71
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
+
def on_body(chunk)
|
73
|
+
@body << chunk
|
74
|
+
end
|
72
75
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
76
|
+
def on_message_complete
|
77
|
+
p [@headers, @body]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
```
|
77
81
|
|
78
82
|
### Stop parsing after headers
|
79
83
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
offset = parser << request_data
|
84
|
-
body = request_data[offset..-1]
|
84
|
+
```ruby
|
85
|
+
parser = Http::Parser.new
|
86
|
+
parser.on_headers_complete = proc{ :stop }
|
85
87
|
|
88
|
+
offset = parser << request_data
|
89
|
+
body = request_data[offset..-1]
|
90
|
+
```
|
data/Rakefile
CHANGED
data/bench/standalone.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
3
|
+
require "rubygems"
|
4
|
+
require "http/parser"
|
5
|
+
require "benchmark/ips"
|
6
|
+
|
7
|
+
request = <<-REQUEST
|
8
|
+
GET / HTTP/1.1
|
9
|
+
Host: www.example.com
|
10
|
+
Connection: keep-alive
|
11
|
+
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.78 S
|
12
|
+
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
13
|
+
Accept-Encoding: gzip,deflate,sdch
|
14
|
+
Accept-Language: en-US,en;q=0.8
|
15
|
+
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
|
16
|
+
|
17
|
+
REQUEST
|
18
|
+
request.gsub!(/\n/m, "\r\n")
|
19
|
+
|
20
|
+
Benchmark.ips do |ips|
|
21
|
+
ips.report("instance") { Http::Parser.new }
|
22
|
+
ips.report("parsing") { Http::Parser.new << request }
|
23
|
+
end
|
data/bench/thin.rb
CHANGED
@@ -1,36 +1,45 @@
|
|
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
|
+
|
12
|
+
import org.jcodings.Encoding;
|
13
|
+
import org.jcodings.specific.UTF8Encoding;
|
3
14
|
import org.jruby.Ruby;
|
4
15
|
import org.jruby.RubyArray;
|
5
16
|
import org.jruby.RubyClass;
|
6
17
|
import org.jruby.RubyHash;
|
7
|
-
import org.jruby.RubyModule;
|
8
18
|
import org.jruby.RubyNumeric;
|
9
19
|
import org.jruby.RubyObject;
|
10
20
|
import org.jruby.RubyString;
|
11
|
-
|
21
|
+
import org.jruby.RubySymbol;
|
22
|
+
import org.jruby.anno.JRubyMethod;
|
23
|
+
import org.jruby.exceptions.RaiseException;
|
12
24
|
import org.jruby.runtime.ObjectAllocator;
|
13
25
|
import org.jruby.runtime.ThreadContext;
|
14
26
|
import org.jruby.runtime.builtin.IRubyObject;
|
15
|
-
|
16
|
-
import org.jruby.anno.JRubyMethod;
|
17
|
-
import org.jruby.exceptions.RaiseException;
|
18
|
-
|
19
|
-
import java.nio.ByteBuffer;
|
20
|
-
import http_parser.*;
|
21
|
-
import http_parser.lolevel.ParserSettings;
|
22
|
-
import http_parser.lolevel.HTTPCallback;
|
23
|
-
import http_parser.lolevel.HTTPDataCallback;
|
27
|
+
import org.jruby.util.ByteList;
|
24
28
|
|
25
29
|
public class RubyHttpParser extends RubyObject {
|
26
30
|
|
31
|
+
@JRubyMethod(name = "strict?", module = true)
|
32
|
+
public static IRubyObject strict(IRubyObject recv) {
|
33
|
+
return recv.getRuntime().newBoolean(true);
|
34
|
+
}
|
35
|
+
|
27
36
|
public static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
28
37
|
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
29
38
|
return new RubyHttpParser(runtime, klass);
|
30
39
|
}
|
31
40
|
};
|
32
41
|
|
33
|
-
byte[] fetchBytes
|
42
|
+
byte[] fetchBytes(ByteBuffer b, int pos, int len) {
|
34
43
|
byte[] by = new byte[len];
|
35
44
|
int saved = b.position();
|
36
45
|
b.position(pos);
|
@@ -55,6 +64,7 @@ public class RubyHttpParser extends RubyObject {
|
|
55
64
|
private IRubyObject on_body;
|
56
65
|
private IRubyObject on_message_complete;
|
57
66
|
|
67
|
+
private IRubyObject status;
|
58
68
|
private IRubyObject requestUrl;
|
59
69
|
private IRubyObject requestPath;
|
60
70
|
private IRubyObject queryString;
|
@@ -65,14 +75,18 @@ public class RubyHttpParser extends RubyObject {
|
|
65
75
|
|
66
76
|
private IRubyObject callback_object;
|
67
77
|
|
68
|
-
private
|
69
|
-
|
78
|
+
private boolean completed;
|
79
|
+
|
80
|
+
private byte[] _current_header;
|
81
|
+
private byte[] _last_header;
|
82
|
+
|
83
|
+
private static final Encoding UTF8 = UTF8Encoding.INSTANCE;
|
70
84
|
|
71
85
|
public RubyHttpParser(final Ruby runtime, RubyClass clazz) {
|
72
|
-
super(runtime,clazz);
|
86
|
+
super(runtime, clazz);
|
73
87
|
|
74
88
|
this.runtime = runtime;
|
75
|
-
this.eParserError = (RubyClass)runtime.getModule("HTTP").getClass("Parser").getConstant("Error");
|
89
|
+
this.eParserError = (RubyClass) runtime.getModule("HTTP").getClass("Parser").getConstant("Error");
|
76
90
|
|
77
91
|
this.on_message_begin = null;
|
78
92
|
this.on_headers_complete = null;
|
@@ -81,7 +95,10 @@ public class RubyHttpParser extends RubyObject {
|
|
81
95
|
|
82
96
|
this.callback_object = null;
|
83
97
|
|
84
|
-
this.
|
98
|
+
this.completed = false;
|
99
|
+
|
100
|
+
this.header_value_type = runtime.getModule("HTTP").getClass("Parser")
|
101
|
+
.getInstanceVariable("@default_header_value_type");
|
85
102
|
|
86
103
|
initSettings();
|
87
104
|
init();
|
@@ -90,49 +107,52 @@ public class RubyHttpParser extends RubyObject {
|
|
90
107
|
private void initSettings() {
|
91
108
|
this.settings = new ParserSettings();
|
92
109
|
|
93
|
-
this.settings.
|
94
|
-
public int cb
|
110
|
+
this.settings.on_status = new HTTPDataCallback() {
|
111
|
+
public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
|
95
112
|
byte[] data = fetchBytes(buf, pos, len);
|
96
|
-
((
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
|
102
|
-
byte[] data = fetchBytes(buf, pos, len);
|
103
|
-
((RubyString)requestPath).concat(runtime.newString(new String(data)));
|
104
|
-
return 0;
|
105
|
-
}
|
106
|
-
};
|
107
|
-
this.settings.on_query_string = new HTTPDataCallback() {
|
108
|
-
public int cb (http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
|
109
|
-
byte[] data = fetchBytes(buf, pos, len);
|
110
|
-
((RubyString)queryString).concat(runtime.newString(new String(data)));
|
113
|
+
if (runtime.is1_9() || runtime.is2_0()) {
|
114
|
+
((RubyString) status).cat(data, 0, data.length, UTF8);
|
115
|
+
} else {
|
116
|
+
((RubyString) status).cat(data);
|
117
|
+
}
|
111
118
|
return 0;
|
112
119
|
}
|
113
120
|
};
|
114
|
-
|
115
|
-
|
121
|
+
|
122
|
+
this.settings.on_url = new HTTPDataCallback() {
|
123
|
+
public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
|
116
124
|
byte[] data = fetchBytes(buf, pos, len);
|
117
|
-
((
|
125
|
+
if (runtime.is1_9() || runtime.is2_0()) {
|
126
|
+
((RubyString) requestUrl).cat(data, 0, data.length, UTF8);
|
127
|
+
} else {
|
128
|
+
((RubyString) requestUrl).cat(data);
|
129
|
+
}
|
118
130
|
return 0;
|
119
131
|
}
|
120
132
|
};
|
121
133
|
|
122
134
|
this.settings.on_header_field = new HTTPDataCallback() {
|
123
|
-
public int cb
|
135
|
+
public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
|
124
136
|
byte[] data = fetchBytes(buf, pos, len);
|
125
137
|
|
126
138
|
if (_current_header == null)
|
127
|
-
_current_header =
|
128
|
-
else
|
129
|
-
|
139
|
+
_current_header = data;
|
140
|
+
else {
|
141
|
+
byte[] tmp = new byte[_current_header.length + data.length];
|
142
|
+
System.arraycopy(_current_header, 0, tmp, 0, _current_header.length);
|
143
|
+
System.arraycopy(data, 0, tmp, _current_header.length, data.length);
|
144
|
+
_current_header = tmp;
|
145
|
+
}
|
130
146
|
|
131
147
|
return 0;
|
132
148
|
}
|
133
149
|
};
|
150
|
+
final RubySymbol arraysSym = runtime.newSymbol("arrays");
|
151
|
+
final RubySymbol mixedSym = runtime.newSymbol("mixed");
|
152
|
+
final RubySymbol stopSym = runtime.newSymbol("stop");
|
153
|
+
final RubySymbol resetSym = runtime.newSymbol("reset");
|
134
154
|
this.settings.on_header_value = new HTTPDataCallback() {
|
135
|
-
public int cb
|
155
|
+
public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
|
136
156
|
byte[] data = fetchBytes(buf, pos, len);
|
137
157
|
ThreadContext context = headers.getRuntime().getCurrentContext();
|
138
158
|
IRubyObject key, val;
|
@@ -144,57 +164,76 @@ public class RubyHttpParser extends RubyObject {
|
|
144
164
|
_current_header = null;
|
145
165
|
}
|
146
166
|
|
147
|
-
key =
|
167
|
+
key = RubyString.newString(runtime, new ByteList(_last_header, UTF8, false));
|
148
168
|
val = headers.op_aref(context, key);
|
149
169
|
|
150
170
|
if (new_field == 1) {
|
151
171
|
if (val.isNil()) {
|
152
|
-
if (header_value_type ==
|
153
|
-
headers.op_aset(context, key,
|
172
|
+
if (header_value_type == arraysSym) {
|
173
|
+
headers.op_aset(context, key,
|
174
|
+
RubyArray.newArrayLight(runtime, RubyString.newStringLight(runtime, 10, UTF8)));
|
154
175
|
} else {
|
155
|
-
headers.op_aset(context, key,
|
176
|
+
headers.op_aset(context, key, RubyString.newStringLight(runtime, 10, UTF8));
|
156
177
|
}
|
157
178
|
} else {
|
158
|
-
if (header_value_type ==
|
179
|
+
if (header_value_type == mixedSym) {
|
159
180
|
if (val instanceof RubyString) {
|
160
|
-
headers.op_aset(context, key,
|
181
|
+
headers.op_aset(context, key,
|
182
|
+
RubyArray.newArrayLight(runtime, val, RubyString.newStringLight(runtime, 10, UTF8)));
|
161
183
|
} else {
|
162
|
-
((RubyArray)val).add(
|
184
|
+
((RubyArray) val).add(RubyString.newStringLight(runtime, 10, UTF8));
|
163
185
|
}
|
164
|
-
} else if (header_value_type ==
|
165
|
-
((RubyArray)val).add(
|
186
|
+
} else if (header_value_type == arraysSym) {
|
187
|
+
((RubyArray) val).add(RubyString.newStringLight(runtime, 10, UTF8));
|
166
188
|
} else {
|
167
|
-
((
|
189
|
+
if (runtime.is1_9() || runtime.is2_0()) {
|
190
|
+
((RubyString) val).cat(',', UTF8).cat(' ', UTF8);
|
191
|
+
} else {
|
192
|
+
((RubyString) val).cat(',').cat(' ');
|
193
|
+
}
|
168
194
|
}
|
169
195
|
}
|
170
196
|
val = headers.op_aref(context, key);
|
171
197
|
}
|
172
198
|
|
173
199
|
if (val instanceof RubyArray) {
|
174
|
-
val = ((RubyArray)val).entry(-1);
|
200
|
+
val = ((RubyArray) val).entry(-1);
|
175
201
|
}
|
176
202
|
|
177
|
-
((
|
203
|
+
if (runtime.is1_9() || runtime.is2_0()) {
|
204
|
+
((RubyString) val).cat(data, 0, data.length, UTF8);
|
205
|
+
} else {
|
206
|
+
((RubyString) val).cat(data);
|
207
|
+
}
|
178
208
|
|
179
209
|
return 0;
|
180
210
|
}
|
181
211
|
};
|
182
212
|
|
183
213
|
this.settings.on_message_begin = new HTTPCallback() {
|
184
|
-
public int cb
|
214
|
+
public int cb(http_parser.lolevel.HTTPParser p) {
|
185
215
|
headers = new RubyHash(runtime);
|
186
216
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
217
|
+
if (runtime.is1_9() || runtime.is2_0()) {
|
218
|
+
status = RubyString.newEmptyString(runtime, UTF8);
|
219
|
+
requestUrl = RubyString.newEmptyString(runtime, UTF8);
|
220
|
+
requestPath = RubyString.newEmptyString(runtime, UTF8);
|
221
|
+
queryString = RubyString.newEmptyString(runtime, UTF8);
|
222
|
+
fragment = RubyString.newEmptyString(runtime, UTF8);
|
223
|
+
upgradeData = RubyString.newEmptyString(runtime, UTF8);
|
224
|
+
} else {
|
225
|
+
status = RubyString.newEmptyString(runtime);
|
226
|
+
requestUrl = RubyString.newEmptyString(runtime);
|
227
|
+
requestPath = RubyString.newEmptyString(runtime);
|
228
|
+
queryString = RubyString.newEmptyString(runtime);
|
229
|
+
fragment = RubyString.newEmptyString(runtime);
|
230
|
+
upgradeData = RubyString.newEmptyString(runtime);
|
231
|
+
}
|
193
232
|
|
194
233
|
IRubyObject ret = runtime.getNil();
|
195
234
|
|
196
235
|
if (callback_object != null) {
|
197
|
-
if (((RubyObject)callback_object).
|
236
|
+
if (((RubyObject) callback_object).respondsTo("on_message_begin")) {
|
198
237
|
ThreadContext context = callback_object.getRuntime().getCurrentContext();
|
199
238
|
ret = callback_object.callMethod(context, "on_message_begin");
|
200
239
|
}
|
@@ -203,7 +242,7 @@ public class RubyHttpParser extends RubyObject {
|
|
203
242
|
ret = on_message_begin.callMethod(context, "call");
|
204
243
|
}
|
205
244
|
|
206
|
-
if (ret ==
|
245
|
+
if (ret == stopSym) {
|
207
246
|
throw new StopException();
|
208
247
|
} else {
|
209
248
|
return 0;
|
@@ -211,11 +250,13 @@ public class RubyHttpParser extends RubyObject {
|
|
211
250
|
}
|
212
251
|
};
|
213
252
|
this.settings.on_message_complete = new HTTPCallback() {
|
214
|
-
public int cb
|
253
|
+
public int cb(http_parser.lolevel.HTTPParser p) {
|
215
254
|
IRubyObject ret = runtime.getNil();
|
216
255
|
|
256
|
+
completed = true;
|
257
|
+
|
217
258
|
if (callback_object != null) {
|
218
|
-
if (((RubyObject)callback_object).
|
259
|
+
if (((RubyObject) callback_object).respondsTo("on_message_complete")) {
|
219
260
|
ThreadContext context = callback_object.getRuntime().getCurrentContext();
|
220
261
|
ret = callback_object.callMethod(context, "on_message_complete");
|
221
262
|
}
|
@@ -224,7 +265,7 @@ public class RubyHttpParser extends RubyObject {
|
|
224
265
|
ret = on_message_complete.callMethod(context, "call");
|
225
266
|
}
|
226
267
|
|
227
|
-
if (ret ==
|
268
|
+
if (ret == stopSym) {
|
228
269
|
throw new StopException();
|
229
270
|
} else {
|
230
271
|
return 0;
|
@@ -232,11 +273,11 @@ public class RubyHttpParser extends RubyObject {
|
|
232
273
|
}
|
233
274
|
};
|
234
275
|
this.settings.on_headers_complete = new HTTPCallback() {
|
235
|
-
public int cb
|
276
|
+
public int cb(http_parser.lolevel.HTTPParser p) {
|
236
277
|
IRubyObject ret = runtime.getNil();
|
237
278
|
|
238
279
|
if (callback_object != null) {
|
239
|
-
if (((RubyObject)callback_object).
|
280
|
+
if (((RubyObject) callback_object).respondsTo("on_headers_complete")) {
|
240
281
|
ThreadContext context = callback_object.getRuntime().getCurrentContext();
|
241
282
|
ret = callback_object.callMethod(context, "on_headers_complete", headers);
|
242
283
|
}
|
@@ -245,29 +286,32 @@ public class RubyHttpParser extends RubyObject {
|
|
245
286
|
ret = on_headers_complete.callMethod(context, "call", headers);
|
246
287
|
}
|
247
288
|
|
248
|
-
if (ret ==
|
289
|
+
if (ret == stopSym) {
|
249
290
|
throw new StopException();
|
291
|
+
} else if (ret == resetSym) {
|
292
|
+
return 1;
|
250
293
|
} else {
|
251
294
|
return 0;
|
252
295
|
}
|
253
296
|
}
|
254
297
|
};
|
255
298
|
this.settings.on_body = new HTTPDataCallback() {
|
256
|
-
public int cb
|
299
|
+
public int cb(http_parser.lolevel.HTTPParser p, ByteBuffer buf, int pos, int len) {
|
257
300
|
IRubyObject ret = runtime.getNil();
|
258
301
|
byte[] data = fetchBytes(buf, pos, len);
|
259
302
|
|
260
303
|
if (callback_object != null) {
|
261
|
-
if (((RubyObject)callback_object).
|
304
|
+
if (((RubyObject) callback_object).respondsTo("on_body")) {
|
262
305
|
ThreadContext context = callback_object.getRuntime().getCurrentContext();
|
263
|
-
ret = callback_object.callMethod(context, "on_body",
|
306
|
+
ret = callback_object.callMethod(context, "on_body",
|
307
|
+
RubyString.newString(runtime, new ByteList(data, UTF8, false)));
|
264
308
|
}
|
265
309
|
} else if (on_body != null) {
|
266
310
|
ThreadContext context = on_body.getRuntime().getCurrentContext();
|
267
|
-
ret = on_body.callMethod(context, "call",
|
311
|
+
ret = on_body.callMethod(context, "call", RubyString.newString(runtime, new ByteList(data, UTF8, false)));
|
268
312
|
}
|
269
313
|
|
270
|
-
if (ret ==
|
314
|
+
if (ret == stopSym) {
|
271
315
|
throw new StopException();
|
272
316
|
} else {
|
273
317
|
return 0;
|
@@ -278,8 +322,10 @@ public class RubyHttpParser extends RubyObject {
|
|
278
322
|
|
279
323
|
private void init() {
|
280
324
|
this.parser = new HTTPParser();
|
325
|
+
this.parser.HTTP_PARSER_STRICT = true;
|
281
326
|
this.headers = null;
|
282
|
-
|
327
|
+
|
328
|
+
this.status = runtime.getNil();
|
283
329
|
this.requestUrl = runtime.getNil();
|
284
330
|
this.requestPath = runtime.getNil();
|
285
331
|
this.queryString = runtime.getNil();
|
@@ -331,8 +377,9 @@ public class RubyHttpParser extends RubyObject {
|
|
331
377
|
|
332
378
|
@JRubyMethod(name = "<<")
|
333
379
|
public IRubyObject execute(IRubyObject data) {
|
334
|
-
RubyString str = (RubyString)data;
|
335
|
-
|
380
|
+
RubyString str = (RubyString) data;
|
381
|
+
ByteList byteList = str.getByteList();
|
382
|
+
ByteBuffer buf = ByteBuffer.wrap(byteList.getUnsafeBytes(), byteList.getBegin(), byteList.getRealSize());
|
336
383
|
boolean stopped = false;
|
337
384
|
|
338
385
|
try {
|
@@ -345,9 +392,12 @@ public class RubyHttpParser extends RubyObject {
|
|
345
392
|
|
346
393
|
if (parser.getUpgrade()) {
|
347
394
|
byte[] upData = fetchBytes(buf, buf.position(), buf.limit() - buf.position());
|
348
|
-
((
|
349
|
-
|
350
|
-
|
395
|
+
if (runtime.is1_9() || runtime.is2_0()) {
|
396
|
+
((RubyString) upgradeData).cat(upData, 0, upData.length, UTF8);
|
397
|
+
} else {
|
398
|
+
((RubyString) upgradeData).cat(upData);
|
399
|
+
}
|
400
|
+
} else if (buf.hasRemaining() && !completed) {
|
351
401
|
if (!stopped)
|
352
402
|
throw new RaiseException(runtime, eParserError, "Could not parse data entirely", true);
|
353
403
|
}
|
@@ -357,12 +407,12 @@ public class RubyHttpParser extends RubyObject {
|
|
357
407
|
|
358
408
|
@JRubyMethod(name = "keep_alive?")
|
359
409
|
public IRubyObject shouldKeepAlive() {
|
360
|
-
return parser.shouldKeepAlive()
|
410
|
+
return runtime.newBoolean(parser.shouldKeepAlive());
|
361
411
|
}
|
362
412
|
|
363
413
|
@JRubyMethod(name = "upgrade?")
|
364
414
|
public IRubyObject shouldUpgrade() {
|
365
|
-
return parser.getUpgrade()
|
415
|
+
return runtime.newBoolean(parser.getUpgrade());
|
366
416
|
}
|
367
417
|
|
368
418
|
@JRubyMethod(name = "http_major")
|
@@ -412,6 +462,11 @@ public class RubyHttpParser extends RubyObject {
|
|
412
462
|
return headers == null ? runtime.getNil() : headers;
|
413
463
|
}
|
414
464
|
|
465
|
+
@JRubyMethod(name = "status")
|
466
|
+
public IRubyObject getStatus() {
|
467
|
+
return status == null ? runtime.getNil() : status;
|
468
|
+
}
|
469
|
+
|
415
470
|
@JRubyMethod(name = "request_url")
|
416
471
|
public IRubyObject getRequestUrl() {
|
417
472
|
return requestUrl == null ? runtime.getNil() : requestUrl;
|
@@ -439,7 +494,8 @@ public class RubyHttpParser extends RubyObject {
|
|
439
494
|
|
440
495
|
@JRubyMethod(name = "header_value_type=")
|
441
496
|
public IRubyObject set_header_value_type(IRubyObject val) {
|
442
|
-
|
497
|
+
String valString = val.toString();
|
498
|
+
if (valString != "mixed" && valString != "arrays" && valString != "strings") {
|
443
499
|
throw runtime.newArgumentError("Invalid header value type");
|
444
500
|
}
|
445
501
|
header_value_type = val;
|