http_parser.rb 0.5.3 → 0.6.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
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
  }