midori_http_parser 0.6.1.1-java

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.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.gitmodules +6 -0
  4. data/.travis.yml +33 -0
  5. data/Gemfile +2 -0
  6. data/LICENSE-MIT +20 -0
  7. data/README.md +90 -0
  8. data/Rakefile +6 -0
  9. data/bench/standalone.rb +23 -0
  10. data/bench/thin.rb +58 -0
  11. data/ext/ruby_http_parser/.gitignore +1 -0
  12. data/ext/ruby_http_parser/RubyHttpParserService.java +18 -0
  13. data/ext/ruby_http_parser/ext_help.h +18 -0
  14. data/ext/ruby_http_parser/extconf.rb +24 -0
  15. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +495 -0
  16. data/ext/ruby_http_parser/ruby_http_parser.c +516 -0
  17. data/ext/ruby_http_parser/vendor/.gitkeep +0 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/AUTHORS +32 -0
  19. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +48 -0
  20. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +183 -0
  21. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +28 -0
  22. data/ext/ruby_http_parser/vendor/http-parser-java/build.xml +74 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +2175 -0
  24. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.gyp +79 -0
  25. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +304 -0
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/Http-parser.java.iml +22 -0
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/FieldData.java +41 -0
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPCallback.java +8 -0
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPDataCallback.java +34 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPErrorCallback.java +12 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +9 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +113 -0
  33. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +36 -0
  34. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParserUrl.java +76 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +256 -0
  36. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserType.java +13 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/Util.java +111 -0
  38. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPCallback.java +5 -0
  39. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPDataCallback.java +25 -0
  40. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPErrorCallback.java +7 -0
  41. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +2171 -0
  42. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +83 -0
  43. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +374 -0
  44. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/ParseUrl.java +51 -0
  45. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +69 -0
  46. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +52 -0
  47. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +16 -0
  48. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +48 -0
  49. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +212 -0
  50. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +62 -0
  51. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +117 -0
  52. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +27 -0
  53. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Url.java +127 -0
  54. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +236 -0
  55. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +59 -0
  56. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +3425 -0
  57. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +845 -0
  58. data/ext/ruby_http_parser/vendor/http-parser-java/tests.utf8 +17 -0
  59. data/ext/ruby_http_parser/vendor/http-parser-java/tools/byte_constants.rb +6 -0
  60. data/ext/ruby_http_parser/vendor/http-parser-java/tools/const_char.rb +13 -0
  61. data/ext/ruby_http_parser/vendor/http-parser-java/tools/lowcase.rb +15 -0
  62. data/ext/ruby_http_parser/vendor/http-parser-java/tools/parse_tests.rb +33 -0
  63. data/ext/ruby_http_parser/vendor/http-parser/AUTHORS +68 -0
  64. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +23 -0
  65. data/ext/ruby_http_parser/vendor/http-parser/README.md +246 -0
  66. data/ext/ruby_http_parser/vendor/http-parser/bench.c +111 -0
  67. data/ext/ruby_http_parser/vendor/http-parser/contrib/parsertrace.c +160 -0
  68. data/ext/ruby_http_parser/vendor/http-parser/contrib/url_parser.c +47 -0
  69. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +2470 -0
  70. data/ext/ruby_http_parser/vendor/http-parser/http_parser.gyp +111 -0
  71. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +432 -0
  72. data/ext/ruby_http_parser/vendor/http-parser/test.c +4226 -0
  73. data/ext/ruby_http_parser/vendor/http-parser/test_fast +0 -0
  74. data/ext/ruby_http_parser/vendor/http-parser/test_g +0 -0
  75. data/lib/http/parser.rb +1 -0
  76. data/lib/http_parser.rb +21 -0
  77. data/midori_http_parser.gemspec +24 -0
  78. data/spec/parser_spec.rb +376 -0
  79. data/spec/spec_helper.rb +1 -0
  80. data/spec/support/requests.json +631 -0
  81. data/spec/support/responses.json +375 -0
  82. data/tasks/compile.rake +42 -0
  83. data/tasks/fixtures.rake +71 -0
  84. data/tasks/spec.rake +5 -0
  85. data/tasks/submodules.rake +7 -0
  86. metadata +206 -0
