jls-http_parser.rb 0.5.3.1

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