http_tools 0.1.0 → 0.2.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.
- data/README.rdoc +11 -5
- data/bench/parser/request_bench.rb +11 -15
- data/bench/parser/response_bench.rb +12 -0
- data/bench/transfer_encoding_chunked_bench.rb +26 -0
- data/example/http_client.rb +81 -32
- data/example/http_server.rb +82 -0
- data/lib/http_tools.rb +4 -0
- data/lib/http_tools/encoding.rb +25 -24
- data/lib/http_tools/errors.rb +1 -0
- data/lib/http_tools/parser.rb +170 -157
- data/test/encoding/transfer_encoding_chunked_test.rb +22 -0
- data/test/parser/request_test.rb +169 -126
- data/test/parser/response_test.rb +520 -52
- metadata +7 -5
@@ -8,18 +8,31 @@ class ResponseTest < Test::Unit::TestCase
|
|
8
8
|
parser = HTTPTools::Parser.new
|
9
9
|
version = nil
|
10
10
|
|
11
|
-
parser.add_listener(:
|
11
|
+
parser.add_listener(:header) {version = parser.version}
|
12
12
|
|
13
|
-
parser << "HTTP/1.1 "
|
13
|
+
parser << "HTTP/1.1 200 OK\r\n\r\n"
|
14
14
|
|
15
15
|
assert_equal("1.1", version)
|
16
16
|
end
|
17
17
|
|
18
|
+
def test_one_dot_x_version
|
19
|
+
parser = HTTPTools::Parser.new
|
20
|
+
version = nil
|
21
|
+
|
22
|
+
parser.add_listener(:header) {version = parser.version}
|
23
|
+
|
24
|
+
parser << "HTTP/1.x 200 OK\r\n\r\n"
|
25
|
+
|
26
|
+
assert_equal("1.x", version)
|
27
|
+
end
|
28
|
+
|
18
29
|
def test_ok
|
19
30
|
parser = HTTPTools::Parser.new
|
20
31
|
code, message = nil
|
21
32
|
|
22
|
-
parser.add_listener(:
|
33
|
+
parser.add_listener(:header) do
|
34
|
+
code, message = parser.status_code, parser.message
|
35
|
+
end
|
23
36
|
|
24
37
|
parser << "HTTP/1.1 200 OK\r\n\r\n"
|
25
38
|
|
@@ -32,7 +45,9 @@ class ResponseTest < Test::Unit::TestCase
|
|
32
45
|
parser = HTTPTools::Parser.new
|
33
46
|
code, message = nil
|
34
47
|
|
35
|
-
parser.add_listener(:
|
48
|
+
parser.add_listener(:header) do
|
49
|
+
code, message = parser.status_code, parser.message
|
50
|
+
end
|
36
51
|
|
37
52
|
parser << "HTTP/1.1 404 Not Found\r\n\r\n"
|
38
53
|
|
@@ -41,11 +56,61 @@ class ResponseTest < Test::Unit::TestCase
|
|
41
56
|
assert(!parser.finished?, "parser should not be finished")
|
42
57
|
end
|
43
58
|
|
59
|
+
def test_missing_message
|
60
|
+
parser = HTTPTools::Parser.new
|
61
|
+
code, message = nil
|
62
|
+
|
63
|
+
parser.add_listener(:header) do
|
64
|
+
code, message = parser.status_code, parser.message
|
65
|
+
end
|
66
|
+
|
67
|
+
parser << "HTTP/1.1 302\r\n\r\n"
|
68
|
+
|
69
|
+
assert_equal(302, code)
|
70
|
+
assert_equal("", message)
|
71
|
+
assert(!parser.finished?, "parser should not be finished")
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_non_standard_message
|
75
|
+
parser = HTTPTools::Parser.new
|
76
|
+
version, code, message = nil
|
77
|
+
|
78
|
+
parser.add_listener(:header) do
|
79
|
+
version = parser.version
|
80
|
+
code = parser.status_code
|
81
|
+
message = parser.message
|
82
|
+
end
|
83
|
+
|
84
|
+
parser << "HTTP/1.0 200 (OK)\r\n\r\n"
|
85
|
+
|
86
|
+
assert_equal("1.0", version)
|
87
|
+
assert_equal(200, code)
|
88
|
+
assert_equal("(OK)", message)
|
89
|
+
assert(!parser.finished?, "parser should not be finished")
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_status_message_with_accent
|
93
|
+
parser = HTTPTools::Parser.new
|
94
|
+
code, message = nil
|
95
|
+
|
96
|
+
parser.add_listener(:header) do
|
97
|
+
code, message = parser.status_code, parser.message
|
98
|
+
end
|
99
|
+
|
100
|
+
parser << "HTTP/1.1 403 Accès interdit\r\n\r\n"
|
101
|
+
|
102
|
+
assert_equal(403, code)
|
103
|
+
assert_equal("Accès interdit", message)
|
104
|
+
assert(!parser.finished?, "parser should not be finished")
|
105
|
+
end
|
106
|
+
|
44
107
|
def test_no_content
|
45
108
|
parser = HTTPTools::Parser.new
|
46
109
|
code, message = nil
|
47
110
|
|
48
|
-
parser.add_listener(:
|
111
|
+
parser.add_listener(:header) do
|
112
|
+
code, message = parser.status_code, parser.message
|
113
|
+
end
|
49
114
|
|
50
115
|
parser << "HTTP/1.1 204 No Content\r\n\r\n"
|
51
116
|
|
@@ -58,7 +123,9 @@ class ResponseTest < Test::Unit::TestCase
|
|
58
123
|
parser = HTTPTools::Parser.new
|
59
124
|
code, message = nil
|
60
125
|
|
61
|
-
parser.add_listener(:
|
126
|
+
parser.add_listener(:header) do
|
127
|
+
code, message = parser.status_code, parser.message
|
128
|
+
end
|
62
129
|
|
63
130
|
parser << "HTTP/1.1 304 Not Modified\r\n\r\n"
|
64
131
|
|
@@ -72,7 +139,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
72
139
|
parser.force_no_body = true
|
73
140
|
headers = nil
|
74
141
|
|
75
|
-
parser.add_listener(:
|
142
|
+
parser.add_listener(:header) {headers = parser.header}
|
76
143
|
|
77
144
|
parser << "HTTP/1.1 200 OK\r\n"
|
78
145
|
parser << "Content-Length: 20\r\n"
|
@@ -82,12 +149,178 @@ class ResponseTest < Test::Unit::TestCase
|
|
82
149
|
assert(parser.finished?, "parser should be finished")
|
83
150
|
end
|
84
151
|
|
152
|
+
def test_messed_up_iis_headers
|
153
|
+
parser = HTTPTools::Parser.new
|
154
|
+
headers = nil
|
155
|
+
|
156
|
+
parser.add_listener(:header) {headers = parser.header}
|
157
|
+
|
158
|
+
parser << "HTTP/1.1 200 OK\r\n"
|
159
|
+
parser << "Server:: Harris Associates L.P.\r\n"
|
160
|
+
parser << "X-DIP:202\r\n"
|
161
|
+
parser << "\r\n"
|
162
|
+
|
163
|
+
assert_equal({
|
164
|
+
"Server" => ": Harris Associates L.P.",
|
165
|
+
"X-DIP" => "202"}, headers)
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_space_in_header_key
|
169
|
+
parser = HTTPTools::Parser.new
|
170
|
+
headers = nil
|
171
|
+
|
172
|
+
parser.add_listener(:header) {headers = parser.header}
|
173
|
+
|
174
|
+
parser << "HTTP/1.1 200 OK\r\n"
|
175
|
+
parser << "X-Powered-By: PHP/5.3.5\r\n"
|
176
|
+
parser << "HTTP Status Code: HTTP/1.1 404 Not Found\r\n"
|
177
|
+
parser << "\r\n"
|
178
|
+
|
179
|
+
assert_equal({
|
180
|
+
"X-Powered-By" => "PHP/5.3.5",
|
181
|
+
"HTTP Status Code" => "HTTP/1.1 404 Not Found"}, headers)
|
182
|
+
end
|
183
|
+
|
184
|
+
def test_header_empty_value
|
185
|
+
parser = HTTPTools::Parser.new
|
186
|
+
headers = nil
|
187
|
+
|
188
|
+
parser.add_listener(:header) {headers = parser.header}
|
189
|
+
|
190
|
+
parser << "HTTP/1.1 200 OK\r\n"
|
191
|
+
parser << "X-Empty: \r\n"
|
192
|
+
parser << "Content-Type: text/html\r\n\r\n"
|
193
|
+
|
194
|
+
assert_equal({
|
195
|
+
"X-Empty" => "",
|
196
|
+
"Content-Type" => "text/html"}, headers)
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_weird_iis_content_header
|
200
|
+
parser = HTTPTools::Parser.new
|
201
|
+
code, message, headers = nil
|
202
|
+
body = ""
|
203
|
+
|
204
|
+
parser.add_listener(:header) do
|
205
|
+
code = parser.status_code
|
206
|
+
message = parser.message
|
207
|
+
headers = parser.header
|
208
|
+
end
|
209
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
210
|
+
|
211
|
+
parser << "HTTP/1.1 200 OK\r\n"
|
212
|
+
parser << "Content-Length: 20\r\n"
|
213
|
+
parser << "Content:\r\n"
|
214
|
+
parser << "\r\n"
|
215
|
+
parser << "<h1>Hello world</h1>"
|
216
|
+
|
217
|
+
assert_equal(200, code)
|
218
|
+
assert_equal("OK", message)
|
219
|
+
assert_equal({"Content-Length" => "20", "Content" => ""}, headers)
|
220
|
+
assert_equal("<h1>Hello world</h1>", body)
|
221
|
+
assert(parser.finished?, "parser should be finished")
|
222
|
+
end
|
223
|
+
|
224
|
+
def test_multiple_set_cookie_headers
|
225
|
+
parser = HTTPTools::Parser.new
|
226
|
+
headers = nil
|
227
|
+
|
228
|
+
parser.add_listener(:header) {headers = parser.header}
|
229
|
+
|
230
|
+
parser << "HTTP/1.1 200 OK\r\n"
|
231
|
+
parser << "Set-Cookie: foo=bar\r\n"
|
232
|
+
parser << "Set-Cookie: baz=qux\r\n\r\n"
|
233
|
+
|
234
|
+
assert_equal({"Set-Cookie" => ["foo=bar", "baz=qux"]}, headers)
|
235
|
+
end
|
236
|
+
|
237
|
+
def test_skip_junk_headers_at_end
|
238
|
+
parser = HTTPTools::Parser.new
|
239
|
+
code, message, headers = nil
|
240
|
+
body = ""
|
241
|
+
|
242
|
+
parser.add_listener(:header) do
|
243
|
+
code = parser.status_code
|
244
|
+
message = parser.message
|
245
|
+
headers = parser.header
|
246
|
+
end
|
247
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
248
|
+
|
249
|
+
parser << "HTTP/1.1 301 Redirect\r\n"
|
250
|
+
parser << "Location: /index.html\r\n"
|
251
|
+
parser << "Content-Length: 74\r\n"
|
252
|
+
parser << "301 Moved Permanently\r\n\r\n"
|
253
|
+
parser << "You should have been redirected to\n"
|
254
|
+
parser << "<a href=\"/index.html\">/index.html</a>.\n"
|
255
|
+
|
256
|
+
assert_equal(301, code)
|
257
|
+
assert_equal("Redirect", message)
|
258
|
+
assert_equal({"Location" => "/index.html", "Content-Length" => "74"}, headers)
|
259
|
+
assert_equal("You should have been redirected to\n<a href=\"/index.html\">/index.html</a>.\n", body)
|
260
|
+
assert(parser.finished?, "parser should be finished")
|
261
|
+
end
|
262
|
+
|
263
|
+
def test_skip_junk_headers_at_start
|
264
|
+
parser = HTTPTools::Parser.new
|
265
|
+
code, message, headers = nil
|
266
|
+
body = ""
|
267
|
+
|
268
|
+
parser.add_listener(:header) do
|
269
|
+
code = parser.status_code
|
270
|
+
message = parser.message
|
271
|
+
headers = parser.header
|
272
|
+
end
|
273
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
274
|
+
|
275
|
+
parser << "HTTP/1.0 200 OK\r\n"
|
276
|
+
parser << "QWEBS/1.0 (HP 3000)\r\n"
|
277
|
+
parser << "Content-Type: text/html\r\n\r\n"
|
278
|
+
parser << "<h1>Hello world</h1>"
|
279
|
+
parser.finish
|
280
|
+
|
281
|
+
assert_equal(200, code)
|
282
|
+
assert_equal("OK", message)
|
283
|
+
assert_equal({"Content-Type" => "text/html"}, headers)
|
284
|
+
assert_equal("<h1>Hello world</h1>", body)
|
285
|
+
assert(parser.finished?, "parser should be finished")
|
286
|
+
end
|
287
|
+
|
288
|
+
def test_skip_junk_headers_in_the_middle
|
289
|
+
parser = HTTPTools::Parser.new
|
290
|
+
code, message, headers = nil
|
291
|
+
body = ""
|
292
|
+
|
293
|
+
parser.add_listener(:header) do
|
294
|
+
code = parser.status_code
|
295
|
+
message = parser.message
|
296
|
+
headers = parser.header
|
297
|
+
end
|
298
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
299
|
+
|
300
|
+
parser << "HTTP/1.1 200 OK\r\n"
|
301
|
+
parser << "Content-Length: 20\r\n"
|
302
|
+
parser << "random\t"
|
303
|
+
parser << "garbage\n"
|
304
|
+
parser << "Content-Type: text/html\r\n"
|
305
|
+
parser << "\r\n"
|
306
|
+
parser << "<h1>Hello world</h1>"
|
307
|
+
|
308
|
+
assert_equal(200, code)
|
309
|
+
assert_equal("OK", message)
|
310
|
+
assert_equal({"Content-Length" => "20", "Content-Type" => "text/html"}, headers)
|
311
|
+
assert_equal("<h1>Hello world</h1>", body)
|
312
|
+
assert(parser.finished?, "parser should be finished")
|
313
|
+
end
|
314
|
+
|
85
315
|
def test_apple_dot_com
|
86
316
|
parser = HTTPTools::Parser.new
|
87
317
|
code, message, headers = nil
|
88
318
|
|
89
|
-
parser.add_listener(:
|
90
|
-
|
319
|
+
parser.add_listener(:header) do
|
320
|
+
code = parser.status_code
|
321
|
+
message = parser.message
|
322
|
+
headers = parser.header
|
323
|
+
end
|
91
324
|
|
92
325
|
parser << "HTTP/1.1 200 OK\r\n"
|
93
326
|
parser << "Server: Apache/2.2.11 (Unix)\r\n"
|
@@ -114,9 +347,12 @@ class ResponseTest < Test::Unit::TestCase
|
|
114
347
|
code, message, headers = nil
|
115
348
|
body = []
|
116
349
|
|
117
|
-
parser.add_listener(:
|
118
|
-
|
119
|
-
|
350
|
+
parser.add_listener(:header) do
|
351
|
+
code = parser.status_code
|
352
|
+
message = parser.message
|
353
|
+
headers = parser.header
|
354
|
+
end
|
355
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
120
356
|
|
121
357
|
parser << "HTTP/1.1 200 OK\r\n"
|
122
358
|
parser << "Content-Length: 20\r\n"
|
@@ -133,11 +369,15 @@ class ResponseTest < Test::Unit::TestCase
|
|
133
369
|
|
134
370
|
def test_body
|
135
371
|
parser = HTTPTools::Parser.new
|
136
|
-
code, message, headers
|
372
|
+
code, message, headers = nil
|
373
|
+
body = ""
|
137
374
|
|
138
|
-
parser.add_listener(:
|
139
|
-
|
140
|
-
|
375
|
+
parser.add_listener(:header) do
|
376
|
+
code = parser.status_code
|
377
|
+
message = parser.message
|
378
|
+
headers = parser.header
|
379
|
+
end
|
380
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
141
381
|
|
142
382
|
parser << "HTTP/1.1 200 OK\r\n"
|
143
383
|
parser << "Content-Length: 20\r\n"
|
@@ -152,13 +392,67 @@ class ResponseTest < Test::Unit::TestCase
|
|
152
392
|
assert(parser.finished?, "parser should be finished")
|
153
393
|
end
|
154
394
|
|
155
|
-
def
|
395
|
+
def test_zero_length_body
|
396
|
+
parser = HTTPTools::Parser.new
|
397
|
+
code, message, headers, body = nil
|
398
|
+
stream = []
|
399
|
+
body = ""
|
400
|
+
|
401
|
+
parser.add_listener(:header) do
|
402
|
+
code = parser.status_code
|
403
|
+
message = parser.message
|
404
|
+
headers = parser.header
|
405
|
+
end
|
406
|
+
parser.add_listener(:stream) {|chunk| body << chunk; stream.push(chunk)}
|
407
|
+
|
408
|
+
parser << "HTTP/1.1 302 Moved Temporarily\r\n"
|
409
|
+
parser << "Location: http://www.example.com/\r\n"
|
410
|
+
parser << "Content-Length: 0\r\n\r\n"
|
411
|
+
|
412
|
+
assert_equal(302, code)
|
413
|
+
assert_equal("Moved Temporarily", message)
|
414
|
+
assert_equal({"Location" => "http://www.example.com/", "Content-Length" => "0"}, headers)
|
415
|
+
assert_equal([""], stream)
|
416
|
+
assert_equal("", body)
|
417
|
+
assert(parser.finished?, "parser should be finished")
|
418
|
+
end
|
419
|
+
|
420
|
+
def test_zero_length_body_terminated_by_close
|
156
421
|
parser = HTTPTools::Parser.new
|
157
422
|
code, message, headers, body = nil
|
423
|
+
stream = []
|
424
|
+
body = ""
|
425
|
+
|
426
|
+
parser.add_listener(:header) do
|
427
|
+
code = parser.status_code
|
428
|
+
message = parser.message
|
429
|
+
headers = parser.header
|
430
|
+
end
|
431
|
+
parser.add_listener(:stream) {|chunk| body << chunk; stream.push(chunk)}
|
432
|
+
|
433
|
+
parser << "HTTP/1.1 302 Moved Temporarily\r\n"
|
434
|
+
parser << "Location: http://www.example.com/\r\n\r\n"
|
435
|
+
parser.finish # notify parser the connection has closed
|
436
|
+
|
437
|
+
assert_equal(302, code)
|
438
|
+
assert_equal("Moved Temporarily", message)
|
439
|
+
assert_equal({"Location" => "http://www.example.com/"}, headers)
|
440
|
+
assert_equal([""], stream)
|
441
|
+
assert_equal("", body)
|
442
|
+
assert(parser.finished?, "parser should be finished")
|
443
|
+
end
|
444
|
+
|
445
|
+
def test_sub_line_chunks
|
446
|
+
parser = HTTPTools::Parser.new
|
447
|
+
code, message, headers = nil
|
448
|
+
body = ""
|
158
449
|
|
159
|
-
parser.add_listener(:
|
160
|
-
|
161
|
-
|
450
|
+
parser.add_listener(:header) do
|
451
|
+
code = parser.status_code
|
452
|
+
message = parser.message
|
453
|
+
headers = parser.header
|
454
|
+
end
|
455
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
162
456
|
|
163
457
|
parser << "HTTP/"
|
164
458
|
parser << "1."
|
@@ -177,13 +471,60 @@ class ResponseTest < Test::Unit::TestCase
|
|
177
471
|
assert(parser.finished?, "parser should be finished")
|
178
472
|
end
|
179
473
|
|
474
|
+
def test_break_between_crlf
|
475
|
+
parser = HTTPTools::Parser.new
|
476
|
+
code, message, headers = nil
|
477
|
+
body = ""
|
478
|
+
|
479
|
+
parser.add_listener(:header) do
|
480
|
+
code = parser.status_code
|
481
|
+
message = parser.message
|
482
|
+
headers = parser.header
|
483
|
+
end
|
484
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
485
|
+
|
486
|
+
parser << "HTTP/1.1 200 OK\r"
|
487
|
+
parser << "\nContent-Length: 20\r"
|
488
|
+
parser << "\n\r"
|
489
|
+
parser << "\n<h1>Hello world</h1>"
|
490
|
+
|
491
|
+
assert_equal(200, code)
|
492
|
+
assert_equal("OK", message)
|
493
|
+
assert_equal({"Content-Length" => "20"}, headers)
|
494
|
+
assert_equal("<h1>Hello world</h1>", body)
|
495
|
+
assert(parser.finished?, "parser should be finished")
|
496
|
+
end
|
497
|
+
|
498
|
+
def test_double_cr
|
499
|
+
parser = HTTPTools::Parser.new
|
500
|
+
headers = nil
|
501
|
+
body = ""
|
502
|
+
|
503
|
+
parser.add_listener(:header) {headers = parser.header}
|
504
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
505
|
+
|
506
|
+
parser << "HTTP/1.1 200 OK\r\n"
|
507
|
+
parser << "Page-Completion-Status: Normal\r\r\n"
|
508
|
+
parser << "Content-Length: 20\r\n"
|
509
|
+
parser << "\r\n"
|
510
|
+
parser << "<h1>Hello world</h1>"
|
511
|
+
|
512
|
+
assert_equal({"Page-Completion-Status" => "Normal\r", "Content-Length" => "20"}, headers)
|
513
|
+
assert_equal("<h1>Hello world</h1>", body)
|
514
|
+
assert(parser.finished?, "parser should be finished")
|
515
|
+
end
|
516
|
+
|
180
517
|
def test_body_with_key_terminator_like_value
|
181
518
|
parser = HTTPTools::Parser.new
|
182
|
-
code, message, headers
|
519
|
+
code, message, headers = nil
|
520
|
+
body = ""
|
183
521
|
|
184
|
-
parser.add_listener(:
|
185
|
-
|
186
|
-
|
522
|
+
parser.add_listener(:header) do
|
523
|
+
code = parser.status_code
|
524
|
+
message = parser.message
|
525
|
+
headers = parser.header
|
526
|
+
end
|
527
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
187
528
|
|
188
529
|
parser << "HTTP/1.1 200 OK\r\n"
|
189
530
|
parser << "Content-Length: 21\r\n"
|
@@ -198,11 +539,15 @@ class ResponseTest < Test::Unit::TestCase
|
|
198
539
|
|
199
540
|
def test_lazy_server
|
200
541
|
parser = HTTPTools::Parser.new
|
201
|
-
code, message, headers
|
542
|
+
code, message, headers = nil
|
543
|
+
body = ""
|
202
544
|
|
203
|
-
parser.add_listener(:
|
204
|
-
|
205
|
-
|
545
|
+
parser.add_listener(:header) do
|
546
|
+
code = parser.status_code
|
547
|
+
message = parser.message
|
548
|
+
headers = parser.header
|
549
|
+
end
|
550
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
206
551
|
|
207
552
|
parser << "HTTP/1.1 200 OK\n"
|
208
553
|
parser << "Content-Type: text/html; charset=utf-8\n"
|
@@ -219,11 +564,15 @@ class ResponseTest < Test::Unit::TestCase
|
|
219
564
|
|
220
565
|
def test_chunked
|
221
566
|
parser = HTTPTools::Parser.new
|
222
|
-
code, message, headers
|
567
|
+
code, message, headers = nil
|
568
|
+
body = ""
|
223
569
|
|
224
|
-
parser.add_listener(:
|
225
|
-
|
226
|
-
|
570
|
+
parser.add_listener(:header) do
|
571
|
+
code = parser.status_code
|
572
|
+
message = parser.message
|
573
|
+
headers = parser.header
|
574
|
+
end
|
575
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
227
576
|
|
228
577
|
parser << "HTTP/1.1 200 OK\r\n"
|
229
578
|
parser << "Transfer-Encoding: chunked\r\n"
|
@@ -244,9 +593,12 @@ class ResponseTest < Test::Unit::TestCase
|
|
244
593
|
code, message, headers = nil
|
245
594
|
body = []
|
246
595
|
|
247
|
-
parser.add_listener(:
|
248
|
-
|
249
|
-
|
596
|
+
parser.add_listener(:header) do
|
597
|
+
code = parser.status_code
|
598
|
+
message = parser.message
|
599
|
+
headers = parser.header
|
600
|
+
end
|
601
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
250
602
|
|
251
603
|
parser << "HTTP/1.1 200 OK\r\n"
|
252
604
|
parser << "Transfer-Encoding: chunked\r\n"
|
@@ -267,9 +619,12 @@ class ResponseTest < Test::Unit::TestCase
|
|
267
619
|
code, message, headers = nil
|
268
620
|
body = []
|
269
621
|
|
270
|
-
parser.add_listener(:
|
271
|
-
|
272
|
-
|
622
|
+
parser.add_listener(:header) do
|
623
|
+
code = parser.status_code
|
624
|
+
message = parser.message
|
625
|
+
headers = parser.header
|
626
|
+
end
|
627
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
273
628
|
|
274
629
|
parser << "HTTP/1.1 200 OK\r\n"
|
275
630
|
parser << "Transfer-Encoding: chunked\r\n"
|
@@ -285,13 +640,82 @@ class ResponseTest < Test::Unit::TestCase
|
|
285
640
|
assert(parser.finished?, "parser should be finished")
|
286
641
|
end
|
287
642
|
|
643
|
+
# shouldn't really be allowed, but IIS can't do chunked encoding properly
|
644
|
+
def test_chunked_terminated_by_close
|
645
|
+
parser = HTTPTools::Parser.new
|
646
|
+
code, message, headers = nil
|
647
|
+
body = []
|
648
|
+
|
649
|
+
parser.add_listener(:header) do
|
650
|
+
code = parser.status_code
|
651
|
+
message = parser.message
|
652
|
+
headers = parser.header
|
653
|
+
end
|
654
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
655
|
+
|
656
|
+
parser << "HTTP/1.1 200 OK\r\n"
|
657
|
+
parser << "Connection: close\r\n"
|
658
|
+
parser << "Transfer-Encoding: chunked\r\n"
|
659
|
+
parser << "\r\n"
|
660
|
+
parser << "9\r\n<h1>Hello\r\n"
|
661
|
+
parser << "b\r\n world</h1>\r\n"
|
662
|
+
parser.finish # notify parser the connection has closed
|
663
|
+
|
664
|
+
assert_equal(200, code)
|
665
|
+
assert_equal("OK", message)
|
666
|
+
assert_equal({
|
667
|
+
"Transfer-Encoding" => "chunked",
|
668
|
+
"Connection" => "close"}, headers)
|
669
|
+
assert_equal(["<h1>Hello", " world</h1>"], body)
|
670
|
+
assert(parser.finished?, "parser should be finished")
|
671
|
+
end
|
672
|
+
|
673
|
+
def test_html_body_only_not_allowed
|
674
|
+
parser = HTTPTools::Parser.new
|
675
|
+
|
676
|
+
assert_raise(HTTPTools::ParseError) do
|
677
|
+
parser << "<html><p>HTTP is hard</p></html>"
|
678
|
+
end
|
679
|
+
end
|
680
|
+
|
681
|
+
def test_html_body_only_allowed
|
682
|
+
parser = HTTPTools::Parser.new
|
683
|
+
version, code, message, headers = nil
|
684
|
+
body = ""
|
685
|
+
|
686
|
+
parser.allow_html_without_header = true
|
687
|
+
|
688
|
+
parser.add_listener(:header) do
|
689
|
+
version = parser.version
|
690
|
+
code = parser.status_code
|
691
|
+
message = parser.message
|
692
|
+
headers = parser.header
|
693
|
+
end
|
694
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
695
|
+
|
696
|
+
parser << "<html><p>HTTP is hard</p></html>"
|
697
|
+
parser.finish
|
698
|
+
|
699
|
+
assert_equal("0.0", version)
|
700
|
+
assert_equal(200, code)
|
701
|
+
assert_equal("", message)
|
702
|
+
assert_equal({}, headers)
|
703
|
+
assert_equal("<html><p>HTTP is hard</p></html>", body)
|
704
|
+
assert(parser.finished?, "parser should be finished")
|
705
|
+
end
|
706
|
+
|
288
707
|
def test_finished
|
289
708
|
parser = HTTPTools::Parser.new
|
290
|
-
code, message,
|
709
|
+
code, message, remainder = nil
|
710
|
+
body = ""
|
291
711
|
|
292
|
-
parser.add_listener(:
|
293
|
-
|
294
|
-
|
712
|
+
parser.add_listener(:header) do
|
713
|
+
code = parser.status_code
|
714
|
+
message = parser.message
|
715
|
+
headers = parser.header
|
716
|
+
end
|
717
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
718
|
+
parser.add_listener(:finish) {|r| remainder = r}
|
295
719
|
|
296
720
|
parser << "HTTP/1.1 200 OK\r\nContent-Length: 20\r\n\r\n"
|
297
721
|
parser << "<h1>Hello world</h1>HTTP/1.1 404 Not Found\r\n"
|
@@ -305,11 +729,16 @@ class ResponseTest < Test::Unit::TestCase
|
|
305
729
|
|
306
730
|
def test_finished_chunked
|
307
731
|
parser = HTTPTools::Parser.new
|
308
|
-
code, message,
|
732
|
+
code, message, remainder = nil
|
733
|
+
body = ""
|
309
734
|
|
310
|
-
parser.add_listener(:
|
311
|
-
|
312
|
-
|
735
|
+
parser.add_listener(:header) do
|
736
|
+
code = parser.status_code
|
737
|
+
message = parser.message
|
738
|
+
headers = parser.header
|
739
|
+
end
|
740
|
+
parser.add_listener(:stream) {|chunk| body << chunk}
|
741
|
+
parser.add_listener(:finish) {|r| remainder = r}
|
313
742
|
|
314
743
|
parser << "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"
|
315
744
|
parser << "14\r\n<h1>Hello world</h1>\r\n0\r\nHTTP/1.1 404 Not Found\r\n"
|
@@ -325,7 +754,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
325
754
|
parser = HTTPTools::Parser.new
|
326
755
|
trailer = nil
|
327
756
|
|
328
|
-
parser.add_listener(:trailer) {
|
757
|
+
parser.add_listener(:trailer) {trailer = parser.trailer}
|
329
758
|
|
330
759
|
parser << "HTTP/1.1 200 OK\r\n"
|
331
760
|
parser << "Transfer-Encoding: chunked\r\nTrailer: X-Checksum\r\n\r\n"
|
@@ -340,7 +769,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
340
769
|
parser = HTTPTools::Parser.new
|
341
770
|
trailer = nil
|
342
771
|
|
343
|
-
parser.add_listener(:trailer) {
|
772
|
+
parser.add_listener(:trailer) {trailer = parser.trailer}
|
344
773
|
|
345
774
|
parser << "HTTP/1.1 200 OK\r\n"
|
346
775
|
parser << "Transfer-Encoding: chunked\r\nTrailer: X-Checksum\r\n\r\n"
|
@@ -356,7 +785,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
356
785
|
parser = HTTPTools::Parser.new
|
357
786
|
trailer = nil
|
358
787
|
|
359
|
-
parser.add_listener(:trailer) {
|
788
|
+
parser.add_listener(:trailer) {trailer = parser.trailer}
|
360
789
|
|
361
790
|
parser.force_trailer = true
|
362
791
|
|
@@ -369,6 +798,39 @@ class ResponseTest < Test::Unit::TestCase
|
|
369
798
|
assert(parser.finished?, "parser should be finished")
|
370
799
|
end
|
371
800
|
|
801
|
+
def test_messed_up_iis_header_style_trailer_1
|
802
|
+
parser = HTTPTools::Parser.new
|
803
|
+
trailer = nil
|
804
|
+
|
805
|
+
parser.add_listener(:trailer) {trailer = parser.trailer}
|
806
|
+
|
807
|
+
parser << "HTTP/1.1 200 OK\r\n"
|
808
|
+
parser << "Server: Microsoft-IIS/6.0\r\n"
|
809
|
+
parser << "Transfer-Encoding: chunked\r\nTrailer: Server::\r\n\r\n"
|
810
|
+
parser << "14\r\n<h1>Hello world</h1>\r\n0\r\n"
|
811
|
+
parser << "Server:: Harris Associates L.P.\r\n"
|
812
|
+
parser << "\r\n"
|
813
|
+
|
814
|
+
assert_equal({"Server" => ": Harris Associates L.P."}, trailer)
|
815
|
+
assert(parser.finished?, "parser should be finished")
|
816
|
+
end
|
817
|
+
|
818
|
+
def test_messed_up_iis_header_style_trailer_2
|
819
|
+
parser = HTTPTools::Parser.new
|
820
|
+
trailer = nil
|
821
|
+
|
822
|
+
parser.add_listener(:trailer) {trailer = parser.trailer}
|
823
|
+
|
824
|
+
parser << "HTTP/1.1 200 OK\r\n"
|
825
|
+
parser << "Transfer-Encoding: chunked\r\nTrailer: Server::\r\n\r\n"
|
826
|
+
parser << "14\r\n<h1>Hello world</h1>\r\n0\r\n"
|
827
|
+
parser << "X-DIP:202\r\n"
|
828
|
+
parser << "\r\n"
|
829
|
+
|
830
|
+
assert_equal({"X-DIP" => "202"}, trailer)
|
831
|
+
assert(parser.finished?, "parser should be finished")
|
832
|
+
end
|
833
|
+
|
372
834
|
def test_error_on_unallowed_trailer
|
373
835
|
parser = HTTPTools::Parser.new
|
374
836
|
|
@@ -397,14 +859,14 @@ class ResponseTest < Test::Unit::TestCase
|
|
397
859
|
parser = HTTPTools::Parser.new
|
398
860
|
trailer = nil
|
399
861
|
|
400
|
-
parser.add_listener(:trailer) {
|
862
|
+
parser.add_listener(:trailer) {trailer = parser.trailer}
|
401
863
|
|
402
864
|
parser << "HTTP/1.1 200 OK\r\n"
|
403
865
|
parser << "Transfer-Encoding: chunked\r\nTrailer: X-Checksum\r\n\r\n"
|
404
866
|
parser << "14\r\n<h1>Hello world</h1>\r\n0\r\n"
|
405
867
|
|
406
868
|
assert_raise(HTTPTools::ParseError) do
|
407
|
-
parser << "x-invalid
|
869
|
+
parser << "x-invalid\0key: value\r\n\r\n"
|
408
870
|
end
|
409
871
|
end
|
410
872
|
|
@@ -412,7 +874,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
412
874
|
parser = HTTPTools::Parser.new
|
413
875
|
trailer = nil
|
414
876
|
|
415
|
-
parser.add_listener(:trailer) {
|
877
|
+
parser.add_listener(:trailer) {trailer = parser.trailer}
|
416
878
|
|
417
879
|
parser << "HTTP/1.1 200 OK\r\n"
|
418
880
|
parser << "Transfer-Encoding: chunked\r\nTrailer: X-Checksum\r\n\r\n"
|
@@ -443,4 +905,10 @@ class ResponseTest < Test::Unit::TestCase
|
|
443
905
|
assert_raise(HTTPTools::MessageIncompleteError) {parser.finish}
|
444
906
|
end
|
445
907
|
|
908
|
+
def test_empty
|
909
|
+
parser = HTTPTools::Parser.new
|
910
|
+
|
911
|
+
assert_raise(HTTPTools::EmptyMessageError) {parser.finish}
|
912
|
+
end
|
913
|
+
|
446
914
|
end
|