http_parser.rb 0.5.1 → 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 (70) hide show
  1. data/.gitmodules +4 -4
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +11 -4
  4. data/LICENSE-MIT +20 -0
  5. data/README.md +51 -46
  6. data/bench/standalone.rb +23 -0
  7. data/bench/thin.rb +1 -0
  8. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +121 -56
  9. data/ext/ruby_http_parser/ruby_http_parser.c +89 -48
  10. data/ext/ruby_http_parser/vendor/http-parser/AUTHORS +32 -0
  11. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +5 -1
  12. data/ext/ruby_http_parser/vendor/http-parser/Makefile +34 -17
  13. data/ext/ruby_http_parser/vendor/http-parser/README.md +9 -2
  14. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1045 -589
  15. data/ext/ruby_http_parser/vendor/http-parser/http_parser.gyp +79 -0
  16. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +148 -17
  17. data/ext/ruby_http_parser/vendor/http-parser/test.c +1138 -128
  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 +30 -1
  20. data/ext/ruby_http_parser/vendor/http-parser-java/Makefile +34 -17
  21. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +30 -18
  22. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +9 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/build.xml +74 -0
  24. data/ext/ruby_http_parser/vendor/http-parser-java/ext/primitives.jar +0 -0
  25. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1075 -607
  26. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.gyp +79 -0
  27. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +190 -40
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/Http-parser.java.iml +22 -0
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/FieldData.java +41 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPCallback.java +8 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPDataCallback.java +34 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPErrorCallback.java +12 -0
  33. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +4 -2
  34. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPHeadersCompleteCallback.java +13 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +67 -52
  36. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +5 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParserUrl.java +76 -0
  38. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserSettings.java +323 -0
  39. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/{lolevel/Util.java → Util.java} +34 -35
  40. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPHeadersCompleteCallback.java +12 -0
  41. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +839 -587
  42. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +2 -1
  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 +171 -446
  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 +3 -1
  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 +1318 -184
  57. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +355 -49
  58. data/ext/ruby_http_parser/vendor/http-parser-java/tests.utf8 +17 -0
  59. data/http_parser.rb.gemspec +10 -2
  60. data/lib/http_parser.rb +17 -0
  61. data/spec/parser_spec.rb +157 -23
  62. data/spec/support/requests.json +236 -24
  63. data/spec/support/responses.json +182 -36
  64. data/tasks/compile.rake +5 -3
  65. data/tasks/fixtures.rake +8 -2
  66. metadata +209 -127
  67. data/ext/ruby_http_parser/vendor/http-parser-java/CONTRIBUTIONS +0 -4
  68. data/ext/ruby_http_parser/vendor/http-parser-java/compile +0 -1
  69. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +0 -1
  70. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +0 -1
data/spec/parser_spec.rb CHANGED
@@ -24,9 +24,35 @@ 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
27
+
28
+ @parser.header_value_type.should == :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
+ @parser.header_value_type.should == type
35
+
36
+ parser_tmp = HTTP::Parser.new(nil, type)
37
+ parser_tmp.header_value_type.should == 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
+ parser.header_value_type.should == type
47
+ end
48
+ end
49
+
50
+ it "should throw an Argument Error if header value type is invalid" do
51
+ proc{ @parser.header_value_type = 'bob' }.should raise_error(ArgumentError)
52
+ end
53
+
54
+ it "should throw an Argument Error if default header value type is invalid" do
55
+ proc{ HTTP::Parser.default_header_value_type = 'bob' }.should raise_error(ArgumentError)
30
56
  end
31
57
 
32
58
  it "should implement basic api" do
@@ -49,9 +75,6 @@ describe HTTP::Parser do
49
75
  @parser.status_code.should be_nil
50
76
 
51
77
  @parser.request_url.should == '/test?ok=1'
52
- @parser.request_path.should == '/test'
53
- @parser.query_string.should == 'ok=1'
54
- @parser.fragment.should be_empty
55
78
 
56
79
  @parser.headers.should == @headers
57
80
  @parser.headers['User-Agent'].should == 'curl/7.18.0'
@@ -90,9 +113,6 @@ describe HTTP::Parser do
90
113
  @parser.http_version.should == [1,0]
91
114
 
92
115
  @parser.request_url.should == '/'
93
- @parser.request_path.should == '/'
94
- @parser.query_string.should == ''
95
- @parser.fragment.should == ''
96
116
 
97
117
  @parser.reset!.should be_true
98
118
 
@@ -101,9 +121,25 @@ describe HTTP::Parser do
101
121
  @parser.status_code.should be_nil
102
122
 
103
123
  @parser.request_url.should be_nil
