http_parser.rb 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.gitignore +11 -0
  2. data/.gitmodules +6 -0
  3. data/README.md +45 -0
  4. data/Rakefile +6 -0
  5. data/bench/thin.rb +57 -0
  6. data/ext/ruby_http_parser/.gitignore +1 -0
  7. data/ext/ruby_http_parser/RubyHttpParserService.java +18 -0
  8. data/ext/ruby_http_parser/ext_help.h +18 -0
  9. data/ext/ruby_http_parser/extconf.rb +16 -0
  10. data/ext/ruby_http_parser/org/ruby_http_parser/RubyHttpParser.java +403 -0
  11. data/ext/ruby_http_parser/ruby_http_parser.c +474 -0
  12. data/ext/ruby_http_parser/vendor/.gitkeep +0 -0
  13. data/ext/ruby_http_parser/vendor/http-parser-java/CONTRIBUTIONS +4 -0
  14. data/ext/ruby_http_parser/vendor/http-parser-java/LICENSE-MIT +19 -0
  15. data/ext/ruby_http_parser/vendor/http-parser-java/README.md +171 -0
  16. data/ext/ruby_http_parser/vendor/http-parser-java/TODO +19 -0
  17. data/ext/ruby_http_parser/vendor/http-parser-java/compile +1 -0
  18. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.c +1590 -0
  19. data/ext/ruby_http_parser/vendor/http-parser-java/http_parser.h +167 -0
  20. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPException.java +7 -0
  21. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPMethod.java +90 -0
  22. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/HTTPParser.java +31 -0
  23. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/ParserType.java +13 -0
  24. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPCallback.java +5 -0
  25. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPDataCallback.java +25 -0
  26. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPErrorCallback.java +7 -0
  27. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/HTTPParser.java +1894 -0
  28. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/ParserSettings.java +78 -0
  29. data/ext/ruby_http_parser/vendor/http-parser-java/src/impl/http_parser/lolevel/Util.java +112 -0
  30. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/TestLoaderNG.java +487 -0
  31. data/ext/ruby_http_parser/vendor/http-parser-java/src/test/http_parser/lolevel/UnitTest.java +115 -0
  32. data/ext/ruby_http_parser/vendor/http-parser-java/test.c +1865 -0
  33. data/ext/ruby_http_parser/vendor/http-parser-java/test_permutations +1 -0
  34. data/ext/ruby_http_parser/vendor/http-parser-java/test_unit +1 -0
  35. data/ext/ruby_http_parser/vendor/http-parser-java/tests.dumped +539 -0
  36. data/ext/ruby_http_parser/vendor/http-parser-java/tools/byte_constants.rb +6 -0
  37. data/ext/ruby_http_parser/vendor/http-parser-java/tools/const_char.rb +13 -0
  38. data/ext/ruby_http_parser/vendor/http-parser-java/tools/lowcase.rb +15 -0
  39. data/ext/ruby_http_parser/vendor/http-parser-java/tools/parse_tests.rb +33 -0
  40. data/ext/ruby_http_parser/vendor/http-parser/CONTRIBUTIONS +4 -0
  41. data/ext/ruby_http_parser/vendor/http-parser/LICENSE-MIT +19 -0
  42. data/ext/ruby_http_parser/vendor/http-parser/README.md +171 -0
  43. data/ext/ruby_http_parser/vendor/http-parser/http_parser.c +1590 -0
  44. data/ext/ruby_http_parser/vendor/http-parser/http_parser.h +167 -0
  45. data/ext/ruby_http_parser/vendor/http-parser/test.c +1755 -0
  46. data/http_parser.rb.gemspec +15 -0
  47. data/lib/http/parser.rb +1 -0
  48. data/lib/http_parser.rb +4 -0
  49. data/spec/parser_spec.rb +187 -0
  50. data/spec/spec_helper.rb +2 -0
  51. data/spec/support/requests.json +381 -0
  52. data/spec/support/responses.json +186 -0
  53. data/tasks/compile.rake +39 -0
  54. data/tasks/spec.rake +5 -0
  55. data/tasks/submodules.rake +7 -0
  56. metadata +121 -0
