http_parser.rb 0.5.3 → 0.6.0.beta.1

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 (58) hide show
  1. data/.gitmodules +3 -3
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +9 -2
  4. data/README.md +50 -45
  5. data/bench/standalone.rb +23 -0
  6. data/bench/thin.rb +1 -0
  7. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +66 -58
  8. data/ext/ruby_http_parser/ruby_http_parser.c +10 -41
  9. data/ext/ruby_http_parser/vendor/http-parser-java/AUTHORS +32 -0
  10. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +5 -1
  11. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +133 -1
  12. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +6 -0
  13. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1029 -615
  14. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.gyp +79 -0
  15. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +177 -43
  16. data/ext/ruby_http_parser/vendor/http-parser-java/src/Http-parser.java.iml +22 -0
  17. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/FieldData.java +41 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPHeadersCompleteCallback.java +13 -0
  19. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +4 -1
  20. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParserUrl.java +76 -0
  21. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +2 -2
  22. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/Util.java +6 -6
  23. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPHeadersCompleteCallback.java +12 -0
  24. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +715 -637
  25. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +1 -1
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Message.java +71 -21
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/ParseUrl.java +51 -0
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Requests.java +1 -1
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Responses.java +1 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Test.java +2 -1
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestHeaderOverflowError.java +1 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +6 -17
  33. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestNoOverflowLongBody.java +1 -0
  34. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +1 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Upgrade.java +1 -0
  36. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Url.java +127 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/Util.java +80 -9
  38. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/WrongContentLength.java +2 -1
  39. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1141 -210
  40. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +230 -71
  41. data/ext/ruby_http_parser/vendor/http-parser/AUTHORS +32 -0
  42. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +5 -1
  43. data/ext/ruby_http_parser/vendor/http-parser/README.md +9 -2
  44. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1029 -615
  45. data/ext/ruby_http_parser/vendor/http-parser/http_parser.gyp +79 -0
  46. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +145 -16
  47. data/ext/ruby_http_parser/vendor/http-parser/test.c +1065 -141
  48. data/http_parser.rb.gemspec +3 -1
  49. data/spec/parser_spec.rb +41 -17
  50. data/spec/support/requests.json +236 -24
  51. data/spec/support/responses.json +182 -36
  52. data/tasks/compile.rake +2 -2
  53. data/tasks/fixtures.rake +7 -1
  54. metadata +57 -19
  55. data/ext/ruby_http_parser/vendor/http-parser-java/compile +0 -1
  56. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +0 -1
  57. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +0 -1
  58. data/ext/ruby_http_parser/vendor/http-parser-java/test_utf8 +0 -1
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "http_parser.rb"
3
- s.version = "0.5.3"
3
+ s.version = "0.6.0.beta.1"
4
4
  s.summary = "Simple callback-based HTTP request/response parser"
5
5
  s.description = "Ruby bindings to http://github.com/ry/http-parser and http://github.com/a2800276/http-parser.java"
6
6
 
@@ -16,6 +16,8 @@ Gem::Specification.new do |s|
16
16
  s.add_development_dependency 'rake-compiler', '>= 0.7.9'
17
17
  s.add_development_dependency 'rspec', '>= 2.0.1'
18
18
  s.add_development_dependency 'json', '>= 1.4.6'
19
+ s.add_development_dependency 'benchmark_suite'
20
+ s.add_development_dependency 'ffi'
19
21
 
20
22
  if RUBY_PLATFORM =~ /java/
21
23
  s.add_development_dependency 'jruby-openssl'
@@ -24,9 +24,6 @@ describe HTTP::Parser do
24
24
  @parser.status_code.should be_nil
25
25
 
26
26
  @parser.request_url.should be_nil
27
- @parser.request_path.should be_nil
28
- @parser.query_string.should be_nil
29
- @parser.fragment.should be_nil
30
27
 
31
28
  @parser.header_value_type.should == :mixed
32
29
  end
@@ -78,9 +75,6 @@ describe HTTP::Parser do
78
75
  @parser.status_code.should be_nil
79
76
 
80
77
  @parser.request_url.should == '/test?ok=1'
81
- @parser.request_path.should == '/test'
82
- @parser.query_string.should == 'ok=1'
83
- @parser.fragment.should be_empty
84
78
 
85
79
  @parser.headers.should == @headers
86
80
  @parser.headers['User-Agent'].should == 'curl/7.18.0'
@@ -119,9 +113,6 @@ describe HTTP::Parser do
119
113
  @parser.http_version.should == [1,0]
