http_tools 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,12 @@
1
+ base = File.expand_path(File.dirname(__FILE__) + '/../../lib')
2
+ require base + '/http_tools'
3
+ require 'rubygems'
4
+ require 'ruby-prof'
5
+
6
+ request = "GET / HTTP/1.1\r\nHost: example.com\r\nUser-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-gb) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16\r\nAccept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\nAccept-Language: en-gb\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n\r\n"
7
+ parser = HTTPTools::Parser.new
8
+
9
+ result = RubyProf.profile do
10
+ parser << request
11
+ end
12
+ RubyProf::FlatPrinter.new(result).print(STDOUT, 0)
@@ -0,0 +1,12 @@
1
+ base = File.expand_path(File.dirname(__FILE__) + '/../../lib')
2
+ require base + '/http_tools'
3
+ require 'rubygems'
4
+ require 'ruby-prof'
5
+
6
+ response = "HTTP/1.1 200 OK\r\nServer: Apache/2.2.3 (CentOS)\r\nLast-Modified: Thu, 03 Jun 2010 17:40:12 GMT\r\nETag: \"4d2c-23e-48823b2cf3700\"\r\nAccept-Ranges: bytes\r\nContent-Type: text/html; charset=UTF-8\r\nConnection: Keep-Alive\r\nDate: Wed, 21 Jul 2010 16:26:04 GMT\r\nAge: 7985 \r\nContent-Length: 574\r\n\r\n<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n<HTML>\r\n<HEAD>\r\n <META http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\r\n <TITLE>Example Web Page</TITLE>\r\n</HEAD> \r\n<body> \r\n<p>You have reached this web page by typing &quot;example.com&quot;,\r\n&quot;example.net&quot;,\r\n or &quot;example.org&quot; into your web browser.</p>\r\n<p>These domain names are reserved for use in documentation and are not available \r\n for registration. See <a href=\"http://www.rfc-editor.org/rfc/rfc2606.txt\">RFC \r\n 2606</a>, Section 3.</p>\r\n</BODY>\r\n</HTML>\r\n\r\n"
7
+ parser = HTTPTools::Parser.new
8
+
9
+ result = RubyProf.profile do
10
+ parser << response
11
+ end
12
+ RubyProf::FlatPrinter.new(result).print(STDOUT, 0)
@@ -0,0 +1,26 @@
1
+ base = File.expand_path(File.dirname(__FILE__) + '/../../lib')
2
+ require base + '/http_tools'
3
+ require 'test/unit'
4
+ require 'uri'
5
+
6
+ class RequestTest < Test::Unit::TestCase
7
+
8
+ def test_get
9
+ result = HTTPTools::Builder.request(:get, "www.example.com", "/test")
10
+
11
+ assert_equal("GET /test HTTP/1.1\r\nHost: www.example.com\r\n\r\n", result)
12
+ end
13
+
14
+ def test_post
15
+ result = HTTPTools::Builder.request(:post, "www.test.com", "/example")
16
+
17
+ assert_equal("POST /example HTTP/1.1\r\nHost: www.test.com\r\n\r\n", result)
18
+ end
19
+
20
+ def test_headers
21
+ result = HTTPTools::Builder.request(:get, "www.foobar.com", "/", "x-test" => "foo")
22
+
23
+ assert_equal("GET / HTTP/1.1\r\nHost: www.foobar.com\r\nx-test: foo\r\n\r\n", result)
24
+ end
25
+
26
+ end
@@ -0,0 +1,32 @@
1
+ base = File.expand_path(File.dirname(__FILE__) + '/../../lib')
2
+ require base + '/http_tools'
3
+ require 'test/unit'
4
+
5
+ class ResponseTest < Test::Unit::TestCase
6
+
7
+ def test_status_ok
8
+ result = HTTPTools::Builder.response(:ok)
9
+
10
+ assert_equal("HTTP/1.1 200 OK\r\n\r\n", result)
11
+ end
12
+
13
+ def test_status_not_found
14
+ result = HTTPTools::Builder.response(:not_found)
15
+
16
+ assert_equal("HTTP/1.1 404 Not Found\r\n\r\n", result)
17
+ end
18
+
19
+ def test_status_with_code
20
+ result = HTTPTools::Builder.response(500)
21
+
22
+ assert_equal("HTTP/1.1 500 Internal Server Error\r\n\r\n", result)
23
+ end
24
+
25
+ def test_headers
26
+ result = HTTPTools::Builder.response(:ok, "Content-Type" => "text/html", "Content-Length" => 1024)
27
+
28
+ expected = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 1024\r\n\r\n"
29
+ assert_equal(expected, result)
30
+ end
31
+
32
+ end
data/test/cover.rb ADDED
@@ -0,0 +1,28 @@
1
+ #!/opt/local/bin/ruby1.9
2
+
3
+ require 'coverage' # >= ruby 1.9 only
4
+
5
+ at_exit do
6
+ testing = Dir["../lib/**/*.rb"].map(&File.method(:expand_path))
7
+
8
+ results = Coverage.result.select {|key, value| testing.include?(key)}
9
+
10
+ puts
11
+ total = results.map(&:last).flatten.compact
12
+ puts "#{total.select {|i| i > 0}.length}/#{total.length} executable lines covered"
13
+ puts
14
+
15
+ results.each do |key, value|
16
+ next unless value.include?(0)
17
+ puts key
18
+ puts
19
+ File.readlines(key).zip(value).each_with_index do |(line, value), i|
20
+ print "%3i %3s %s" % [(i + 1), value, line]
21
+ end
22
+ puts
23
+ puts
24
+ end
25
+ end
26
+
27
+ Coverage.start
28
+ Dir["**/*_test.rb"].each {|test| require test}
@@ -0,0 +1,141 @@
1
+ base = File.expand_path(File.dirname(__FILE__) + '/../../lib')
2
+ require base + '/http_tools'
3
+ require 'test/unit'
4
+
5
+ class TransferEncodingChunkedTest < Test::Unit::TestCase
6
+
7
+ def test_decode_1_chunk
8
+ encoded = "14\r\n<h1>Hello world</h1>\r\n0\r\n"
9
+ decoded = HTTPTools::Encoding.transfer_encoding_chunked_decode(encoded)
10
+
11
+ assert_equal(["<h1>Hello world</h1>", nil], decoded)
12
+ assert_equal("14\r\n<h1>Hello world</h1>\r\n0\r\n", encoded)
13
+ end
14
+
15
+ def test_decode_2_chunks
16
+ encoded = "14\r\n<h1>Hello world</h1>\r\n12\r\n<p>Lorem ipsum</p>\r\n0\r\n"
17
+ decoded = HTTPTools::Encoding.transfer_encoding_chunked_decode(encoded)
18
+
19
+ assert_equal(["<h1>Hello world</h1><p>Lorem ipsum</p>", nil], decoded)
20
+ end
21
+
22
+ def test_decode_incomplete
23
+ encoded = "14\r\n<h1>Hello world</h1>\r\n12\r\n<p>Lorem ipsum"
24
+ decoded = HTTPTools::Encoding.transfer_encoding_chunked_decode(encoded)
25
+
26
+ assert_equal(["<h1>Hello world</h1>", "12\r\n<p>Lorem ipsum"], decoded)
27
+ end
28
+
29
+ def test_decode_line_by_line
30
+ part1 = "14\r\n"
31
+ part2 = "<h1>Hello world</h1>\r\n"
32
+ part3 = "12\r\n"
33
+ part4 = "<p>Lorem ipsum</p>\r\n"
34
+ part5 = "0\r\n"
35
+
36
+ result = HTTPTools::Encoding.transfer_encoding_chunked_decode(part1)
37
+ assert_equal([nil, "14\r\n"], result)
38
+ decoded, remainder = result
39
+ decoded ||= ""
40
+
41
+ res = HTTPTools::Encoding.transfer_encoding_chunked_decode(remainder+part2)
42
+ assert_equal(["<h1>Hello world</h1>", ""], res)
43
+ part, remainder = res
44
+ decoded += part if part
45
+
46
+ res = HTTPTools::Encoding.transfer_encoding_chunked_decode(remainder+part3)
47
+ assert_equal([nil, "12\r\n"], res)
48
+ part, remainder = res
49
+ decoded += part if part
50
+
51
+ res = HTTPTools::Encoding.transfer_encoding_chunked_decode(remainder+part4)
52
+ assert_equal(["<p>Lorem ipsum</p>", ""], res)
53
+ part, remainder = res
54
+ decoded += part if part
55
+
56
+ res = HTTPTools::Encoding.transfer_encoding_chunked_decode(remainder+part5)
57
+ assert_equal([nil, nil], res)
58
+ part, remainder = res
59
+ decoded += part if part
60
+
61
+ assert_equal("<h1>Hello world</h1><p>Lorem ipsum</p>", decoded)
62
+ assert_nil(remainder)
63
+ end
64
+
65
+ def test_decode_broken_between_lines
66
+ part1 = "14\r\n<h1>Hello"
67
+ part2 = " world</h1>\r\nf"
68
+ part3 = "\r\n<p>Lorem ipsum "
69
+ part4 = "\r\n12\r\n"
70
+ part5 = "dolor sit amet</p"
71
+ part6 = ">\r\n0\r\n"
72
+
73
+ result = HTTPTools::Encoding.transfer_encoding_chunked_decode(part1)
74
+ assert_equal([nil, "14\r\n<h1>Hello"], result)
75
+ decoded, remainder = result
76
+ decoded ||= ""
77
+
78
+ res = HTTPTools::Encoding.transfer_encoding_chunked_decode(remainder+part2)
79
+ assert_equal(["<h1>Hello world</h1>", "f"], res)
80
+ part, remainder = res
81
+ decoded += part if part
82
+
83
+ res = HTTPTools::Encoding.transfer_encoding_chunked_decode(remainder+part3)
84
+ assert_equal([nil, "f\r\n<p>Lorem ipsum "], res)
85
+ part, remainder = res
86
+ decoded += part if part
87
+
88
+ res = HTTPTools::Encoding.transfer_encoding_chunked_decode(remainder+part4)
89
+ assert_equal(["<p>Lorem ipsum ", "12\r\n"], res)
90
+ part, remainder = res
91
+ decoded += part if part
92
+
93
+ res = HTTPTools::Encoding.transfer_encoding_chunked_decode(remainder+part5)
94
+ assert_equal([nil, "12\r\ndolor sit amet</p"], res)
95
+ part, remainder = res
96
+ decoded += part if part
97
+
98
+ res = HTTPTools::Encoding.transfer_encoding_chunked_decode(remainder+part6)
99
+ assert_equal(["dolor sit amet</p>", nil], res)
100
+ part, remainder = res
101
+ decoded += part if part
102
+
103
+ expected = "<h1>Hello world</h1><p>Lorem ipsum dolor sit amet</p>"
104
+ assert_equal(expected, decoded)
105
+ assert_nil(remainder)
106
+ end
107
+
108
+ def test_decode_with_empty_line
109
+ encode="16\r\n<h1>Hello world</h1>\r\n\r\n12\r\n<p>Lorem ipsum</p>\r\n0\r\n"
110
+ decoded = HTTPTools::Encoding.transfer_encoding_chunked_decode(encode)
111
+
112
+ assert_equal(["<h1>Hello world</h1>\r\n<p>Lorem ipsum</p>", nil], decoded)
113
+ end
114
+
115
+ def test_decode_doesnt_mangle_input
116
+ encoded = "14\r\n<h1>Hello world</h1>\r\n0\r\n"
117
+ HTTPTools::Encoding.transfer_encoding_chunked_decode(encoded)
118
+
119
+ assert_equal("14\r\n<h1>Hello world</h1>\r\n0\r\n", encoded)
120
+ end
121
+
122
+ def test_encode
123
+ encoded = HTTPTools::Encoding.transfer_encoding_chunked_encode("foo")
124
+
125
+ assert_equal("3\r\nfoo\r\n", encoded)
126
+ end
127
+
128
+ def test_encode_empty_string
129
+ encoded = HTTPTools::Encoding.transfer_encoding_chunked_encode("")
130
+
131
+ assert_equal("0\r\n", encoded)
132
+ end
133
+
134
+
135
+ def test_encode_nil
136
+ encoded = HTTPTools::Encoding.transfer_encoding_chunked_encode(nil)
137
+
138
+ assert_equal("0\r\n", encoded)
139
+ end
140
+
141
+ end
@@ -0,0 +1,37 @@
1
+ base = File.expand_path(File.dirname(__FILE__) + '/../../lib')
2
+ require base + '/http_tools'
3
+ require 'test/unit'
4
+
5
+ class URLEncodingTest < Test::Unit::TestCase
6
+
7
+ def test_encode
8
+ result = HTTPTools::Encoding.url_encode("[A] (test/example)=<string>?")
9
+
10
+ assert_equal("%5bA%5d%20%28test%2fexample%29%3d%3cstring%3e%3f", result)
11
+ end
12
+
13
+ def test_decode
14
+ result = HTTPTools::Encoding.url_decode("%5bA%5d%20%28test%2fexample%29%3d%3cstring%3e%3f")
15
+
16
+ assert_equal("[A] (test/example)=<string>?", result)
17
+ end
18
+
19
+ def test_encode_allowed_characters
20
+ result = HTTPTools::Encoding.url_encode("A_test-string~1.")
21
+
22
+ assert_equal("A_test-string~1.", result)
23
+ end
24
+
25
+ def test_encode_latin_capital_letter_a_with_grave
26
+ result = HTTPTools::Encoding.url_encode("À")
27
+
28
+ assert_equal("%c3%80", result)
29
+ end
30
+
31
+ def test_decode_latin_capital_letter_a_with_grave
32
+ result = HTTPTools::Encoding.url_decode("%C3%80")
33
+
34
+ assert_equal("À", result)
35
+ end
36
+
37
+ end
@@ -0,0 +1,42 @@
1
+ base = File.expand_path(File.dirname(__FILE__) + '/../../lib')
2
+ require base + '/http_tools'
3
+ require 'test/unit'
4
+
5
+ class WWWFormTest < Test::Unit::TestCase
6
+
7
+ def test_encode
8
+ result = HTTPTools::Encoding.www_form_encode("foo" => "bar", "baz" => "qux")
9
+
10
+ # may fail under Ruby < 1.9 because of the unpredictable ordering of Hash
11
+ assert_equal("foo=bar&baz=qux", result)
12
+ end
13
+
14
+ def test_encode_with_array
15
+ result = HTTPTools::Encoding.www_form_encode("lang" => ["en", "fr"], "q" => ["foo", "bar"])
16
+
17
+ # may fail under Ruby < 1.9 because of the unpredictable ordering of Hash
18
+ assert_equal("lang=en&lang=fr&q=foo&q=bar", result)
19
+ end
20
+
21
+ def test_decode
22
+ result = HTTPTools::Encoding.www_form_decode("foo=bar&baz=qux")
23
+
24
+ assert_equal({"foo" => "bar", "baz" => "qux"}, result)
25
+ end
26
+
27
+ def test_decode_with_array
28
+ result = HTTPTools::Encoding.www_form_decode("lang=en&lang=fr&q=foo&q=bar")
29
+
30
+ assert_equal({"lang" => ["en", "fr"], "q" => ["foo", "bar"]}, result)
31
+ end
32
+
33
+ def test_encode_decode
34
+ orginal = {"query" => "fish", "lang" => ["en", "fr"]}
35
+
36
+ encoded = HTTPTools::Encoding.www_form_encode(orginal)
37
+ decoded = HTTPTools::Encoding.www_form_decode(encoded)
38
+
39
+ assert_equal(orginal, decoded)
40
+ end
41
+
42
+ end
@@ -0,0 +1,481 @@
1
+ base = File.expand_path(File.dirname(__FILE__) + '/../../lib')
2
+ require base + '/http_tools'
3
+ require 'test/unit'
4
+
5
+ class RequestTest < Test::Unit::TestCase
6
+
7
+ def test_get
8
+ parser = HTTPTools::Parser.new
9
+ result = nil
10
+
11
+ parser.add_listener(:method) do |method|
12
+ result = method
13
+ end
14
+
15
+ parser << "GET / HTTP/1.1\r\n"
16
+
17
+ assert_equal("GET", result)
18
+ end
19
+
20
+ def test_post
21
+ parser = HTTPTools::Parser.new
22
+ result = nil
23
+
24
+ parser.add_listener(:method) do |method|
25
+ result = method
26
+ end
27
+
28
+ parser << "POST / HTTP/1.1\r\n"
29
+
30
+ assert_equal("POST", result)
31
+ end
32
+
33
+ def test_empty_path
34
+ parser = HTTPTools::Parser.new
35
+ result = nil
36
+
37
+ parser.add_listener(:path) do |path, query|
38
+ result = path
39
+ end
40
+
41
+ parser << "GET / HTTP/1.1\r\n"
42
+
43
+ assert_equal("/", result)
44
+ end
45
+
46
+ def test_basic_path
47
+ parser = HTTPTools::Parser.new
48
+ result = nil
49
+
50
+ parser.add_listener(:path) do |path, query|
51
+ result = path
52
+ end
53
+
54
+ parser << "GET /foo HTTP/1.1\r\n"
55
+
56
+ assert_equal("/foo", result)
57
+ end
58
+
59
+ def test_complicated_path
60
+ parser = HTTPTools::Parser.new
61
+ path = nil
62
+ query = nil
63
+
64
+ parser.add_listener(:path) do |p, q|
65
+ path = p
66
+ query = q
67
+ end
68
+
69
+ parser << "GET /foo%20bar/baz.html?key=value#qux HTTP/1.1\r\n"
70
+
71
+ assert_equal("/foo%20bar/baz.html", path)
72
+ assert_equal("key=value", query)
73
+ end
74
+
75
+ def test_invalid_path
76
+ parser = HTTPTools::Parser.new
77
+
78
+ assert_raise(HTTPTools::ParseError) do
79
+ parser << "GET \\ HTTP/1.1\r\n"
80
+ end
81
+ end
82
+
83
+ def test_uri
84
+ parser = HTTPTools::Parser.new
85
+ result = nil
86
+
87
+ parser.add_listener(:uri) do |uri|
88
+ result = uri
89
+ end
90
+
91
+ parser << "GET http://example.com/foo HTTP/1.1\r\n"
92
+
93
+ assert_equal("http://example.com/foo", result)
94
+ end
95
+
96
+ def test_path_callback_not_called_with_uri
97
+ parser = HTTPTools::Parser.new
98
+ result = nil
99
+
100
+ parser.add_listener(:path) do |path, query|
101
+ result = path
102
+ end
103
+
104
+ parser << "GET http://example.com/foo HTTP/1.1\r\n"
105
+
106
+ assert_nil(result)
107
+ end
108
+
109
+ def test_uri_callback_called_with_path
110
+ parser = HTTPTools::Parser.new
111
+ uri = nil
112
+ path = nil
113
+ query = nil
114
+
115
+ parser.add_listener(:uri) {|u| uri = u}
116
+ parser.add_listener(:path) do |p, q|
117
+ path = p
118
+ query = q
119
+ end
120
+
121
+ parser << "GET /foo/bar.html?key=value HTTP/1.1\r\n"
122
+
123
+ assert_equal("/foo/bar.html?key=value", uri)
124
+ assert_equal("/foo/bar.html", path)
125
+ assert_equal("key=value", query)
126
+ end
127
+
128
+ def test_fragment_with_path
129
+ parser = HTTPTools::Parser.new
130
+ path = nil
131
+ fragment = nil
132
+
133
+ parser.add_listener(:path) {|p, q| path = p}
134
+ parser.add_listener(:fragment) {|f| fragment = f}
135
+
136
+ parser << "GET /foo#bar HTTP/1.1\r\n"
137
+
138
+ assert_equal("/foo", path)
139
+ assert_equal("bar", fragment)
140
+ end
141
+
142
+ def test_fragment_with_uri
143
+ parser = HTTPTools::Parser.new
144
+ uri = nil
145
+ fragment = nil
146
+
147
+ parser.add_listener(:uri) {|u| uri = u}
148
+ parser.add_listener(:fragment) {|f| fragment = f}
149
+
150
+ parser << "GET http://example.com/foo#bar HTTP/1.1\r\n"
151
+
152
+ assert_equal("http://example.com/foo", uri)
153
+ assert_equal("bar", fragment)
154
+ end
155
+
156
+ def test_with_header
157
+ parser = HTTPTools::Parser.new
158
+ method = nil
159
+ path = nil
160
+ headers = nil
161
+
162
+ parser.add_listener(:method) {|m| method = m}
163
+ parser.add_listener(:path) {|p, q| path = p}
164
+ parser.add_listener(:headers) {|h| headers = h}
165
+
166
+ parser << "GET / HTTP/1.1\r\n"
167
+ parser << "Host: www.example.com\r\n"
168
+ parser << "\r\n"
169
+
170
+ assert_equal("GET", method)
171
+ assert_equal("/", path)
172
+ assert_equal({"Host" => "www.example.com"}, headers)
173
+ end
174
+
175
+ def test_with_headers
176
+ parser = HTTPTools::Parser.new
177
+ method = nil
178
+ path = nil
179
+ headers = nil
180
+
181
+ parser.add_listener(:method) {|m| method = m}
182
+ parser.add_listener(:path) {|p, q| path = p}
183
+ parser.add_listener(:headers) {|h| headers = h}
184
+
185
+ parser << "GET / HTTP/1.1\r\n"
186
+ parser << "Host: www.example.com\r\n"
187
+ parser << "Accept: text/plain\r\n"
188
+ parser << "\r\n"
189
+
190
+ assert_equal("GET", method)
191
+ assert_equal("/", path)
192
+ assert_equal({"Host"=>"www.example.com", "Accept"=>"text/plain"}, headers)
193
+ end
194
+
195
+ def test_sub_line_chunks
196
+ parser = HTTPTools::Parser.new
197
+ method = nil
198
+ path = nil
199
+ headers = nil
200
+
201
+ parser.add_listener(:method) {|m| method = m}
202
+ parser.add_listener(:path) {|p, q| path = p}
203
+ parser.add_listener(:headers) {|h| headers = h}
204
+
205
+ parser << "GE"
206
+ parser << "T /foo/"
207
+ parser << "bar HT"
208
+ parser << "TP/1.1\r\n"
209
+ parser << "Host: www.exam"
210
+ parser << "ple.com\r\n"
211
+ parser << "Accep"
212
+ parser << "t: text/plain\r\n\r\n"
213
+
214
+ assert_equal("GET", method)
215
+ assert_equal("/foo/bar", path)
216
+ assert_equal({"Host"=>"www.example.com", "Accept"=>"text/plain"}, headers)
217
+ end
218
+
219
+ def test_sub_line_chunks_2
220
+ parser = HTTPTools::Parser.new
221
+ method = nil
222
+ path = nil
223
+ headers = nil
224
+
225
+ parser.add_listener(:method) {|m| method = m}
226
+ parser.add_listener(:path) {|p, q| path = p}
227
+ parser.add_listener(:headers) {|h| headers = h}
228
+
229
+ parser << "POST"
230
+ parser << " /bar/foo"
231
+ parser << " HTTP/"
232
+ parser << "1."
233
+ parser << "1\r\n"
234
+ parser << "Host: "
235
+ parser << "www.example.com\r\n"
236
+ parser << "Content-Length:"
237
+ parser << " 11\r\n\r\n"
238
+ parser << "hello="
239
+ parser << ""
240
+ parser << "world"
241
+
242
+ assert_equal("POST", method)
243
+ assert_equal("/bar/foo", path)
244
+ assert_equal({"Host"=>"www.example.com", "Content-Length"=>"11"}, headers)
245
+ end
246
+
247
+ def test_all_at_once
248
+ parser = HTTPTools::Parser.new
249
+ method = nil
250
+ path = nil
251
+ headers = nil
252
+
253
+ parser.add_listener(:method) {|m| method = m}
254
+ parser.add_listener(:path) {|p, q| path = p}
255
+ parser.add_listener(:headers) {|h| headers = h}
256
+
257
+ request = "GET /foo/bar HTTP/1.1\r\n"
258
+ request << "Host: www.example.com\r\nAccept: text/plain\r\n\r\n"
259
+
260
+ parser << request
261
+
262
+ assert_equal("GET", method)
263
+ assert_equal("/foo/bar", path)
264
+ assert_equal({"Host"=>"www.example.com", "Accept"=>"text/plain"}, headers)
265
+ end
266
+
267
+ def test_accepts_imaginary_method
268
+ parser = HTTPTools::Parser.new
269
+ method = nil
270
+
271
+ parser.add_listener(:method) {|m| method = m}
272
+
273
+ parser << "UNICORNS / HTTP/1.1\r\n"
274
+
275
+ assert_equal("UNICORNS", method)
276
+ end
277
+
278
+ def test_without_http
279
+ parser = HTTPTools::Parser.new
280
+ method = nil
281
+ path = nil
282
+ headers = nil
283
+
284
+ parser.add_listener(:method) {|m| method = m}
285
+ parser.add_listener(:path) {|p, q| path = p}
286
+ parser.add_listener(:headers) {|h| headers = h}
287
+
288
+ parser << "GET /\r\nHost: www.example.com\r\n\r\n"
289
+
290
+ assert_equal("GET", method)
291
+ assert_equal("/", path)
292
+ assert_equal({"Host"=>"www.example.com"}, headers)
293
+ end
294
+
295
+ def test_unknown_protocol
296
+ parser = HTTPTools::Parser.new
297
+
298
+ assert_raise(HTTPTools::ParseError) {parser << "GET / SPDY/1.1\r\n"}
299
+ end
300
+
301
+ def test_protocol_version
302
+ parser = HTTPTools::Parser.new
303
+ version = nil
304
+
305
+ parser.add_listener(:version) {|v| version = v}
306
+
307
+ parser << "GET / HTTP/1.1\r\n"
308
+
309
+ assert_equal("1.1", version)
310
+ end
311
+
312
+ def test_protocol_without_version
313
+ parser = HTTPTools::Parser.new
314
+
315
+ assert_raise(HTTPTools::ParseError) {parser << "GET / HTTP\r\n"}
316
+ end
317
+
318
+ def test_reset
319
+ parser = HTTPTools::Parser.new
320
+ method = nil
321
+ method_calls = 0
322
+ path = nil
323
+ path_calls = 0
324
+ headers = nil
325
+ header_calls = 0
326
+
327
+ parser.add_listener(:method) {|m| method = m; method_calls += 1}
328
+ parser.add_listener(:path) {|p, q| path = p; path_calls += 1}
329
+ parser.add_listener(:headers) {|h| headers = h; header_calls += 1}
330
+
331
+ parser << "GET / HTTP/1.1\r\n"
332
+ parser << "Host: www.example.com\r\n"
333
+ parser << "Accept: text/plain\r\n"
334
+ parser << "\r\n"
335
+
336
+ assert_equal("GET", method)
337
+ assert_equal("/", path)
338
+ assert_equal({"Host"=>"www.example.com", "Accept"=>"text/plain"}, headers)
339
+ assert_equal([1, 1, 1], [method_calls, path_calls, header_calls])
340
+
341
+ parser.reset
342
+
343
+ parser << "POST /example HTTP/1.1\r\n"
344
+ parser << "Host: www.test.co.uk\r\n"
345
+ parser << "Accept: text/html\r\n"
346
+ parser << "\r\n"
347
+
348
+ assert_equal("POST", method)
349
+ assert_equal("/example", path)
350
+ assert_equal({"Host"=>"www.test.co.uk", "Accept"=>"text/html"}, headers)
351
+ assert_equal([2, 2, 2], [method_calls, path_calls, header_calls])
352
+ end
353
+
354
+ def test_not_a_http_request
355
+ parser = HTTPTools::Parser.new
356
+
357
+ assert_raise(HTTPTools::ParseError) {parser << "not a http request"}
358
+ end
359
+
360
+ def test_data_past_end
361
+ parser = HTTPTools::Parser.new
362
+ parser << "POST /example HTTP/1.1\r\n"
363
+ parser << "Content-Length: 8\r\n"
364
+ parser << "\r\n"
365
+ parser << "test=foo"
366
+
367
+ assert_raise(HTTPTools::EndOfMessageError) {parser << "more"}
368
+ end
369
+
370
+ def test_lowecase_method
371
+ parser = HTTPTools::Parser.new
372
+ result = nil
373
+
374
+ parser.add_listener(:method) do |method|
375
+ result = method
376
+ end
377
+
378
+ parser << "get / HTTP/1.1\r\n"
379
+
380
+ assert_equal("GET", result)
381
+ end
382
+
383
+ def test_lowercase_http
384
+ parser = HTTPTools::Parser.new
385
+ version = nil
386
+
387
+ parser.add_listener(:version) {|v| version = v}
388
+
389
+ parser << "GET / http/1.1\r\n"
390
+
391
+ assert_equal("1.1", version)
392
+ end
393
+
394
+ def test_delegate
395
+ request_class = Class.new
396
+ request_class.class_eval do
397
+ attr_reader :http_method, :path, :headers, :body
398
+ def on_method(name)
399
+ @http_method = name
400
+ end
401
+ def on_path(path, query)
402
+ @path = path
403
+ @query = query
404
+ end
405
+ def on_headers(headers)
406
+ @headers = headers
407
+ end
408
+ def on_body(body)
409
+ @body = body
410
+ end
411
+ end
412
+ request = request_class.new
413
+
414
+ parser = HTTPTools::Parser.new(request)
415
+ parser << "POST /test HTTP/1.1\r\n"
416
+ parser << "Content-Length: 13\r\n"
417
+ parser << "\r\n"
418
+ parser << "query=example"
419
+
420
+ assert_equal("POST", request.http_method)
421
+ assert_equal("/test", request.path)
422
+ assert_equal({"Content-Length" => "13"}, request.headers)
423
+ assert_equal("query=example", request.body)
424
+ end
425
+
426
+ def test_invalid_version
427
+ parser = HTTPTools::Parser.new
428
+
429
+ assert_raise(HTTPTools::ParseError) {parser << "GET / HTTP/one dot one\r\n"}
430
+ end
431
+
432
+ def test_invalid_header_key_with_space
433
+ parser = HTTPTools::Parser.new
434
+
435
+ assert_raise(HTTPTools::ParseError) do
436
+ parser << "GET / HTTP/1.1\r\nx-invalid key: text/plain\r\n"
437
+ end
438
+ end
439
+
440
+ def test_invalid_header_key_with_colon
441
+ parser = HTTPTools::Parser.new
442
+
443
+ assert_raise(HTTPTools::ParseError) do
444
+ parser << "GET / HTTP/1.1\r\nx-invalid:key: text/plain\r\n"
445
+ end
446
+ end
447
+
448
+ def test_invalid_header_key_with_control_character
449
+ parser = HTTPTools::Parser.new
450
+
451
+ assert_raise(HTTPTools::ParseError) do
452
+ parser << "GET / HTTP/1.1\r\nx-invalid\0key: text/plain\r\n"
453
+ end
454
+ end
455
+
456
+ def test_invalid_header_key_with_non_ascii_character
457
+ parser = HTTPTools::Parser.new
458
+
459
+ assert_raise(HTTPTools::ParseError) do
460
+ parser << "GET / HTTP/1.1\r\nx-invalid-kéy: text/plain\r\n"
461
+ end
462
+ end
463
+
464
+ def test_invalid_header_value_with_non_ascii_character
465
+ parser = HTTPTools::Parser.new
466
+
467
+ assert_raise(HTTPTools::ParseError) do
468
+ parser << "GET / HTTP/1.1\r\nAccept: téxt/plain\r\n"
469
+ end
470
+ end
471
+
472
+ def test_error_callback
473
+ parser = HTTPTools::Parser.new
474
+ error = nil
475
+ parser.on(:error) {|e| error = e}
476
+
477
+ assert_nothing_raised(Exception) {parser << "1"}
478
+ assert_instance_of(HTTPTools::ParseError, error)
479
+ end
480
+
481
+ end