@@ -0,0 +1,15 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "http_parser.rb"
3
+ s.version = "0.5.0"
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
+ end
@@ -0,0 +1 @@
1
+ require 'http_parser'
@@ -0,0 +1,4 @@
1
+ $:.unshift File.expand_path('../', __FILE__)
2
+ require 'ruby_http_parser'
3
+
4
+ Http = HTTP
@@ -0,0 +1,187 @@
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
+ end
31
+
32
+ it "should implement basic api" do
33
+ @parser <<
34
+ "GET /test?ok=1 HTTP/1.1\r\n" +
35
+ "User-Agent: curl/7.18.0\r\n" +
36
+ "Host: 0.0.0.0:5000\r\n" +
37
+ "Accept: */*\r\n" +
38
+ "Content-Length: 5\r\n" +
39
+ "\r\n" +
40
+ "World"
41
+
42
+ @started.should be_true
43
+ @done.should be_true
44
+
45
+ @parser.http_major.should == 1
46
+ @parser.http_minor.should == 1
47
+ @parser.http_version.should == [1,1]
48
+ @parser.http_method.should == 'GET'
49
+ @parser.status_code.should be_nil
50
+
51
+ @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
+
56
+ @parser.headers.should == @headers
57
+ @parser.headers['User-Agent'].should == 'curl/7.18.0'
58
+ @parser.headers['Host'].should == '0.0.0.0:5000'
59
+
60
+ @body.should == "World"
61
+ end
62
+
63
+ it "should raise errors on invalid data" do
64
+ proc{ @parser << "BLAH" }.should raise_error(HTTP::Parser::Error)
65
+ end
66
+
67
+ it "should abort parser via callback" do
68
+ @parser.on_headers_complete = proc { |e| @headers = e; :stop }
69
+
70
+ data =
71
+ "GET / HTTP/1.0\r\n" +
72
+ "Content-Length: 5\r\n" +
73
+ "\r\n" +
74
+ "World"
75
+
76
+ bytes = @parser << data
77
+
78
+ bytes.should == 37
79
+ data[bytes..-1].should == 'World'
80
+
81
+ @headers.should == {'Content-Length' => '5'}
82
+ @body.should be_empty
83
+ @done.should be_false
84
+ end
85
+
86
+ it "should reset to initial state" do
87
+ @parser << "GET / HTTP/1.0\r\n\r\n"
88
+
89
+ @parser.http_method.should == 'GET'
90
+ @parser.http_version.should == [1,0]
91
+
92
+ @parser.request_url.should == '/'
93
+ @parser.request_path.should == '/'
94
+ @parser.query_string.should == ''
95
+ @parser.fragment.should == ''
96
+
97
+ @parser.reset!.should be_true
98
+
99
+ @parser.http_version.should be_nil
100
+ @parser.http_method.should be_nil
101
+ @parser.status_code.should be_nil
102
+
103
+ @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
107
+ end
108
+
109
+ it "should retain callbacks after reset" do
110
+ @parser.reset!.should be_true
111
+
112
+ @parser << "GET / HTTP/1.0\r\n\r\n"
113
+ @started.should be_true
114
+ @headers.should == {}
115
+ @done.should be_true
116
+ end
117
+
118
+ it "should handle multiple headers" do
119
+ @parser <<
120
+ "GET / HTTP/1.0\r\n" +
121
+ "Header: value 1\r\n" +
122
+ "Header: value 2\r\n" +
123
+ "\r\n"
124
+
125
+ @parser.headers.should == {
126
+ 'Header' => 'value 1, value 2'
127
+ }
128
+ end
129
+
130
+ it "should support alternative api" do
131
+ callbacks = double('callbacks')
132
+ callbacks.stub(:on_message_begin){ @started = true }
133
+ callbacks.stub(:on_headers_complete){ |e| @headers = e }
134
+ callbacks.stub(:on_body){ |chunk| @body << chunk }
135
+ callbacks.stub(:on_message_complete){ @done = true }
136
+
137
+ @parser = HTTP::Parser.new(callbacks)
138
+ @parser << "GET / HTTP/1.0\r\n\r\n"
139
+
140
+ @started.should be_true
141
+ @headers.should == {}
142
+ @body.should == ''
143
+ @done.should be_true
144
+ end
145
+
146
+ %w[ request response ].each do |type|
147
+ JSON.parse(File.read(File.expand_path("../support/#{type}s.json", __FILE__))).each do |test|
148
+ test['headers'] ||= {}
149
+
150
+ it "should parse #{type}: #{test['name']}" do
151
+ @parser << test['raw']
152
+
153
+ @parser.keep_alive?.should == test['should_keep_alive']
154
+ @parser.upgrade?.should == (test['upgrade']==1)
155
+ @parser.http_method.should == test['method']
156
+
157
+ fields = %w[
158
+ http_major
159
+ http_minor
160
+ ]
161
+
162
+ if test['type'] == 'HTTP_REQUEST'
163
+ fields += %w[
164
+ request_url
165
+ request_path
166
+ query_string
167
+ fragment
168
+ ]
169
+ else
170
+ fields += %w[
171
+ status_code
172
+ ]
173
+ end
174
+
175
+ fields.each do |field|
176
+ @parser.send(field).should == test[field]
177
+ end
178
+
179
+ @headers.size.should == test['num_headers']
180
+ @headers.should == test['headers']
181
+
182
+ @body.should == test['body']
183
+ @body.size.should == test['body_size'] if test['body_size']
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,2 @@
1
+ require "rubygems"
2
+ require "http_parser"
@@ -0,0 +1,381 @@
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 home.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": "home.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
+ ]