120
114
 
121
115
  @parser.request_url.should == '/'
122
- @parser.request_path.should == '/'
123
- @parser.query_string.should == ''
124
- @parser.fragment.should == ''
125
116
 
126
117
  @parser.reset!.should be_true
127
118
 
@@ -130,9 +121,6 @@ describe HTTP::Parser do
130
121
  @parser.status_code.should be_nil
131
122
 
132
123
  @parser.request_url.should be_nil
133
- @parser.request_path.should be_nil
134
- @parser.query_string.should be_nil
135
- @parser.fragment.should be_nil
136
124
  end
137
125
 
138
126
  it "should optionally reset parser state on no-body responses" do
@@ -282,16 +270,55 @@ describe HTTP::Parser do
282
270
  @parser.upgrade_data.should == ''
283
271
  end
284
272
 
273
+ it 'should stop parsing headers when instructed' do
274
+ request = "GET /websocket HTTP/1.1\r\n" +
275
+ "host: localhost\r\n" +
276
+ "connection: Upgrade\r\n" +
277
+ "upgrade: websocket\r\n" +
278
+ "sec-websocket-key: SD6/hpYbKjQ6Sown7pBbWQ==\r\n" +
279
+ "sec-websocket-version: 13\r\n" +
280
+ "\r\n"
281
+
282
+ @parser.on_headers_complete = proc { |e| :stop }
283
+ offset = (@parser << request)
284
+ @parser.upgrade?.should be_true
285
+ @parser.upgrade_data.should == ''
286
+ offset.should == request.length
287
+ end
288
+
289
+ it "should execute on_body on requests with no content-length" do
290
+ @parser.reset!.should be_true
291
+
292
+ @head, @complete, @body = 0, 0, 0
293
+ @parser.on_headers_complete = proc {|h| @head += 1 }
294
+ @parser.on_message_complete = proc { @complete += 1 }
295
+ @parser.on_body = proc {|b| @body += 1 }
296
+
297
+ head_response = "HTTP/1.1 200 OK\r\n\r\nstuff"
298
+
299
+ @parser << head_response
300
+ @parser << ''
301
+ @head.should == 1
302
+ @complete.should == 1
303
+ @body.should == 1
304
+ end
305
+
306
+
285
307
  %w[ request response ].each do |type|
286
308
  JSON.parse(File.read(File.expand_path("../support/#{type}s.json", __FILE__))).each do |test|
287
309
  test['headers'] ||= {}
310
+ next unless HTTP::Parser.strict? == test['strict']
288
311
 
289
312
  it "should parse #{type}: #{test['name']}" do
290
313
  @parser << test['raw']
291
314
 
292
- @parser.keep_alive?.should == test['should_keep_alive']
293
- @parser.upgrade?.should == (test['upgrade']==1)
294
315
  @parser.http_method.should == test['method']
316
+ @parser.keep_alive?.should == test['should_keep_alive']
317
+
318
+ if test.has_key?('upgrade') and test['upgrade'] != 0
319
+ @parser.upgrade?.should be_true
320
+ @parser.upgrade_data.should == test['upgrade']
321
+ end
295
322
 
296
323
  fields = %w[
297
324
  http_major
@@ -301,9 +328,6 @@ describe HTTP::Parser do
301
328
  if test['type'] == 'HTTP_REQUEST'
302
329
  fields += %w[
303
330
  request_url
304
- request_path
305
- query_string
306
- fragment
307
331
  ]
308
332
  else
309
333
  fields += %w[
@@ -18,7 +18,8 @@
18
18
  "Host": "0.0.0.0=5000",
19
19
  "Accept": "*/*"
20
20
  },
21
- "body": ""
21
+ "body": "",
22
+ "strict": true
22
23
  },
23
24
  {
24
25
  "name": "firefox get",
@@ -44,7 +45,8 @@
44
45
  "Keep-Alive": "300",
45
46
  "Connection": "keep-alive"
46
47
  },
47
- "body": ""
48
+ "body": "",
49
+ "strict": true
48
50
  },
49
51
  {
50
52
  "name": "dumbfuck",
@@ -63,7 +65,8 @@
63
65
  "headers": {
64
66
  "aaaaaaaaaaaaa": "++++++++++"
65
67
  },
66
- "body": ""
68
+ "body": "",
69
+ "strict": true
67
70
  },