104
- @parser.request_path.should be_nil
105
- @parser.query_string.should be_nil
106
- @parser.fragment.should be_nil
124
+ end
125
+
126
+ it "should optionally reset parser state on no-body responses" do
127
+ @parser.reset!.should be_true
128
+
129
+ @head, @complete = 0, 0
130
+ @parser.on_headers_complete = proc {|h| @head += 1; :reset }
131
+ @parser.on_message_complete = proc { @complete += 1 }
132
+ @parser.on_body = proc {|b| fail }
133
+
134
+ head_response = "HTTP/1.1 200 OK\r\nContent-Length:10\r\n\r\n"
135
+
136
+ @parser << head_response
137
+ @head.should == 1
138
+ @complete.should == 1
139
+
140
+ @parser << head_response
141
+ @head.should == 2
142
+ @complete.should == 2
107
143
  end
108
144
 
109
145
  it "should retain callbacks after reset" do
@@ -132,16 +168,57 @@ describe HTTP::Parser do
132
168
  }
133
169
  end
134
170
 
135
- it "should handle multiple headers" do
171
+ it "should handle multiple headers using strings" do
172
+ @parser.header_value_type = :strings
173
+
136
174
  @parser <<
137
175
  "GET / HTTP/1.0\r\n" +
138
- "Header: value 1\r\n" +
139
- "Header: value 2\r\n" +
176
+ "Set-Cookie: PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com\r\n" +
177
+ "Set-Cookie: NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly\r\n" +
140
178
  "\r\n"
141
179
 
142
- @parser.headers.should == {
143
- 'Header' => 'value 1, value 2'
144
- }
180
+ @parser.headers["Set-Cookie"].should == "PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com, NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly"
181
+ end
182
+
183
+ it "should handle multiple headers using strings" do
184
+ @parser.header_value_type = :arrays
185
+
186
+ @parser <<
187
+ "GET / HTTP/1.0\r\n" +
188
+ "Set-Cookie: PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com\r\n" +
189
+ "Set-Cookie: NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly\r\n" +
190
+ "\r\n"
191
+
192
+ @parser.headers["Set-Cookie"].should == [
193
+ "PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com",
194
+ "NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly"
195
+ ]
196
+ end
197
+
198
+ it "should handle multiple headers using mixed" do
199
+ @parser.header_value_type = :mixed
200
+
201
+ @parser <<
202
+ "GET / HTTP/1.0\r\n" +
203
+ "Set-Cookie: PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com\r\n" +
204
+ "Set-Cookie: NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly\r\n" +
205
+ "\r\n"
206
+
207
+ @parser.headers["Set-Cookie"].should == [
208
+ "PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com",
209
+ "NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly"
210
+ ]
211
+ end
212
+
213
+ it "should handle a single cookie using mixed" do
214
+ @parser.header_value_type = :mixed
215
+
216
+ @parser <<
217
+ "GET / HTTP/1.0\r\n" +
218
+ "Set-Cookie: PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com\r\n" +
219
+ "\r\n"
220
+
221
+ @parser.headers["Set-Cookie"].should == "PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com"
145
222
  end
146
223
 
147
224
  it "should support alternative api" do
@@ -172,16 +249,76 @@ describe HTTP::Parser do
172
249
  @done.should be_true
173
250
  end
174
251
 
252
+ it 'sets upgrade_data if available' do
253
+ @parser <<
254
+ "GET /demo HTTP/1.1\r\n" +
255
+ "Connection: Upgrade\r\n" +
256
+ "Upgrade: WebSocket\r\n\r\n" +
257
+ "third key data"
258
+
259
+ @parser.upgrade?.should be_true
260
+ @parser.upgrade_data.should == 'third key data'
261
+ end
262
+
263
+ it 'sets upgrade_data to blank if un-available' do
264
+ @parser <<
265
+ "GET /demo HTTP/1.1\r\n" +
266
+ "Connection: Upgrade\r\n" +
267
+ "Upgrade: WebSocket\r\n\r\n"
268
+
269
+ @parser.upgrade?.should be_true
270
+ @parser.upgrade_data.should == ''
271
+ end
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
+
175
307
  %w[ request response ].each do |type|
176
308
  JSON.parse(File.read(File.expand_path("../support/#{type}s.json", __FILE__))).each do |test|
177
309
  test['headers'] ||= {}
310
+ next unless HTTP::Parser.strict? == test['strict']
178
311
 
179
312
  it "should parse #{type}: #{test['name']}" do
180
313
  @parser << test['raw']
181
314
 
182
- @parser.keep_alive?.should == test['should_keep_alive']
183
- @parser.upgrade?.should == (test['upgrade']==1)
184
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
185
322
 
186
323
  fields = %w[
187
324
  http_major
@@ -191,9 +328,6 @@ describe HTTP::Parser do
191
328
  if test['type'] == 'HTTP_REQUEST'
192
329
  fields += %w[
193
330
  request_url
194
- request_path
195
- query_string
196
- fragment
197
331
  ]
198
332
  else
199
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
  }