http-parser 1.0.5 → 1.2.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 +4 -4
- data/LICENSE +20 -20
- data/README.md +69 -69
- data/Rakefile +19 -19
- data/ext/Rakefile +8 -8
- data/ext/http-parser/http_parser.c +2470 -2234
- data/ext/http-parser/http_parser.h +362 -318
- data/http-parser.gemspec +35 -33
- data/lib/http-parser.rb +11 -9
- data/lib/http-parser/errors.rb +77 -74
- data/lib/http-parser/ext.rb +9 -7
- data/lib/http-parser/parser.rb +258 -224
- data/lib/http-parser/types.rb +335 -311
- data/lib/http-parser/version.rb +5 -3
- data/spec/error_spec.rb +45 -45
- data/spec/instance_spec.rb +78 -78
- data/spec/parser_spec.rb +313 -313
- metadata +51 -46
data/lib/http-parser/version.rb
CHANGED
data/spec/error_spec.rb
CHANGED
@@ -1,45 +1,45 @@
|
|
1
|
-
require 'http-parser'
|
2
|
-
|
3
|
-
describe HttpParser::Parser, "#initialize" do
|
4
|
-
before :each do
|
5
|
-
@inst = HttpParser::Parser.new_instance
|
6
|
-
end
|
7
|
-
|
8
|
-
it "should return true when error" do
|
9
|
-
expect(subject.parse(@inst, "GETS / HTTP/1.1\r\n")).to eq(true)
|
10
|
-
expect(@inst.error?).to eq(true)
|
11
|
-
end
|
12
|
-
|
13
|
-
it "should return false on success" do
|
14
|
-
expect(subject.parse(@inst, "GET / HTTP/1.1\r\n")).to eq(false)
|
15
|
-
expect(@inst.error?).to eq(false)
|
16
|
-
end
|
17
|
-
|
18
|
-
it "the error should be inspectable" do
|
19
|
-
expect(subject.parse(@inst, "GETS / HTTP/1.1\r\n")).to eq(true)
|
20
|
-
expect(@inst.error).to be_kind_of(::HttpParser::Error::INVALID_METHOD)
|
21
|
-
expect(@inst.error?).to eq(true)
|
22
|
-
end
|
23
|
-
|
24
|
-
it "raises different error types depending on the error" do
|
25
|
-
expect(subject.parse(@inst, "GET / HTTP/23\r\n")).to eq(true)
|
26
|
-
expect(@inst.error).to be_kind_of(::HttpParser::Error::INVALID_VERSION)
|
27
|
-
expect(@inst.error?).to eq(true)
|
28
|
-
end
|
29
|
-
|
30
|
-
context "callback errors" do
|
31
|
-
subject do
|
32
|
-
described_class.new do |parser|
|
33
|
-
parser.on_url { |inst, data| raise 'unhandled' }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
it "should handle unhandled errors gracefully" do
|
38
|
-
expect(subject.parse(@inst, "GET /foo?q=1 HTTP/1.1")).to eq(true)
|
39
|
-
|
40
|
-
expect(@inst.error?).to eq(true)
|
41
|
-
expect(@inst.error).to be_kind_of(::HttpParser::Error::CALLBACK)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
1
|
+
require 'http-parser'
|
2
|
+
|
3
|
+
describe HttpParser::Parser, "#initialize" do
|
4
|
+
before :each do
|
5
|
+
@inst = HttpParser::Parser.new_instance
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should return true when error" do
|
9
|
+
expect(subject.parse(@inst, "GETS / HTTP/1.1\r\n")).to eq(true)
|
10
|
+
expect(@inst.error?).to eq(true)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should return false on success" do
|
14
|
+
expect(subject.parse(@inst, "GET / HTTP/1.1\r\n")).to eq(false)
|
15
|
+
expect(@inst.error?).to eq(false)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "the error should be inspectable" do
|
19
|
+
expect(subject.parse(@inst, "GETS / HTTP/1.1\r\n")).to eq(true)
|
20
|
+
expect(@inst.error).to be_kind_of(::HttpParser::Error::INVALID_METHOD)
|
21
|
+
expect(@inst.error?).to eq(true)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises different error types depending on the error" do
|
25
|
+
expect(subject.parse(@inst, "GET / HTTP/23\r\n")).to eq(true)
|
26
|
+
expect(@inst.error).to be_kind_of(::HttpParser::Error::INVALID_VERSION)
|
27
|
+
expect(@inst.error?).to eq(true)
|
28
|
+
end
|
29
|
+
|
30
|
+
context "callback errors" do
|
31
|
+
subject do
|
32
|
+
described_class.new do |parser|
|
33
|
+
parser.on_url { |inst, data| raise 'unhandled' }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should handle unhandled errors gracefully" do
|
38
|
+
expect(subject.parse(@inst, "GET /foo?q=1 HTTP/1.1")).to eq(true)
|
39
|
+
|
40
|
+
expect(@inst.error?).to eq(true)
|
41
|
+
expect(@inst.error).to be_kind_of(::HttpParser::Error::CALLBACK)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
data/spec/instance_spec.rb
CHANGED
@@ -1,78 +1,78 @@
|
|
1
|
-
require 'http-parser'
|
2
|
-
|
3
|
-
describe ::HttpParser::Instance, "#initialize" do
|
4
|
-
context "when given a block" do
|
5
|
-
it "should yield the new Instance" do
|
6
|
-
expected = nil
|
7
|
-
|
8
|
-
described_class.new { |inst| expected = inst }
|
9
|
-
|
10
|
-
expect(expected).to be_kind_of(described_class)
|
11
|
-
end
|
12
|
-
|
13
|
-
it "should allow changing the parser type" do
|
14
|
-
inst = described_class.new do |inst|
|
15
|
-
inst.type = :request
|
16
|
-
end
|
17
|
-
|
18
|
-
expect(inst.type).to eq(:request)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe "#type" do
|
23
|
-
it "should default to :both" do
|
24
|
-
expect(subject.type).to eq(:both)
|
25
|
-
end
|
26
|
-
|
27
|
-
it "should convert the type to a Symbol" do
|
28
|
-
subject[:type_flags] = ::HttpParser::TYPES[:request]
|
29
|
-
|
30
|
-
expect(subject.type).to eq(:request)
|
31
|
-
end
|
32
|
-
|
33
|
-
it "should extract the type from the type_flags field" do
|
34
|
-
subject[:type_flags] = ((0xff & ~0x3) | ::HttpParser::TYPES[:response])
|
35
|
-
|
36
|
-
expect(subject.type).to eq(:response)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
describe "#type=" do
|
41
|
-
it "should set the type" do
|
42
|
-
subject.type = :response
|
43
|
-
|
44
|
-
expect(subject.type).to eq(:response)
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should not change flags" do
|
48
|
-
flags = (0xff & ~0x3)
|
49
|
-
subject[:type_flags] = flags
|
50
|
-
|
51
|
-
subject.type = :request
|
52
|
-
|
53
|
-
expect(subject[:type_flags]).to eq((flags | ::HttpParser::TYPES[:request]))
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
describe "#stop!" do
|
58
|
-
it "should throw :return, 1" do
|
59
|
-
expect { subject.stop! }.to throw_symbol(:return,1)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
describe "#error!" do
|
64
|
-
it "should throw :return, -1" do
|
65
|
-
expect { subject.error! }.to throw_symbol(:return,-1)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
it "should not change the type" do
|
70
|
-
inst = described_class.new do |inst|
|
71
|
-
inst.type = :request
|
72
|
-
end
|
73
|
-
|
74
|
-
inst.reset!
|
75
|
-
expect(inst.type).to eq(:request)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
1
|
+
require 'http-parser'
|
2
|
+
|
3
|
+
describe ::HttpParser::Instance, "#initialize" do
|
4
|
+
context "when given a block" do
|
5
|
+
it "should yield the new Instance" do
|
6
|
+
expected = nil
|
7
|
+
|
8
|
+
described_class.new { |inst| expected = inst }
|
9
|
+
|
10
|
+
expect(expected).to be_kind_of(described_class)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should allow changing the parser type" do
|
14
|
+
inst = described_class.new do |inst|
|
15
|
+
inst.type = :request
|
16
|
+
end
|
17
|
+
|
18
|
+
expect(inst.type).to eq(:request)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#type" do
|
23
|
+
it "should default to :both" do
|
24
|
+
expect(subject.type).to eq(:both)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should convert the type to a Symbol" do
|
28
|
+
subject[:type_flags] = ::HttpParser::TYPES[:request]
|
29
|
+
|
30
|
+
expect(subject.type).to eq(:request)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should extract the type from the type_flags field" do
|
34
|
+
subject[:type_flags] = ((0xff & ~0x3) | ::HttpParser::TYPES[:response])
|
35
|
+
|
36
|
+
expect(subject.type).to eq(:response)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#type=" do
|
41
|
+
it "should set the type" do
|
42
|
+
subject.type = :response
|
43
|
+
|
44
|
+
expect(subject.type).to eq(:response)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should not change flags" do
|
48
|
+
flags = (0xff & ~0x3)
|
49
|
+
subject[:type_flags] = flags
|
50
|
+
|
51
|
+
subject.type = :request
|
52
|
+
|
53
|
+
expect(subject[:type_flags]).to eq((flags | ::HttpParser::TYPES[:request]))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#stop!" do
|
58
|
+
it "should throw :return, 1" do
|
59
|
+
expect { subject.stop! }.to throw_symbol(:return,1)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#error!" do
|
64
|
+
it "should throw :return, -1" do
|
65
|
+
expect { subject.error! }.to throw_symbol(:return,-1)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should not change the type" do
|
70
|
+
inst = described_class.new do |inst|
|
71
|
+
inst.type = :request
|
72
|
+
end
|
73
|
+
|
74
|
+
inst.reset!
|
75
|
+
expect(inst.type).to eq(:request)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
data/spec/parser_spec.rb
CHANGED
@@ -1,313 +1,313 @@
|
|
1
|
-
require 'http-parser'
|
2
|
-
|
3
|
-
describe HttpParser::Parser, "#initialize" do
|
4
|
-
before :each do
|
5
|
-
@inst = HttpParser::Parser.new_instance
|
6
|
-
end
|
7
|
-
|
8
|
-
describe "callbacks" do
|
9
|
-
describe "on_message_begin" do
|
10
|
-
subject do
|
11
|
-
described_class.new do |parser|
|
12
|
-
parser.on_message_begin { @begun = true }
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
it "should trigger on a new request" do
|
17
|
-
subject.parse @inst, "GET / HTTP/1.1"
|
18
|
-
expect(@begun).to eq(true)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe "on_url" do
|
23
|
-
let(:expected) { '/foo?q=1' }
|
24
|
-
|
25
|
-
subject do
|
26
|
-
described_class.new do |parser|
|
27
|
-
parser.on_url { |inst, data| @url = data }
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
it "should pass the recognized url" do
|
32
|
-
subject.parse @inst, "GET "
|
33
|
-
|
34
|
-
expect(@url).to be_nil
|
35
|
-
|
36
|
-
subject.parse @inst, "#{expected} HTTP/1.1"
|
37
|
-
|
38
|
-
expect(@url).to eq(expected)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
describe "on_header_field" do
|
43
|
-
let(:expected) { 'Host' }
|
44
|
-
|
45
|
-
subject do
|
46
|
-
described_class.new do |parser|
|
47
|
-
parser.on_header_field { |inst, data| @header_field = data }
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
it "should pass the recognized header-name" do
|
52
|
-
subject.parse @inst, "GET /foo HTTP/1.1\r\n"
|
53
|
-
|
54
|
-
expect(@header_field).to be_nil
|
55
|
-
|
56
|
-
subject.parse @inst, "#{expected}: example.com\r\n"
|
57
|
-
|
58
|
-
expect(@header_field).to eq(expected)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
describe "on_header_value" do
|
63
|
-
let(:expected) { 'example.com' }
|
64
|
-
|
65
|
-
subject do
|
66
|
-
described_class.new do |parser|
|
67
|
-
parser.on_header_value { |inst, data| @header_value = data }
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
it "should pass the recognized header-value" do
|
72
|
-
subject.parse @inst, "GET /foo HTTP/1.1\r\n"
|
73
|
-
|
74
|
-
expect(@header_value).to be_nil
|
75
|
-
|
76
|
-
subject.parse @inst, "Host: #{expected}\r\n"
|
77
|
-
|
78
|
-
expect(@header_value).to eq(expected)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
describe "on_headers_complete" do
|
83
|
-
subject do
|
84
|
-
described_class.new do |parser|
|
85
|
-
parser.on_headers_complete { @header_complete = true }
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
it "should trigger on the last header" do
|
90
|
-
subject.parse @inst, "GET / HTTP/1.1\r\nHost: example.com\r\n"
|
91
|
-
|
92
|
-
expect(@header_complete).to be_nil
|
93
|
-
|
94
|
-
subject.parse @inst, "\r\n"
|
95
|
-
|
96
|
-
expect(@header_complete).to eq(true)
|
97
|
-
end
|
98
|
-
|
99
|
-
context "when #stop! is called" do
|
100
|
-
subject do
|
101
|
-
described_class.new do |parser|
|
102
|
-
parser.on_headers_complete { @inst.stop! }
|
103
|
-
|
104
|
-
parser.on_body { |inst, data| @body = data }
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
it "should indicate there is no request body to parse" do
|
109
|
-
subject.parse @inst, "GET / HTTP/1.1\r\nHost: example.com\r\n\r\nBody"
|
110
|
-
|
111
|
-
expect(@body).to be_nil
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
describe "on_body" do
|
117
|
-
let(:expected) { "Body" }
|
118
|
-
|
119
|
-
subject do
|
120
|
-
described_class.new do |parser|
|
121
|
-
parser.on_body { |inst, data| @body = data }
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
it "should trigger on the body" do
|
126
|
-
subject.parse @inst, "POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
|
127
|
-
|
128
|
-
expect(@body).to be_nil
|
129
|
-
|
130
|
-
subject.parse @inst, "#{"%x" % expected.length}\r\n#{expected}"
|
131
|
-
|
132
|
-
expect(@body).to eq(expected)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
describe "on_message_complete" do
|
137
|
-
subject do
|
138
|
-
described_class.new do |parser|
|
139
|
-
parser.on_message_complete { @message_complete = true }
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
it "should trigger at the end of the message" do
|
144
|
-
subject.parse @inst, "GET / HTTP/1.1\r\n"
|
145
|
-
|
146
|
-
expect(@message_complete).to be_nil
|
147
|
-
|
148
|
-
subject.parse @inst, "Host: example.com\r\n\r\n"
|
149
|
-
|
150
|
-
expect(@message_complete).to eq(true)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
|
156
|
-
describe "#http_method" do
|
157
|
-
let(:expected) { :POST }
|
158
|
-
|
159
|
-
it "should set the http_method field" do
|
160
|
-
subject.parse @inst, "#{expected} / HTTP/1.1\r\n"
|
161
|
-
|
162
|
-
expect(@inst.http_method).to eq(expected)
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
describe "#http_major" do
|
167
|
-
let(:expected) { 1 }
|
168
|
-
|
169
|
-
before do
|
170
|
-
@inst.type = :request
|
171
|
-
end
|
172
|
-
|
173
|
-
context "when parsing requests" do
|
174
|
-
it "should set the http_major field" do
|
175
|
-
subject.parse @inst, "GET / HTTP/#{expected}."
|
176
|
-
|
177
|
-
expect(@inst.http_major).to eq(expected)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
context "when parsing responses" do
|
182
|
-
before do
|
183
|
-
@inst.type = :response
|
184
|
-
end
|
185
|
-
|
186
|
-
it "should set the http_major field" do
|
187
|
-
subject.parse @inst, "HTTP/#{expected}."
|
188
|
-
|
189
|
-
expect(@inst.http_major).to eq(expected)
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
describe "#http_minor" do
|
195
|
-
let(:expected) { 2 }
|
196
|
-
|
197
|
-
context "when parsing requests" do
|
198
|
-
it "should set the http_minor field" do
|
199
|
-
subject.parse @inst, "GET / HTTP/1.#{expected}\r\n"
|
200
|
-
|
201
|
-
expect(@inst.http_minor).to eq(expected)
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
context "when parsing responses" do
|
206
|
-
before do
|
207
|
-
@inst.type = :response
|
208
|
-
end
|
209
|
-
|
210
|
-
it "should set the http_major field" do
|
211
|
-
subject.parse @inst, "HTTP/1.#{expected} "
|
212
|
-
|
213
|
-
expect(@inst.http_minor).to eq(expected)
|
214
|
-
end
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
describe "#http_version" do
|
219
|
-
let(:expected) { '1.1' }
|
220
|
-
|
221
|
-
before do
|
222
|
-
subject.parse @inst, "GET / HTTP/#{expected}\r\n"
|
223
|
-
end
|
224
|
-
|
225
|
-
it "should combine #http_major and #http_minor" do
|
226
|
-
expect(@inst.http_version).to eq(expected)
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
describe "#http_status" do
|
231
|
-
context "when parsing requests" do
|
232
|
-
before do
|
233
|
-
subject.parse @inst, "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
|
234
|
-
end
|
235
|
-
|
236
|
-
it "should not be set" do
|
237
|
-
expect(@inst.http_status).to be_zero
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
context "when parsing responses" do
|
242
|
-
let(:expected) { 200 }
|
243
|
-
|
244
|
-
before do
|
245
|
-
@inst.type = :response
|
246
|
-
subject.parse @inst, "HTTP/1.1 #{expected} OK\r\n"
|
247
|
-
subject.parse @inst, "Location: http://example.com/\r\n\r\n"
|
248
|
-
end
|
249
|
-
|
250
|
-
it "should set the http_status field" do
|
251
|
-
expect(@inst.http_status).to eq(expected)
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
describe "#upgrade?" do
|
257
|
-
let(:upgrade) { 'WebSocket' }
|
258
|
-
|
259
|
-
before do
|
260
|
-
subject.parse @inst, "GET /demo HTTP/1.1\r\n"
|
261
|
-
subject.parse @inst, "Upgrade: #{upgrade}\r\n"
|
262
|
-
subject.parse @inst, "Connection: Upgrade\r\n"
|
263
|
-
subject.parse @inst, "Host: example.com\r\n"
|
264
|
-
subject.parse @inst, "Origin: http://example.com\r\n"
|
265
|
-
subject.parse @inst, "WebSocket-Protocol: sample\r\n"
|
266
|
-
subject.parse @inst, "\r\n"
|
267
|
-
end
|
268
|
-
|
269
|
-
it "should return true if the Upgrade header was set" do
|
270
|
-
expect(@inst.upgrade?).to eq(true)
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
describe "pipelined requests" do
|
275
|
-
subject do
|
276
|
-
@begun = 0
|
277
|
-
described_class.new do |parser|
|
278
|
-
parser.on_message_begin { @begun += 1 }
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
it "should trigger on a new request" do
|
283
|
-
subject.parse @inst, "GET /demo HTTP/1.1\r\n\r\nGET /demo HTTP/1.1\r\n\r\n"
|
284
|
-
expect(@begun).to eq(2)
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
describe "method based instead of block based" do
|
289
|
-
class SomeParserClass
|
290
|
-
attr_reader :url
|
291
|
-
|
292
|
-
def on_url(inst, data)
|
293
|
-
@url = data
|
294
|
-
end
|
295
|
-
end
|
296
|
-
|
297
|
-
let(:expected) { '/foo?q=1' }
|
298
|
-
|
299
|
-
it "should simplify the process" do
|
300
|
-
callbacks = SomeParserClass.new
|
301
|
-
parser = described_class.new(callbacks)
|
302
|
-
|
303
|
-
parser.parse @inst, "GET "
|
304
|
-
|
305
|
-
expect(callbacks.url).to be_nil
|
306
|
-
|
307
|
-
parser.parse @inst, "#{expected} HTTP/1.1"
|
308
|
-
|
309
|
-
expect(callbacks.url).to eq(expected)
|
310
|
-
end
|
311
|
-
end
|
312
|
-
end
|
313
|
-
|
1
|
+
require 'http-parser'
|
2
|
+
|
3
|
+
describe HttpParser::Parser, "#initialize" do
|
4
|
+
before :each do
|
5
|
+
@inst = HttpParser::Parser.new_instance
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "callbacks" do
|
9
|
+
describe "on_message_begin" do
|
10
|
+
subject do
|
11
|
+
described_class.new do |parser|
|
12
|
+
parser.on_message_begin { @begun = true }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should trigger on a new request" do
|
17
|
+
subject.parse @inst, "GET / HTTP/1.1"
|
18
|
+
expect(@begun).to eq(true)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "on_url" do
|
23
|
+
let(:expected) { '/foo?q=1' }
|
24
|
+
|
25
|
+
subject do
|
26
|
+
described_class.new do |parser|
|
27
|
+
parser.on_url { |inst, data| @url = data }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should pass the recognized url" do
|
32
|
+
subject.parse @inst, "GET "
|
33
|
+
|
34
|
+
expect(@url).to be_nil
|
35
|
+
|
36
|
+
subject.parse @inst, "#{expected} HTTP/1.1"
|
37
|
+
|
38
|
+
expect(@url).to eq(expected)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "on_header_field" do
|
43
|
+
let(:expected) { 'Host' }
|
44
|
+
|
45
|
+
subject do
|
46
|
+
described_class.new do |parser|
|
47
|
+
parser.on_header_field { |inst, data| @header_field = data }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should pass the recognized header-name" do
|
52
|
+
subject.parse @inst, "GET /foo HTTP/1.1\r\n"
|
53
|
+
|
54
|
+
expect(@header_field).to be_nil
|
55
|
+
|
56
|
+
subject.parse @inst, "#{expected}: example.com\r\n"
|
57
|
+
|
58
|
+
expect(@header_field).to eq(expected)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "on_header_value" do
|
63
|
+
let(:expected) { 'example.com' }
|
64
|
+
|
65
|
+
subject do
|
66
|
+
described_class.new do |parser|
|
67
|
+
parser.on_header_value { |inst, data| @header_value = data }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should pass the recognized header-value" do
|
72
|
+
subject.parse @inst, "GET /foo HTTP/1.1\r\n"
|
73
|
+
|
74
|
+
expect(@header_value).to be_nil
|
75
|
+
|
76
|
+
subject.parse @inst, "Host: #{expected}\r\n"
|
77
|
+
|
78
|
+
expect(@header_value).to eq(expected)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "on_headers_complete" do
|
83
|
+
subject do
|
84
|
+
described_class.new do |parser|
|
85
|
+
parser.on_headers_complete { @header_complete = true }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should trigger on the last header" do
|
90
|
+
subject.parse @inst, "GET / HTTP/1.1\r\nHost: example.com\r\n"
|
91
|
+
|
92
|
+
expect(@header_complete).to be_nil
|
93
|
+
|
94
|
+
subject.parse @inst, "\r\n"
|
95
|
+
|
96
|
+
expect(@header_complete).to eq(true)
|
97
|
+
end
|
98
|
+
|
99
|
+
context "when #stop! is called" do
|
100
|
+
subject do
|
101
|
+
described_class.new do |parser|
|
102
|
+
parser.on_headers_complete { @inst.stop! }
|
103
|
+
|
104
|
+
parser.on_body { |inst, data| @body = data }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should indicate there is no request body to parse" do
|
109
|
+
subject.parse @inst, "GET / HTTP/1.1\r\nHost: example.com\r\n\r\nBody"
|
110
|
+
|
111
|
+
expect(@body).to be_nil
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "on_body" do
|
117
|
+
let(:expected) { "Body" }
|
118
|
+
|
119
|
+
subject do
|
120
|
+
described_class.new do |parser|
|
121
|
+
parser.on_body { |inst, data| @body = data }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should trigger on the body" do
|
126
|
+
subject.parse @inst, "POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
|
127
|
+
|
128
|
+
expect(@body).to be_nil
|
129
|
+
|
130
|
+
subject.parse @inst, "#{"%x" % expected.length}\r\n#{expected}"
|
131
|
+
|
132
|
+
expect(@body).to eq(expected)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "on_message_complete" do
|
137
|
+
subject do
|
138
|
+
described_class.new do |parser|
|
139
|
+
parser.on_message_complete { @message_complete = true }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should trigger at the end of the message" do
|
144
|
+
subject.parse @inst, "GET / HTTP/1.1\r\n"
|
145
|
+
|
146
|
+
expect(@message_complete).to be_nil
|
147
|
+
|
148
|
+
subject.parse @inst, "Host: example.com\r\n\r\n"
|
149
|
+
|
150
|
+
expect(@message_complete).to eq(true)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
describe "#http_method" do
|
157
|
+
let(:expected) { :POST }
|
158
|
+
|
159
|
+
it "should set the http_method field" do
|
160
|
+
subject.parse @inst, "#{expected} / HTTP/1.1\r\n"
|
161
|
+
|
162
|
+
expect(@inst.http_method).to eq(expected)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe "#http_major" do
|
167
|
+
let(:expected) { 1 }
|
168
|
+
|
169
|
+
before do
|
170
|
+
@inst.type = :request
|
171
|
+
end
|
172
|
+
|
173
|
+
context "when parsing requests" do
|
174
|
+
it "should set the http_major field" do
|
175
|
+
subject.parse @inst, "GET / HTTP/#{expected}."
|
176
|
+
|
177
|
+
expect(@inst.http_major).to eq(expected)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context "when parsing responses" do
|
182
|
+
before do
|
183
|
+
@inst.type = :response
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should set the http_major field" do
|
187
|
+
subject.parse @inst, "HTTP/#{expected}."
|
188
|
+
|
189
|
+
expect(@inst.http_major).to eq(expected)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe "#http_minor" do
|
195
|
+
let(:expected) { 2 }
|
196
|
+
|
197
|
+
context "when parsing requests" do
|
198
|
+
it "should set the http_minor field" do
|
199
|
+
subject.parse @inst, "GET / HTTP/1.#{expected}\r\n"
|
200
|
+
|
201
|
+
expect(@inst.http_minor).to eq(expected)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context "when parsing responses" do
|
206
|
+
before do
|
207
|
+
@inst.type = :response
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should set the http_major field" do
|
211
|
+
subject.parse @inst, "HTTP/1.#{expected} "
|
212
|
+
|
213
|
+
expect(@inst.http_minor).to eq(expected)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe "#http_version" do
|
219
|
+
let(:expected) { '1.1' }
|
220
|
+
|
221
|
+
before do
|
222
|
+
subject.parse @inst, "GET / HTTP/#{expected}\r\n"
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should combine #http_major and #http_minor" do
|
226
|
+
expect(@inst.http_version).to eq(expected)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe "#http_status" do
|
231
|
+
context "when parsing requests" do
|
232
|
+
before do
|
233
|
+
subject.parse @inst, "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should not be set" do
|
237
|
+
expect(@inst.http_status).to be_zero
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
context "when parsing responses" do
|
242
|
+
let(:expected) { 200 }
|
243
|
+
|
244
|
+
before do
|
245
|
+
@inst.type = :response
|
246
|
+
subject.parse @inst, "HTTP/1.1 #{expected} OK\r\n"
|
247
|
+
subject.parse @inst, "Location: http://example.com/\r\n\r\n"
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should set the http_status field" do
|
251
|
+
expect(@inst.http_status).to eq(expected)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
describe "#upgrade?" do
|
257
|
+
let(:upgrade) { 'WebSocket' }
|
258
|
+
|
259
|
+
before do
|
260
|
+
subject.parse @inst, "GET /demo HTTP/1.1\r\n"
|
261
|
+
subject.parse @inst, "Upgrade: #{upgrade}\r\n"
|
262
|
+
subject.parse @inst, "Connection: Upgrade\r\n"
|
263
|
+
subject.parse @inst, "Host: example.com\r\n"
|
264
|
+
subject.parse @inst, "Origin: http://example.com\r\n"
|
265
|
+
subject.parse @inst, "WebSocket-Protocol: sample\r\n"
|
266
|
+
subject.parse @inst, "\r\n"
|
267
|
+
end
|
268
|
+
|
269
|
+
it "should return true if the Upgrade header was set" do
|
270
|
+
expect(@inst.upgrade?).to eq(true)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe "pipelined requests" do
|
275
|
+
subject do
|
276
|
+
@begun = 0
|
277
|
+
described_class.new do |parser|
|
278
|
+
parser.on_message_begin { @begun += 1 }
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should trigger on a new request" do
|
283
|
+
subject.parse @inst, "GET /demo HTTP/1.1\r\n\r\nGET /demo HTTP/1.1\r\n\r\n"
|
284
|
+
expect(@begun).to eq(2)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
describe "method based instead of block based" do
|
289
|
+
class SomeParserClass
|
290
|
+
attr_reader :url
|
291
|
+
|
292
|
+
def on_url(inst, data)
|
293
|
+
@url = data
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
let(:expected) { '/foo?q=1' }
|
298
|
+
|
299
|
+
it "should simplify the process" do
|
300
|
+
callbacks = SomeParserClass.new
|
301
|
+
parser = described_class.new(callbacks)
|
302
|
+
|
303
|
+
parser.parse @inst, "GET "
|
304
|
+
|
305
|
+
expect(callbacks.url).to be_nil
|
306
|
+
|
307
|
+
parser.parse @inst, "#{expected} HTTP/1.1"
|
308
|
+
|
309
|
+
expect(callbacks.url).to eq(expected)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|