68
71
  {
69
72
  "name": "fragment in url",
@@ -79,7 +82,8 @@
79
82
  "request_path": "/forums/1/topics/2375",
80
83
  "request_url": "/forums/1/topics/2375?page=1#posts-17408",
81
84
  "num_headers": 0,
82
- "body": ""
85
+ "body": "",
86
+ "strict": true
83
87
  },
84
88
  {
85
89
  "name": "get no headers no body",
@@ -95,7 +99,8 @@
95
99
  "request_path": "/get_no_headers_no_body/world",
96
100
  "request_url": "/get_no_headers_no_body/world",
97
101
  "num_headers": 0,
98
- "body": ""
102
+ "body": "",
103
+ "strict": true
99
104
  },
100
105
  {
101
106
  "name": "get one header no body",
@@ -114,7 +119,8 @@
114
119
  "headers": {
115
120
  "Accept": "*/*"
116
121
  },
117
- "body": ""
122
+ "body": "",
123
+ "strict": true
118
124
  },
119
125
  {
120
126
  "name": "get funky content length body hello",
@@ -133,7 +139,8 @@
133
139
  "headers": {
134
140
  "conTENT-Length": "5"
135
141
  },
136
- "body": "HELLO"
142
+ "body": "HELLO",
143
+ "strict": true
137
144
  },
138
145
  {
139
146
  "name": "post identity body world",
@@ -154,7 +161,8 @@
154
161
  "Transfer-Encoding": "identity",
155
162
  "Content-Length": "5"
156
163
  },
157
- "body": "World"
164
+ "body": "World",
165
+ "strict": true
158
166
  },
159
167
  {
160
168
  "name": "post - chunked body: all your base are belong to us",
@@ -173,7 +181,8 @@
173
181
  "headers": {
174
182
  "Transfer-Encoding": "chunked"
175
183
  },
176
- "body": "all your base are belong to us"
184
+ "body": "all your base are belong to us",
185
+ "strict": true
177
186
  },
178
187
  {
179
188
  "name": "two chunks ; triple zero ending",
@@ -192,7 +201,8 @@
192
201
  "headers": {
193
202
  "Transfer-Encoding": "chunked"
194
203
  },
195
- "body": "hello world"
204
+ "body": "hello world",
205
+ "strict": true
196
206
  },
197
207
  {
198
208
  "name": "chunked with trailing headers. blech.",
@@ -213,7 +223,8 @@
213
223
  "Vary": "*",
214
224
  "Content-Type": "text/plain"
215
225
  },
216
- "body": "hello world"
226
+ "body": "hello world",
227
+ "strict": true
217
228
  },
218
229
  {
219
230
  "name": "with bullshit after the length",
@@ -232,7 +243,8 @@
232
243
  "headers": {
233
244
  "Transfer-Encoding": "chunked"
234
245
  },
235
- "body": "hello world"
246
+ "body": "hello world",
247
+ "strict": true
236
248
  },
237
249
  {
238
250
  "name": "with quotes",
@@ -251,7 +263,8 @@
251
263
  "headers": {
252
264
 
253
265
  },
254
- "body": ""
266
+ "body": "",
267
+ "strict": true
255
268
  },
256
269
  {
257
270
  "name": "apachebench get",
@@ -272,7 +285,8 @@
272
285
  "User-Agent": "ApacheBench/2.3",
273
286
  "Accept": "*/*"
274
287
  },
275
- "body": ""
288
+ "body": "",
289
+ "strict": true
276
290
  },
277
291
  {
278
292
  "name": "query url with question mark",
@@ -291,7 +305,8 @@
291
305
  "headers": {
292
306
 
293
307
  },
294
- "body": ""
308
+ "body": "",
309
+ "strict": true
295
310
  },
296
311
  {
297
312
  "name": "newline prefix get",
@@ -310,12 +325,13 @@
310
325
  "headers": {
311
326
 
312
327
  },
313
- "body": ""
328
+ "body": "",
329
+ "strict": true
314
330
  },
315
331
  {
316
332
  "name": "upgrade request",
317
333
  "type": "HTTP_REQUEST",
318
- "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\n",
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",
319
335
  "should_keep_alive": true,
320
336
  "message_complete_on_eof": false,
321
337
  "http_major": 1,
@@ -326,7 +342,7 @@
326
342
  "request_path": "/demo",
327
343
  "request_url": "/demo",
328
344
  "num_headers": 7,
329
- "upgrade": 1,
345
+ "upgrade": "Hot diggity dogg",
330
346
  "headers": {
331
347
  "Host": "example.com",
332
348
  "Connection": "Upgrade",
@@ -336,12 +352,13 @@
336
352
  "Sec-WebSocket-Key1": "4 @1 46546xW%0l 1 5",
337
353
  "Origin": "http://example.com"
338
354
  },
339
- "body": ""
355
+ "body": "",
356
+ "strict": true
340
357
  },
