httpclient 2.1.5 → 2.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +85 -0
  3. data/bin/httpclient +77 -0
  4. data/bin/jsonclient +85 -0
  5. data/lib/hexdump.rb +50 -0
  6. data/lib/http-access2.rb +6 -4
  7. data/lib/httpclient/auth.rb +575 -173
  8. data/lib/httpclient/cacert.pem +3952 -0
  9. data/lib/httpclient/cacert1024.pem +3866 -0
  10. data/lib/httpclient/connection.rb +6 -2
  11. data/lib/httpclient/cookie.rb +162 -504
  12. data/lib/httpclient/http.rb +334 -119
  13. data/lib/httpclient/include_client.rb +85 -0
  14. data/lib/httpclient/jruby_ssl_socket.rb +588 -0
  15. data/lib/httpclient/session.rb +385 -288
  16. data/lib/httpclient/ssl_config.rb +195 -155
  17. data/lib/httpclient/ssl_socket.rb +150 -0
  18. data/lib/httpclient/timeout.rb +14 -10
  19. data/lib/httpclient/util.rb +142 -6
  20. data/lib/httpclient/version.rb +3 -0
  21. data/lib/httpclient/webagent-cookie.rb +459 -0
  22. data/lib/httpclient.rb +509 -202
  23. data/lib/jsonclient.rb +63 -0
  24. data/lib/oauthclient.rb +111 -0
  25. data/sample/async.rb +8 -0
  26. data/sample/auth.rb +11 -0
  27. data/sample/cookie.rb +18 -0
  28. data/sample/dav.rb +103 -0
  29. data/sample/howto.rb +49 -0
  30. data/sample/jsonclient.rb +67 -0
  31. data/sample/oauth_buzz.rb +57 -0
  32. data/sample/oauth_friendfeed.rb +59 -0
  33. data/sample/oauth_twitter.rb +61 -0
  34. data/sample/ssl/0cert.pem +22 -0
  35. data/sample/ssl/0key.pem +30 -0
  36. data/sample/ssl/1000cert.pem +19 -0
  37. data/sample/ssl/1000key.pem +18 -0
  38. data/sample/ssl/htdocs/index.html +10 -0
  39. data/sample/ssl/ssl_client.rb +22 -0
  40. data/sample/ssl/webrick_httpsd.rb +29 -0
  41. data/sample/stream.rb +21 -0
  42. data/sample/thread.rb +27 -0
  43. data/sample/wcat.rb +21 -0
  44. data/test/ca-chain.pem +44 -0
  45. data/test/ca.cert +23 -0
  46. data/test/client-pass.key +18 -0
  47. data/test/client.cert +19 -0
  48. data/test/client.key +15 -0
  49. data/test/helper.rb +131 -0
  50. data/test/htdigest +1 -0
  51. data/test/htpasswd +2 -0
  52. data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
  53. data/test/runner.rb +2 -0
  54. data/test/server.cert +19 -0
  55. data/test/server.key +15 -0
  56. data/test/sslsvr.rb +65 -0
  57. data/test/subca.cert +21 -0
  58. data/test/test_auth.rb +492 -0
  59. data/test/test_cookie.rb +309 -0
  60. data/test/test_hexdump.rb +14 -0
  61. data/test/test_http-access2.rb +508 -0
  62. data/test/test_httpclient.rb +2145 -0
  63. data/test/test_include_client.rb +52 -0
  64. data/test/test_jsonclient.rb +80 -0
  65. data/test/test_ssl.rb +559 -0
  66. data/test/test_webagent-cookie.rb +465 -0
  67. metadata +85 -44
  68. data/lib/httpclient/auth.rb.orig +0 -513
  69. data/lib/httpclient/cacert.p7s +0 -1579
  70. data/lib/httpclient.rb.orig +0 -1020
  71. data/lib/tags +0 -908