@@ -0,0 +1 @@
1
+ require 'http_parser'
@@ -0,0 +1,21 @@
1
+ $:.unshift File.expand_path('../', __FILE__)
2
+ require 'ruby_http_parser'
3
+
4
+ Http = HTTP
5
+
6
+ module HTTP
7
+ class Parser
8
+ class << self
9
+ attr_reader :default_header_value_type
10
+
11
+ def default_header_value_type=(val)
12
+ if (val != :mixed && val != :strings && val != :arrays)
13
+ raise ArgumentError, "Invalid header value type"
14
+ end
15
+ @default_header_value_type = val
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ HTTP::Parser.default_header_value_type = :mixed
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "midori_http_parser"
3
+ s.version = "0.6.1.1"
4
+ s.summary = "Simple callback-based HTTP request/response parser"
5
+ s.description = "Fast Ruby Parser inpired by Node"
6
+ s.platform = 'java' if RUBY_PLATFORM =~ /java/
7
+ s.authors = ["Delton Ding"]
8
+ s.email = ["delton@heckpsi.com"]
9
+ s.license = 'MIT'
10
+
11
+ s.homepage = "https://github.com/heckpsi-lab/http_parser.rb"
12
+ s.files = `git ls-files`.split("\n") + Dir['ext/ruby_http_parser/vendor/**/*']
13
+
14
+ s.require_paths = ["lib"]
15
+ s.extensions = ["ext/ruby_http_parser/extconf.rb"]
16
+
17
+ s.add_development_dependency 'rake-compiler', '>= 0.7.9'
18
+ s.add_development_dependency 'rspec', '>= 3'
19
+ s.add_development_dependency 'json', '>= 1.4.6'
20
+ s.add_development_dependency 'benchmark_suite'
21
+ s.add_development_dependency 'ffi'
22
+
23
+ s.add_development_dependency 'yajl-ruby', '>= 0.8.1' unless RUBY_PLATFORM =~ /java/
24
+ end
@@ -0,0 +1,376 @@
1
+ require "spec_helper"
2
+ require "json"
3
+
4
+ describe HTTP::Parser do
5
+ before do
6
+ @parser = HTTP::Parser.new
7
+
8
+ @headers = nil
9
+ @body = ""
10
+ @started = false
11
+ @done = false
12
+
13
+ @parser.on_message_begin = proc{ @started = true }
14
+ @parser.on_headers_complete = proc { |e| @headers = e }
15
+ @parser.on_body = proc { |chunk| @body << chunk }
16
+ @parser.on_message_complete = proc{ @done = true }
17
+ end
18
+
19
+ it "should have initial state" do
20
+ expect(@parser.headers).to be_nil
21
+
22
+ expect(@parser.http_version).to be_nil
23
+ expect(@parser.http_method).to be_nil
24
+ expect(@parser.status_code).to be_nil
25
+
26
+ expect(@parser.request_url).to be_nil
27
+
28
+ expect(@parser.header_value_type).to eq(:mixed)
29
+ end
30
+
31
+ it "should allow us to set the header value type" do
32
+ [:mixed, :arrays, :strings].each do |type|
33
+ @parser.header_value_type = type
34
+ expect(@parser.header_value_type).to eq(type)
35
+
36
+ parser_tmp = HTTP::Parser.new(nil, type)
37
+ expect(parser_tmp.header_value_type).to eq(type)
38
+ end
39
+ end
40
+
41
+ it "should allow us to set the default header value type" do
42
+ [:mixed, :arrays, :strings].each do |type|
43
+ HTTP::Parser.default_header_value_type = type
44
+
45
+ parser = HTTP::Parser.new
46
+ expect(parser.header_value_type).to eq(type)
47
+ end
48
+ end
49
+
50
+ it "should throw an Argument Error if header value type is invalid" do
51
+ expect{ @parser.header_value_type = 'bob' }.to raise_error(ArgumentError)
52
+ end
53
+
54
+ it "should throw an Argument Error if default header value type is invalid" do
55
+ expect{ HTTP::Parser.default_header_value_type = 'bob' }.to raise_error(ArgumentError)
56
+ end
57
+
58
+ it "should implement basic api" do
59
+ @parser <<
60
+ "GET /test?ok=1 HTTP/1.1\r\n" +
61
+ "User-Agent: curl/7.18.0\r\n" +
62
+ "Host: 0.0.0.0:5000\r\n" +
63
+ "Accept: */*\r\n" +
64
+ "Content-Length: 5\r\n" +
65
+ "\r\n" +
66
+ "World"
67
+
68
+ expect(@started).to be true
69
+ expect(@done).to be true
70
+
71
+ expect(@parser.http_major).to eq(1)
72
+ expect(@parser.http_minor).to eq(1)
73
+ expect(@parser.http_version).to eq([1,1])
74
+ expect(@parser.http_method).to eq('GET')
75
+ expect(@parser.status_code).to be_nil
76
+
77
+ expect(@parser.request_url).to eq('/test?ok=1')
78
+
79
+ expect(@parser.headers).to eq(@headers)
80
+ expect(@parser.headers['User-Agent']).to eq('curl/7.18.0')
81
+ expect(@parser.headers['Host']).to eq('0.0.0.0:5000')
82
+
83
+ expect(@body).to eq("World")
84
+ end
85
+
86
+ it "should raise errors on invalid data" do
87
+ expect{ @parser << "BLAH" }.to raise_error(HTTP::Parser::Error)
88
+ end
89
+
90
+ it "should abort parser via header complete callback with a body" do
91
+ @parser.on_headers_complete = proc { |e| @headers = e; :stop }
92
+
93
+ data =
94
+ "GET / HTTP/1.0\r\n" +
95
+ "Content-Length: 5\r\n" +
96
+ "\r\n" +
97
+ "World"
98
+
99
+ bytes = @parser << data
100
+
101
+ expect(bytes).to eq(37)
102
+ expect(data[bytes..-1]).to eq('World')
103
+
104
+ expect(@headers).to eq({'Content-Length' => '5'})
105
+ expect(@body).to be_empty
106
+ expect(@done).to be false
107
+ end
108
+
109
+ it "should abort parser via header complete callback without a body" do
110
+ @parser.on_headers_complete = proc { |e| @headers = e; :stop }
111
+
112
+ data =
113
+ "GET / HTTP/1.0\r\n" +
114
+ "Content-Length: 0\r\n" +
115
+ "\r\n"
116
+
117
+ bytes = @parser << data
118
+
119
+ expect(bytes).to eq(37)
120
+ expect(data[bytes..-1]).to eq('')
121
+
122
+ expect(@headers).to eq({'Content-Length' => '0'})
123
+ expect(@body).to be_empty
124
+ expect(@done).to be false
125
+ end
126
+
127
+ it "should abort parser via message complete callback with a body" do
128
+ @parser.on_message_complete = proc { :stop }
129
+
130
+ data =
131
+ "CONNECT www.example.com:443 HTTP/1.0\r\n" +
132
+ "Connection: keep-alive\r\n" +
133
+ "\r\n" +
134
+ "World"
135
+
136
+ bytes = @parser << data
137
+
138
+ expect(bytes).to eq(64)
139
+ expect(data[bytes..-1]).to eq('World')
140
+
141
+ expect(@headers).to eq({'Connection' => 'keep-alive'})
142
+ expect(@parser.upgrade_data).to eq('World')
143
+ expect(@body).to be_empty
144
+ expect(@done).to be false
145
+ end
146
+
147
+ it "should abort parser via message complete callback without a body" do
148
+ @parser.on_message_complete = proc { :stop }
149
+
150
+ data =
151
+ "CONNECT www.example.com:443 HTTP/1.0\r\n" +
152
+ "Connection: keep-alive\r\n" +
153
+ "\r\n"
154
+
155
+ bytes = @parser << data
156
+
157
+ expect(bytes).to eq(64)
158
+ expect(data[bytes..-1]).to eq('')
159
+
160
+ expect(@headers).to eq({'Connection' => 'keep-alive'})
161
+ expect(@parser.upgrade_data).to eq('')
162
+ expect(@body).to be_empty
163
+ expect(@done).to be false
164
+ end
165
+
166
+ it "should reset to initial state" do
167
+ @parser << "GET / HTTP/1.0\r\n\r\n"
168
+
169
+ expect(@parser.http_method).to eq('GET')
170
+ expect(@parser.http_version).to eq([1,0])
171
+
172
+ expect(@parser.request_url).to eq('/')
173
+
174
+ expect(@parser.reset!).to be true
175
+
176
+ expect(@parser.http_version).to be_nil
177
+ expect(@parser.http_method).to be_nil
178
+ expect(@parser.status_code).to be_nil
179
+
180
+ expect(@parser.request_url).to be_nil
181
+ end
182
+
183
+ it "should retain callbacks after reset" do
184
+ expect(@parser.reset!).to be true
185
+
186
+ @parser << "GET / HTTP/1.0\r\n\r\n"
187
+ expect(@started).to be true
188
+ expect(@headers).to eq({})
189
+ expect(@done).to be true
190
+ end
191
+
192
+ it "should parse headers incrementally" do
193
+ request =
194
+ "GET / HTTP/1.0\r\n" +
195
+ "Header1: value 1\r\n" +
196
+ "Header2: value 2\r\n" +
197
+ "\r\n"
198
+
199
+ while chunk = request.slice!(0,2) and !chunk.empty?
200
+ @parser << chunk
201
+ end
202
+
203
+ expect(@parser.headers).to eq({
204
+ 'Header1' => 'value 1',
205
+ 'Header2' => 'value 2'
206
+ })
207
+ end
208
+
209
+ it "should handle multiple headers using strings" do
210
+ @parser.header_value_type = :strings
211
+
212
+ @parser <<
213
+ "GET / HTTP/1.0\r\n" +
214
+ "Set-Cookie: PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com\r\n" +
215
+ "Set-Cookie: NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly\r\n" +
216
+ "\r\n"
217
+
218
+ expect(@parser.headers["Set-Cookie"]).to eq("PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com, NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly")
219
+ end
220
+
221
+ it "should handle multiple headers using strings" do
222
+ @parser.header_value_type = :arrays
223
+
224
+ @parser <<
225
+ "GET / HTTP/1.0\r\n" +
226
+ "Set-Cookie: PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com\r\n" +
227
+ "Set-Cookie: NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly\r\n" +
228
+ "\r\n"
229
+
230
+ expect(@parser.headers["Set-Cookie"]).to eq([
231
+ "PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com",
232
+ "NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly"
233
+ ])
234
+ end
235
+
236
+ it "should handle multiple headers using mixed" do
237
+ @parser.header_value_type = :mixed
238
+
239
+ @parser <<
240
+ "GET / HTTP/1.0\r\n" +
241
+ "Set-Cookie: PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com\r\n" +
242
+ "Set-Cookie: NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly\r\n" +
243
+ "\r\n"
244
+
245
+ expect(@parser.headers["Set-Cookie"]).to eq([
246
+ "PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com",
247
+ "NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly"
248
+ ])
249
+ end
250
+
251
+ it "should handle a single cookie using mixed" do
252
+ @parser.header_value_type = :mixed
253
+
254
+ @parser <<
255
+ "GET / HTTP/1.0\r\n" +
256
+ "Set-Cookie: PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com\r\n" +
257
+ "\r\n"
258
+
259
+ expect(@parser.headers["Set-Cookie"]).to eq("PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com")
260
+ end
261
+
262
+ it "should support alternative api" do
263
+ callbacks = double('callbacks')
264
+ allow(callbacks).to receive(:on_message_begin){ @started = true }
265
+ allow(callbacks).to receive(:on_headers_complete){ |e| @headers = e }
266
+ allow(callbacks).to receive(:on_body){ |chunk| @body << chunk }
267
+ allow(callbacks).to receive(:on_message_complete){ @done = true }
268
+
269
+ @parser = HTTP::Parser.new(callbacks)
270
+ @parser << "GET / HTTP/1.0\r\n\r\n"
271
+
272
+ expect(@started).to be true
273
+ expect(@headers).to eq({})
274
+ expect(@body).to eq('')
275
+ expect(@done).to be true
276
+ end
277
+
278
+ it 'sets upgrade_data if available' do
279
+ @parser <<
280
+ "GET /demo HTTP/1.1\r\n" +
281
+ "Connection: Upgrade\r\n" +
282
+ "Upgrade: WebSocket\r\n\r\n" +
283
+ "third key data"
284
+
285
+ expect(@parser.upgrade?).to be true
286
+ expect(@parser.upgrade_data).to eq('third key data')
287
+ end
288
+
289
+ it 'sets upgrade_data to blank if un-available' do
290
+ @parser <<
291
+ "GET /demo HTTP/1.1\r\n" +
292
+ "Connection: Upgrade\r\n" +
293
+ "Upgrade: WebSocket\r\n\r\n"
294
+
295
+ expect(@parser.upgrade?).to be true
296
+ expect(@parser.upgrade_data).to eq('')
297
+ end
298
+
299
+ it 'should stop parsing headers when instructed' do
300
+ request = "GET /websocket HTTP/1.1\r\n" +
301
+ "host: localhost\r\n" +
302
+ "connection: Upgrade\r\n" +
303
+ "upgrade: websocket\r\n" +
304
+ "sec-websocket-key: SD6/hpYbKjQ6Sown7pBbWQ==\r\n" +
305
+ "sec-websocket-version: 13\r\n" +
306
+ "\r\n"
307
+
308
+ @parser.on_headers_complete = proc { |e| :stop }
309
+ offset = (@parser << request)
310
+ expect(@parser.upgrade?).to be true
311
+ expect(@parser.upgrade_data).to eq('')
312
+ expect(offset).to eq(request.length)
313
+ end
314
+
315
+ it "should execute on_body on requests with no content-length" do
316
+ expect(@parser.reset!).to be true
317
+
318
+ @head, @complete, @body = 0, 0, 0
319
+ @parser.on_headers_complete = proc {|h| @head += 1 }
320
+ @parser.on_message_complete = proc { @complete += 1 }
321
+ @parser.on_body = proc {|b| @body += 1 }
322
+
323
+ head_response = "HTTP/1.1 200 OK\r\n\r\nstuff"
324
+
325
+ @parser << head_response
326
+ @parser << ''
327
+ expect(@head).to eq(1)
328
+ expect(@complete).to eq(1)
329
+ expect(@body).to eq(1)
330
+ end
331
+
332
+
333
+ %w[ request response ].each do |type|
334
+ JSON.parse(File.read(File.expand_path("../support/#{type}s.json", __FILE__))).each do |test|
335
+ test['headers'] ||= {}
336
+ next if !defined?(JRUBY_VERSION) and HTTP::Parser.strict? != test['strict']
337
+
338
+ it "should parse #{type}: #{test['name']}" do
339
+ @parser << test['raw']
340
+
341
+ expect(@parser.http_method).to eq(test['method'])
342
+ expect(@parser.keep_alive?).to eq(test['should_keep_alive'])
343
+
344
+ if test.has_key?('upgrade') and test['upgrade'] != 0
345
+ expect(@parser.upgrade?).to be true
346
+ expect(@parser.upgrade_data).to eq(test['upgrade'])
347
+ end
348
+
349
+ fields = %w[
350
+ http_major
351
+ http_minor
352
+ ]
353
+
354
+ if test['type'] == 'HTTP_REQUEST'
355
+ fields += %w[
356
+ request_url
357
+ ]
358
+ else
359
+ fields += %w[
360
+ status_code
361
+ ]
362
+ end
363
+
364
+ fields.each do |field|
365
+ expect(@parser.send(field)).to eq(test[field])
366
+ end
367
+
368
+ expect(@headers.size).to eq(test['num_headers'])
369
+ expect(@headers).to eq(test['headers'])
370
+
371
+ expect(@body).to eq(test['body'])
372
+ expect(@body.size).to eq(test['body_size']) if test['body_size']
373
+ end
374
+ end
375
+ end
376
+ end
@@ -0,0 +1 @@
1
+ require "http_parser"
@@ -0,0 +1,631 @@
1
+ [
2
+ {
3
+ "name": "curl get",
4
+ "type": "HTTP_REQUEST",
5
+ "raw": "GET /test HTTP/1.1\r\nUser-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\nHost: 0.0.0.0=5000\r\nAccept: */*\r\n\r\n",
6
+ "should_keep_alive": true,
7
+ "message_complete_on_eof": false,
8
+ "http_major": 1,
9
+ "http_minor": 1,
10
+ "method": "GET",
11
+ "query_string": "",
12
+ "fragment": "",
13
+ "request_path": "/test",
14
+ "request_url": "/test",
15
+ "num_headers": 3,
16
+ "headers": {
17
+ "User-Agent": "curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1",
18
+ "Host": "0.0.0.0=5000",
19
+ "Accept": "*/*"
20
+ },
21
+ "body": "",
22
+ "strict": true
23
+ },
24
+ {
25
+ "name": "firefox get",
26
+ "type": "HTTP_REQUEST",
27
+ "raw": "GET /favicon.ico HTTP/1.1\r\nHost: 0.0.0.0=5000\r\nUser-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip,deflate\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\nKeep-Alive: 300\r\nConnection: keep-alive\r\n\r\n",
28
+ "should_keep_alive": true,
29
+ "message_complete_on_eof": false,
30
+ "http_major": 1,
31
+ "http_minor": 1,
32
+ "method": "GET",
33
+ "query_string": "",
34
+ "fragment": "",
35
+ "request_path": "/favicon.ico",
36
+ "request_url": "/favicon.ico",
37
+ "num_headers": 8,
38
+ "headers": {
39
+ "Host": "0.0.0.0=5000",
40
+ "User-Agent": "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0",
41
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
42
+ "Accept-Language": "en-us,en;q=0.5",
43
+ "Accept-Encoding": "gzip,deflate",
44
+ "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
45
+ "Keep-Alive": "300",
46
+ "Connection": "keep-alive"
47
+ },
48
+ "body": "",
49
+ "strict": true
50
+ },
51
+ {
52
+ "name": "dumbfuck",
53
+ "type": "HTTP_REQUEST",
54
+ "raw": "GET /dumbfuck HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n",
55
+ "should_keep_alive": true,
56
+ "message_complete_on_eof": false,
57
+ "http_major": 1,
58
+ "http_minor": 1,
59
+ "method": "GET",
60
+ "query_string": "",
61
+ "fragment": "",
62
+ "request_path": "/dumbfuck",
63
+ "request_url": "/dumbfuck",
64
+ "num_headers": 1,
65
+ "headers": {
66
+ "aaaaaaaaaaaaa": "++++++++++"
67
+ },
68
+ "body": "",
69
+ "strict": true
70
+ },
71
+ {
72
+ "name": "fragment in url",
73
+ "type": "HTTP_REQUEST",
74
+ "raw": "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n",
75
+ "should_keep_alive": true,
76
+ "message_complete_on_eof": false,
77
+ "http_major": 1,
78
+ "http_minor": 1,
79
+ "method": "GET",
80
+ "query_string": "page=1",
81
+ "fragment": "posts-17408",
82
+ "request_path": "/forums/1/topics/2375",
83
+ "request_url": "/forums/1/topics/2375?page=1#posts-17408",
84
+ "num_headers": 0,
85
+ "body": "",
86
+ "strict": true
87
+ },
88
+ {
89
+ "name": "get no headers no body",
90
+ "type": "HTTP_REQUEST",
91
+ "raw": "GET /get_no_headers_no_body/world HTTP/1.1\r\n\r\n",
92
+ "should_keep_alive": true,
93
+ "message_complete_on_eof": false,
94
+ "http_major": 1,
95
+ "http_minor": 1,
96
+ "method": "GET",
97
+ "query_string": "",
98
+ "fragment": "",
99
+ "request_path": "/get_no_headers_no_body/world",
100
+ "request_url": "/get_no_headers_no_body/world",
101
+ "num_headers": 0,
102
+ "body": "",
103
+ "strict": true
104
+ },
105
+ {
106
+ "name": "get one header no body",
107
+ "type": "HTTP_REQUEST",
108
+ "raw": "GET /get_one_header_no_body HTTP/1.1\r\nAccept: */*\r\n\r\n",
109
+ "should_keep_alive": true,
110
+ "message_complete_on_eof": false,
111
+ "http_major": 1,
112
+ "http_minor": 1,
113
+ "method": "GET",
114
+ "query_string": "",
115
+ "fragment": "",
116
+ "request_path": "/get_one_header_no_body",
117
+ "request_url": "/get_one_header_no_body",
118
+ "num_headers": 1,
119
+ "headers": {
120
+ "Accept": "*/*"
121
+ },
122
+ "body": "",
123
+ "strict": true
124
+ },
125
+ {
126
+ "name": "get funky content length body hello",
127
+ "type": "HTTP_REQUEST",
128
+ "raw": "GET /get_funky_content_length_body_hello HTTP/1.0\r\nconTENT-Length: 5\r\n\r\nHELLO",
129
+ "should_keep_alive": false,
130
+ "message_complete_on_eof": false,
131
+ "http_major": 1,
132
+ "http_minor": 0,
133
+ "method": "GET",
134
+ "query_string": "",
135
+ "fragment": "",
136
+ "request_path": "/get_funky_content_length_body_hello",
137
+ "request_url": "/get_funky_content_length_body_hello",
138
+ "num_headers": 1,
139
+ "headers": {
140
+ "conTENT-Length": "5"
141
+ },
142
+ "body": "HELLO",
143
+ "strict": true
144
+ },
145
+ {
146
+ "name": "post identity body world",
147
+ "type": "HTTP_REQUEST",
148
+ "raw": "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\nAccept: */*\r\nTransfer-Encoding: identity\r\nContent-Length: 5\r\n\r\nWorld",
149
+ "should_keep_alive": true,
150
+ "message_complete_on_eof": false,
151
+ "http_major": 1,
152
+ "http_minor": 1,
153
+ "method": "POST",
154
+ "query_string": "q=search",
155
+ "fragment": "hey",
156
+ "request_path": "/post_identity_body_world",
157
+ "request_url": "/post_identity_body_world?q=search#hey",
158
+ "num_headers": 3,
159
+ "headers": {
160
+ "Accept": "*/*",
161
+ "Transfer-Encoding": "identity",
162
+ "Content-Length": "5"
163
+ },
164
+ "body": "World",
165
+ "strict": true
166
+ },
167
+ {
168
+ "name": "post - chunked body: all your base are belong to us",
169
+ "type": "HTTP_REQUEST",
170
+ "raw": "POST /post_chunked_all_your_base HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n1e\r\nall your base are belong to us\r\n0\r\n\r\n",
171
+ "should_keep_alive": true,
172
+ "message_complete_on_eof": false,
173
+ "http_major": 1,
174
+ "http_minor": 1,
175
+ "method": "POST",
176
+ "query_string": "",
177
+ "fragment": "",
178
+ "request_path": "/post_chunked_all_your_base",
179
+ "request_url": "/post_chunked_all_your_base",
180
+ "num_headers": 1,
181
+ "headers": {
182
+ "Transfer-Encoding": "chunked"
183
+ },
184
+ "body": "all your base are belong to us",
185
+ "strict": true
186
+ },
187
+ {
188
+ "name": "two chunks ; triple zero ending",
189
+ "type": "HTTP_REQUEST",
190
+ "raw": "POST /two_chunks_mult_zero_end HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n6\r\n world\r\n000\r\n\r\n",
191
+ "should_keep_alive": true,
192
+ "message_complete_on_eof": false,
193
+ "http_major": 1,
194
+ "http_minor": 1,
195
+ "method": "POST",
196
+ "query_string": "",
197
+ "fragment": "",
198
+ "request_path": "/two_chunks_mult_zero_end",
199
+ "request_url": "/two_chunks_mult_zero_end",
200
+ "num_headers": 1,
201
+ "headers": {
202
+ "Transfer-Encoding": "chunked"
203
+ },
204
+ "body": "hello world",
205
+ "strict": true
206
+ },
207
+ {
208
+ "name": "chunked with trailing headers. blech.",
209
+ "type": "HTTP_REQUEST",
210
+ "raw": "POST /chunked_w_trailing_headers HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n6\r\n world\r\n0\r\nVary: *\r\nContent-Type: text/plain\r\n\r\n",
211
+ "should_keep_alive": true,
212
+ "message_complete_on_eof": false,
213
+ "http_major": 1,
214
+ "http_minor": 1,
215
+ "method": "POST",
216
+ "query_string": "",
217
+ "fragment": "",
218
+ "request_path": "/chunked_w_trailing_headers",
219
+ "request_url": "/chunked_w_trailing_headers",
220
+ "num_headers": 3,
221
+ "headers": {
222
+ "Transfer-Encoding": "chunked",
223
+ "Vary": "*",
224
+ "Content-Type": "text/plain"
225
+ },
226
+ "body": "hello world",
227
+ "strict": true
228
+ },
229
+ {
230
+ "name": "with bullshit after the length",
231
+ "type": "HTTP_REQUEST",
232
+ "raw": "POST /chunked_w_bullshit_after_length HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n6; blahblah; blah\r\n world\r\n0\r\n\r\n",
233
+ "should_keep_alive": true,
234
+ "message_complete_on_eof": false,
235
+ "http_major": 1,
236
+ "http_minor": 1,
237
+ "method": "POST",
238
+ "query_string": "",
239
+ "fragment": "",
240
+ "request_path": "/chunked_w_bullshit_after_length",
241
+ "request_url": "/chunked_w_bullshit_after_length",
242
+ "num_headers": 1,
243
+ "headers": {
244
+ "Transfer-Encoding": "chunked"
245
+ },
246
+ "body": "hello world",
247
+ "strict": true
248
+ },
249
+ {
250
+ "name": "with quotes",
251
+ "type": "HTTP_REQUEST",
252
+ "raw": "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n",
253
+ "should_keep_alive": true,
254
+ "message_complete_on_eof": false,
255
+ "http_major": 1,
256
+ "http_minor": 1,
257
+ "method": "GET",
258
+ "query_string": "foo=\"bar\"",
259
+ "fragment": "",
260
+ "request_path": "/with_\"stupid\"_quotes",
261
+ "request_url": "/with_\"stupid\"_quotes?foo=\"bar\"",
262
+ "num_headers": 0,
263
+ "headers": {
264
+
265
+ },
266
+ "body": "",
267
+ "strict": true
268
+ },
269
+ {
270
+ "name": "apachebench get",
271
+ "type": "HTTP_REQUEST",
272
+ "raw": "GET /test HTTP/1.0\r\nHost: 0.0.0.0:5000\r\nUser-Agent: ApacheBench/2.3\r\nAccept: */*\r\n\r\n",
273
+ "should_keep_alive": false,
274
+ "message_complete_on_eof": false,
275
+ "http_major": 1,
276
+ "http_minor": 0,
277
+ "method": "GET",
278
+ "query_string": "",
279
+ "fragment": "",
280
+ "request_path": "/test",
281
+ "request_url": "/test",
282
+ "num_headers": 3,
283
+ "headers": {
284
+ "Host": "0.0.0.0:5000",
285
+ "User-Agent": "ApacheBench/2.3",
286
+ "Accept": "*/*"
287
+ },
288
+ "body": "",
289
+ "strict": true
290
+ },
291
+ {
292
+ "name": "query url with question mark",
293
+ "type": "HTTP_REQUEST",
294
+ "raw": "GET /test.cgi?foo=bar?baz HTTP/1.1\r\n\r\n",
295
+ "should_keep_alive": true,
296
+ "message_complete_on_eof": false,
297
+ "http_major": 1,
298
+ "http_minor": 1,
299
+ "method": "GET",
300
+ "query_string": "foo=bar?baz",
301
+ "fragment": "",
302
+ "request_path": "/test.cgi",
303
+ "request_url": "/test.cgi?foo=bar?baz",
304
+ "num_headers": 0,
305
+ "headers": {
306
+
307
+ },
308
+ "body": "",
309
+ "strict": true
310
+ },
311
+ {
312
+ "name": "newline prefix get",
313
+ "type": "HTTP_REQUEST",
314
+ "raw": "\r\nGET /test HTTP/1.1\r\n\r\n",
315
+ "should_keep_alive": true,
316
+ "message_complete_on_eof": false,
317
+ "http_major": 1,
318
+ "http_minor": 1,
319
+ "method": "GET",
320
+ "query_string": "",
321
+ "fragment": "",
322
+ "request_path": "/test",
323
+ "request_url": "/test",
324
+ "num_headers": 0,
325
+ "headers": {
326
+
327
+ },
328
+ "body": "",
329
+ "strict": true
330
+ },
331
+ {
332
+ "name": "upgrade request",
333
+ "type": "HTTP_REQUEST",
334
+ "raw": "GET /demo HTTP/1.1\r\nHost: example.com\r\nConnection: Upgrade\r\nSec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\nSec-WebSocket-Protocol: sample\r\nUpgrade: WebSocket\r\nSec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\nOrigin: http://example.com\r\n\r\nHot diggity dogg",
335
+ "should_keep_alive": true,
336
+ "message_complete_on_eof": false,
337
+ "http_major": 1,
338
+ "http_minor": 1,
339
+ "method": "GET",
340
+ "query_string": "",
341
+ "fragment": "",
342
+ "request_path": "/demo",
343
+ "request_url": "/demo",
344
+ "num_headers": 7,
345
+ "upgrade": "Hot diggity dogg",
346
+ "headers": {
347
+ "Host": "example.com",
348
+ "Connection": "Upgrade",
349
+ "Sec-WebSocket-Key2": "12998 5 Y3 1 .P00",
350
+ "Sec-WebSocket-Protocol": "sample",
351
+ "Upgrade": "WebSocket",
352
+ "Sec-WebSocket-Key1": "4 @1 46546xW%0l 1 5",
353
+ "Origin": "http://example.com"
354
+ },
355
+ "body": "",
356
+ "strict": true
357
+ },
358
+ {
359
+ "name": "connect request",
360
+ "type": "HTTP_REQUEST",
361
+ "raw": "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\nUser-agent: Mozilla/1.1N\r\nProxy-authorization: basic aGVsbG86d29ybGQ=\r\n\r\nsome data\r\nand yet even more data",
362
+ "should_keep_alive": false,
363
+ "message_complete_on_eof": false,
364
+ "http_major": 1,
365
+ "http_minor": 0,
366
+ "method": "CONNECT",
367
+ "query_string": "",
368
+ "fragment": "",
369
+ "request_path": "",
370
+ "request_url": "0-home0.netscape.com:443",
371
+ "num_headers": 2,
372
+ "upgrade": "some data\r\nand yet even more data",
373
+ "headers": {
374
+ "User-agent": "Mozilla/1.1N",
375
+ "Proxy-authorization": "basic aGVsbG86d29ybGQ="
376
+ },
377
+ "body": "",
378
+ "strict": true
379
+ },
380
+ {
381
+ "name": "report request",
382
+ "type": "HTTP_REQUEST",
383
+ "raw": "REPORT /test HTTP/1.1\r\n\r\n",
384
+ "should_keep_alive": true,
385
+ "message_complete_on_eof": false,
386
+ "http_major": 1,
387
+ "http_minor": 1,
388
+ "method": "REPORT",
389
+ "query_string": "",
390
+ "fragment": "",
391
+ "request_path": "/test",
392
+ "request_url": "/test",
393
+ "num_headers": 0,
394
+ "headers": {
395
+
396
+ },
397
+ "body": "",
398
+ "strict": true
399
+ },
400
+ {
401
+ "name": "request with no http version",
402
+ "type": "HTTP_REQUEST",
403
+ "raw": "GET /\r\n\r\n",
404
+ "should_keep_alive": false,
405
+ "message_complete_on_eof": false,
406
+ "http_major": 0,
407
+ "http_minor": 9,
408
+ "method": "GET",
409
+ "query_string": "",
410
+ "fragment": "",
411
+ "request_path": "/",
412
+ "request_url": "/",
413
+ "num_headers": 0,
414
+ "headers": {
415
+
416
+ },
417
+ "body": "",
418
+ "strict": true
419
+ },
420
+ {
421
+ "name": "link request",
422
+ "type": "HTTP_REQUEST",
423
+ "raw": "LINK /test HTTP/1.1\r\n\r\n",
424
+ "should_keep_alive": true,
425
+ "message_complete_on_eof": false,
426
+ "http_major": 1,
427
+ "http_minor": 1,
428
+ "method": "LINK",
429
+ "query_string": "",
430
+ "fragment": "",
431
+ "request_path": "/test",
432
+ "request_url": "/test",
433
+ "num_headers": 0,
434
+ "headers": {
435
+
436
+ },
437
+ "body": "",
438
+ "strict": true
439
+ },
440
+ {
441
+ "name": "unlink request",
442
+ "type": "HTTP_REQUEST",
443
+ "raw": "UNLINK /test HTTP/1.1\r\n\r\n",
444
+ "should_keep_alive": true,
445
+ "message_complete_on_eof": false,
446
+ "http_major": 1,
447
+ "http_minor": 1,
448
+ "method": "UNLINK",
449
+ "query_string": "",
450
+ "fragment": "",
451
+ "request_path": "/test",
452
+ "request_url": "/test",
453
+ "num_headers": 0,
454
+ "headers": {
455
+
456
+ },
457
+ "body": "",
458
+ "strict": true
459
+ },
460
+ {
461
+ "name": "m-search request",
462
+ "type": "HTTP_REQUEST",
463
+ "raw": "M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: \"ssdp:discover\"\r\nST: \"ssdp:all\"\r\n\r\n",
464
+ "should_keep_alive": true,
465
+ "message_complete_on_eof": false,
466
+ "http_major": 1,
467
+ "http_minor": 1,
468
+ "method": "M-SEARCH",
469
+ "query_string": "",
470
+ "fragment": "",
471
+ "request_path": "*",
472
+ "request_url": "*",
473
+ "num_headers": 3,
474
+ "headers": {
475
+ "HOST": "239.255.255.250:1900",
476
+ "MAN": "\"ssdp:discover\"",
477
+ "ST": "\"ssdp:all\""
478
+ },
479
+ "body": "",
480
+ "strict": true
481
+ },
482
+ {
483
+ "name": "host terminated by a query string",
484
+ "type": "HTTP_REQUEST",
485
+ "raw": "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n\r\n",
486
+ "should_keep_alive": true,
487
+ "message_complete_on_eof": false,
488
+ "http_major": 1,
489
+ "http_minor": 1,
490
+ "method": "GET",
491
+ "query_string": "hail=all",
492
+ "fragment": "",
493
+ "request_path": "",
494
+ "request_url": "http://hypnotoad.org?hail=all",
495
+ "num_headers": 0,
496
+ "headers": {
497
+
498
+ },
499
+ "body": "",
500
+ "strict": true
501
+ },
502
+ {
503
+ "name": "host:port terminated by a query string",
504
+ "type": "HTTP_REQUEST",
505
+ "raw": "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n\r\n",
506
+ "should_keep_alive": true,
507
+ "message_complete_on_eof": false,
508
+ "http_major": 1,
509
+ "http_minor": 1,
510
+ "method": "GET",
511
+ "query_string": "hail=all",
512
+ "fragment": "",
513
+ "request_path": "",
514
+ "request_url": "http://hypnotoad.org:1234?hail=all",
515
+ "port": 1234,
516
+ "num_headers": 0,
517
+ "headers": {
518
+
519
+ },
520
+ "body": "",
521
+ "strict": true
522
+ },
523
+ {
524
+ "name": "host:port terminated by a space",
525
+ "type": "HTTP_REQUEST",
526
+ "raw": "GET http://hypnotoad.org:1234 HTTP/1.1\r\n\r\n",
527
+ "should_keep_alive": true,
528
+ "message_complete_on_eof": false,
529
+ "http_major": 1,
530
+ "http_minor": 1,
531
+ "method": "GET",
532
+ "query_string": "",
533
+ "fragment": "",
534
+ "request_path": "",
535
+ "request_url": "http://hypnotoad.org:1234",
536
+ "port": 1234,
537
+ "num_headers": 0,
538
+ "headers": {
539
+
540
+ },
541
+ "body": "",
542
+ "strict": true
543
+ },
544
+ {
545
+ "name": "PATCH request",
546
+ "type": "HTTP_REQUEST",
547
+ "raw": "PATCH /file.txt HTTP/1.1\r\nHost: www.example.com\r\nContent-Type: application/example\r\nIf-Match: \"e0023aa4e\"\r\nContent-Length: 10\r\n\r\ncccccccccc",
548
+ "should_keep_alive": true,
549
+ "message_complete_on_eof": false,
550
+ "http_major": 1,
551
+ "http_minor": 1,
552
+ "method": "PATCH",
553
+ "query_string": "",
554
+ "fragment": "",
555
+ "request_path": "/file.txt",
556
+ "request_url": "/file.txt",
557
+ "num_headers": 4,
558
+ "headers": {
559
+ "Host": "www.example.com",
560
+ "Content-Type": "application/example",
561
+ "If-Match": "\"e0023aa4e\"",
562
+ "Content-Length": "10"
563
+ },
564
+ "body": "cccccccccc",
565
+ "strict": true
566
+ },
567
+ {
568
+ "name": "connect caps request",
569
+ "type": "HTTP_REQUEST",
570
+ "raw": "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\nUser-agent: Mozilla/1.1N\r\nProxy-authorization: basic aGVsbG86d29ybGQ=\r\n\r\n",
571
+ "should_keep_alive": false,
572
+ "message_complete_on_eof": false,
573
+ "http_major": 1,
574
+ "http_minor": 0,
575
+ "method": "CONNECT",
576
+ "query_string": "",
577
+ "fragment": "",
578
+ "request_path": "",
579
+ "request_url": "HOME0.NETSCAPE.COM:443",
580
+ "num_headers": 2,
581
+ "upgrade": "",
582
+ "headers": {
583
+ "User-agent": "Mozilla/1.1N",
584
+ "Proxy-authorization": "basic aGVsbG86d29ybGQ="
585
+ },
586
+ "body": "",
587
+ "strict": true
588
+ },
589
+ {
590
+ "name": "utf-8 path request",
591
+ "type": "HTTP_REQUEST",
592
+ "strict": false,
593
+ "raw": "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\nHost: github.com\r\n\r\n",
594
+ "should_keep_alive": true,
595
+ "message_complete_on_eof": false,
596
+ "http_major": 1,
597
+ "http_minor": 1,
598
+ "method": "GET",
599
+ "query_string": "q=1",
600
+ "fragment": "narf",
601
+ "request_path": "/δ¶/δt/pope",
602
+ "request_url": "/δ¶/δt/pope?q=1#narf",
603
+ "num_headers": 1,
604
+ "headers": {
605
+ "Host": "github.com"
606
+ },
607
+ "body": ""
608
+ },
609
+ {
610
+ "name": "hostname underscore",
611
+ "type": "HTTP_REQUEST",
612
+ "strict": false,
613
+ "raw": "CONNECT home_0.netscape.com:443 HTTP/1.0\r\nUser-agent: Mozilla/1.1N\r\nProxy-authorization: basic aGVsbG86d29ybGQ=\r\n\r\n",
614
+ "should_keep_alive": false,
615
+ "message_complete_on_eof": false,
616
+ "http_major": 1,
617
+ "http_minor": 0,
618
+ "method": "CONNECT",
619
+ "query_string": "",
620
+ "fragment": "",
621
+ "request_path": "",
622
+ "request_url": "home_0.netscape.com:443",
623
+ "num_headers": 2,
624
+ "upgrade": "",
625
+ "headers": {
626
+ "User-agent": "Mozilla/1.1N",
627
+ "Proxy-authorization": "basic aGVsbG86d29ybGQ="
628
+ },
629
+ "body": ""
630
+ }
631
+ ]