341
358
  {
342
359
  "name": "connect request",
343
360
  "type": "HTTP_REQUEST",
344
- "raw": "CONNECT home0.netscape.com:443 HTTP/1.0\r\nUser-agent: Mozilla/1.1N\r\nProxy-authorization: basic aGVsbG86d29ybGQ=\r\n\r\n",
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",
345
362
  "should_keep_alive": false,
346
363
  "message_complete_on_eof": false,
347
364
  "http_major": 1,
@@ -350,14 +367,15 @@
350
367
  "query_string": "",
351
368
  "fragment": "",
352
369
  "request_path": "",
353
- "request_url": "home0.netscape.com:443",
370
+ "request_url": "0-home0.netscape.com:443",
354
371
  "num_headers": 2,
355
- "upgrade": 1,
372
+ "upgrade": "some data\r\nand yet even more data",
356
373
  "headers": {
357
374
  "User-agent": "Mozilla/1.1N",
358
375
  "Proxy-authorization": "basic aGVsbG86d29ybGQ="
359
376
  },
360
- "body": ""
377
+ "body": "",
378
+ "strict": true
361
379
  },
362
380
  {
363
381
  "name": "report request",
@@ -376,7 +394,8 @@
376
394
  "headers": {
377
395
 
378
396
  },
379
- "body": ""
397
+ "body": "",
398
+ "strict": true
380
399
  },