@@ -0,0 +1,2145 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('helper', File.dirname(__FILE__))
3
+ require 'tempfile'
4
+
5
+
6
+ class TestHTTPClient < Test::Unit::TestCase
7
+ include Helper
8
+ include HTTPClient::Util
9
+
10
+ def setup
11
+ super
12
+ setup_server
13
+ setup_client
14
+ end
15
+
16
+ def teardown
17
+ super
18
+ end
19
+
20
+ def test_initialize
21
+ setup_proxyserver
22
+ escape_noproxy do
23
+ @proxyio.string = ""
24
+ @client = HTTPClient.new(proxyurl)
25
+ assert_equal(urify(proxyurl), @client.proxy)
26
+ assert_equal(200, @client.head(serverurl).status)
27
+ assert(/accept/ =~ @proxyio.string)
28
+ end
29
+ end
30
+
31
+ def test_agent_name
32
+ @client = HTTPClient.new(nil, "agent_name_foo")
33
+ str = ""
34
+ @client.debug_dev = str
35
+ @client.get(serverurl)
36
+ lines = str.split(/(?:\r?\n)+/)
37
+ assert_equal("= Request", lines[0])
38
+ assert_match(/^User-Agent: agent_name_foo \(#{HTTPClient::VERSION}/, lines[4])
39
+ end
40
+
41
+ def test_from
42
+ @client = HTTPClient.new(nil, nil, "from_bar")
43
+ str = ""
44
+ @client.debug_dev = str
45
+ @client.get(serverurl)
46
+ lines = str.split(/(?:\r?\n)+/)
47
+ assert_equal("= Request", lines[0])
48
+ assert_match(/^From: from_bar/, lines[5])
49
+ end
50
+
51
+ def test_debug_dev
52
+ str = ""
53
+ @client.debug_dev = str
54
+ assert_equal(str.object_id, @client.debug_dev.object_id)
55
+ assert(str.empty?)
56
+ @client.get(serverurl)
57
+ assert(!str.empty?)
58
+ end
59
+
60
+ def test_debug_dev_stream
61
+ str = ""
62
+ @client.debug_dev = str
63
+ conn = @client.get_async(serverurl)
64
+ Thread.pass while !conn.finished?
65
+ assert(!str.empty?)
66
+ end
67
+
68
+ def test_protocol_version_http09
69
+ @client.protocol_version = 'HTTP/0.9'
70
+ @client.debug_dev = str = ''
71
+ @client.test_loopback_http_response << "hello\nworld\n"
72
+ res = @client.get(serverurl + 'hello')
73
+ assert_equal('0.9', res.http_version)
74
+ assert_equal(nil, res.status)
75
+ assert_equal(nil, res.reason)
76
+ assert_equal("hello\nworld\n", res.content)
77
+ lines = str.split(/(?:\r?\n)+/)
78
+ assert_equal("= Request", lines[0])
79
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
80
+ assert_equal("GET /hello HTTP/0.9", lines[3])
81
+ assert_equal("Connection: close", lines[7])
82
+ assert_equal("= Response", lines[8])
83
+ assert_match(/^hello$/, lines[9])
84
+ assert_match(/^world$/, lines[10])
85
+ end
86
+
87
+ def test_protocol_version_http10
88
+ assert_equal(nil, @client.protocol_version)
89
+ @client.protocol_version = 'HTTP/1.0'
90
+ assert_equal('HTTP/1.0', @client.protocol_version)
91
+ str = ""
92
+ @client.debug_dev = str
93
+ @client.get(serverurl + 'hello')
94
+ lines = str.split(/(?:\r?\n)+/)
95
+ assert_equal("= Request", lines[0])
96
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
97
+ assert_equal("GET /hello HTTP/1.0", lines[3])
98
+ assert_equal("Connection: close", lines[7])
99
+ assert_equal("= Response", lines[8])
100
+ end
101
+
102
+ def test_header_accept_by_default
103
+ str = ""
104
+ @client.debug_dev = str
105
+ @client.get(serverurl)
106
+ lines = str.split(/(?:\r?\n)+/)
107
+ assert_equal("Accept: */*", lines[5])
108
+ end
109
+
110
+ def test_header_accept
111
+ str = ""
112
+ @client.debug_dev = str
113
+ @client.get(serverurl, :header => {:Accept => 'text/html'})
114
+ lines = str.split(/(?:\r?\n)+/)
115
+ assert_equal("Accept: text/html", lines[4])
116
+ end
117
+
118
+ def test_header_symbol
119
+ str = ""
120
+ @client.debug_dev = str
121
+ @client.post(serverurl + 'servlet', :header => {:'Content-Type' => 'application/json'}, :body => 'hello')
122
+ lines = str.split(/(?:\r?\n)+/).grep(/^Content-Type/)
123
+ assert_equal(2, lines.size) # 1 for both request and response
124
+ end
125
+
126
+ def test_host_given
127
+ str = ""
128
+ @client.debug_dev = str
129
+ @client.get(serverurl)
130
+ lines = str.split(/(?:\r?\n)+/)
131
+ assert_equal("= Request", lines[0])
132
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
133
+ assert_equal("GET / HTTP/1.1", lines[3])
134
+ assert_equal("Host: localhost:#{serverport}", lines[7])
135
+ #
136
+ @client.reset_all
137
+ str = ""
138
+ @client.debug_dev = str
139
+ @client.get(serverurl, nil, {'Host' => 'foo'})
140
+ lines = str.split(/(?:\r?\n)+/)
141
+ assert_equal("= Request", lines[0])
142
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
143
+ assert_equal("GET / HTTP/1.1", lines[3])
144
+ assert_equal("Host: foo", lines[4]) # use given param
145
+ end
146
+
147
+ def test_redirect_returns_not_modified
148
+ assert_nothing_raised do
149
+ ::Timeout.timeout(2) do
150
+ @client.get(serverurl + 'status', {:status => 306}, {:follow_redirect => true})
151
+ end
152
+ end
153
+ end
154
+
155
+ class LocationRemoveFilter
156
+ def filter_request(req); end
157
+ def filter_response(req, res); res.header.delete('Location'); end
158
+ end
159
+
160
+ def test_redirect_without_location_should_gracefully_fail
161
+ @client.request_filter << LocationRemoveFilter.new
162
+ assert_raises(HTTPClient::BadResponseError) do
163
+ @client.get(serverurl + 'redirect1', :follow_redirect => true)
164
+ end
165
+ end
166
+
167
+ def test_protocol_version_http11
168
+ assert_equal(nil, @client.protocol_version)
169
+ str = ""
170
+ @client.debug_dev = str
171
+ @client.get(serverurl)
172
+ lines = str.split(/(?:\r?\n)+/)
173
+ assert_equal("= Request", lines[0])
174
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
175
+ assert_equal("GET / HTTP/1.1", lines[3])
176
+ assert_equal("Host: localhost:#{serverport}", lines[7])
177
+ @client.protocol_version = 'HTTP/1.1'
178
+ assert_equal('HTTP/1.1', @client.protocol_version)
179
+ str = ""
180
+ @client.debug_dev = str
181
+ @client.get(serverurl)
182
+ lines = str.split(/(?:\r?\n)+/)
183
+ assert_equal("= Request", lines[0])
184
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
185
+ assert_equal("GET / HTTP/1.1", lines[3])
186
+ @client.protocol_version = 'HTTP/1.0'
187
+ str = ""
188
+ @client.debug_dev = str
189
+ @client.get(serverurl)
190
+ lines = str.split(/(?:\r?\n)+/)
191
+ assert_equal("= Request", lines[0])
192
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
193
+ assert_equal("GET / HTTP/1.0", lines[3])
194
+ end
195
+
196
+ def test_proxy
197
+ setup_proxyserver
198
+ escape_noproxy do
199
+ begin
200
+ @client.proxy = "http://あ"
201
+ rescue => e
202
+ assert_match(/InvalidURIError/, e.class.to_s)
203
+ end
204
+ @client.proxy = ""
205
+ assert_nil(@client.proxy)
206
+ @client.proxy = "http://admin:admin@foo:1234"
207
+ assert_equal(urify("http://admin:admin@foo:1234"), @client.proxy)
208
+ uri = urify("http://bar:2345")
209
+ @client.proxy = uri
210
+ assert_equal(uri, @client.proxy)
211
+ #
212
+ @proxyio.string = ""
213
+ @client.proxy = nil
214
+ assert_equal(200, @client.head(serverurl).status)
215
+ assert(/accept/ !~ @proxyio.string)
216
+ #
217
+ @proxyio.string = ""
218
+ @client.proxy = proxyurl
219
+ @client.debug_dev = str = ""
220
+ assert_equal(200, @client.head(serverurl).status)
221
+ assert(/accept/ =~ @proxyio.string)
222
+ assert(/Host: localhost:#{serverport}/ =~ str)
223
+ end
224
+ end
225
+
226
+ def test_host_header
227
+ @client.proxy = proxyurl
228
+ @client.debug_dev = str = ""
229
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n"
230
+ assert_equal(200, @client.head('http://www.example.com/foo').status)
231
+ # ensure no ':80' is added. some servers dislike that.
232
+ assert(/\r\nHost: www\.example\.com\r\n/ =~ str)
233
+ #
234
+ @client.debug_dev = str = ""
235
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n"
236
+ assert_equal(200, @client.head('http://www.example.com:12345/foo').status)
237
+ # ensure ':12345' exists.
238
+ assert(/\r\nHost: www\.example\.com:12345\r\n/ =~ str)
239
+ end
240
+
241
+ def test_proxy_env
242
+ setup_proxyserver
243
+ escape_env do
244
+ ENV['http_proxy'] = "http://admin:admin@foo:1234"
245
+ ENV['NO_PROXY'] = "foobar"
246
+ client = HTTPClient.new
247
+ assert_equal(urify("http://admin:admin@foo:1234"), client.proxy)
248
+ assert_equal('foobar', client.no_proxy)
249
+ end
250
+ end
251
+
252
+ def test_proxy_env_cgi
253
+ setup_proxyserver
254
+ escape_env do
255
+ ENV['REQUEST_METHOD'] = 'GET' # CGI environment emulation
256
+ ENV['http_proxy'] = "http://admin:admin@foo:1234"
257
+ ENV['no_proxy'] = "foobar"
258
+ client = HTTPClient.new
259
+ assert_equal(nil, client.proxy)
260
+ ENV['CGI_HTTP_PROXY'] = "http://admin:admin@foo:1234"
261
+ client = HTTPClient.new
262
+ assert_equal(urify("http://admin:admin@foo:1234"), client.proxy)
263
+ end
264
+ end
265
+
266
+ def test_empty_proxy_env
267
+ setup_proxyserver
268
+ escape_env do
269
+ ENV['http_proxy'] = ""
270
+ client = HTTPClient.new
271
+ assert_equal(nil, client.proxy)
272
+ end
273
+ end
274
+
275
+ def test_noproxy_for_localhost
276
+ @proxyio.string = ""
277
+ @client.proxy = proxyurl
278
+ assert_equal(200, @client.head(serverurl).status)
279
+ assert(/accept/ !~ @proxyio.string)
280
+ end
281
+
282
+ def test_no_proxy
283
+ setup_proxyserver
284
+ escape_noproxy do
285
+ # proxy is not set.
286
+ assert_equal(nil, @client.no_proxy)
287
+ @client.no_proxy = 'localhost'
288
+ assert_equal('localhost', @client.no_proxy)
289
+ @proxyio.string = ""
290
+ @client.proxy = nil
291
+ assert_equal(200, @client.head(serverurl).status)
292
+ assert(/accept/ !~ @proxyio.string)
293
+ #
294
+ @proxyio.string = ""
295
+ @client.proxy = proxyurl
296
+ assert_equal(200, @client.head(serverurl).status)
297
+ assert(/accept/ !~ @proxyio.string)
298
+ #
299
+ @client.no_proxy = 'foobar'
300
+ @proxyio.string = ""
301
+ @client.proxy = proxyurl
302
+ assert_equal(200, @client.head(serverurl).status)
303
+ assert(/accept/ =~ @proxyio.string)
304
+ #
305
+ @client.no_proxy = 'foobar,localhost:baz'
306
+ @proxyio.string = ""
307
+ @client.proxy = proxyurl
308
+ assert_equal(200, @client.head(serverurl).status)
309
+ assert(/accept/ !~ @proxyio.string)
310
+ #
311
+ @client.no_proxy = 'foobar,localhost:443'
312
+ @proxyio.string = ""
313
+ @client.proxy = proxyurl
314
+ assert_equal(200, @client.head(serverurl).status)
315
+ assert(/accept/ =~ @proxyio.string)
316
+ #
317
+ @client.no_proxy = "foobar,localhost:443:localhost:#{serverport},baz"
318
+ @proxyio.string = ""
319
+ @client.proxy = proxyurl
320
+ assert_equal(200, @client.head(serverurl).status)
321
+ assert(/accept/ !~ @proxyio.string)
322
+ end
323
+ end
324
+
325
+ def test_no_proxy_with_initial_dot
326
+ @client.debug_dev = str = ""
327
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n"
328
+ @client.no_proxy = ''
329
+ @client.proxy = proxyurl
330
+ @client.head('http://www.foo.com')
331
+ assert(/CONNECT TO localhost/ =~ str, 'via proxy')
332
+ #
333
+ @client.debug_dev = str = ""
334
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n"
335
+ @client.no_proxy = '.foo.com'
336
+ @client.proxy = proxyurl
337
+ @client.head('http://www.foo.com')
338
+ assert(/CONNECT TO www.foo.com/ =~ str, 'no proxy because .foo.com matches with www.foo.com')
339
+ #
340
+ @client.debug_dev = str = ""
341
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n"
342
+ @client.no_proxy = '.foo.com'
343
+ @client.proxy = proxyurl
344
+ @client.head('http://foo.com')
345
+ assert(/CONNECT TO localhost/ =~ str, 'via proxy because .foo.com does not matche with foo.com')
346
+ #
347
+ @client.debug_dev = str = ""
348
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n"
349
+ @client.no_proxy = 'foo.com'
350
+ @client.proxy = proxyurl
351
+ @client.head('http://foo.com')
352
+ assert(/CONNECT TO foo.com/ =~ str, 'no proxy because foo.com matches with foo.com')
353
+ end
354
+
355
+ def test_cookie_update_while_authentication
356
+ escape_noproxy do
357
+ @client.test_loopback_http_response << <<EOS
358
+ HTTP/1.0 401\r
359
+ Date: Fri, 19 Dec 2008 11:57:29 GMT\r
360
+ Content-Type: text/plain\r
361
+ Content-Length: 0\r
362
+ WWW-Authenticate: Basic realm="hello"\r
363
+ Set-Cookie: foo=bar; path=/; domain=.example.org; expires=#{Time.at(1924873200).httpdate}\r
364
+ \r
365
+ EOS
366
+ @client.test_loopback_http_response << <<EOS
367
+ HTTP/1.1 200 OK\r
368
+ Content-Length: 5\r
369
+ Connection: close\r
370
+ \r
371
+ hello
372
+ EOS
373
+ @client.debug_dev = str = ''
374
+ @client.set_auth("http://www.example.org/baz/", 'admin', 'admin')
375
+ assert_equal('hello', @client.get('http://www.example.org/baz/foo').content)
376
+ assert_match(/^Cookie: foo=bar/, str)
377
+ assert_match(/^Authorization: Basic YWRtaW46YWRtaW4=/, str)
378
+ end
379
+ end
380
+
381
+
382
+ def test_proxy_ssl
383
+ escape_noproxy do
384
+ @client.proxy = 'http://admin:admin@localhost:8080/'
385
+ # disconnected at initial 'CONNECT' so there're 2 loopback responses
386
+ @client.test_loopback_http_response << <<EOS
387
+ HTTP/1.0 407 Proxy Authentication Required\r
388
+ Date: Fri, 19 Dec 2008 11:57:29 GMT\r
389
+ Content-Type: text/plain\r
390
+ Content-Length: 0\r
391
+ Proxy-Authenticate: Basic realm="hello"\r
392
+ Proxy-Connection: close\r
393
+ \r
394
+ EOS
395
+ @client.test_loopback_http_response << <<EOS
396
+ HTTP/1.0 200 Connection established\r
397
+ \r
398
+ HTTP/1.1 200 OK\r
399
+ Content-Length: 5\r
400
+ Connection: close\r
401
+ \r
402
+ hello
403
+ EOS
404
+ assert_equal('hello', @client.get('https://localhost:17171/baz').content)
405
+ end
406
+ end
407
+
408
+ def test_loopback_response
409
+ @client.test_loopback_response << 'message body 1'
410
+ @client.test_loopback_response << 'message body 2'
411
+ assert_equal('message body 1', @client.get_content('http://somewhere'))
412
+ assert_equal('message body 2', @client.get_content('http://somewhere'))
413
+ #
414
+ @client.debug_dev = str = ''
415
+ @client.test_loopback_response << 'message body 3'
416
+ assert_equal('message body 3', @client.get_content('http://somewhere'))
417
+ assert_match(/message body 3/, str)
418
+ end
419
+
420
+ def test_loopback_response_stream
421
+ @client.test_loopback_response << 'message body 1'
422
+ @client.test_loopback_response << 'message body 2'
423
+ conn = @client.get_async('http://somewhere')
424
+ Thread.pass while !conn.finished?
425
+ assert_equal('message body 1', conn.pop.content.read)
426
+ conn = @client.get_async('http://somewhere')
427
+ Thread.pass while !conn.finished?
428
+ assert_equal('message body 2', conn.pop.content.read)
429
+ end
430
+
431
+ def test_loopback_http_response
432
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\ncontent-length: 100\n\nmessage body 1"
433
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\ncontent-length: 100\n\nmessage body 2"
434
+ assert_equal('message body 1', @client.get_content('http://somewhere'))
435
+ assert_equal('message body 2', @client.get_content('http://somewhere'))
436
+ end
437
+
438
+ def test_multiline_header
439
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\nX-Foo: XXX\n YYY\nX-Bar: \n XXX\n\tYYY\ncontent-length: 100\n\nmessage body 1"
440
+ res = @client.get('http://somewhere')
441
+ assert_equal('message body 1', res.content)
442
+ assert_equal(['XXX YYY'], res.header['x-foo'])
443
+ assert_equal(['XXX YYY'], res.header['x-bar'])
444
+ end
445
+
446
+ def test_broken_header
447
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\nXXXXX\ncontent-length: 100\n\nmessage body 1"
448
+ res = @client.get('http://somewhere')
449
+ assert_equal('message body 1', res.content)
450
+ end
451
+
452
+ def test_request_uri_in_response
453
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\ncontent-length: 100\n\nmessage body"
454
+ assert_equal(urify('http://google.com/'), @client.get('http://google.com/').header.request_uri)
455
+ end
456
+
457
+ def test_request_uri_in_response_when_redirect
458
+ expected = urify(serverurl + 'hello')
459
+ assert_equal(expected, @client.get(serverurl + 'redirect1', :follow_redirect => true).header.request_uri)
460
+ assert_equal(expected, @client.get(serverurl + 'redirect2', :follow_redirect => true).header.request_uri)
461
+ end
462
+
463
+ def test_redirect_non_https
464
+ url = serverurl + 'redirect1'
465
+ https_url = urify(url)
466
+ https_url.scheme = 'https'
467
+ #
468
+ redirect_to_http = "HTTP/1.0 302 OK\nLocation: #{url}\n\n"
469
+ redirect_to_https = "HTTP/1.0 302 OK\nLocation: #{https_url}\n\n"
470
+ #
471
+ # https -> http is denied
472
+ @client.test_loopback_http_response << redirect_to_http
473
+ assert_raises(HTTPClient::BadResponseError) do
474
+ @client.get_content(https_url)
475
+ end
476
+ #
477
+ # http -> http is OK
478
+ @client.reset_all
479
+ @client.test_loopback_http_response << redirect_to_http
480
+ assert_equal('hello', @client.get_content(url))
481
+ #
482
+ # http -> https is OK
483
+ @client.reset_all
484
+ @client.test_loopback_http_response << redirect_to_https
485
+ assert_raises(OpenSSL::SSL::SSLError) do
486
+ # trying to normal endpoint with SSL -> SSL negotiation failure
487
+ @client.get_content(url)
488
+ end
489
+ #
490
+ # https -> https is OK
491
+ @client.reset_all
492
+ @client.test_loopback_http_response << redirect_to_https
493
+ assert_raises(OpenSSL::SSL::SSLError) do
494
+ # trying to normal endpoint with SSL -> SSL negotiation failure
495
+ @client.get_content(https_url)
496
+ end
497
+ #
498
+ # https -> http with strict_redirect_uri_callback
499
+ @client.redirect_uri_callback = @client.method(:strict_redirect_uri_callback)
500
+ @client.test_loopback_http_response << redirect_to_http
501
+ assert_raises(HTTPClient::BadResponseError) do
502
+ @client.get_content(https_url)
503
+ end
504
+ end
505
+
506
+ def test_redirect_see_other
507
+ assert_equal('hello', @client.post_content(serverurl + 'redirect_see_other'))
508
+ end
509
+
510
+ def test_redirect_relative
511
+ @client.test_loopback_http_response << "HTTP/1.0 302 OK\nLocation: hello\n\n"
512
+ silent do
513
+ assert_equal('hello', @client.get_content(serverurl + 'redirect1'))
514
+ end
515
+ #
516
+ @client.reset_all
517
+ @client.redirect_uri_callback = @client.method(:strict_redirect_uri_callback)
518
+ assert_equal('hello', @client.get_content(serverurl + 'redirect1'))
519
+ @client.reset_all
520
+ @client.test_loopback_http_response << "HTTP/1.0 302 OK\nLocation: hello\n\n"
521
+ begin
522
+ @client.get_content(serverurl + 'redirect1')
523
+ assert(false)
524
+ rescue HTTPClient::BadResponseError => e
525
+ assert_equal(302, e.res.status)
526
+ end
527
+ end
528
+
529
+ def test_redirect_https_relative
530
+ url = serverurl + 'redirect1'
531
+ https_url = urify(url)
532
+ https_url.scheme = 'https'
533
+ @client.test_loopback_http_response << "HTTP/1.0 302 OK\nLocation: /foo\n\n"
534
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\n\nhello"
535
+ silent do
536
+ assert_equal('hello', @client.get_content(https_url))
537
+ end
538
+ end
539
+
540
+ def test_no_content
541
+ assert_nothing_raised do
542
+ ::Timeout.timeout(2) do
543
+ @client.get(serverurl + 'status', :status => 101)
544
+ @client.get(serverurl + 'status', :status => 204)
545
+ @client.get(serverurl + 'status', :status => 304)
546
+ end
547
+ end
548
+ end
549
+
550
+ def test_get_content
551
+ assert_equal('hello', @client.get_content(serverurl + 'hello'))
552
+ assert_equal('hello', @client.get_content(serverurl + 'redirect1'))
553
+ assert_equal('hello', @client.get_content(serverurl + 'redirect2'))
554
+ url = serverurl.sub(/localhost/, '127.0.0.1')
555
+ assert_equal('hello', @client.get_content(url + 'hello'))
556
+ assert_equal('hello', @client.get_content(url + 'redirect1'))
557
+ assert_equal('hello', @client.get_content(url + 'redirect2'))
558
+ @client.reset(serverurl)
559
+ @client.reset(url)
560
+ @client.reset(serverurl)
561
+ @client.reset(url)
562
+ assert_raises(HTTPClient::BadResponseError) do
563
+ @client.get_content(serverurl + 'notfound')
564
+ end
565
+ assert_raises(HTTPClient::BadResponseError) do
566
+ @client.get_content(serverurl + 'redirect_self')
567
+ end
568
+ called = false
569
+ @client.redirect_uri_callback = lambda { |uri, res|
570
+ newuri = res.header['location'][0]
571
+ called = true
572
+ newuri
573
+ }
574
+ assert_equal('hello', @client.get_content(serverurl + 'relative_redirect'))
575
+ assert(called)
576
+ end
577
+
578
+ def test_get_content_with_base_url
579
+ @client = HTTPClient.new(:base_url => serverurl)
580
+ assert_equal('hello', @client.get_content('/hello'))
581
+ assert_equal('hello', @client.get_content('/redirect1'))
582
+ assert_equal('hello', @client.get_content('/redirect2'))
583
+ @client.reset('/')
584
+ assert_raises(HTTPClient::BadResponseError) do
585
+ @client.get_content('/notfound')
586
+ end
587
+ assert_raises(HTTPClient::BadResponseError) do
588
+ @client.get_content('/redirect_self')
589
+ end
590
+ called = false
591
+ @client.redirect_uri_callback = lambda { |uri, res|
592
+ newuri = res.header['location'][0]
593
+ called = true
594
+ newuri
595
+ }
596
+ assert_equal('hello', @client.get_content('/relative_redirect'))
597
+ assert(called)
598
+ end
599
+
600
+ GZIP_CONTENT = "\x1f\x8b\x08\x00\x1a\x96\xe0\x4c\x00\x03\xcb\x48\xcd\xc9\xc9\x07\x00\x86\xa6\x10\x36\x05\x00\x00\x00"
601
+ DEFLATE_CONTENT = "\x78\x9c\xcb\x48\xcd\xc9\xc9\x07\x00\x06\x2c\x02\x15"
602
+ DEFLATE_NOHEADER_CONTENT = "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15"
603
+ [GZIP_CONTENT, DEFLATE_CONTENT, DEFLATE_NOHEADER_CONTENT].each do |content|
604
+ content.force_encoding('BINARY') if content.respond_to?(:force_encoding)
605
+ end
606
+ def test_get_gzipped_content
607
+ @client.transparent_gzip_decompression = false
608
+ content = @client.get_content(serverurl + 'compressed?enc=gzip')
609
+ assert_not_equal('hello', content)
610
+ assert_equal(GZIP_CONTENT, content)
611
+ @client.transparent_gzip_decompression = true
612
+ @client.reset_all
613
+ assert_equal('hello', @client.get_content(serverurl + 'compressed?enc=gzip'))
614
+ assert_equal('hello', @client.get_content(serverurl + 'compressed?enc=deflate'))
615
+ assert_equal('hello', @client.get_content(serverurl + 'compressed?enc=deflate_noheader'))
616
+ @client.transparent_gzip_decompression = false
617
+ @client.reset_all
618
+ end
619
+
620
+ def test_get_content_with_block
621
+ @client.get_content(serverurl + 'hello') do |str|
622
+ assert_equal('hello', str)
623
+ end
624
+ @client.get_content(serverurl + 'redirect1') do |str|
625
+ assert_equal('hello', str)
626
+ end
627
+ @client.get_content(serverurl + 'redirect2') do |str|
628
+ assert_equal('hello', str)
629
+ end
630
+ end
631
+
632
+ def test_post_content
633
+ assert_equal('hello', @client.post_content(serverurl + 'hello'))
634
+ assert_equal('hello', @client.post_content(serverurl + 'redirect1'))
635
+ assert_equal('hello', @client.post_content(serverurl + 'redirect2'))
636
+ assert_raises(HTTPClient::BadResponseError) do
637
+ @client.post_content(serverurl + 'notfound')
638
+ end
639
+ assert_raises(HTTPClient::BadResponseError) do
640
+ @client.post_content(serverurl + 'redirect_self')
641
+ end
642
+ called = false
643
+ @client.redirect_uri_callback = lambda { |uri, res|
644
+ newuri = res.header['location'][0]
645
+ called = true
646
+ newuri
647
+ }
648
+ assert_equal('hello', @client.post_content(serverurl + 'relative_redirect'))
649
+ assert(called)
650
+ end
651
+
652
+ def test_post_content_io
653
+ post_body = StringIO.new("1234567890")
654
+ assert_equal('post,1234567890', @client.post_content(serverurl + 'servlet', post_body))
655
+ post_body = StringIO.new("1234567890")
656
+ assert_equal('post,1234567890', @client.post_content(serverurl + 'servlet_redirect', post_body))
657
+ #
658
+ post_body = StringIO.new("1234567890")
659
+ post_body.read(5)
660
+ assert_equal('post,67890', @client.post_content(serverurl + 'servlet_redirect', post_body))
661
+ end
662
+
663
+ def test_head
664
+ assert_equal("head", @client.head(serverurl + 'servlet').header["x-head"][0])
665
+ param = {'1'=>'2', '3'=>'4'}
666
+ res = @client.head(serverurl + 'servlet', param)
667
+ assert_equal(param, params(res.header["x-query"][0]))
668
+ end
669
+
670
+ def test_head_async
671
+ param = {'1'=>'2', '3'=>'4'}
672
+ conn = @client.head_async(serverurl + 'servlet', param)
673
+ Thread.pass while !conn.finished?
674
+ res = conn.pop
675
+ assert_equal(param, params(res.header["x-query"][0]))
676
+ end
677
+
678
+ def test_get
679
+ assert_equal("get", @client.get(serverurl + 'servlet').content)
680
+ param = {'1'=>'2', '3'=>'4'}
681
+ res = @client.get(serverurl + 'servlet', param)
682
+ assert_equal(param, params(res.header["x-query"][0]))
683
+ assert_nil(res.contenttype)
684
+ #
685
+ url = serverurl.to_s + 'servlet?5=6&7=8'
686
+ res = @client.get(url, param)
687
+ assert_equal(param.merge("5"=>"6", "7"=>"8"), params(res.header["x-query"][0]))
688
+ assert_nil(res.contenttype)
689
+ end
690
+
691
+ def test_get_with_base_url
692
+ @client = HTTPClient.new(:base_url => serverurl)
693
+ assert_equal("get", @client.get('/servlet').content)
694
+ param = {'1'=>'2', '3'=>'4'}
695
+ res = @client.get('/servlet', param)
696
+ assert_equal(param, params(res.header["x-query"][0]))
697
+ assert_nil(res.contenttype)
698
+ #
699
+ @client.base_url = serverurl[0...-1] + '/servlet'
700
+ url = '?5=6&7=8'
701
+ res = @client.get(url, param)
702
+ assert_equal(param.merge("5"=>"6", "7"=>"8"), params(res.header["x-query"][0]))
703
+ assert_nil(res.contenttype)
704
+ end
705
+
706
+ def test_get_with_default_header
707
+ @client = HTTPClient.new(:base_url => serverurl, :default_header => {'x-header' => 'custom'})
708
+ assert_equal('custom', @client.get('/servlet').headers['X-Header'])
709
+ @client.default_header = {'x-header' => 'custom2'}
710
+ assert_equal('custom2', @client.get('/servlet').headers['X-Header'])
711
+ # passing Hash overrides
712
+ assert_equal('custom3', @client.get('/servlet', :header => {'x-header' => 'custom3'}).headers['X-Header'])
713
+ # passing Array does not override
714
+ assert_equal('custom2, custom4', @client.get('/servlet', :header => [['x-header', 'custom4']]).headers['X-Header'])
715
+ end
716
+
717
+ def test_head_follow_redirect
718
+ expected = urify(serverurl + 'hello')
719
+ assert_equal(expected, @client.head(serverurl + 'hello', :follow_redirect => true).header.request_uri)
720
+ assert_equal(expected, @client.head(serverurl + 'redirect1', :follow_redirect => true).header.request_uri)
721
+ assert_equal(expected, @client.head(serverurl + 'redirect2', :follow_redirect => true).header.request_uri)
722
+ end
723
+
724
+ def test_get_follow_redirect
725
+ assert_equal('hello', @client.get(serverurl + 'hello', :follow_redirect => true).body)
726
+ assert_equal('hello', @client.get(serverurl + 'redirect1', :follow_redirect => true).body)
727
+
728
+ res = @client.get(serverurl + 'redirect2', :follow_redirect => true)
729
+ assert_equal('hello', res.body)
730
+ assert_equal("http://localhost:#{@serverport}/hello", res.header.request_uri.to_s)
731
+ assert_equal("http://localhost:#{@serverport}/redirect3", res.previous.header.request_uri.to_s)
732
+ assert_equal("http://localhost:#{@serverport}/redirect2", res.previous.previous.header.request_uri.to_s)
733
+ assert_equal(nil, res.previous.previous.previous)
734
+ end
735
+
736
+ def test_get_follow_redirect_with_query
737
+ assert_equal('hello?1=2&3=4', @client.get(serverurl + 'redirect1', :query => {1 => 2, 3 => 4}, :follow_redirect => true).body)
738
+ end
739
+
740
+ def test_get_async
741
+ param = {'1'=>'2', '3'=>'4'}
742
+ conn = @client.get_async(serverurl + 'servlet', param)
743
+ Thread.pass while !conn.finished?
744
+ res = conn.pop
745
+ assert_equal(param, params(res.header["x-query"][0]))
746
+ end
747
+
748
+ def test_get_async_with_base_url
749
+ param = {'1'=>'2', '3'=>'4'}
750
+ @client = HTTPClient.new(:base_url => serverurl)
751
+
752
+ # Use preconfigured :base_url
753
+ conn = @client.get_async('servlet', param)
754
+ Thread.pass while !conn.finished?
755
+ res = conn.pop
756
+ assert_equal(param, params(res.header["x-query"][0]))
757
+ # full URL still works
758
+ conn = @client.get_async(serverurl + 'servlet', param)
759
+ Thread.pass while !conn.finished?
760
+ res = conn.pop
761
+ assert_equal(param, params(res.header["x-query"][0]))
762
+ end
763
+
764
+ def test_get_async_for_largebody
765
+ conn = @client.get_async(serverurl + 'largebody')
766
+ res = conn.pop
767
+ assert_equal(1000*1000, res.content.read.length)
768
+ end
769
+
770
+ if RUBY_VERSION > "1.9"
771
+ def test_post_async_with_default_internal
772
+ original_encoding = Encoding.default_internal
773
+ Encoding.default_internal = Encoding::UTF_8
774
+ begin
775
+ post_body = StringIO.new("こんにちは")
776
+ conn = @client.post_async(serverurl + 'servlet', post_body)
777
+ Thread.pass while !conn.finished?
778
+ res = conn.pop
779
+ assert_equal 'post,こんにちは', res.content.read
780
+ ensure
781
+ Encoding.default_internal = original_encoding
782
+ end
783
+ end
784
+ end
785
+
786
+ def test_get_with_block
787
+ called = false
788
+ res = @client.get(serverurl + 'servlet') { |str|
789
+ assert_equal('get', str)
790
+ called = true
791
+ }
792
+ assert(called)
793
+ # res does not have a content
794
+ assert_nil(res.content)
795
+ end
796
+
797
+ def test_get_with_block_arity_2
798
+ called = false
799
+ res = @client.get(serverurl + 'servlet') { |blk_res, str|
800
+ assert_equal(200, blk_res.status)
801
+ assert_equal('get', str)
802
+ called = true
803
+ }
804
+ assert(called)
805
+ # res does not have a content
806
+ assert_nil(res.content)
807
+ end
808
+
809
+ def test_get_with_block_and_redirects
810
+ called = false
811
+ res = @client.get(serverurl + 'servlet', :follow_redirect => true) { |str|
812
+ assert_equal('get', str)
813
+ called = true
814
+ }
815
+ assert(called)
816
+ # res does not have a content
817
+ assert_nil(res.content)
818
+ end
819
+
820
+ def test_get_with_block_arity_2_and_redirects
821
+ called = false
822
+ res = @client.get(serverurl + 'servlet', :follow_redirect => true) { |blk_res, str|
823
+ assert_equal(200, blk_res.status)
824
+ assert_equal('get', str)
825
+ called = true
826
+ }
827
+ assert(called)
828
+ # res does not have a content
829
+ assert_nil(res.content)
830
+ end
831
+
832
+ def test_get_with_block_string_recycle
833
+ @client.read_block_size = 2
834
+ body = []
835
+ _res = @client.get(serverurl + 'servlet') { |str|
836
+ body << str
837
+ }
838
+ assert_equal(2, body.size)
839
+ assert_equal("get", body.join) # Was "tt" by String object recycle...
840
+ end
841
+
842
+ def test_get_with_block_chunked_string_recycle
843
+ server = TCPServer.open('localhost', 0)
844
+ server_thread = Thread.new {
845
+ Thread.abort_on_exception = true
846
+ sock = server.accept
847
+ create_keepalive_thread(1, sock)
848
+ }
849
+ url = "http://localhost:#{server.addr[1]}/"
850
+ body = []
851
+ begin
852
+ _res = @client.get(url + 'chunked') { |str|
853
+ body << str
854
+ }
855
+ ensure
856
+ server.close
857
+ server_thread.join
858
+ end
859
+ assert_equal('abcdefghijklmnopqrstuvwxyz1234567890abcdef', body.join)
860
+ end
861
+
862
+ def test_post
863
+ assert_equal("post", @client.post(serverurl + 'servlet', '').content[0, 4])
864
+ param = {'1'=>'2', '3'=>'4'}
865
+ res = @client.post(serverurl + 'servlet', param)
866
+ assert_equal(param, params(res.header["x-query"][0]))
867
+ end
868
+
869
+ def test_post_empty
870
+ @client.debug_dev = str = ''
871
+ # nil body means 'no content' that is allowed but WEBrick cannot handle it.
872
+ @client.post(serverurl + 'servlet', :body => nil)
873
+ # request does not have 'Content-Type'
874
+ assert_equal(1, str.scan(/content-type/i).size)
875
+ end
876
+
877
+ def test_post_with_query
878
+ # this {:query => 'query'} recognized as body
879
+ res = @client.post(serverurl + 'servlet', :query => 'query')
880
+ assert_equal("post", res.content[0, 4])
881
+ assert_equal("query=query", res.headers["X-Query"])
882
+ assert_equal("", res.headers["X-Request-Query"])
883
+ end
884
+
885
+ def test_post_with_query_and_body
886
+ res = @client.post(serverurl + 'servlet', :query => {:query => 'query'}, :body => {:body => 'body'})
887
+ assert_equal("post", res.content[0, 4])
888
+ assert_equal("body=body", res.headers["X-Query"])
889
+ assert_equal("query=query", res.headers["X-Request-Query"])
890
+ end
891
+
892
+ def test_post_follow_redirect
893
+ assert_equal('hello', @client.post(serverurl + 'hello', :follow_redirect => true).body)
894
+ assert_equal('hello', @client.post(serverurl + 'redirect1', :follow_redirect => true).body)
895
+ assert_equal('hello', @client.post(serverurl + 'redirect2', :follow_redirect => true).body)
896
+ end
897
+
898
+ def test_post_with_content_type
899
+ param = [['1', '2'], ['3', '4']]
900
+ ext = {'content-type' => 'application/x-www-form-urlencoded', 'hello' => 'world'}
901
+ assert_equal("post", @client.post(serverurl + 'servlet', '').content[0, 4], ext)
902
+ res = @client.post(serverurl + 'servlet', param, ext)
903
+ assert_equal(Hash[param], params(res.header["x-query"][0]))
904
+ #
905
+ ext = [['content-type', 'multipart/form-data'], ['hello', 'world']]
906
+ assert_equal("post", @client.post(serverurl + 'servlet', '').content[0, 4], ext)
907
+ res = @client.post(serverurl + 'servlet', param, ext)
908
+ assert_match(/Content-Disposition: form-data; name="1"/, res.content)
909
+ assert_match(/Content-Disposition: form-data; name="3"/, res.content)
910
+ #
911
+ ext = {'content-type' => 'multipart/form-data; boundary=hello'}
912
+ assert_equal("post", @client.post(serverurl + 'servlet', '').content[0, 4], ext)
913
+ res = @client.post(serverurl + 'servlet', param, ext)
914
+ assert_match(/Content-Disposition: form-data; name="1"/, res.content)
915
+ assert_match(/Content-Disposition: form-data; name="3"/, res.content)
916
+ assert_equal("post,--hello\r\nContent-Disposition: form-data; name=\"1\"\r\n\r\n2\r\n--hello\r\nContent-Disposition: form-data; name=\"3\"\r\n\r\n4\r\n--hello--\r\n\r\n", res.content)
917
+ end
918
+
919
+ def test_post_with_custom_multipart_and_boolean_params
920
+ param = [['boolean_true', true]]
921
+ ext = { 'content-type' => 'multipart/form-data' }
922
+ assert_equal("post", @client.post(serverurl + 'servlet', '').content[0, 4], ext)
923
+ res = @client.post(serverurl + 'servlet', param, ext)
924
+ assert_match(/Content-Disposition: form-data; name="boolean_true"\r\n\r\ntrue\r\n/, res.content)
925
+ #
926
+ param = [['boolean_false', false]]
927
+ res = @client.post(serverurl + 'servlet', param, ext)
928
+ assert_match(/Content-Disposition: form-data; name="boolean_false"\r\n\r\nfalse\r\n/, res.content)
929
+ #
930
+ param = [['nil', nil]]
931
+ res = @client.post(serverurl + 'servlet', param, ext)
932
+ assert_match(/Content-Disposition: form-data; name="nil"\r\n\r\n\r\n/, res.content)
933
+ end
934
+
935
+ def test_post_with_file
936
+ STDOUT.sync = true
937
+ File.open(__FILE__) do |file|
938
+ res = @client.post(serverurl + 'servlet', {1=>2, 3=>file})
939
+ assert_match(/^Content-Disposition: form-data; name="1"\r\n/nm, res.content)
940
+ assert_match(/^Content-Disposition: form-data; name="3";/, res.content)
941
+ assert_match(/FIND_TAG_IN_THIS_FILE/, res.content)
942
+ end
943
+ end
944
+
945
+ def test_post_with_file_without_size
946
+ STDOUT.sync = true
947
+ File.open(__FILE__) do |file|
948
+ def file.size
949
+ # Simulates some strange Windows behaviour
950
+ raise SystemCallError.new "Unknown Error (20047)"
951
+ end
952
+ assert_nothing_raised do
953
+ @client.post(serverurl + 'servlet', {1=>2, 3=>file})
954
+ end
955
+ end
956
+ end
957
+
958
+ def test_post_with_io # streaming, but not chunked
959
+ myio = StringIO.new("X" * (HTTP::Message::Body::DEFAULT_CHUNK_SIZE + 1))
960
+ def myio.read(*args)
961
+ @called ||= 0
962
+ @called += 1
963
+ super
964
+ end
965
+ def myio.called
966
+ @called
967
+ end
968
+ @client.debug_dev = str = StringIO.new
969
+ res = @client.post(serverurl + 'servlet', {1=>2, 3=>myio})
970
+ assert_match(/\r\nContent-Disposition: form-data; name="1"\r\n/m, res.content)
971
+ assert_match(/\r\n2\r\n/m, res.content)
972
+ assert_match(/\r\nContent-Disposition: form-data; name="3"; filename=""\r\n/m, res.content)
973
+ assert_match(/\r\nContent-Length:/m, str.string)
974
+ # HTTPClient reads from head to 'size'; CHUNK_SIZE bytes then 1 byte, that's all.
975
+ assert_equal(2, myio.called)
976
+ end
977
+
978
+ def test_post_with_io_nosize # streaming + chunked post
979
+ myio = StringIO.new("4")
980
+ def myio.size
981
+ nil
982
+ end
983
+ @client.debug_dev = str = StringIO.new
984
+ res = @client.post(serverurl + 'servlet', {1=>2, 3=>myio})
985
+ assert_match(/\r\nContent-Disposition: form-data; name="1"\r\n/m, res.content)
986
+ assert_match(/\r\n2\r\n/m, res.content)
987
+ assert_match(/\r\nContent-Disposition: form-data; name="3"; filename=""\r\n/m, res.content)
988
+ assert_match(/\r\n4\r\n/m, res.content)
989
+ assert_match(/\r\nTransfer-Encoding: chunked\r\n/m, str.string)
990
+ end
991
+
992
+ def test_post_with_sized_io
993
+ myio = StringIO.new("45")
994
+ def myio.size
995
+ 1
996
+ end
997
+ res = @client.post(serverurl + 'servlet', myio)
998
+ assert_equal('post,4', res.content)
999
+ end
1000
+
1001
+ def test_post_with_sized_io_part
1002
+ myio = StringIO.new("45")
1003
+ def myio.size
1004
+ 1
1005
+ end
1006
+ @client.debug_dev = str = StringIO.new
1007
+ _res = @client.post(serverurl + 'servlet', { :file => myio })
1008
+ assert_match(/\r\n4\r\n/, str.string, 'should send "4" not "45"')
1009
+ end
1010
+
1011
+ def test_post_with_unknown_sized_io_part
1012
+ myio1 = StringIO.new("123")
1013
+ myio2 = StringIO.new("45")
1014
+ class << myio1
1015
+ undef :size
1016
+ end
1017
+ class << myio2
1018
+ # This does not work because other file is 'unknown sized'
1019
+ def size
1020
+ 1
1021
+ end
1022
+ end
1023
+ @client.debug_dev = str = StringIO.new
1024
+ _res = @client.post(serverurl + 'servlet', { :file1 => myio1, :file2 => myio2 })
1025
+ assert_match(/\r\n45\r\n/, str.string)
1026
+ end
1027
+
1028
+ def test_post_async
1029
+ param = {'1'=>'2', '3'=>'4'}
1030
+ conn = @client.post_async(serverurl + 'servlet', param)
1031
+ Thread.pass while !conn.finished?
1032
+ res = conn.pop
1033
+ assert_equal(param, params(res.header["x-query"][0]))
1034
+ end
1035
+
1036
+ def test_post_with_block
1037
+ called = false
1038
+ res = @client.post(serverurl + 'servlet', '') { |str|
1039
+ assert_equal('post,', str)
1040
+ called = true
1041
+ }
1042
+ assert(called)
1043
+ assert_nil(res.content)
1044
+ #
1045
+ called = false
1046
+ param = [['1', '2'], ['3', '4']]
1047
+ res = @client.post(serverurl + 'servlet', param) { |str|
1048
+ assert_equal('post,1=2&3=4', str)
1049
+ called = true
1050
+ }
1051
+ assert(called)
1052
+ assert_equal('1=2&3=4', res.header["x-query"][0])
1053
+ assert_nil(res.content)
1054
+ end
1055
+
1056
+ def test_post_with_custom_multipart
1057
+ ext = {'content-type' => 'multipart/form-data'}
1058
+ assert_equal("post", @client.post(serverurl + 'servlet', '').content[0, 4], ext)
1059
+ body = [{ 'Content-Disposition' => 'form-data; name="1"', :content => "2"},
1060
+ { 'Content-Disposition' => 'form-data; name="3"', :content => "4"}]
1061
+ res = @client.post(serverurl + 'servlet', body, ext)
1062
+ assert_match(/Content-Disposition: form-data; name="1"/, res.content)
1063
+ assert_match(/Content-Disposition: form-data; name="3"/, res.content)
1064
+ #
1065
+ ext = {'content-type' => 'multipart/form-data; boundary=hello'}
1066
+ assert_equal("post", @client.post(serverurl + 'servlet', '').content[0, 4], ext)
1067
+ res = @client.post(serverurl + 'servlet', body, ext)
1068
+ assert_match(/Content-Disposition: form-data; name="1"/, res.content)
1069
+ assert_match(/Content-Disposition: form-data; name="3"/, res.content)
1070
+ assert_equal("post,--hello\r\nContent-Disposition: form-data; name=\"1\"\r\n\r\n2\r\n--hello\r\nContent-Disposition: form-data; name=\"3\"\r\n\r\n4\r\n--hello--\r\n\r\n", res.content)
1071
+ end
1072
+
1073
+ def test_post_with_custom_multipart_and_file
1074
+ STDOUT.sync = true
1075
+ File.open(__FILE__) do |file|
1076
+ def file.original_filename
1077
+ 'file.txt'
1078
+ end
1079
+
1080
+ ext = { 'Content-Type' => 'multipart/alternative' }
1081
+ body = [{ 'Content-Type' => 'text/plain', :content => "this is only a test" },
1082
+ { 'Content-Type' => 'application/x-ruby', :content => file }]
1083
+ res = @client.post(serverurl + 'servlet', body, ext)
1084
+ assert_match(/^Content-Type: text\/plain\r\n/m, res.content)
1085
+ assert_match(/^this is only a test\r\n/m, res.content)
1086
+ assert_match(/^Content-Type: application\/x-ruby\r\n/m, res.content)
1087
+ assert_match(/Content-Disposition: form-data; name="3"; filename="file.txt"/, res.content)
1088
+ assert_match(/FIND_TAG_IN_THIS_FILE/, res.content)
1089
+ end
1090
+ end
1091
+
1092
+ def test_patch
1093
+ assert_equal("patch", @client.patch(serverurl + 'servlet', '').content)
1094
+ param = {'1'=>'2', '3'=>'4'}
1095
+ @client.debug_dev = str = ''
1096
+ res = @client.patch(serverurl + 'servlet', param)
1097
+ assert_equal(param, params(res.header["x-query"][0]))
1098
+ assert_equal('Content-Type: application/x-www-form-urlencoded', str.split(/\r?\n/)[5])
1099
+ end
1100
+
1101
+ def test_patch_with_query_and_body
1102
+ res = @client.patch(serverurl + 'servlet', :query => {:query => 'query'}, :body => {:body => 'body'})
1103
+ assert_equal("patch", res.content)
1104
+ assert_equal("body=body", res.headers["X-Query"])
1105
+ assert_equal("query=query", res.headers["X-Request-Query"])
1106
+ end
1107
+
1108
+ def test_patch_bytesize
1109
+ res = @client.patch(serverurl + 'servlet', 'txt' => 'あいうえお')
1110
+ assert_equal('txt=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A', res.header["x-query"][0])
1111
+ assert_equal('15', res.header["x-size"][0])
1112
+ end
1113
+
1114
+ def test_patch_async
1115
+ param = {'1'=>'2', '3'=>'4'}
1116
+ conn = @client.patch_async(serverurl + 'servlet', param)
1117
+ Thread.pass while !conn.finished?
1118
+ res = conn.pop
1119
+ assert_equal(param, params(res.header["x-query"][0]))
1120
+ end
1121
+
1122
+ def test_put
1123
+ assert_equal("put", @client.put(serverurl + 'servlet', '').content)
1124
+ param = {'1'=>'2', '3'=>'4'}
1125
+ @client.debug_dev = str = ''
1126
+ res = @client.put(serverurl + 'servlet', param)
1127
+ assert_equal(param, params(res.header["x-query"][0]))
1128
+ assert_equal('Content-Type: application/x-www-form-urlencoded', str.split(/\r?\n/)[5])
1129
+ end
1130
+
1131
+ def test_put_with_query_and_body
1132
+ res = @client.put(serverurl + 'servlet', :query => {:query => 'query'}, :body => {:body => 'body'})
1133
+ assert_equal("put", res.content)
1134
+ assert_equal("body=body", res.headers["X-Query"])
1135
+ assert_equal("query=query", res.headers["X-Request-Query"])
1136
+ end
1137
+
1138
+ def test_put_bytesize
1139
+ res = @client.put(serverurl + 'servlet', 'txt' => 'あいうえお')
1140
+ assert_equal('txt=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A', res.header["x-query"][0])
1141
+ assert_equal('15', res.header["x-size"][0])
1142
+ end
1143
+
1144
+ def test_put_async
1145
+ param = {'1'=>'2', '3'=>'4'}
1146
+ conn = @client.put_async(serverurl + 'servlet', param)
1147
+ Thread.pass while !conn.finished?
1148
+ res = conn.pop
1149
+ assert_equal(param, params(res.header["x-query"][0]))
1150
+ end
1151
+
1152
+ def test_delete
1153
+ assert_equal("delete", @client.delete(serverurl + 'servlet').content)
1154
+ end
1155
+
1156
+ def test_delete_with_query
1157
+ res = @client.delete(serverurl + 'servlet', :query => {:query => 'query'})
1158
+ assert_equal("delete", res.content)
1159
+ assert_equal('query=query', res.headers['X-Request-Query'])
1160
+ end
1161
+
1162
+ def test_delete_with_query_and_body
1163
+ res = @client.delete(serverurl + 'servlet', :query => {:query => 'query'}, :body => {:body => 'body'})
1164
+ assert_equal("delete", res.content)
1165
+ assert_equal('query=query', res.headers['X-Request-Query'])
1166
+ assert_equal('body=body', res.headers['X-Query'])
1167
+ end
1168
+
1169
+ # Not prohibited by spec, but normally it's ignored
1170
+ def test_delete_with_body
1171
+ param = {'1'=>'2', '3'=>'4'}
1172
+ @client.debug_dev = str = ''
1173
+ assert_equal("delete", @client.delete(serverurl + 'servlet', param).content)
1174
+ assert_equal({'1' => ['2'], '3' => ['4']}, HTTP::Message.parse(str.split(/\r?\n\r?\n/)[2]))
1175
+ end
1176
+
1177
+ def test_delete_async
1178
+ conn = @client.delete_async(serverurl + 'servlet')
1179
+ Thread.pass while !conn.finished?
1180
+ res = conn.pop
1181
+ assert_equal('delete', res.content.read)
1182
+ end
1183
+
1184
+ def test_options
1185
+ assert_equal('options', @client.options(serverurl + 'servlet').content)
1186
+ end
1187
+
1188
+ def test_options_with_header
1189
+ res = @client.options(serverurl + 'servlet', {'x-header' => 'header'})
1190
+ assert_equal('header', res.headers['X-Header'])
1191
+ end
1192
+
1193
+ def test_options_with_body
1194
+ res = @client.options(serverurl + 'servlet', :body => 'body')
1195
+ assert_equal('body', res.headers['X-Body'])
1196
+ end
1197
+
1198
+ def test_options_with_body_and_header
1199
+ res = @client.options(serverurl + 'servlet', :body => 'body', :header => {'x-header' => 'header'})
1200
+ assert_equal('header', res.headers['X-Header'])
1201
+ assert_equal('body', res.headers['X-Body'])
1202
+ end
1203
+
1204
+ def test_options_async
1205
+ conn = @client.options_async(serverurl + 'servlet')
1206
+ Thread.pass while !conn.finished?
1207
+ res = conn.pop
1208
+ assert_equal('options', res.content.read)
1209
+ end
1210
+
1211
+ def test_propfind
1212
+ assert_equal("propfind", @client.propfind(serverurl + 'servlet').content)
1213
+ end
1214
+
1215
+ def test_propfind_async
1216
+ conn = @client.propfind_async(serverurl + 'servlet')
1217
+ Thread.pass while !conn.finished?
1218
+ res = conn.pop
1219
+ assert_equal('propfind', res.content.read)
1220
+ end
1221
+
1222
+ def test_proppatch
1223
+ assert_equal("proppatch", @client.proppatch(serverurl + 'servlet').content)
1224
+ param = {'1'=>'2', '3'=>'4'}
1225
+ res = @client.proppatch(serverurl + 'servlet', param)
1226
+ assert_equal('proppatch', res.content)
1227
+ assert_equal(param, params(res.header["x-query"][0]))
1228
+ end
1229
+
1230
+ def test_proppatch_async
1231
+ param = {'1'=>'2', '3'=>'4'}
1232
+ conn = @client.proppatch_async(serverurl + 'servlet', param)
1233
+ Thread.pass while !conn.finished?
1234
+ res = conn.pop
1235
+ assert_equal('proppatch', res.content.read)
1236
+ assert_equal(param, params(res.header["x-query"][0]))
1237
+ end
1238
+
1239
+ def test_trace
1240
+ assert_equal("trace", @client.trace(serverurl + 'servlet').content)
1241
+ param = {'1'=>'2', '3'=>'4'}
1242
+ res = @client.trace(serverurl + 'servlet', param)
1243
+ assert_equal(param, params(res.header["x-query"][0]))
1244
+ end
1245
+
1246
+ def test_trace_async
1247
+ param = {'1'=>'2', '3'=>'4'}
1248
+ conn = @client.trace_async(serverurl + 'servlet', param)
1249
+ Thread.pass while !conn.finished?
1250
+ res = conn.pop
1251
+ assert_equal(param, params(res.header["x-query"][0]))
1252
+ end
1253
+
1254
+ def test_chunked
1255
+ assert_equal('chunked', @client.get_content(serverurl + 'chunked', { 'msg' => 'chunked' }))
1256
+ assert_equal('あいうえお', @client.get_content(serverurl + 'chunked', { 'msg' => 'あいうえお' }))
1257
+ end
1258
+
1259
+ def test_chunked_empty
1260
+ assert_equal('', @client.get_content(serverurl + 'chunked', { 'msg' => '' }))
1261
+ end
1262
+
1263
+ def test_get_query
1264
+ assert_equal({'1'=>'2'}, check_query_get({1=>2}))
1265
+ assert_equal({'a'=>'A', 'B'=>'b'}, check_query_get({"a"=>"A", "B"=>"b"}))
1266
+ assert_equal({'&'=>'&'}, check_query_get({"&"=>"&"}))
1267
+ assert_equal({'= '=>' =+'}, check_query_get({"= "=>" =+"}))
1268
+ assert_equal(
1269
+ ['=', '&'].sort,
1270
+ check_query_get([["=", "="], ["=", "&"]])['='].to_ary.sort
1271
+ )
1272
+ assert_equal({'123'=>'45'}, check_query_get('123=45'))
1273
+ assert_equal({'12 3'=>'45', ' '=>' '}, check_query_get('12+3=45&+=+'))
1274
+ assert_equal({}, check_query_get(''))
1275
+ assert_equal({'1'=>'2'}, check_query_get({1=>StringIO.new('2')}))
1276
+ assert_equal({'1'=>'2', '3'=>'4'}, check_query_get(StringIO.new('3=4&1=2')))
1277
+
1278
+ hash = check_query_get({"a"=>["A","a"], "B"=>"b"})
1279
+ assert_equal({'a'=>'A', 'B'=>'b'}, hash)
1280
+ assert_equal(['A','a'], hash['a'].to_ary)
1281
+
1282
+ hash = check_query_get({"a"=>WEBrick::HTTPUtils::FormData.new("A","a"), "B"=>"b"})
1283
+ assert_equal({'a'=>'A', 'B'=>'b'}, hash)
1284
+ assert_equal(['A','a'], hash['a'].to_ary)
1285
+
1286
+ hash = check_query_get({"a"=>[StringIO.new("A"),StringIO.new("a")], "B"=>StringIO.new("b")})
1287
+ assert_equal({'a'=>'A', 'B'=>'b'}, hash)
1288
+ assert_equal(['A','a'], hash['a'].to_ary)
1289
+ end
1290
+
1291
+ def test_post_body
1292
+ assert_equal({'1'=>'2'}, check_query_post({1=>2}))
1293
+ assert_equal({'a'=>'A', 'B'=>'b'}, check_query_post({"a"=>"A", "B"=>"b"}))
1294
+ assert_equal({'&'=>'&'}, check_query_post({"&"=>"&"}))
1295
+ assert_equal({'= '=>' =+'}, check_query_post({"= "=>" =+"}))
1296
+ assert_equal(
1297
+ ['=', '&'].sort,
1298
+ check_query_post([["=", "="], ["=", "&"]])['='].to_ary.sort
1299
+ )
1300
+ assert_equal({'123'=>'45'}, check_query_post('123=45'))
1301
+ assert_equal({'12 3'=>'45', ' '=>' '}, check_query_post('12+3=45&+=+'))
1302
+ assert_equal({}, check_query_post(''))
1303
+ #
1304
+ post_body = StringIO.new("foo=bar&foo=baz")
1305
+ assert_equal(
1306
+ ["bar", "baz"],
1307
+ check_query_post(post_body)["foo"].to_ary.sort
1308
+ )
1309
+ end
1310
+
1311
+ def test_extra_headers
1312
+ str = ""
1313
+ @client.debug_dev = str
1314
+ @client.head(serverurl, nil, {"ABC" => "DEF"})
1315
+ lines = str.split(/(?:\r?\n)+/)
1316
+ assert_equal("= Request", lines[0])
1317
+ assert_match("ABC: DEF", lines[4])
1318
+ #
1319
+ str = ""
1320
+ @client.debug_dev = str
1321
+ @client.get(serverurl, nil, [["ABC", "DEF"], ["ABC", "DEF"]])
1322
+ lines = str.split(/(?:\r?\n)+/)
1323
+ assert_equal("= Request", lines[0])
1324
+ assert_match("ABC: DEF", lines[4])
1325
+ assert_match("ABC: DEF", lines[5])
1326
+ end
1327
+
1328
+ def test_http_custom_date_header
1329
+ @client.debug_dev = (str = "")
1330
+ _res = @client.get(serverurl + 'hello', :header => {'Date' => 'foo'})
1331
+ lines = str.split(/(?:\r?\n)+/)
1332
+ assert_equal('Date: foo', lines[4])
1333
+ end
1334
+
1335
+ def test_timeout
1336
+ assert_equal(60, @client.connect_timeout)
1337
+ assert_equal(120, @client.send_timeout)
1338
+ assert_equal(60, @client.receive_timeout)
1339
+ #
1340
+ @client.connect_timeout = 1
1341
+ @client.send_timeout = 2
1342
+ @client.receive_timeout = 3
1343
+ assert_equal(1, @client.connect_timeout)
1344
+ assert_equal(2, @client.send_timeout)
1345
+ assert_equal(3, @client.receive_timeout)
1346
+ end
1347
+
1348
+ def test_connect_timeout
1349
+ # ToDo
1350
+ end
1351
+
1352
+ def test_send_timeout
1353
+ # ToDo
1354
+ end
1355
+
1356
+ def test_receive_timeout
1357
+ # this test takes 2 sec
1358
+ assert_equal('hello?sec=2', @client.get_content(serverurl + 'sleep?sec=2'))
1359
+ @client.receive_timeout = 1
1360
+ @client.reset_all
1361
+ assert_equal('hello?sec=0', @client.get_content(serverurl + 'sleep?sec=0'))
1362
+ assert_raise(HTTPClient::ReceiveTimeoutError) do
1363
+ @client.get_content(serverurl + 'sleep?sec=2')
1364
+ end
1365
+ @client.receive_timeout = 3
1366
+ @client.reset_all
1367
+ assert_equal('hello?sec=2', @client.get_content(serverurl + 'sleep?sec=2'))
1368
+ end
1369
+
1370
+ def test_receive_timeout_post
1371
+ # this test takes 2 sec
1372
+ assert_equal('hello', @client.post(serverurl + 'sleep', :sec => 2).content)
1373
+ @client.receive_timeout = 1
1374
+ @client.reset_all
1375
+ assert_equal('hello', @client.post(serverurl + 'sleep', :sec => 0).content)
1376
+ assert_raise(HTTPClient::ReceiveTimeoutError) do
1377
+ @client.post(serverurl + 'sleep', :sec => 2)
1378
+ end
1379
+ @client.receive_timeout = 3
1380
+ @client.reset_all
1381
+ assert_equal('hello', @client.post(serverurl + 'sleep', :sec => 2).content)
1382
+ end
1383
+
1384
+ def test_reset
1385
+ url = serverurl + 'servlet'
1386
+ assert_nothing_raised do
1387
+ 5.times do
1388
+ @client.get(url)
1389
+ @client.reset(url)
1390
+ end
1391
+ end
1392
+ end
1393
+
1394
+ def test_reset_all
1395
+ assert_nothing_raised do
1396
+ 5.times do
1397
+ @client.get(serverurl + 'servlet')
1398
+ @client.reset_all
1399
+ end
1400
+ end
1401
+ end
1402
+
1403
+ def test_cookies
1404
+ cookiefile = Tempfile.new('test_cookies_file')
1405
+ File.open(cookiefile.path, "wb") do |f|
1406
+ f << "http://rubyforge.org/account/login.php\tsession_ser\tLjEwMy45Ni40Ni0q%2A-fa0537de8cc31\t2000000000\trubyforge.org\t/account/\t9\n"
1407
+ end
1408
+ @client.set_cookie_store(cookiefile.path)
1409
+ #
1410
+ @client.reset_all
1411
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\nSet-Cookie: session_ser=bar; expires=#{Time.at(1924873200).gmtime.httpdate}\n\nOK"
1412
+ @client.get_content('http://rubyforge.org/account/login.php')
1413
+ @client.save_cookie_store
1414
+ str = File.read(cookiefile.path)
1415
+ assert_match(%r(http://rubyforge.org/account/login.php\tsession_ser\tbar\t1924873200\trubyforge.org\t/account/\t9), str)
1416
+ end
1417
+
1418
+ def test_eof_error_length
1419
+ io = StringIO.new('')
1420
+ def io.gets(*arg)
1421
+ @buf ||= ["HTTP/1.0 200 OK\n", "content-length: 123\n", "\n"]
1422
+ @buf.shift
1423
+ end
1424
+ def io.readpartial(size, buf)
1425
+ @second ||= false
1426
+ if !@second
1427
+ @second = '1st'
1428
+ buf << "abc"
1429
+ buf
1430
+ elsif @second == '1st'
1431
+ @second = '2nd'
1432
+ raise EOFError.new
1433
+ else
1434
+ raise Exception.new
1435
+ end
1436
+ end
1437
+ def io.eof?
1438
+ true
1439
+ end
1440
+ @client.test_loopback_http_response << io
1441
+ assert_nothing_raised do
1442
+ @client.get('http://foo/bar')
1443
+ end
1444
+ end
1445
+
1446
+ def test_eof_error_rest
1447
+ io = StringIO.new('')
1448
+ def io.gets(*arg)
1449
+ @buf ||= ["HTTP/1.0 200 OK\n", "\n"]
1450
+ @buf.shift
1451
+ end
1452
+ def io.readpartial(size, buf)
1453
+ @second ||= false
1454
+ if !@second
1455
+ @second = '1st'
1456
+ buf << "abc"
1457
+ buf
1458
+ elsif @second == '1st'
1459
+ @second = '2nd'
1460
+ raise EOFError.new
1461
+ else
1462
+ raise Exception.new
1463
+ end
1464
+ end
1465
+ def io.eof?
1466
+ true
1467
+ end
1468
+ @client.test_loopback_http_response << io
1469
+ assert_nothing_raised do
1470
+ @client.get('http://foo/bar')
1471
+ end
1472
+ end
1473
+
1474
+ def test_urify
1475
+ extend HTTPClient::Util
1476
+ assert_nil(urify(nil))
1477
+ uri = 'http://foo'
1478
+ assert_equal(urify(uri), urify(uri))
1479
+ assert_equal(urify(uri), urify(urify(uri)))
1480
+ end
1481
+
1482
+ def test_connection
1483
+ c = HTTPClient::Connection.new
1484
+ assert(c.finished?)
1485
+ assert_nil(c.join)
1486
+ end
1487
+
1488
+ def test_site
1489
+ site = HTTPClient::Site.new
1490
+ assert_equal('tcp', site.scheme)
1491
+ assert_equal('0.0.0.0', site.host)
1492
+ assert_equal(0, site.port)
1493
+ assert_equal('tcp://0.0.0.0:0', site.addr)
1494
+ assert_equal('tcp://0.0.0.0:0', site.to_s)
1495
+ assert_nothing_raised do
1496
+ site.inspect
1497
+ end
1498
+ #
1499
+ site = HTTPClient::Site.new(urify('http://localhost:12345/foo'))
1500
+ assert_equal('http', site.scheme)
1501
+ assert_equal('localhost', site.host)
1502
+ assert_equal(12345, site.port)
1503
+ assert_equal('http://localhost:12345', site.addr)
1504
+ assert_equal('http://localhost:12345', site.to_s)
1505
+ assert_nothing_raised do
1506
+ site.inspect
1507
+ end
1508
+ #
1509
+ site1 = HTTPClient::Site.new(urify('http://localhost:12341/'))
1510
+ site2 = HTTPClient::Site.new(urify('http://localhost:12342/'))
1511
+ site3 = HTTPClient::Site.new(urify('http://localhost:12342/'))
1512
+ assert(!(site1 == site2))
1513
+ h = { site1 => 'site1', site2 => 'site2' }
1514
+ h[site3] = 'site3'
1515
+ assert_equal('site1', h[site1])
1516
+ assert_equal('site3', h[site2])
1517
+ end
1518
+
1519
+ def test_http_header
1520
+ res = @client.get(serverurl + 'hello')
1521
+ assert_equal('text/html', res.contenttype)
1522
+ assert_equal(5, res.header.get(nil).size)
1523
+ #
1524
+ res.header.delete('connection')
1525
+ assert_equal(4, res.header.get(nil).size)
1526
+ #
1527
+ res.header['foo'] = 'bar'
1528
+ assert_equal(['bar'], res.header['foo'])
1529
+ #
1530
+ assert_equal([['foo', 'bar']], res.header.get('foo'))
1531
+ res.header['foo'] = ['bar', 'bar2']
1532
+ assert_equal([['foo', 'bar'], ['foo', 'bar2']], res.header.get('foo'))
1533
+ end
1534
+
1535
+ def test_mime_type
1536
+ assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
1537
+ assert_equal('text/html', HTTP::Message.mime_type('foo.html'))
1538
+ assert_equal('text/html', HTTP::Message.mime_type('foo.htm'))
1539
+ assert_equal('text/xml', HTTP::Message.mime_type('foo.xml'))
1540
+ assert_equal('application/msword', HTTP::Message.mime_type('foo.doc'))
1541
+ assert_equal('image/png', HTTP::Message.mime_type('foo.png'))
1542
+ assert_equal('image/gif', HTTP::Message.mime_type('foo.gif'))
1543
+ assert_equal('image/jpeg', HTTP::Message.mime_type('foo.jpg'))
1544
+ assert_equal('image/jpeg', HTTP::Message.mime_type('foo.jpeg'))
1545
+ assert_equal('application/octet-stream', HTTP::Message.mime_type('foo.unknown'))
1546
+ #
1547
+ handler = lambda { |path| 'hello/world' }
1548
+ assert_nil(HTTP::Message.mime_type_handler)
1549
+ assert_nil(HTTP::Message.get_mime_type_func)
1550
+ HTTP::Message.mime_type_handler = handler
1551
+ assert_not_nil(HTTP::Message.mime_type_handler)
1552
+ assert_not_nil(HTTP::Message.get_mime_type_func)
1553
+ assert_equal('hello/world', HTTP::Message.mime_type('foo.txt'))
1554
+ HTTP::Message.mime_type_handler = nil
1555
+ assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
1556
+ HTTP::Message.set_mime_type_func(nil)
1557
+ assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
1558
+ #
1559
+ handler = lambda { |path| nil }
1560
+ HTTP::Message.mime_type_handler = handler
1561
+ assert_equal('application/octet-stream', HTTP::Message.mime_type('foo.txt'))
1562
+ end
1563
+
1564
+ def test_connect_request
1565
+ req = HTTP::Message.new_connect_request(urify('https://foo/bar'))
1566
+ assert_equal("CONNECT foo:443 HTTP/1.0\r\n\r\n", req.dump)
1567
+ req = HTTP::Message.new_connect_request(urify('https://example.com/'))
1568
+ assert_equal("CONNECT example.com:443 HTTP/1.0\r\n\r\n", req.dump)
1569
+ end
1570
+
1571
+ def test_response
1572
+ res = HTTP::Message.new_response('response')
1573
+ res.contenttype = 'text/plain'
1574
+ res.header.body_date = Time.at(946652400)
1575
+ assert_equal(
1576
+ [
1577
+ "",
1578
+ "Content-Length: 8",
1579
+ "Content-Type: text/plain",
1580
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
1581
+ "Status: 200 OK",
1582
+ "response"
1583
+ ],
1584
+ res.dump.split(/\r\n/).sort
1585
+ )
1586
+ assert_equal(['8'], res.header['Content-Length'])
1587
+ assert_equal('8', res.headers['Content-Length'])
1588
+ res.header.set('foo', 'bar')
1589
+ assert_equal(
1590
+ [
1591
+ "",
1592
+ "Content-Length: 8",
1593
+ "Content-Type: text/plain",
1594
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
1595
+ "Status: 200 OK",
1596
+ "foo: bar",
1597
+ "response"
1598
+ ],
1599
+ res.dump.split(/\r\n/).sort
1600
+ )
1601
+ # nil body
1602
+ res = HTTP::Message.new_response(nil)
1603
+ assert_equal(
1604
+ [
1605
+ "Content-Length: 0",
1606
+ "Content-Type: text/html; charset=us-ascii",
1607
+ "Status: 200 OK"
1608
+ ],
1609
+ res.dump.split(/\r\n/).sort
1610
+ )
1611
+ # for mod_ruby env
1612
+ Object.const_set('Apache', nil)
1613
+ begin
1614
+ res = HTTP::Message.new_response('response')
1615
+ assert(res.dump.split(/\r\n/).any? { |line| /^Date/ =~ line })
1616
+ #
1617
+ res = HTTP::Message.new_response('response')
1618
+ res.contenttype = 'text/plain'
1619
+ res.header.body_date = Time.at(946652400)
1620
+ res.header['Date'] = Time.at(946652400).httpdate
1621
+ assert_equal(
1622
+ [
1623
+ "",
1624
+ "Content-Length: 8",
1625
+ "Content-Type: text/plain",
1626
+ "Date: Fri, 31 Dec 1999 15:00:00 GMT",
1627
+ "HTTP/1.1 200 OK",
1628
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
1629
+ "response"
1630
+ ],
1631
+ res.dump.split(/\r\n/).sort
1632
+ )
1633
+ ensure
1634
+ Object.instance_eval { remove_const('Apache') }
1635
+ end
1636
+ end
1637
+
1638
+ def test_response_cookies
1639
+ res = HTTP::Message.new_response('response')
1640
+ res.contenttype = 'text/plain'
1641
+ res.header.body_date = Time.at(946652400)
1642
+ res.header.request_uri = 'http://www.example.com/'
1643
+ assert_nil(res.cookies)
1644
+ #
1645
+ res.header['Set-Cookie'] = [
1646
+ 'CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT',
1647
+ 'PART_NUMBER=ROCKET_LAUNCHER_0001; path=/'
1648
+ ]
1649
+ assert_equal(
1650
+ [
1651
+ "",
1652
+ "Content-Length: 8",
1653
+ "Content-Type: text/plain",
1654
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
1655
+ "Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT",
1656
+ "Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/",
1657
+ "Status: 200 OK",
1658
+ "response"
1659
+ ],
1660
+ res.dump.split(/\r\n/).sort
1661
+ )
1662
+ assert_equal(2, res.cookies.size)
1663
+ assert_equal('CUSTOMER', res.cookies[0].name)
1664
+ assert_equal('PART_NUMBER', res.cookies[1].name)
1665
+ end
1666
+
1667
+ def test_ok_response_success
1668
+ res = HTTP::Message.new_response('response')
1669
+ assert_equal(true, res.ok?)
1670
+ res.status = 404
1671
+ assert_equal(false, res.ok?)
1672
+ res.status = 500
1673
+ assert_equal(false, res.ok?)
1674
+ res.status = 302
1675
+ assert_equal(false, res.ok?)
1676
+ end
1677
+
1678
+ if !defined?(JRUBY_VERSION) and RUBY_VERSION < '1.9'
1679
+ def test_timeout_scheduler
1680
+ assert_equal('hello', @client.get_content(serverurl + 'hello'))
1681
+ status = HTTPClient.timeout_scheduler.instance_eval { @thread.kill; @thread.join; @thread.status }
1682
+ assert(!status) # dead
1683
+ assert_equal('hello', @client.get_content(serverurl + 'hello'))
1684
+ end
1685
+ end
1686
+
1687
+ def test_session_manager
1688
+ mgr = HTTPClient::SessionManager.new(@client)
1689
+ assert_nil(mgr.instance_eval { @proxy })
1690
+ assert_nil(mgr.debug_dev)
1691
+ @client.debug_dev = Object.new
1692
+ @client.proxy = 'http://myproxy:12345'
1693
+ mgr = HTTPClient::SessionManager.new(@client)
1694
+ assert_equal('http://myproxy:12345', mgr.instance_eval { @proxy }.to_s)
1695
+ assert_equal(@client.debug_dev, mgr.debug_dev)
1696
+ end
1697
+
1698
+ def create_keepalive_disconnected_thread(idx, sock)
1699
+ Thread.new {
1700
+ # return "12345" for the first connection
1701
+ sock.gets
1702
+ sock.gets
1703
+ sock.write("HTTP/1.1 200 OK\r\n")
1704
+ sock.write("Content-Length: 5\r\n")
1705
+ sock.write("\r\n")
1706
+ sock.write("12345")
1707
+ # for the next connection, close while reading the request for emulating
1708
+ # KeepAliveDisconnected
1709
+ sock.gets
1710
+ sock.close
1711
+ }
1712
+ end
1713
+
1714
+ def test_keepalive_disconnected
1715
+ client = HTTPClient.new
1716
+ server = TCPServer.open('127.0.0.1', 0)
1717
+ server.listen(30) # set enough backlogs
1718
+ endpoint = "http://127.0.0.1:#{server.addr[1]}/"
1719
+ queue = Queue.new
1720
+ Thread.new(queue) { |qs|
1721
+ Thread.abort_on_exception = true
1722
+ # want 5 requests issued
1723
+ 5.times { qs.pop }
1724
+ # emulate 10 keep-alive connections
1725
+ 10.times do |idx|
1726
+ sock = server.accept
1727
+ create_keepalive_disconnected_thread(idx, sock)
1728
+ end
1729
+ # return "23456" for the request which gets KeepAliveDisconnected
1730
+ 5.times do
1731
+ sock = server.accept
1732
+ sock.gets
1733
+ sock.gets
1734
+ sock.write("HTTP/1.1 200 OK\r\n")
1735
+ sock.write("\r\n")
1736
+ sock.write("23456")
1737
+ sock.close
1738
+ end
1739
+ # return "34567" for the rest requests
1740
+ while true
1741
+ sock = server.accept
1742
+ sock.gets
1743
+ sock.gets
1744
+ sock.write("HTTP/1.1 200 OK\r\n")
1745
+ sock.write("Connection: close\r\n")
1746
+ sock.write("Content-Length: 5\r\n")
1747
+ sock.write("\r\n")
1748
+ sock.write("34567")
1749
+ sock.close
1750
+ end
1751
+ }
1752
+ # try to allocate 10 keep-alive connections; it's a race so some
1753
+ # threads can reuse the connection so actual number of keep-alive
1754
+ # connections should be smaller than 10.
1755
+ (0...10).to_a.map {
1756
+ Thread.new(queue) { |qc|
1757
+ Thread.abort_on_exception = true
1758
+ conn = client.get_async(endpoint)
1759
+ qc.push(true)
1760
+ assert_equal("12345", conn.pop.content.read)
1761
+ }
1762
+ }.each { |th| th.join }
1763
+ # send 5 requests, some of these should get KeepAliveDesconnected
1764
+ # but should retry with new connection.
1765
+ (0...5).to_a.map {
1766
+ Thread.new {
1767
+ Thread.abort_on_exception = true
1768
+ assert_equal("23456", client.get(endpoint).content)
1769
+ }
1770
+ }.each { |th| th.join }
1771
+ # rest requests won't get KeepAliveDisconnected
1772
+ (0...10).to_a.map {
1773
+ Thread.new {
1774
+ Thread.abort_on_exception = true
1775
+ assert_equal("34567", client.get(endpoint).content)
1776
+ }
1777
+ }.each { |th| th.join }
1778
+ end
1779
+
1780
+ def create_keepalive_thread(count, sock)
1781
+ Thread.new {
1782
+ Thread.abort_on_exception = true
1783
+ count.times do
1784
+ req = sock.gets
1785
+ while line = sock.gets
1786
+ break if line.chomp.empty?
1787
+ end
1788
+ case req
1789
+ when /chunked/
1790
+ sock.write("HTTP/1.1 200 OK\r\n")
1791
+ sock.write("Transfer-Encoding: chunked\r\n")
1792
+ sock.write("\r\n")
1793
+ sock.write("1a\r\n")
1794
+ sock.write("abcdefghijklmnopqrstuvwxyz\r\n")
1795
+ sock.write("10\r\n")
1796
+ sock.write("1234567890abcdef\r\n")
1797
+ sock.write("0\r\n")
1798
+ sock.write("\r\n")
1799
+ else
1800
+ sock.write("HTTP/1.1 200 OK\r\n")
1801
+ sock.write("Content-Length: 5\r\n")
1802
+ sock.write("\r\n")
1803
+ sock.write("12345")
1804
+ end
1805
+ end
1806
+ sock.close
1807
+ }
1808
+ end
1809
+
1810
+ def test_keepalive
1811
+ server = TCPServer.open('localhost', 0)
1812
+ server_thread = Thread.new {
1813
+ Thread.abort_on_exception = true
1814
+ sock = server.accept
1815
+ create_keepalive_thread(10, sock)
1816
+ }
1817
+ url = "http://localhost:#{server.addr[1]}/"
1818
+ begin
1819
+ # content-length
1820
+ 5.times do
1821
+ assert_equal('12345', @client.get(url).body)
1822
+ end
1823
+ # chunked
1824
+ 5.times do
1825
+ assert_equal('abcdefghijklmnopqrstuvwxyz1234567890abcdef', @client.get(url + 'chunked').body)
1826
+ end
1827
+ ensure
1828
+ server.close
1829
+ server_thread.join
1830
+ end
1831
+ end
1832
+
1833
+ def test_strict_response_size_check
1834
+ @client.strict_response_size_check = false
1835
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\nContent-Length: 12345\r\n\r\nhello world"
1836
+ assert_equal('hello world', @client.get_content('http://dummy'))
1837
+
1838
+ @client.reset_all
1839
+ @client.strict_response_size_check = true
1840
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\nContent-Length: 12345\r\n\r\nhello world"
1841
+ assert_raise(HTTPClient::BadResponseError) do
1842
+ @client.get_content('http://dummy')
1843
+ end
1844
+ end
1845
+
1846
+ def test_socket_local
1847
+ @client.socket_local.host = '127.0.0.1'
1848
+ assert_equal('hello', @client.get_content(serverurl + 'hello'))
1849
+ @client.reset_all
1850
+ @client.socket_local.port = serverport
1851
+ begin
1852
+ @client.get_content(serverurl + 'hello')
1853
+ rescue Errno::EADDRINUSE, SocketError
1854
+ assert(true)
1855
+ end
1856
+ end
1857
+
1858
+ def test_body_param_order
1859
+ ary = ('b'..'d').map { |k| ['key2', k] } << ['key1', 'a'] << ['key3', 'z']
1860
+ assert_equal("key2=b&key2=c&key2=d&key1=a&key3=z", HTTP::Message.escape_query(ary))
1861
+ end
1862
+
1863
+ if RUBY_VERSION > "1.9"
1864
+ def test_charset
1865
+ body = @client.get(serverurl + 'charset').body
1866
+ assert_equal(Encoding::EUC_JP, body.encoding)
1867
+ assert_equal('あいうえお'.encode(Encoding::EUC_JP), body)
1868
+ end
1869
+ end
1870
+
1871
+ if RUBY_VERSION >= "1.9.3"
1872
+ def test_continue
1873
+ @client.debug_dev = str = ''
1874
+ res = @client.get(serverurl + 'continue', :header => {:Expect => '100-continue'})
1875
+ assert_equal(200, res.status)
1876
+ assert_equal('done!', res.body)
1877
+ assert_match(/Expect: 100-continue/, str)
1878
+ end
1879
+ end
1880
+
1881
+ def test_ipv6literaladdress_in_uri
1882
+ server = TCPServer.open('::1', 0) rescue return # Skip if IPv6 is unavailable.
1883
+ server_thread = Thread.new {
1884
+ Thread.abort_on_exception = true
1885
+ sock = server.accept
1886
+ while line = sock.gets
1887
+ break if line.chomp.empty?
1888
+ end
1889
+ sock.write("HTTP/1.1 200 OK\r\n")
1890
+ sock.write("Content-Length: 5\r\n")
1891
+ sock.write("\r\n")
1892
+ sock.write("12345")
1893
+ sock.close
1894
+ }
1895
+ uri = "http://[::1]:#{server.addr[1]}/"
1896
+ begin
1897
+ assert_equal('12345', @client.get(uri).body)
1898
+ ensure
1899
+ server.close
1900
+ server_thread.kill
1901
+ server_thread.join
1902
+ end
1903
+ end
1904
+
1905
+ def test_uri_no_schema
1906
+ assert_raise(ArgumentError) do
1907
+ @client.get_content("www.example.com")
1908
+ end
1909
+ end
1910
+
1911
+ def test_tcp_keepalive
1912
+ @client.tcp_keepalive = true
1913
+ @client.get(serverurl)
1914
+
1915
+ # expecting HTTP keepalive caches the socket
1916
+ session = @client.instance_variable_get(:@session_manager).send(:get_cached_session, HTTPClient::Site.new(URI.parse(serverurl)))
1917
+ socket = session.instance_variable_get(:@socket)
1918
+
1919
+ assert_true(session.tcp_keepalive)
1920
+ assert_equal(Socket::SO_KEEPALIVE, socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE).optname)
1921
+ end
1922
+
1923
+ private
1924
+
1925
+ def check_query_get(query)
1926
+ WEBrick::HTTPUtils.parse_query(
1927
+ @client.get(serverurl + 'servlet', query).header["x-query"][0]
1928
+ )
1929
+ end
1930
+
1931
+ def check_query_post(query)
1932
+ WEBrick::HTTPUtils.parse_query(
1933
+ @client.post(serverurl + 'servlet', query).header["x-query"][0]
1934
+ )
1935
+ end
1936
+
1937
+ def setup_server
1938
+ @server = WEBrick::HTTPServer.new(
1939
+ :BindAddress => "localhost",
1940
+ :Logger => @logger,
1941
+ :Port => 0,
1942
+ :AccessLog => [],
1943
+ :DocumentRoot => File.dirname(File.expand_path(__FILE__))
1944
+ )
1945
+ @serverport = @server.config[:Port]
1946
+ [
1947
+ :hello, :sleep, :servlet_redirect, :redirect1, :redirect2, :redirect3,
1948
+ :redirect_self, :relative_redirect, :redirect_see_other, :chunked,
1949
+ :largebody, :status, :compressed, :charset, :continue
1950
+ ].each do |sym|
1951
+ @server.mount(
1952
+ "/#{sym}",
1953
+ WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
1954
+ )
1955
+ end
1956
+ @server.mount('/servlet', TestServlet.new(@server))
1957
+ @server_thread = start_server_thread(@server)
1958
+ end
1959
+
1960
+ def add_query_string(req)
1961
+ if req.query_string
1962
+ '?' + req.query_string
1963
+ else
1964
+ ''
1965
+ end
1966
+ end
1967
+
1968
+ def do_hello(req, res)
1969
+ res['content-type'] = 'text/html'
1970
+ res.body = "hello" + add_query_string(req)
1971
+ end
1972
+
1973
+ def do_sleep(req, res)
1974
+ sec = req.query['sec'].to_i
1975
+ sleep sec
1976
+ res['content-type'] = 'text/html'
1977
+ res.body = "hello" + add_query_string(req)
1978
+ end
1979
+
1980
+ def do_servlet_redirect(req, res)
1981
+ res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "servlet" + add_query_string(req))
1982
+ end
1983
+
1984
+ def do_redirect1(req, res)
1985
+ res.set_redirect(WEBrick::HTTPStatus::MovedPermanently, serverurl + "hello" + add_query_string(req))
1986
+ end
1987
+
1988
+ def do_redirect2(req, res)
1989
+ res.set_redirect(WEBrick::HTTPStatus::TemporaryRedirect, serverurl + "redirect3" + add_query_string(req))
1990
+ end
1991
+
1992
+ def do_redirect3(req, res)
1993
+ res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "hello" + add_query_string(req))
1994
+ end
1995
+
1996
+ def do_redirect_self(req, res)
1997
+ res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "redirect_self" + add_query_string(req))
1998
+ end
1999
+
2000
+ def do_relative_redirect(req, res)
2001
+ res.set_redirect(WEBrick::HTTPStatus::Found, "hello" + add_query_string(req))
2002
+ end
2003
+
2004
+ def do_redirect_see_other(req, res)
2005
+ if req.request_method == 'POST'
2006
+ res.set_redirect(WEBrick::HTTPStatus::SeeOther, serverurl + "redirect_see_other" + add_query_string(req)) # self
2007
+ else
2008
+ res.body = 'hello'
2009
+ end
2010
+ end
2011
+
2012
+ def do_chunked(req, res)
2013
+ res.chunked = true
2014
+ res['content-type'] = 'text/plain; charset=UTF-8'
2015
+ piper, pipew = IO.pipe
2016
+ res.body = piper
2017
+ pipew << req.query['msg']
2018
+ pipew.close
2019
+ end
2020
+
2021
+ def do_largebody(req, res)
2022
+ res['content-type'] = 'text/html'
2023
+ res.body = "a" * 1000 * 1000
2024
+ end
2025
+
2026
+ def do_compressed(req, res)
2027
+ res['content-type'] = 'application/octet-stream'
2028
+ if req.query['enc'] == 'gzip'
2029
+ res['content-encoding'] = 'gzip'
2030
+ res.body = GZIP_CONTENT
2031
+ elsif req.query['enc'] == 'deflate'
2032
+ res['content-encoding'] = 'deflate'
2033
+ res.body = DEFLATE_CONTENT
2034
+ elsif req.query['enc'] == 'deflate_noheader'
2035
+ res['content-encoding'] = 'deflate'
2036
+ res.body = DEFLATE_NOHEADER_CONTENT
2037
+ end
2038
+ end
2039
+
2040
+ def do_charset(req, res)
2041
+ if RUBY_VERSION > "1.9"
2042
+ res.body = 'あいうえお'.encode("euc-jp")
2043
+ res['Content-Type'] = 'text/plain; charset=euc-jp'
2044
+ else
2045
+ res.body = 'this endpoint is for 1.9 or later'
2046
+ end
2047
+ end
2048
+
2049
+ def do_status(req, res)
2050
+ res.status = req.query['status'].to_i
2051
+ end
2052
+
2053
+ def do_continue(req, res)
2054
+ req.continue
2055
+ res.body = 'done!'
2056
+ end
2057
+
2058
+ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
2059
+ def get_instance(*arg)
2060
+ self
2061
+ end
2062
+
2063
+ def do_HEAD(req, res)
2064
+ res["x-head"] = 'head' # use this for test purpose only.
2065
+ res["x-query"] = query_response(req)
2066
+ end
2067
+
2068
+ def do_GET(req, res)
2069
+ res.body = 'get'
2070
+ res['x-header'] = req['X-Header']
2071
+ res["x-query"] = query_response(req)
2072
+ end
2073
+
2074
+ def do_POST(req, res)
2075
+ res["content-type"] = "text/plain" # iso-8859-1, not US-ASCII
2076
+ res.body = 'post,' + req.body.to_s
2077
+ res["x-query"] = body_response(req)
2078
+ res["x-request-query"] = req.query_string
2079
+ end
2080
+
2081
+ def do_PATCH(req, res)
2082
+ res["x-query"] = body_response(req)
2083
+ param = WEBrick::HTTPUtils.parse_query(req.body) || {}
2084
+ res["x-size"] = (param['txt'] || '').size
2085
+ res.body = param['txt'] || 'patch'
2086
+ res["x-request-query"] = req.query_string
2087
+ end
2088
+
2089
+ def do_PUT(req, res)
2090
+ res["x-query"] = body_response(req)
2091
+ param = WEBrick::HTTPUtils.parse_query(req.body) || {}
2092
+ res["x-size"] = (param['txt'] || '').size
2093
+ res.body = param['txt'] || 'put'
2094
+ res["x-request-query"] = req.query_string
2095
+ end
2096
+
2097
+ def do_DELETE(req, res)
2098
+ res.body = 'delete'
2099
+ res["x-query"] = body_response(req)
2100
+ res["x-request-query"] = req.query_string
2101
+ end
2102
+
2103
+ def do_OPTIONS(req, res)
2104
+ res.body = 'options'
2105
+ res['x-header'] = req['X-Header']
2106
+ res['x-body'] = req.body
2107
+ end
2108
+
2109
+ def do_PROPFIND(req, res)
2110
+ res.body = 'propfind'
2111
+ end
2112
+
2113
+ def do_PROPPATCH(req, res)
2114
+ res.body = 'proppatch'
2115
+ res["x-query"] = body_response(req)
2116
+ end
2117
+
2118
+ def do_TRACE(req, res)
2119
+ # client SHOULD reflect the message received back to the client as the
2120
+ # entity-body of a 200 (OK) response. [RFC2616]
2121
+ res.body = 'trace'
2122
+ res["x-query"] = query_response(req)
2123
+ end
2124
+
2125
+ private
2126
+
2127
+ def query_response(req)
2128
+ query_escape(WEBrick::HTTPUtils.parse_query(req.query_string))
2129
+ end
2130
+
2131
+ def body_response(req)
2132
+ query_escape(WEBrick::HTTPUtils.parse_query(req.body))
2133
+ end
2134
+
2135
+ def query_escape(query)
2136
+ escaped = []
2137
+ query.sort_by { |k, v| k }.collect do |k, v|
2138
+ v.to_ary.each do |ve|
2139
+ escaped << CGI.escape(k) + '=' + CGI.escape(ve)
2140
+ end
2141
+ end
2142
+ escaped.join('&')
2143
+ end
2144
+ end
2145
+ end