http_tools 0.1.0

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.
@@ -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