381
400
  {
382
401
  "name": "request with no http version",
@@ -394,6 +413,199 @@
394
413
  "num_headers": 0,
395
414
  "headers": {
396
415
 
416
+ },
417
+ "body": "",
418
+ "strict": true
419
+ },
420
+ {
421
+ "name": "m-search request",
422
+ "type": "HTTP_REQUEST",
423
+ "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",
424
+ "should_keep_alive": true,
425
+ "message_complete_on_eof": false,
426
+ "http_major": 1,
427
+ "http_minor": 1,
428
+ "method": "M-SEARCH",
429
+ "query_string": "",
430
+ "fragment": "",
431
+ "request_path": "*",
432
+ "request_url": "*",
433
+ "num_headers": 3,
434
+ "headers": {
435
+ "HOST": "239.255.255.250:1900",
436
+ "MAN": "\"ssdp:discover\"",
437
+ "ST": "\"ssdp:all\""
438
+ },
439
+ "body": "",
440
+ "strict": true
441
+ },
442
+ {
443
+ "name": "line folding in header value",
444
+ "type": "HTTP_REQUEST",
445
+ "raw": "GET / HTTP/1.1\r\nLine1: abc\r\n\tdef\r\n ghi\r\n\t\tjkl\r\n mno \r\n\t \tqrs\r\nLine2: \t line2\t\r\n\r\n",
446
+ "should_keep_alive": true,
447
+ "message_complete_on_eof": false,
448
+ "http_major": 1,
449
+ "http_minor": 1,
450
+ "method": "GET",
451
+ "query_string": "",
452
+ "fragment": "",
453
+ "request_path": "/",
454
+ "request_url": "/",
455
+ "num_headers": 2,
456
+ "headers": {
457
+ "Line1": "abcdefghijklmno qrs",
458
+ "Line2": "line2\t"
459
+ },
460
+ "body": "",
461
+ "strict": true
462
+ },
463
+ {
464
+ "name": "host terminated by a query string",
465
+ "type": "HTTP_REQUEST",
466
+ "raw": "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n\r\n",
467
+ "should_keep_alive": true,
468
+ "message_complete_on_eof": false,
469
+ "http_major": 1,
470
+ "http_minor": 1,
471
+ "method": "GET",
472
+ "query_string": "hail=all",
473
+ "fragment": "",
474
+ "request_path": "",
475
+ "request_url": "http://hypnotoad.org?hail=all",
476
+ "num_headers": 0,
477
+ "headers": {
478
+
479
+ },
480
+ "body": "",
481
+ "strict": true
482
+ },
483
+ {
484
+ "name": "host:port terminated by a query string",
485
+ "type": "HTTP_REQUEST",
486
+ "raw": "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n\r\n",
487
+ "should_keep_alive": true,
488
+ "message_complete_on_eof": false,
489
+ "http_major": 1,
490
+ "http_minor": 1,
491
+ "method": "GET",
492
+ "query_string": "hail=all",
493
+ "fragment": "",
494
+ "request_path": "",
495
+ "request_url": "http://hypnotoad.org:1234?hail=all",
496
+ "port": 1234,
497
+ "num_headers": 0,
498
+ "headers": {
499
+
500
+ },
501
+ "body": "",
502
+ "strict": true
503
+ },
504
+ {
505
+ "name": "host:port terminated by a space",
506
+ "type": "HTTP_REQUEST",
507
+ "raw": "GET http://hypnotoad.org:1234 HTTP/1.1\r\n\r\n",
508
+ "should_keep_alive": true,
509
+ "message_complete_on_eof": false,
510
+ "http_major": 1,
511
+ "http_minor": 1,
512
+ "method": "GET",
513
+ "query_string": "",
514
+ "fragment": "",
515
+ "request_path": "",
516
+ "request_url": "http://hypnotoad.org:1234",
517
+ "port": 1234,
518
+ "num_headers": 0,
519
+ "headers": {
520
+
521
+ },
522
+ "body": "",
523
+ "strict": true
524
+ },
525
+ {
526
+ "name": "PATCH request",
527
+ "type": "HTTP_REQUEST",
528
+ "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",
529
+ "should_keep_alive": true,
530
+ "message_complete_on_eof": false,
531
+ "http_major": 1,
532
+ "http_minor": 1,
533
+ "method": "PATCH",
534
+ "query_string": "",
535
+ "fragment": "",
536
+ "request_path": "/file.txt",
537
+ "request_url": "/file.txt",
538
+ "num_headers": 4,
539
+ "headers": {
540
+ "Host": "www.example.com",
541
+ "Content-Type": "application/example",
542
+ "If-Match": "\"e0023aa4e\"",
543
+ "Content-Length": "10"
544
+ },
545
+ "body": "cccccccccc",
546
+ "strict": true
547
+ },
548
+ {
549
+ "name": "connect caps request",
550
+ "type": "HTTP_REQUEST",
551
+ "raw": "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\nUser-agent: Mozilla/1.1N\r\nProxy-authorization: basic aGVsbG86d29ybGQ=\r\n\r\n",
552
+ "should_keep_alive": false,
553
+ "message_complete_on_eof": false,
554
+ "http_major": 1,
555
+ "http_minor": 0,
556
+ "method": "CONNECT",
557
+ "query_string": "",
558
+ "fragment": "",
559
+ "request_path": "",
560
+ "request_url": "HOME0.NETSCAPE.COM:443",
561
+ "num_headers": 2,
562
+ "upgrade": "",
563
+ "headers": {
564
+ "User-agent": "Mozilla/1.1N",
565
+ "Proxy-authorization": "basic aGVsbG86d29ybGQ="
566
+ },
567
+ "body": "",
568
+ "strict": true
569
+ },
570
+ {
571
+ "name": "utf-8 path request",
572
+ "type": "HTTP_REQUEST",
573
+ "strict": false,
574
+ "raw": "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\nHost: github.com\r\n\r\n",
575
+ "should_keep_alive": true,
576
+ "message_complete_on_eof": false,
577
+ "http_major": 1,
578
+ "http_minor": 1,
579
+ "method": "GET",
580
+ "query_string": "q=1",
581
+ "fragment": "narf",
582
+ "request_path": "/δ¶/δt/pope",
583
+ "request_url": "/δ¶/δt/pope?q=1#narf",
584
+ "num_headers": 1,
585
+ "headers": {
586
+ "Host": "github.com"
587
+ },
588
+ "body": ""
589
+ },
590
+ {
591
+ "name": "hostname underscore",
592
+ "type": "HTTP_REQUEST",
593
+ "strict": false,
594
+ "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",
595
+ "should_keep_alive": false,
596
+ "message_complete_on_eof": false,
597
+ "http_major": 1,
598
+ "http_minor": 0,
599
+ "method": "CONNECT",
600
+ "query_string": "",
601
+ "fragment": "",
602
+ "request_path": "",
603
+ "request_url": "home_0.netscape.com:443",
604
+ "num_headers": 2,
605
+ "upgrade": "",
606
+ "headers": {
607
+ "User-agent": "Mozilla/1.1N",
608
+ "Proxy-authorization": "basic aGVsbG86d29ybGQ="
397
609
  },
398
610
  "body": ""
399
611
  }