jls-http_parser.rb 0.5.3.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.
File without changes
@@ -0,0 +1,27 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "jls-http_parser.rb"
3
+ s.version = "0.5.3.1"
4
+ s.summary = "Simple callback-based HTTP request/response parser"
5
+ s.description = "Ruby bindings to http://github.com/ry/http-parser and http://github.com/a2800276/http-parser.java"
6
+
7
+ s.authors = ["Marc-Andre Cournoyer", "Aman Gupta"]
8
+ s.email = ["macournoyer@gmail.com", "aman@tmm1.net"]
9
+
10
+ s.homepage = "http://github.com/tmm1/http_parser.rb"
11
+ s.files = `git ls-files`.split("\n") + Dir['ext/ruby_http_parser/vendor/**/*']
12
+
13
+ s.require_paths = ["lib"]
14
+ s.extensions = ["ext/ruby_http_parser/extconf.rb"]
15
+
16
+ s.add_development_dependency 'rake-compiler', '>= 0.7.9'
17
+ s.add_development_dependency 'rspec', '>= 2.0.1'
18
+ s.add_development_dependency 'json', '>= 1.4.6'
19
+ s.add_development_dependency 'benchmark_suite'
20
+ s.add_development_dependency 'ffi'
21
+
22
+ if RUBY_PLATFORM =~ /java/
23
+ s.add_development_dependency 'jruby-openssl'
24
+ else
25
+ s.add_development_dependency 'yajl-ruby', '>= 0.8.1'
26
+ end
27
+ end
@@ -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,326 @@
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
+ @parser.headers.should be_nil
21
+
22
+ @parser.http_version.should be_nil
23
+ @parser.http_method.should be_nil
24
+ @parser.status_code.should be_nil
25
+
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
+
31
+ @parser.header_value_type.should == :mixed
32
+ end
33
+
34
+ it "should allow us to set the header value type" do
35
+ [:mixed, :arrays, :strings].each do |type|
36
+ @parser.header_value_type = type
37
+ @parser.header_value_type.should == type
38
+
39
+ parser_tmp = HTTP::Parser.new(nil, type)
40
+ parser_tmp.header_value_type.should == type
41
+ end
42
+ end
43
+
44
+ it "should allow us to set the default header value type" do
45
+ [:mixed, :arrays, :strings].each do |type|
46
+ HTTP::Parser.default_header_value_type = type
47
+
48
+ parser = HTTP::Parser.new
49
+ parser.header_value_type.should == type
50
+ end
51
+ end
52
+
53
+ it "should throw an Argument Error if header value type is invalid" do
54
+ proc{ @parser.header_value_type = 'bob' }.should raise_error(ArgumentError)
55
+ end
56
+
57
+ it "should throw an Argument Error if default header value type is invalid" do
58
+ proc{ HTTP::Parser.default_header_value_type = 'bob' }.should raise_error(ArgumentError)
59
+ end
60
+
61
+ it "should implement basic api" do
62
+ @parser <<
63
+ "GET /test?ok=1 HTTP/1.1\r\n" +
64
+ "User-Agent: curl/7.18.0\r\n" +
65
+ "Host: 0.0.0.0:5000\r\n" +
66
+ "Accept: */*\r\n" +
67
+ "Content-Length: 5\r\n" +
68
+ "\r\n" +
69
+ "World"
70
+
71
+ @started.should be_true
72
+ @done.should be_true
73
+
74
+ @parser.http_major.should == 1
75
+ @parser.http_minor.should == 1
76
+ @parser.http_version.should == [1,1]
77
+ @parser.http_method.should == 'GET'
78
+ @parser.status_code.should be_nil
79
+
80
+ @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
+
85
+ @parser.headers.should == @headers
86
+ @parser.headers['User-Agent'].should == 'curl/7.18.0'
87
+ @parser.headers['Host'].should == '0.0.0.0:5000'
88
+
89
+ @body.should == "World"
90
+ end
91
+
92
+ it "should raise errors on invalid data" do
93
+ proc{ @parser << "BLAH" }.should raise_error(HTTP::Parser::Error)
94
+ end
95
+
96
+ it "should abort parser via callback" do
97
+ @parser.on_headers_complete = proc { |e| @headers = e; :stop }
98
+
99
+ data =
100
+ "GET / HTTP/1.0\r\n" +
101
+ "Content-Length: 5\r\n" +
102
+ "\r\n" +
103
+ "World"
104
+
105
+ bytes = @parser << data
106
+
107
+ bytes.should == 37
108
+ data[bytes..-1].should == 'World'
109
+
110
+ @headers.should == {'Content-Length' => '5'}
111
+ @body.should be_empty
112
+ @done.should be_false
113
+ end
114
+
115
+ it "should reset to initial state" do
116
+ @parser << "GET / HTTP/1.0\r\n\r\n"
117
+
118
+ @parser.http_method.should == 'GET'
119
+ @parser.http_version.should == [1,0]
120
+
121
+ @parser.request_url.should == '/'
122
+ @parser.request_path.should == '/'
123
+ @parser.query_string.should == ''
124
+ @parser.fragment.should == ''
125
+
126
+ @parser.reset!.should be_true
127
+
128
+ @parser.http_version.should be_nil
129
+ @parser.http_method.should be_nil
130
+ @parser.status_code.should be_nil
131
+
132
+ @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
+ end
137
+
138
+ it "should optionally reset parser state on no-body responses" do
139
+ @parser.reset!.should be_true
140
+
141
+ @head, @complete = 0, 0
142
+ @parser.on_headers_complete = proc {|h| @head += 1; :reset }
143
+ @parser.on_message_complete = proc { @complete += 1 }
144
+ @parser.on_body = proc {|b| fail }
145
+
146
+ head_response = "HTTP/1.1 200 OK\r\nContent-Length:10\r\n\r\n"
147
+
148
+ @parser << head_response
149
+ @head.should == 1
150
+ @complete.should == 1
151
+
152
+ @parser << head_response
153
+ @head.should == 2
154
+ @complete.should == 2
155
+ end
156
+
157
+ it "should retain callbacks after reset" do
158
+ @parser.reset!.should be_true
159
+
160
+ @parser << "GET / HTTP/1.0\r\n\r\n"
161
+ @started.should be_true
162
+ @headers.should == {}
163
+ @done.should be_true
164
+ end
165
+
166
+ it "should parse headers incrementally" do
167
+ request =
168
+ "GET / HTTP/1.0\r\n" +
169
+ "Header1: value 1\r\n" +
170
+ "Header2: value 2\r\n" +
171
+ "\r\n"
172
+
173
+ while chunk = request.slice!(0,2) and !chunk.empty?
174
+ @parser << chunk
175
+ end
176
+
177
+ @parser.headers.should == {
178
+ 'Header1' => 'value 1',
179
+ 'Header2' => 'value 2'
180
+ }
181
+ end
182
+
183
+ it "should handle multiple headers using strings" do
184
+ @parser.header_value_type = :strings
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 == "PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com, NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly"
193
+ end
194
+
195
+ it "should handle multiple headers using strings" do
196
+ @parser.header_value_type = :arrays
197
+
198
+ @parser <<
199
+ "GET / HTTP/1.0\r\n" +
200
+ "Set-Cookie: PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com\r\n" +
201
+ "Set-Cookie: NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly\r\n" +
202
+ "\r\n"
203
+
204
+ @parser.headers["Set-Cookie"].should == [
205
+ "PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com",
206
+ "NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly"
207
+ ]
208
+ end
209
+
210
+ it "should handle multiple headers using mixed" do
211
+ @parser.header_value_type = :mixed
212
+
213
+ @parser <<
214
+ "GET / HTTP/1.0\r\n" +
215
+ "Set-Cookie: PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com\r\n" +
216
+ "Set-Cookie: NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly\r\n" +
217
+ "\r\n"
218
+
219
+ @parser.headers["Set-Cookie"].should == [
220
+ "PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com",
221
+ "NID=46jSHxPM; path=/; domain=.bob.com; HttpOnly"
222
+ ]
223
+ end
224
+
225
+ it "should handle a single cookie using mixed" do
226
+ @parser.header_value_type = :mixed
227
+
228
+ @parser <<
229
+ "GET / HTTP/1.0\r\n" +
230
+ "Set-Cookie: PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com\r\n" +
231
+ "\r\n"
232
+
233
+ @parser.headers["Set-Cookie"].should == "PREF=ID=a7d2c98; expires=Fri, 05-Apr-2013 05:00:45 GMT; path=/; domain=.bob.com"
234
+ end
235
+
236
+ it "should support alternative api" do
237
+ callbacks = double('callbacks')
238
+ callbacks.stub(:on_message_begin){ @started = true }
239
+ callbacks.stub(:on_headers_complete){ |e| @headers = e }
240
+ callbacks.stub(:on_body){ |chunk| @body << chunk }
241
+ callbacks.stub(:on_message_complete){ @done = true }
242
+
243
+ @parser = HTTP::Parser.new(callbacks)
244
+ @parser << "GET / HTTP/1.0\r\n\r\n"
245
+
246
+ @started.should be_true
247
+ @headers.should == {}
248
+ @body.should == ''
249
+ @done.should be_true
250
+ end
251
+
252
+ it "should ignore extra content beyond specified length" do
253
+ @parser <<
254
+ "GET / HTTP/1.0\r\n" +
255
+ "Content-Length: 5\r\n" +
256
+ "\r\n" +
257
+ "hello" +
258
+ " \n"
259
+
260
+ @body.should == 'hello'
261
+ @done.should be_true
262
+ end
263
+
264
+ it 'sets upgrade_data if available' do
265
+ @parser <<
266
+ "GET /demo HTTP/1.1\r\n" +
267
+ "Connection: Upgrade\r\n" +
268
+ "Upgrade: WebSocket\r\n\r\n" +
269
+ "third key data"
270
+
271
+ @parser.upgrade?.should be_true
272
+ @parser.upgrade_data.should == 'third key data'
273
+ end
274
+
275
+ it 'sets upgrade_data to blank if un-available' do
276
+ @parser <<
277
+ "GET /demo HTTP/1.1\r\n" +
278
+ "Connection: Upgrade\r\n" +
279
+ "Upgrade: WebSocket\r\n\r\n"
280
+
281
+ @parser.upgrade?.should be_true
282
+ @parser.upgrade_data.should == ''
283
+ end
284
+
285
+ %w[ request response ].each do |type|
286
+ JSON.parse(File.read(File.expand_path("../support/#{type}s.json", __FILE__))).each do |test|
287
+ test['headers'] ||= {}
288
+
289
+ it "should parse #{type}: #{test['name']}" do
290
+ @parser << test['raw']
291
+
292
+ @parser.keep_alive?.should == test['should_keep_alive']
293
+ @parser.upgrade?.should == (test['upgrade']==1)
294
+ @parser.http_method.should == test['method']
295
+
296
+ fields = %w[
297
+ http_major
298
+ http_minor
299
+ ]
300
+
301
+ if test['type'] == 'HTTP_REQUEST'
302
+ fields += %w[
303
+ request_url
304
+ request_path
305
+ query_string
306
+ fragment
307
+ ]
308
+ else
309
+ fields += %w[
310
+ status_code
311
+ ]
312
+ end
313
+
314
+ fields.each do |field|
315
+ @parser.send(field).should == test[field]
316
+ end
317
+
318
+ @headers.size.should == test['num_headers']
319
+ @headers.should == test['headers']
320
+
321
+ @body.should == test['body']
322
+ @body.size.should == test['body_size'] if test['body_size']
323
+ end
324
+ end
325
+ end
326
+ end
@@ -0,0 +1 @@
1
+ require "http_parser"
@@ -0,0 +1,400 @@
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
+ },
23
+ {
24
+ "name": "firefox get",
25
+ "type": "HTTP_REQUEST",
26
+ "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",
27
+ "should_keep_alive": true,
28
+ "message_complete_on_eof": false,
29
+ "http_major": 1,
30
+ "http_minor": 1,
31
+ "method": "GET",
32
+ "query_string": "",
33
+ "fragment": "",
34
+ "request_path": "/favicon.ico",
35
+ "request_url": "/favicon.ico",
36
+ "num_headers": 8,
37
+ "headers": {
38
+ "Host": "0.0.0.0=5000",
39
+ "User-Agent": "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0",
40
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
41
+ "Accept-Language": "en-us,en;q=0.5",
42
+ "Accept-Encoding": "gzip,deflate",
43
+ "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
44
+ "Keep-Alive": "300",
45
+ "Connection": "keep-alive"
46
+ },
47
+ "body": ""
48
+ },
49
+ {
50
+ "name": "dumbfuck",
51
+ "type": "HTTP_REQUEST",
52
+ "raw": "GET /dumbfuck HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n",
53
+ "should_keep_alive": true,
54
+ "message_complete_on_eof": false,
55
+ "http_major": 1,
56
+ "http_minor": 1,
57
+ "method": "GET",
58
+ "query_string": "",
59
+ "fragment": "",
60
+ "request_path": "/dumbfuck",
61
+ "request_url": "/dumbfuck",
62
+ "num_headers": 1,
63
+ "headers": {
64
+ "aaaaaaaaaaaaa": "++++++++++"
65
+ },
66
+ "body": ""
67
+ },
68
+ {
69
+ "name": "fragment in url",
70
+ "type": "HTTP_REQUEST",
71
+ "raw": "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n",
72
+ "should_keep_alive": true,
73
+ "message_complete_on_eof": false,
74
+ "http_major": 1,
75
+ "http_minor": 1,
76
+ "method": "GET",
77
+ "query_string": "page=1",
78
+ "fragment": "posts-17408",
79
+ "request_path": "/forums/1/topics/2375",
80
+ "request_url": "/forums/1/topics/2375?page=1#posts-17408",
81
+ "num_headers": 0,
82
+ "body": ""
83
+ },
84
+ {
85
+ "name": "get no headers no body",
86
+ "type": "HTTP_REQUEST",
87
+ "raw": "GET /get_no_headers_no_body/world HTTP/1.1\r\n\r\n",
88
+ "should_keep_alive": true,
89
+ "message_complete_on_eof": false,
90
+ "http_major": 1,
91
+ "http_minor": 1,
92
+ "method": "GET",
93
+ "query_string": "",
94
+ "fragment": "",
95
+ "request_path": "/get_no_headers_no_body/world",
96
+ "request_url": "/get_no_headers_no_body/world",
97
+ "num_headers": 0,
98
+ "body": ""
99
+ },
100
+ {
101
+ "name": "get one header no body",
102
+ "type": "HTTP_REQUEST",
103
+ "raw": "GET /get_one_header_no_body HTTP/1.1\r\nAccept: */*\r\n\r\n",
104
+ "should_keep_alive": true,
105
+ "message_complete_on_eof": false,
106
+ "http_major": 1,
107
+ "http_minor": 1,
108
+ "method": "GET",
109
+ "query_string": "",
110
+ "fragment": "",
111
+ "request_path": "/get_one_header_no_body",
112
+ "request_url": "/get_one_header_no_body",
113
+ "num_headers": 1,
114
+ "headers": {
115
+ "Accept": "*/*"
116
+ },
117
+ "body": ""
118
+ },
119
+ {
120
+ "name": "get funky content length body hello",
121
+ "type": "HTTP_REQUEST",
122
+ "raw": "GET /get_funky_content_length_body_hello HTTP/1.0\r\nconTENT-Length: 5\r\n\r\nHELLO",
123
+ "should_keep_alive": false,
124
+ "message_complete_on_eof": false,
125
+ "http_major": 1,
126
+ "http_minor": 0,
127
+ "method": "GET",
128
+ "query_string": "",
129
+ "fragment": "",
130
+ "request_path": "/get_funky_content_length_body_hello",
131
+ "request_url": "/get_funky_content_length_body_hello",
132
+ "num_headers": 1,
133
+ "headers": {
134
+ "conTENT-Length": "5"
135
+ },
136
+ "body": "HELLO"
137
+ },
138
+ {
139
+ "name": "post identity body world",
140
+ "type": "HTTP_REQUEST",
141
+ "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",
142
+ "should_keep_alive": true,
143
+ "message_complete_on_eof": false,
144
+ "http_major": 1,
145
+ "http_minor": 1,
146
+ "method": "POST",
147
+ "query_string": "q=search",
148
+ "fragment": "hey",
149
+ "request_path": "/post_identity_body_world",
150
+ "request_url": "/post_identity_body_world?q=search#hey",
151
+ "num_headers": 3,
152
+ "headers": {
153
+ "Accept": "*/*",
154
+ "Transfer-Encoding": "identity",
155
+ "Content-Length": "5"
156
+ },
157
+ "body": "World"
158
+ },
159
+ {
160
+ "name": "post - chunked body: all your base are belong to us",
161
+ "type": "HTTP_REQUEST",
162
+ "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",
163
+ "should_keep_alive": true,
164
+ "message_complete_on_eof": false,
165
+ "http_major": 1,
166
+ "http_minor": 1,
167
+ "method": "POST",
168
+ "query_string": "",
169
+ "fragment": "",
170
+ "request_path": "/post_chunked_all_your_base",
171
+ "request_url": "/post_chunked_all_your_base",
172
+ "num_headers": 1,
173
+ "headers": {
174
+ "Transfer-Encoding": "chunked"
175
+ },
176
+ "body": "all your base are belong to us"
177
+ },
178
+ {
179
+ "name": "two chunks ; triple zero ending",
180
+ "type": "HTTP_REQUEST",
181
+ "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",
182
+ "should_keep_alive": true,
183
+ "message_complete_on_eof": false,
184
+ "http_major": 1,
185
+ "http_minor": 1,
186
+ "method": "POST",
187
+ "query_string": "",
188
+ "fragment": "",
189
+ "request_path": "/two_chunks_mult_zero_end",
190
+ "request_url": "/two_chunks_mult_zero_end",
191
+ "num_headers": 1,
192
+ "headers": {
193
+ "Transfer-Encoding": "chunked"
194
+ },
195
+ "body": "hello world"
196
+ },
197
+ {
198
+ "name": "chunked with trailing headers. blech.",
199
+ "type": "HTTP_REQUEST",
200
+ "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",
201
+ "should_keep_alive": true,
202
+ "message_complete_on_eof": false,
203
+ "http_major": 1,
204
+ "http_minor": 1,
205
+ "method": "POST",
206
+ "query_string": "",
207
+ "fragment": "",
208
+ "request_path": "/chunked_w_trailing_headers",
209
+ "request_url": "/chunked_w_trailing_headers",
210
+ "num_headers": 3,
211
+ "headers": {
212
+ "Transfer-Encoding": "chunked",
213
+ "Vary": "*",
214
+ "Content-Type": "text/plain"
215
+ },
216
+ "body": "hello world"
217
+ },
218
+ {
219
+ "name": "with bullshit after the length",
220
+ "type": "HTTP_REQUEST",
221
+ "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",
222
+ "should_keep_alive": true,
223
+ "message_complete_on_eof": false,
224
+ "http_major": 1,
225
+ "http_minor": 1,
226
+ "method": "POST",
227
+ "query_string": "",
228
+ "fragment": "",
229
+ "request_path": "/chunked_w_bullshit_after_length",
230
+ "request_url": "/chunked_w_bullshit_after_length",
231
+ "num_headers": 1,
232
+ "headers": {
233
+ "Transfer-Encoding": "chunked"
234
+ },
235
+ "body": "hello world"
236
+ },
237
+ {
238
+ "name": "with quotes",
239
+ "type": "HTTP_REQUEST",
240
+ "raw": "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n",
241
+ "should_keep_alive": true,
242
+ "message_complete_on_eof": false,
243
+ "http_major": 1,
244
+ "http_minor": 1,
245
+ "method": "GET",
246
+ "query_string": "foo=\"bar\"",
247
+ "fragment": "",
248
+ "request_path": "/with_\"stupid\"_quotes",
249
+ "request_url": "/with_\"stupid\"_quotes?foo=\"bar\"",
250
+ "num_headers": 0,
251
+ "headers": {
252
+
253
+ },
254
+ "body": ""
255
+ },
256
+ {
257
+ "name": "apachebench get",
258
+ "type": "HTTP_REQUEST",
259
+ "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",
260
+ "should_keep_alive": false,
261
+ "message_complete_on_eof": false,
262
+ "http_major": 1,
263
+ "http_minor": 0,
264
+ "method": "GET",
265
+ "query_string": "",
266
+ "fragment": "",
267
+ "request_path": "/test",
268
+ "request_url": "/test",
269
+ "num_headers": 3,
270
+ "headers": {
271
+ "Host": "0.0.0.0:5000",
272
+ "User-Agent": "ApacheBench/2.3",
273
+ "Accept": "*/*"
274
+ },
275
+ "body": ""
276
+ },
277
+ {
278
+ "name": "query url with question mark",
279
+ "type": "HTTP_REQUEST",
280
+ "raw": "GET /test.cgi?foo=bar?baz HTTP/1.1\r\n\r\n",
281
+ "should_keep_alive": true,
282
+ "message_complete_on_eof": false,
283
+ "http_major": 1,
284
+ "http_minor": 1,
285
+ "method": "GET",
286
+ "query_string": "foo=bar?baz",
287
+ "fragment": "",
288
+ "request_path": "/test.cgi",
289
+ "request_url": "/test.cgi?foo=bar?baz",
290
+ "num_headers": 0,
291
+ "headers": {
292
+
293
+ },
294
+ "body": ""
295
+ },
296
+ {
297
+ "name": "newline prefix get",
298
+ "type": "HTTP_REQUEST",
299
+ "raw": "\r\nGET /test HTTP/1.1\r\n\r\n",
300
+ "should_keep_alive": true,
301
+ "message_complete_on_eof": false,
302
+ "http_major": 1,
303
+ "http_minor": 1,
304
+ "method": "GET",
305
+ "query_string": "",
306
+ "fragment": "",
307
+ "request_path": "/test",
308
+ "request_url": "/test",
309
+ "num_headers": 0,
310
+ "headers": {
311
+
312
+ },
313
+ "body": ""
314
+ },
315
+ {
316
+ "name": "upgrade request",
317
+ "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",
319
+ "should_keep_alive": true,
320
+ "message_complete_on_eof": false,
321
+ "http_major": 1,
322
+ "http_minor": 1,
323
+ "method": "GET",
324
+ "query_string": "",
325
+ "fragment": "",
326
+ "request_path": "/demo",
327
+ "request_url": "/demo",
328
+ "num_headers": 7,
329
+ "upgrade": 1,
330
+ "headers": {
331
+ "Host": "example.com",
332
+ "Connection": "Upgrade",
333
+ "Sec-WebSocket-Key2": "12998 5 Y3 1 .P00",
334
+ "Sec-WebSocket-Protocol": "sample",
335
+ "Upgrade": "WebSocket",
336
+ "Sec-WebSocket-Key1": "4 @1 46546xW%0l 1 5",
337
+ "Origin": "http://example.com"
338
+ },
339
+ "body": ""
340
+ },
341
+ {
342
+ "name": "connect request",
343
+ "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",
345
+ "should_keep_alive": false,
346
+ "message_complete_on_eof": false,
347
+ "http_major": 1,
348
+ "http_minor": 0,
349
+ "method": "CONNECT",
350
+ "query_string": "",
351
+ "fragment": "",
352
+ "request_path": "",
353
+ "request_url": "home0.netscape.com:443",
354
+ "num_headers": 2,
355
+ "upgrade": 1,
356
+ "headers": {
357
+ "User-agent": "Mozilla/1.1N",
358
+ "Proxy-authorization": "basic aGVsbG86d29ybGQ="
359
+ },
360
+ "body": ""
361
+ },
362
+ {
363
+ "name": "report request",
364
+ "type": "HTTP_REQUEST",
365
+ "raw": "REPORT /test HTTP/1.1\r\n\r\n",
366
+ "should_keep_alive": true,
367
+ "message_complete_on_eof": false,
368
+ "http_major": 1,
369
+ "http_minor": 1,
370
+ "method": "REPORT",
371
+ "query_string": "",
372
+ "fragment": "",
373
+ "request_path": "/test",
374
+ "request_url": "/test",
375
+ "num_headers": 0,
376
+ "headers": {
377
+
378
+ },
379
+ "body": ""
380
+ },
381
+ {
382
+ "name": "request with no http version",
383
+ "type": "HTTP_REQUEST",
384
+ "raw": "GET /\r\n\r\n",
385
+ "should_keep_alive": false,
386
+ "message_complete_on_eof": false,
387
+ "http_major": 0,
388
+ "http_minor": 9,
389
+ "method": "GET",
390
+ "query_string": "",
391
+ "fragment": "",
392
+ "request_path": "/",
393
+ "request_url": "/",
394
+ "num_headers": 0,
395
+ "headers": {
396
+
397
+ },
398
+ "body": ""
399
+ }
400
+ ]