rs-httpclient 3.0.0.beta1

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.
Files changed (78) 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/cookie.rb +1 -0
  7. data/lib/http-access2/http.rb +1 -0
  8. data/lib/http-access2.rb +55 -0
  9. data/lib/httpclient/auth.rb +924 -0
  10. data/lib/httpclient/cacert.pem +3252 -0
  11. data/lib/httpclient/cacert1024.pem +3866 -0
  12. data/lib/httpclient/connection.rb +88 -0
  13. data/lib/httpclient/cookie.rb +220 -0
  14. data/lib/httpclient/http.rb +1082 -0
  15. data/lib/httpclient/include_client.rb +85 -0
  16. data/lib/httpclient/jruby_ssl_socket.rb +594 -0
  17. data/lib/httpclient/session.rb +960 -0
  18. data/lib/httpclient/ssl_config.rb +452 -0
  19. data/lib/httpclient/ssl_socket.rb +150 -0
  20. data/lib/httpclient/timeout.rb +140 -0
  21. data/lib/httpclient/util.rb +222 -0
  22. data/lib/httpclient/version.rb +3 -0
  23. data/lib/httpclient/webagent-cookie.rb +459 -0
  24. data/lib/httpclient.rb +1331 -0
  25. data/lib/jsonclient.rb +66 -0
  26. data/lib/oauthclient.rb +111 -0
  27. data/sample/async.rb +8 -0
  28. data/sample/auth.rb +11 -0
  29. data/sample/cookie.rb +18 -0
  30. data/sample/dav.rb +103 -0
  31. data/sample/generate_test_keys.rb +99 -0
  32. data/sample/howto.rb +49 -0
  33. data/sample/jsonclient.rb +67 -0
  34. data/sample/oauth_buzz.rb +57 -0
  35. data/sample/oauth_friendfeed.rb +59 -0
  36. data/sample/oauth_twitter.rb +61 -0
  37. data/sample/ssl/0cert.pem +22 -0
  38. data/sample/ssl/0key.pem +30 -0
  39. data/sample/ssl/1000cert.pem +19 -0
  40. data/sample/ssl/1000key.pem +18 -0
  41. data/sample/ssl/htdocs/index.html +10 -0
  42. data/sample/ssl/ssl_client.rb +22 -0
  43. data/sample/ssl/webrick_httpsd.rb +29 -0
  44. data/sample/stream.rb +21 -0
  45. data/sample/thread.rb +27 -0
  46. data/sample/wcat.rb +21 -0
  47. data/test/ca-chain.pem +40 -0
  48. data/test/ca.cert +20 -0
  49. data/test/ca.key +27 -0
  50. data/test/ca.srl +1 -0
  51. data/test/client-pass.key +30 -0
  52. data/test/client.cert +20 -0
  53. data/test/client.key +27 -0
  54. data/test/fixtures/verify.alt.cert +20 -0
  55. data/test/fixtures/verify.foo.cert +20 -0
  56. data/test/fixtures/verify.key +27 -0
  57. data/test/fixtures/verify.localhost.cert +20 -0
  58. data/test/helper.rb +129 -0
  59. data/test/htdigest +1 -0
  60. data/test/htpasswd +2 -0
  61. data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
  62. data/test/runner.rb +2 -0
  63. data/test/server.cert +20 -0
  64. data/test/server.key +27 -0
  65. data/test/sslsvr.rb +65 -0
  66. data/test/subca.cert +20 -0
  67. data/test/subca.key +27 -0
  68. data/test/subca.srl +1 -0
  69. data/test/test_auth.rb +496 -0
  70. data/test/test_cookie.rb +309 -0
  71. data/test/test_hexdump.rb +13 -0
  72. data/test/test_http-access2.rb +516 -0
  73. data/test/test_httpclient.rb +2144 -0
  74. data/test/test_include_client.rb +52 -0
  75. data/test/test_jsonclient.rb +98 -0
  76. data/test/test_ssl.rb +522 -0
  77. data/test/test_webagent-cookie.rb +465 -0
  78. metadata +130 -0
@@ -0,0 +1,2144 @@
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.truncate(0)
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 = "".dup
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 = "".dup
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 = "".dup
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 = "".dup
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 = ''.dup
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 = "".dup
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 = "".dup
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 = "".dup
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 = "".dup
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 = "".dup
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 = "".dup
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 = "".dup
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 = "".dup
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 = "".dup
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.truncate(0)
213
+ @client.proxy = nil
214
+ assert_equal(200, @client.head(serverurl).status)
215
+ assert(/accept/ !~ @proxyio.string)
216
+ #
217
+ @proxyio.truncate(0)
218
+ @client.proxy = proxyurl
219
+ @client.debug_dev = str = "".dup
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 = "".dup
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 = "".dup
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.truncate(0)
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.truncate(0)
290
+ @client.proxy = nil
291
+ assert_equal(200, @client.head(serverurl).status)
292
+ assert(/accept/ !~ @proxyio.string)
293
+ #
294
+ @proxyio.truncate(0)
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.truncate(0)
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.truncate(0)
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.truncate(0)
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.truncate(0)
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 = "".dup
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 = "".dup
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 = "".dup
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 = "".dup
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 = ''.dup
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 = ''.dup
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".b
601
+ DEFLATE_CONTENT = "\x78\x9c\xcb\x48\xcd\xc9\xc9\x07\x00\x06\x2c\x02\x15".b
602
+ DEFLATE_NOHEADER_CONTENT = "x\x9C\xCBH\xCD\xC9\xC9\a\x00\x06,\x02\x15".b
603
+
604
+ def test_get_gzipped_content
605
+ @client.transparent_gzip_decompression = false
606
+ content = @client.get_content(serverurl + 'compressed?enc=gzip')
607
+ assert_not_equal('hello', content)
608
+ assert_equal(GZIP_CONTENT, content)
609
+ @client.transparent_gzip_decompression = true
610
+ @client.reset_all
611
+ assert_equal('hello', @client.get_content(serverurl + 'compressed?enc=gzip'))
612
+ assert_equal('hello', @client.get_content(serverurl + 'compressed?enc=deflate'))
613
+ assert_equal('hello', @client.get_content(serverurl + 'compressed?enc=deflate_noheader'))
614
+ @client.transparent_gzip_decompression = false
615
+ @client.reset_all
616
+ end
617
+
618
+ def test_get_content_with_block
619
+ @client.get_content(serverurl + 'hello') do |str|
620
+ assert_equal('hello', str)
621
+ end
622
+ @client.get_content(serverurl + 'redirect1') do |str|
623
+ assert_equal('hello', str)
624
+ end
625
+ @client.get_content(serverurl + 'redirect2') do |str|
626
+ assert_equal('hello', str)
627
+ end
628
+ end
629
+
630
+ def test_post_content
631
+ assert_equal('hello', @client.post_content(serverurl + 'hello'))
632
+ assert_equal('hello', @client.post_content(serverurl + 'redirect1'))
633
+ assert_equal('hello', @client.post_content(serverurl + 'redirect2'))
634
+ assert_raises(HTTPClient::BadResponseError) do
635
+ @client.post_content(serverurl + 'notfound')
636
+ end
637
+ assert_raises(HTTPClient::BadResponseError) do
638
+ @client.post_content(serverurl + 'redirect_self')
639
+ end
640
+ called = false
641
+ @client.redirect_uri_callback = lambda { |uri, res|
642
+ newuri = res.header['location'][0]
643
+ called = true
644
+ newuri
645
+ }
646
+ assert_equal('hello', @client.post_content(serverurl + 'relative_redirect'))
647
+ assert(called)
648
+ end
649
+
650
+ def test_post_content_io
651
+ post_body = StringIO.new("1234567890")
652
+ assert_equal('post,1234567890', @client.post_content(serverurl + 'servlet', post_body))
653
+ post_body = StringIO.new("1234567890")
654
+ assert_equal('post,1234567890', @client.post_content(serverurl + 'servlet_redirect', post_body))
655
+ #
656
+ post_body = StringIO.new("1234567890")
657
+ post_body.read(5)
658
+ assert_equal('post,67890', @client.post_content(serverurl + 'servlet_redirect', post_body))
659
+ end
660
+
661
+ def test_head
662
+ assert_equal("head", @client.head(serverurl + 'servlet').header["x-head"][0])
663
+ param = {'1'=>'2', '3'=>'4'}
664
+ res = @client.head(serverurl + 'servlet', param)
665
+ assert_equal(param, params(res.header["x-query"][0]))
666
+ end
667
+
668
+ def test_head_async
669
+ param = {'1'=>'2', '3'=>'4'}
670
+ conn = @client.head_async(serverurl + 'servlet', param)
671
+ Thread.pass while !conn.finished?
672
+ res = conn.pop
673
+ assert_equal(param, params(res.header["x-query"][0]))
674
+ end
675
+
676
+ def test_get
677
+ assert_equal("get", @client.get(serverurl + 'servlet').content)
678
+ param = {'1'=>'2', '3'=>'4'}
679
+ res = @client.get(serverurl + 'servlet', param)
680
+ assert_equal(param, params(res.header["x-query"][0]))
681
+ assert_nil(res.contenttype)
682
+ #
683
+ url = serverurl.to_s + 'servlet?5=6&7=8'
684
+ res = @client.get(url, param)
685
+ assert_equal(param.merge("5"=>"6", "7"=>"8"), params(res.header["x-query"][0]))
686
+ assert_nil(res.contenttype)
687
+ end
688
+
689
+ def test_get_with_base_url
690
+ @client = HTTPClient.new(:base_url => serverurl)
691
+ assert_equal("get", @client.get('/servlet').content)
692
+ param = {'1'=>'2', '3'=>'4'}
693
+ res = @client.get('/servlet', param)
694
+ assert_equal(param, params(res.header["x-query"][0]))
695
+ assert_nil(res.contenttype)
696
+ #
697
+ @client.base_url = serverurl[0...-1] + '/servlet'
698
+ url = '?5=6&7=8'
699
+ res = @client.get(url, param)
700
+ assert_equal(param.merge("5"=>"6", "7"=>"8"), params(res.header["x-query"][0]))
701
+ assert_nil(res.contenttype)
702
+ end
703
+
704
+ def test_get_with_default_header
705
+ @client = HTTPClient.new(:base_url => serverurl, :default_header => {'x-header' => 'custom'})
706
+ assert_equal('custom', @client.get('/servlet').headers['X-Header'])
707
+ @client.default_header = {'x-header' => 'custom2'}
708
+ assert_equal('custom2', @client.get('/servlet').headers['X-Header'])
709
+ # passing Hash overrides
710
+ assert_equal('custom3', @client.get('/servlet', :header => {'x-header' => 'custom3'}).headers['X-Header'])
711
+ # passing Array does not override
712
+ assert_equal('custom2, custom4', @client.get('/servlet', :header => [['x-header', 'custom4']]).headers['X-Header'])
713
+ end
714
+
715
+ def test_head_follow_redirect
716
+ expected = urify(serverurl + 'hello')
717
+ assert_equal(expected, @client.head(serverurl + 'hello', :follow_redirect => true).header.request_uri)
718
+ assert_equal(expected, @client.head(serverurl + 'redirect1', :follow_redirect => true).header.request_uri)
719
+ assert_equal(expected, @client.head(serverurl + 'redirect2', :follow_redirect => true).header.request_uri)
720
+ end
721
+
722
+ def test_get_follow_redirect
723
+ assert_equal('hello', @client.get(serverurl + 'hello', :follow_redirect => true).body)
724
+ assert_equal('hello', @client.get(serverurl + 'redirect1', :follow_redirect => true).body)
725
+
726
+ res = @client.get(serverurl + 'redirect2', :follow_redirect => true)
727
+ assert_equal('hello', res.body)
728
+ assert_equal("http://localhost:#{@serverport}/hello", res.header.request_uri.to_s)
729
+ assert_equal("http://localhost:#{@serverport}/redirect3", res.previous.header.request_uri.to_s)
730
+ assert_equal("http://localhost:#{@serverport}/redirect2", res.previous.previous.header.request_uri.to_s)
731
+ assert_equal(nil, res.previous.previous.previous)
732
+ end
733
+
734
+ def test_get_follow_redirect_with_query
735
+ assert_equal('hello?1=2&3=4', @client.get(serverurl + 'redirect1', :query => {1 => 2, 3 => 4}, :follow_redirect => true).body)
736
+ end
737
+
738
+ def test_get_async
739
+ param = {'1'=>'2', '3'=>'4'}
740
+ conn = @client.get_async(serverurl + 'servlet', param)
741
+ Thread.pass while !conn.finished?
742
+ res = conn.pop
743
+ assert_equal(param, params(res.header["x-query"][0]))
744
+ end
745
+
746
+ def test_get_async_with_base_url
747
+ param = {'1'=>'2', '3'=>'4'}
748
+ @client = HTTPClient.new(:base_url => serverurl)
749
+
750
+ # Use preconfigured :base_url
751
+ conn = @client.get_async('servlet', param)
752
+ Thread.pass while !conn.finished?
753
+ res = conn.pop
754
+ assert_equal(param, params(res.header["x-query"][0]))
755
+ # full URL still works
756
+ conn = @client.get_async(serverurl + 'servlet', param)
757
+ Thread.pass while !conn.finished?
758
+ res = conn.pop
759
+ assert_equal(param, params(res.header["x-query"][0]))
760
+ end
761
+
762
+ def test_get_async_for_largebody
763
+ conn = @client.get_async(serverurl + 'largebody')
764
+ res = conn.pop
765
+ assert_equal(1000*1000, res.content.read.length)
766
+ end
767
+
768
+ if RUBY_VERSION > "1.9"
769
+ def test_post_async_with_default_internal
770
+ original_encoding = Encoding.default_internal
771
+ Encoding.default_internal = Encoding::UTF_8
772
+ begin
773
+ post_body = StringIO.new("こんにちは")
774
+ conn = @client.post_async(serverurl + 'servlet', post_body)
775
+ Thread.pass while !conn.finished?
776
+ res = conn.pop
777
+ assert_equal 'post,こんにちは', res.content.read
778
+ ensure
779
+ Encoding.default_internal = original_encoding
780
+ end
781
+ end
782
+ end
783
+
784
+ def test_get_with_block
785
+ called = false
786
+ res = @client.get(serverurl + 'servlet') { |str|
787
+ assert_equal('get', str)
788
+ called = true
789
+ }
790
+ assert(called)
791
+ # res does not have a content
792
+ assert_nil(res.content)
793
+ end
794
+
795
+ def test_get_with_block_arity_2
796
+ called = false
797
+ res = @client.get(serverurl + 'servlet') { |blk_res, str|
798
+ assert_equal(200, blk_res.status)
799
+ assert_equal('get', str)
800
+ called = true
801
+ }
802
+ assert(called)
803
+ # res does not have a content
804
+ assert_nil(res.content)
805
+ end
806
+
807
+ def test_get_with_block_and_redirects
808
+ called = false
809
+ res = @client.get(serverurl + 'servlet', :follow_redirect => true) { |str|
810
+ assert_equal('get', str)
811
+ called = true
812
+ }
813
+ assert(called)
814
+ # res does not have a content
815
+ assert_nil(res.content)
816
+ end
817
+
818
+ def test_get_with_block_arity_2_and_redirects
819
+ called = false
820
+ res = @client.get(serverurl + 'servlet', :follow_redirect => true) { |blk_res, str|
821
+ assert_equal(200, blk_res.status)
822
+ assert_equal('get', str)
823
+ called = true
824
+ }
825
+ assert(called)
826
+ # res does not have a content
827
+ assert_nil(res.content)
828
+ end
829
+
830
+ def test_get_with_block_string_recycle
831
+ @client.read_block_size = 2
832
+ body = []
833
+ _res = @client.get(serverurl + 'servlet') { |str|
834
+ body << str
835
+ }
836
+ assert_equal(2, body.size)
837
+ assert_equal("get", body.join) # Was "tt" by String object recycle...
838
+ end
839
+
840
+ def test_get_with_block_chunked_string_recycle
841
+ server = TCPServer.open('localhost', 0)
842
+ server_thread = Thread.new {
843
+ Thread.abort_on_exception = true
844
+ sock = server.accept
845
+ create_keepalive_thread(1, sock)
846
+ }
847
+ url = "http://localhost:#{server.addr[1]}/"
848
+ body = []
849
+ begin
850
+ _res = @client.get(url + 'chunked') { |str|
851
+ body << str
852
+ }
853
+ ensure
854
+ server.close
855
+ server_thread.join
856
+ end
857
+ assert_equal('abcdefghijklmnopqrstuvwxyz1234567890abcdef', body.join)
858
+ end
859
+
860
+ def test_post
861
+ assert_equal("post", @client.post(serverurl + 'servlet', '').content[0, 4])
862
+ param = {'1'=>'2', '3'=>'4'}
863
+ res = @client.post(serverurl + 'servlet', param)
864
+ assert_equal(param, params(res.header["x-query"][0]))
865
+ end
866
+
867
+ def test_post_empty
868
+ @client.debug_dev = str = ''.dup
869
+ # nil body means 'no content' that is allowed but WEBrick cannot handle it.
870
+ @client.post(serverurl + 'servlet', :body => nil)
871
+ # request does not have 'Content-Type'
872
+ assert_equal(1, str.scan(/content-type/i).size)
873
+ end
874
+
875
+ def test_post_with_query
876
+ # this {:query => 'query'} recognized as body
877
+ res = @client.post(serverurl + 'servlet', :query => 'query')
878
+ assert_equal("post", res.content[0, 4])
879
+ assert_equal("query=query", res.headers["X-Query"])
880
+ assert_equal("", res.headers["X-Request-Query"])
881
+ end
882
+
883
+ def test_post_with_query_and_body
884
+ res = @client.post(serverurl + 'servlet', :query => {:query => 'query'}, :body => {:body => 'body'})
885
+ assert_equal("post", res.content[0, 4])
886
+ assert_equal("body=body", res.headers["X-Query"])
887
+ assert_equal("query=query", res.headers["X-Request-Query"])
888
+ end
889
+
890
+ def test_post_follow_redirect
891
+ assert_equal('hello', @client.post(serverurl + 'hello', :follow_redirect => true).body)
892
+ assert_equal('hello', @client.post(serverurl + 'redirect1', :follow_redirect => true).body)
893
+ assert_equal('hello', @client.post(serverurl + 'redirect2', :follow_redirect => true).body)
894
+ end
895
+
896
+ def test_post_with_content_type
897
+ param = [['1', '2'], ['3', '4']]
898
+ ext = {'content-type' => 'application/x-www-form-urlencoded', 'hello' => 'world'}
899
+ assert_equal("post", @client.post(serverurl + 'servlet', '').content[0, 4], ext)
900
+ res = @client.post(serverurl + 'servlet', param, ext)
901
+ assert_equal(Hash[param], params(res.header["x-query"][0]))
902
+ #
903
+ ext = [['content-type', 'multipart/form-data'], ['hello', 'world']]
904
+ assert_equal("post", @client.post(serverurl + 'servlet', '').content[0, 4], ext)
905
+ res = @client.post(serverurl + 'servlet', param, ext)
906
+ assert_match(/Content-Disposition: form-data; name="1"/, res.content)
907
+ assert_match(/Content-Disposition: form-data; name="3"/, res.content)
908
+ #
909
+ ext = {'content-type' => 'multipart/form-data; boundary=hello'}
910
+ assert_equal("post", @client.post(serverurl + 'servlet', '').content[0, 4], ext)
911
+ res = @client.post(serverurl + 'servlet', param, ext)
912
+ assert_match(/Content-Disposition: form-data; name="1"/, res.content)
913
+ assert_match(/Content-Disposition: form-data; name="3"/, res.content)
914
+ 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)
915
+ end
916
+
917
+ def test_post_with_custom_multipart_and_boolean_params
918
+ param = [['boolean_true', true]]
919
+ ext = { 'content-type' => 'multipart/form-data' }
920
+ assert_equal("post", @client.post(serverurl + 'servlet', '').content[0, 4], ext)
921
+ res = @client.post(serverurl + 'servlet', param, ext)
922
+ assert_match(/Content-Disposition: form-data; name="boolean_true"\r\n\r\ntrue\r\n/, res.content)
923
+ #
924
+ param = [['boolean_false', false]]
925
+ res = @client.post(serverurl + 'servlet', param, ext)
926
+ assert_match(/Content-Disposition: form-data; name="boolean_false"\r\n\r\nfalse\r\n/, res.content)
927
+ #
928
+ param = [['nil', nil]]
929
+ res = @client.post(serverurl + 'servlet', param, ext)
930
+ assert_match(/Content-Disposition: form-data; name="nil"\r\n\r\n\r\n/, res.content)
931
+ end
932
+
933
+ def test_post_with_file
934
+ STDOUT.sync = true
935
+ File.open(__FILE__) do |file|
936
+ res = @client.post(serverurl + 'servlet', {1=>2, 3=>file})
937
+ assert_match(/^Content-Disposition: form-data; name="1"\r\n/nm, res.content)
938
+ assert_match(/^Content-Disposition: form-data; name="3";/, res.content)
939
+ assert_match(/FIND_TAG_IN_THIS_FILE/, res.content)
940
+ end
941
+ end
942
+
943
+ def test_post_with_file_without_size
944
+ STDOUT.sync = true
945
+ File.open(__FILE__) do |file|
946
+ def file.size
947
+ # Simulates some strange Windows behaviour
948
+ raise SystemCallError.new "Unknown Error (20047)"
949
+ end
950
+ assert_nothing_raised do
951
+ @client.post(serverurl + 'servlet', {1=>2, 3=>file})
952
+ end
953
+ end
954
+ end
955
+
956
+ def test_post_with_io # streaming, but not chunked
957
+ myio = StringIO.new("X" * (HTTP::Message::Body::DEFAULT_CHUNK_SIZE + 1))
958
+ def myio.read(*args)
959
+ @called ||= 0
960
+ @called += 1
961
+ super
962
+ end
963
+ def myio.called
964
+ @called
965
+ end
966
+ @client.debug_dev = str = StringIO.new
967
+ res = @client.post(serverurl + 'servlet', {1=>2, 3=>myio})
968
+ assert_match(/\r\nContent-Disposition: form-data; name="1"\r\n/m, res.content)
969
+ assert_match(/\r\n2\r\n/m, res.content)
970
+ assert_match(/\r\nContent-Disposition: form-data; name="3"; filename=""\r\n/m, res.content)
971
+ assert_match(/\r\nContent-Length:/m, str.string)
972
+ # HTTPClient reads from head to 'size'; CHUNK_SIZE bytes then 1 byte, that's all.
973
+ assert_equal(2, myio.called)
974
+ end
975
+
976
+ def test_post_with_io_nosize # streaming + chunked post
977
+ myio = StringIO.new("4")
978
+ def myio.size
979
+ nil
980
+ end
981
+ @client.debug_dev = str = StringIO.new
982
+ res = @client.post(serverurl + 'servlet', {1=>2, 3=>myio})
983
+ assert_match(/\r\nContent-Disposition: form-data; name="1"\r\n/m, res.content)
984
+ assert_match(/\r\n2\r\n/m, res.content)
985
+ assert_match(/\r\nContent-Disposition: form-data; name="3"; filename=""\r\n/m, res.content)
986
+ assert_match(/\r\n4\r\n/m, res.content)
987
+ assert_match(/\r\nTransfer-Encoding: chunked\r\n/m, str.string)
988
+ end
989
+
990
+ def test_post_with_sized_io
991
+ myio = StringIO.new("45")
992
+ def myio.size
993
+ 1
994
+ end
995
+ res = @client.post(serverurl + 'servlet', myio)
996
+ assert_equal('post,4', res.content)
997
+ end
998
+
999
+ def test_post_with_sized_io_part
1000
+ myio = StringIO.new("45")
1001
+ def myio.size
1002
+ 1
1003
+ end
1004
+ @client.debug_dev = str = StringIO.new
1005
+ _res = @client.post(serverurl + 'servlet', { :file => myio })
1006
+ assert_match(/\r\n4\r\n/, str.string, 'should send "4" not "45"')
1007
+ end
1008
+
1009
+ def test_post_with_unknown_sized_io_part
1010
+ myio1 = StringIO.new("123")
1011
+ myio2 = StringIO.new("45")
1012
+ class << myio1
1013
+ undef :size
1014
+ end
1015
+ class << myio2
1016
+ # This does not work because other file is 'unknown sized'
1017
+ def size
1018
+ 1
1019
+ end
1020
+ end
1021
+ @client.debug_dev = str = StringIO.new
1022
+ _res = @client.post(serverurl + 'servlet', { :file1 => myio1, :file2 => myio2 })
1023
+ assert_match(/\r\n45\r\n/, str.string)
1024
+ end
1025
+
1026
+ def test_post_async
1027
+ param = {'1'=>'2', '3'=>'4'}
1028
+ conn = @client.post_async(serverurl + 'servlet', param)
1029
+ Thread.pass while !conn.finished?
1030
+ res = conn.pop
1031
+ assert_equal(param, params(res.header["x-query"][0]))
1032
+ end
1033
+
1034
+ def test_post_with_block
1035
+ called = false
1036
+ res = @client.post(serverurl + 'servlet', '') { |str|
1037
+ assert_equal('post,', str)
1038
+ called = true
1039
+ }
1040
+ assert(called)
1041
+ assert_nil(res.content)
1042
+ #
1043
+ called = false
1044
+ param = [['1', '2'], ['3', '4']]
1045
+ res = @client.post(serverurl + 'servlet', param) { |str|
1046
+ assert_equal('post,1=2&3=4', str)
1047
+ called = true
1048
+ }
1049
+ assert(called)
1050
+ assert_equal('1=2&3=4', res.header["x-query"][0])
1051
+ assert_nil(res.content)
1052
+ end
1053
+
1054
+ def test_post_with_custom_multipart
1055
+ ext = {'content-type' => 'multipart/form-data'}
1056
+ assert_equal("post", @client.post(serverurl + 'servlet', '').content[0, 4], ext)
1057
+ body = [{ 'Content-Disposition' => 'form-data; name="1"', :content => "2"},
1058
+ { 'Content-Disposition' => 'form-data; name="3"', :content => "4"}]
1059
+ res = @client.post(serverurl + 'servlet', body, ext)
1060
+ assert_match(/Content-Disposition: form-data; name="1"/, res.content)
1061
+ assert_match(/Content-Disposition: form-data; name="3"/, res.content)
1062
+ #
1063
+ ext = {'content-type' => 'multipart/form-data; boundary=hello'}
1064
+ assert_equal("post", @client.post(serverurl + 'servlet', '').content[0, 4], ext)
1065
+ res = @client.post(serverurl + 'servlet', body, ext)
1066
+ assert_match(/Content-Disposition: form-data; name="1"/, res.content)
1067
+ assert_match(/Content-Disposition: form-data; name="3"/, res.content)
1068
+ 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)
1069
+ end
1070
+
1071
+ def test_post_with_custom_multipart_and_file
1072
+ STDOUT.sync = true
1073
+ File.open(__FILE__) do |file|
1074
+ def file.original_filename
1075
+ 'file.txt'
1076
+ end
1077
+
1078
+ ext = { 'Content-Type' => 'multipart/alternative' }
1079
+ body = [{ 'Content-Type' => 'text/plain', :content => "this is only a test" },
1080
+ { 'Content-Type' => 'application/x-ruby', :content => file }]
1081
+ res = @client.post(serverurl + 'servlet', body, ext)
1082
+ assert_match(/^Content-Type: text\/plain\r\n/m, res.content)
1083
+ assert_match(/^this is only a test\r\n/m, res.content)
1084
+ assert_match(/^Content-Type: application\/x-ruby\r\n/m, res.content)
1085
+ assert_match(/Content-Disposition: form-data; name="3"; filename="file.txt"/, res.content)
1086
+ assert_match(/FIND_TAG_IN_THIS_FILE/, res.content)
1087
+ end
1088
+ end
1089
+
1090
+ def test_patch
1091
+ assert_equal("patch", @client.patch(serverurl + 'servlet', '').content)
1092
+ param = {'1'=>'2', '3'=>'4'}
1093
+ @client.debug_dev = str = ''.dup
1094
+ res = @client.patch(serverurl + 'servlet', param)
1095
+ assert_equal(param, params(res.header["x-query"][0]))
1096
+ assert_equal('Content-Type: application/x-www-form-urlencoded', str.split(/\r?\n/)[5])
1097
+ end
1098
+
1099
+ def test_patch_with_query_and_body
1100
+ res = @client.patch(serverurl + 'servlet', :query => {:query => 'query'}, :body => {:body => 'body'})
1101
+ assert_equal("patch", res.content)
1102
+ assert_equal("body=body", res.headers["X-Query"])
1103
+ assert_equal("query=query", res.headers["X-Request-Query"])
1104
+ end
1105
+
1106
+ def test_patch_bytesize
1107
+ res = @client.patch(serverurl + 'servlet', 'txt' => 'あいうえお')
1108
+ assert_equal('txt=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A', res.header["x-query"][0])
1109
+ assert_equal('15', res.header["x-size"][0])
1110
+ end
1111
+
1112
+ def test_patch_async
1113
+ param = {'1'=>'2', '3'=>'4'}
1114
+ conn = @client.patch_async(serverurl + 'servlet', param)
1115
+ Thread.pass while !conn.finished?
1116
+ res = conn.pop
1117
+ assert_equal(param, params(res.header["x-query"][0]))
1118
+ end
1119
+
1120
+ def test_put
1121
+ assert_equal("put", @client.put(serverurl + 'servlet', '').content)
1122
+ param = {'1'=>'2', '3'=>'4'}
1123
+ @client.debug_dev = str = ''.dup
1124
+ res = @client.put(serverurl + 'servlet', param)
1125
+ assert_equal(param, params(res.header["x-query"][0]))
1126
+ assert_equal('Content-Type: application/x-www-form-urlencoded', str.split(/\r?\n/)[5])
1127
+ end
1128
+
1129
+ def test_put_with_query_and_body
1130
+ res = @client.put(serverurl + 'servlet', :query => {:query => 'query'}, :body => {:body => 'body'})
1131
+ assert_equal("put", res.content)
1132
+ assert_equal("body=body", res.headers["X-Query"])
1133
+ assert_equal("query=query", res.headers["X-Request-Query"])
1134
+ end
1135
+
1136
+ def test_put_bytesize
1137
+ res = @client.put(serverurl + 'servlet', 'txt' => 'あいうえお')
1138
+ assert_equal('txt=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A', res.header["x-query"][0])
1139
+ assert_equal('15', res.header["x-size"][0])
1140
+ end
1141
+
1142
+ def test_put_async
1143
+ param = {'1'=>'2', '3'=>'4'}
1144
+ conn = @client.put_async(serverurl + 'servlet', param)
1145
+ Thread.pass while !conn.finished?
1146
+ res = conn.pop
1147
+ assert_equal(param, params(res.header["x-query"][0]))
1148
+ end
1149
+
1150
+ def test_delete
1151
+ assert_equal("delete", @client.delete(serverurl + 'servlet').content)
1152
+ end
1153
+
1154
+ def test_delete_with_query
1155
+ res = @client.delete(serverurl + 'servlet', :query => {:query => 'query'})
1156
+ assert_equal("delete", res.content)
1157
+ assert_equal('query=query', res.headers['X-Request-Query'])
1158
+ end
1159
+
1160
+ def test_delete_with_query_and_body
1161
+ res = @client.delete(serverurl + 'servlet', :query => {:query => 'query'}, :body => {:body => 'body'})
1162
+ assert_equal("delete", res.content)
1163
+ assert_equal('query=query', res.headers['X-Request-Query'])
1164
+ assert_equal('body=body', res.headers['X-Query'])
1165
+ end
1166
+
1167
+ # Not prohibited by spec, but normally it's ignored
1168
+ def test_delete_with_body
1169
+ param = {'1'=>'2', '3'=>'4'}
1170
+ @client.debug_dev = str = ''.dup
1171
+ assert_equal("delete", @client.delete(serverurl + 'servlet', param).content)
1172
+ assert_equal({'1' => ['2'], '3' => ['4']}, HTTP::Message.parse(str.split(/\r?\n\r?\n/)[2]))
1173
+ end
1174
+
1175
+ def test_delete_async
1176
+ conn = @client.delete_async(serverurl + 'servlet')
1177
+ Thread.pass while !conn.finished?
1178
+ res = conn.pop
1179
+ assert_equal('delete', res.content.read)
1180
+ end
1181
+
1182
+ def test_options
1183
+ assert_equal('options', @client.options(serverurl + 'servlet').content)
1184
+ end
1185
+
1186
+ def test_options_with_header
1187
+ res = @client.options(serverurl + 'servlet', {'x-header' => 'header'})
1188
+ assert_equal('header', res.headers['X-Header'])
1189
+ end
1190
+
1191
+ def test_options_with_body
1192
+ res = @client.options(serverurl + 'servlet', :body => 'body')
1193
+ assert_equal('body', res.headers['X-Body'])
1194
+ end
1195
+
1196
+ def test_options_with_body_and_header
1197
+ res = @client.options(serverurl + 'servlet', :body => 'body', :header => {'x-header' => 'header'})
1198
+ assert_equal('header', res.headers['X-Header'])
1199
+ assert_equal('body', res.headers['X-Body'])
1200
+ end
1201
+
1202
+ def test_options_async
1203
+ conn = @client.options_async(serverurl + 'servlet')
1204
+ Thread.pass while !conn.finished?
1205
+ res = conn.pop
1206
+ assert_equal('options', res.content.read)
1207
+ end
1208
+
1209
+ def test_propfind
1210
+ assert_equal("propfind", @client.propfind(serverurl + 'servlet').content)
1211
+ end
1212
+
1213
+ def test_propfind_async
1214
+ conn = @client.propfind_async(serverurl + 'servlet')
1215
+ Thread.pass while !conn.finished?
1216
+ res = conn.pop
1217
+ assert_equal('propfind', res.content.read)
1218
+ end
1219
+
1220
+ def test_proppatch
1221
+ assert_equal("proppatch", @client.proppatch(serverurl + 'servlet').content)
1222
+ param = {'1'=>'2', '3'=>'4'}
1223
+ res = @client.proppatch(serverurl + 'servlet', param)
1224
+ assert_equal('proppatch', res.content)
1225
+ assert_equal(param, params(res.header["x-query"][0]))
1226
+ end
1227
+
1228
+ def test_proppatch_async
1229
+ param = {'1'=>'2', '3'=>'4'}
1230
+ conn = @client.proppatch_async(serverurl + 'servlet', param)
1231
+ Thread.pass while !conn.finished?
1232
+ res = conn.pop
1233
+ assert_equal('proppatch', res.content.read)
1234
+ assert_equal(param, params(res.header["x-query"][0]))
1235
+ end
1236
+
1237
+ def test_trace
1238
+ assert_equal("trace", @client.trace(serverurl + 'servlet').content)
1239
+ param = {'1'=>'2', '3'=>'4'}
1240
+ res = @client.trace(serverurl + 'servlet', param)
1241
+ assert_equal(param, params(res.header["x-query"][0]))
1242
+ end
1243
+
1244
+ def test_trace_async
1245
+ param = {'1'=>'2', '3'=>'4'}
1246
+ conn = @client.trace_async(serverurl + 'servlet', param)
1247
+ Thread.pass while !conn.finished?
1248
+ res = conn.pop
1249
+ assert_equal(param, params(res.header["x-query"][0]))
1250
+ end
1251
+
1252
+ def test_chunked
1253
+ assert_equal('chunked', @client.get_content(serverurl + 'chunked', { 'msg' => 'chunked' }))
1254
+ assert_equal('あいうえお', @client.get_content(serverurl + 'chunked', { 'msg' => 'あいうえお' }))
1255
+ end
1256
+
1257
+ def test_chunked_empty
1258
+ assert_equal('', @client.get_content(serverurl + 'chunked', { 'msg' => '' }))
1259
+ end
1260
+
1261
+ def test_get_query
1262
+ assert_equal({'1'=>'2'}, check_query_get({1=>2}))
1263
+ assert_equal({'a'=>'A', 'B'=>'b'}, check_query_get({"a"=>"A", "B"=>"b"}))
1264
+ assert_equal({'&'=>'&'}, check_query_get({"&"=>"&"}))
1265
+ assert_equal({'= '=>' =+'}, check_query_get({"= "=>" =+"}))
1266
+ assert_equal(
1267
+ ['=', '&'].sort,
1268
+ check_query_get([["=", "="], ["=", "&"]])['='].to_ary.sort
1269
+ )
1270
+ assert_equal({'123'=>'45'}, check_query_get('123=45'))
1271
+ assert_equal({'12 3'=>'45', ' '=>' '}, check_query_get('12+3=45&+=+'))
1272
+ assert_equal({}, check_query_get(''))
1273
+ assert_equal({'1'=>'2'}, check_query_get({1=>StringIO.new('2')}))
1274
+ assert_equal({'1'=>'2', '3'=>'4'}, check_query_get(StringIO.new('3=4&1=2')))
1275
+
1276
+ hash = check_query_get({"a"=>["A","a"], "B"=>"b"})
1277
+ assert_equal({'a'=>'A', 'B'=>'b'}, hash)
1278
+ assert_equal(['A','a'], hash['a'].to_ary)
1279
+
1280
+ hash = check_query_get({"a"=>WEBrick::HTTPUtils::FormData.new("A","a"), "B"=>"b"})
1281
+ assert_equal({'a'=>'A', 'B'=>'b'}, hash)
1282
+ assert_equal(['A','a'], hash['a'].to_ary)
1283
+
1284
+ hash = check_query_get({"a"=>[StringIO.new("A"),StringIO.new("a")], "B"=>StringIO.new("b")})
1285
+ assert_equal({'a'=>'A', 'B'=>'b'}, hash)
1286
+ assert_equal(['A','a'], hash['a'].to_ary)
1287
+ end
1288
+
1289
+ def test_post_body
1290
+ assert_equal({'1'=>'2'}, check_query_post({1=>2}))
1291
+ assert_equal({'a'=>'A', 'B'=>'b'}, check_query_post({"a"=>"A", "B"=>"b"}))
1292
+ assert_equal({'&'=>'&'}, check_query_post({"&"=>"&"}))
1293
+ assert_equal({'= '=>' =+'}, check_query_post({"= "=>" =+"}))
1294
+ assert_equal(
1295
+ ['=', '&'].sort,
1296
+ check_query_post([["=", "="], ["=", "&"]])['='].to_ary.sort
1297
+ )
1298
+ assert_equal({'123'=>'45'}, check_query_post('123=45'))
1299
+ assert_equal({'12 3'=>'45', ' '=>' '}, check_query_post('12+3=45&+=+'))
1300
+ assert_equal({}, check_query_post(''))
1301
+ #
1302
+ post_body = StringIO.new("foo=bar&foo=baz")
1303
+ assert_equal(
1304
+ ["bar", "baz"],
1305
+ check_query_post(post_body)["foo"].to_ary.sort
1306
+ )
1307
+ end
1308
+
1309
+ def test_extra_headers
1310
+ str = "".dup
1311
+ @client.debug_dev = str
1312
+ @client.head(serverurl, nil, {"ABC" => "DEF"})
1313
+ lines = str.split(/(?:\r?\n)+/)
1314
+ assert_equal("= Request", lines[0])
1315
+ assert_match("ABC: DEF", lines[4])
1316
+ #
1317
+ str = "".dup
1318
+ @client.debug_dev = str
1319
+ @client.get(serverurl, nil, [["ABC", "DEF"], ["ABC", "DEF"]])
1320
+ lines = str.split(/(?:\r?\n)+/)
1321
+ assert_equal("= Request", lines[0])
1322
+ assert_match("ABC: DEF", lines[4])
1323
+ assert_match("ABC: DEF", lines[5])
1324
+ end
1325
+
1326
+ def test_http_custom_date_header
1327
+ @client.debug_dev = (str = "".dup)
1328
+ _res = @client.get(serverurl + 'hello', :header => {'Date' => 'foo'})
1329
+ lines = str.split(/(?:\r?\n)+/)
1330
+ assert_equal('Date: foo', lines[4])
1331
+ end
1332
+
1333
+ def test_timeout
1334
+ client = HTTPClient.new
1335
+ assert_equal(60, client.connect_timeout)
1336
+ assert_equal(120, client.send_timeout)
1337
+ assert_equal(60, client.receive_timeout)
1338
+ #
1339
+ client.connect_timeout = 1
1340
+ client.send_timeout = 2
1341
+ client.receive_timeout = 3
1342
+ assert_equal(1, client.connect_timeout)
1343
+ assert_equal(2, client.send_timeout)
1344
+ assert_equal(3, client.receive_timeout)
1345
+ end
1346
+
1347
+ def test_connect_timeout
1348
+ # ToDo
1349
+ end
1350
+
1351
+ def test_send_timeout
1352
+ # ToDo
1353
+ end
1354
+
1355
+ def test_receive_timeout
1356
+ # this test takes 2 sec
1357
+ assert_equal('hello?sec=2', @client.get_content(serverurl + 'sleep?sec=2'))
1358
+ @client.receive_timeout = 1
1359
+ @client.reset_all
1360
+ assert_equal('hello?sec=0', @client.get_content(serverurl + 'sleep?sec=0'))
1361
+ assert_raise(HTTPClient::ReceiveTimeoutError) do
1362
+ @client.get_content(serverurl + 'sleep?sec=2')
1363
+ end
1364
+ @client.receive_timeout = 3
1365
+ @client.reset_all
1366
+ assert_equal('hello?sec=2', @client.get_content(serverurl + 'sleep?sec=2'))
1367
+ end
1368
+
1369
+ def test_receive_timeout_post
1370
+ # this test takes 2 sec
1371
+ assert_equal('hello', @client.post(serverurl + 'sleep', :sec => 2).content)
1372
+ @client.receive_timeout = 1
1373
+ @client.reset_all
1374
+ assert_equal('hello', @client.post(serverurl + 'sleep', :sec => 0).content)
1375
+ assert_raise(HTTPClient::ReceiveTimeoutError) do
1376
+ @client.post(serverurl + 'sleep', :sec => 2)
1377
+ end
1378
+ @client.receive_timeout = 3
1379
+ @client.reset_all
1380
+ assert_equal('hello', @client.post(serverurl + 'sleep', :sec => 2).content)
1381
+ end
1382
+
1383
+ def test_reset
1384
+ url = serverurl + 'servlet'
1385
+ assert_nothing_raised do
1386
+ 5.times do
1387
+ @client.get(url)
1388
+ @client.reset(url)
1389
+ end
1390
+ end
1391
+ end
1392
+
1393
+ def test_reset_all
1394
+ assert_nothing_raised do
1395
+ 5.times do
1396
+ @client.get(serverurl + 'servlet')
1397
+ @client.reset_all
1398
+ end
1399
+ end
1400
+ end
1401
+
1402
+ def test_cookies
1403
+ cookiefile = Tempfile.new('test_cookies_file')
1404
+ File.open(cookiefile.path, "wb") do |f|
1405
+ f << "http://rubyforge.org/account/login.php\tsession_ser\tLjEwMy45Ni40Ni0q%2A-fa0537de8cc31\t2000000000\trubyforge.org\t/account/\t9\n"
1406
+ end
1407
+ @client.set_cookie_store(cookiefile.path)
1408
+ #
1409
+ @client.reset_all
1410
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\nSet-Cookie: session_ser=bar; expires=#{Time.at(1924873200).gmtime.httpdate}\n\nOK"
1411
+ @client.get_content('http://rubyforge.org/account/login.php')
1412
+ @client.save_cookie_store
1413
+ str = File.read(cookiefile.path)
1414
+ assert_match(%r(http://rubyforge.org/account/login.php\tsession_ser\tbar\t1924873200\trubyforge.org\t/account/\t9), str)
1415
+ end
1416
+
1417
+ def test_eof_error_length
1418
+ io = StringIO.new('')
1419
+ def io.gets(*arg)
1420
+ @buf ||= ["HTTP/1.0 200 OK\n".dup, "content-length: 123\n".dup, "\n".dup]
1421
+ @buf.shift
1422
+ end
1423
+ def io.readpartial(size, buf)
1424
+ @second ||= false
1425
+ if !@second
1426
+ @second = '1st'
1427
+ buf << "abc"
1428
+ buf
1429
+ elsif @second == '1st'
1430
+ @second = '2nd'
1431
+ raise EOFError.new
1432
+ else
1433
+ raise Exception.new
1434
+ end
1435
+ end
1436
+ def io.eof?
1437
+ true
1438
+ end
1439
+ @client.test_loopback_http_response << io
1440
+ assert_nothing_raised do
1441
+ @client.get('http://foo/bar')
1442
+ end
1443
+ end
1444
+
1445
+ def test_eof_error_rest
1446
+ io = StringIO.new('')
1447
+ def io.gets(*arg)
1448
+ @buf ||= ["HTTP/1.0 200 OK\n".dup, "\n".dup]
1449
+ @buf.shift
1450
+ end
1451
+ def io.readpartial(size, buf)
1452
+ @second ||= false
1453
+ if !@second
1454
+ @second = '1st'
1455
+ buf << "abc"
1456
+ buf
1457
+ elsif @second == '1st'
1458
+ @second = '2nd'
1459
+ raise EOFError.new
1460
+ else
1461
+ raise Exception.new
1462
+ end
1463
+ end
1464
+ def io.eof?
1465
+ true
1466
+ end
1467
+ @client.test_loopback_http_response << io
1468
+ assert_nothing_raised do
1469
+ @client.get('http://foo/bar')
1470
+ end
1471
+ end
1472
+
1473
+ def test_urify
1474
+ extend HTTPClient::Util
1475
+ assert_nil(urify(nil))
1476
+ uri = 'http://foo'
1477
+ assert_equal(urify(uri), urify(uri))
1478
+ assert_equal(urify(uri), urify(urify(uri)))
1479
+ end
1480
+
1481
+ def test_connection
1482
+ c = HTTPClient::Connection.new
1483
+ assert(c.finished?)
1484
+ assert_nil(c.join)
1485
+ end
1486
+
1487
+ def test_site
1488
+ site = HTTPClient::Site.new
1489
+ assert_equal('tcp', site.scheme)
1490
+ assert_equal('0.0.0.0', site.host)
1491
+ assert_equal(0, site.port)
1492
+ assert_equal('tcp://0.0.0.0:0', site.addr)
1493
+ assert_equal('tcp://0.0.0.0:0', site.to_s)
1494
+ assert_nothing_raised do
1495
+ site.inspect
1496
+ end
1497
+ #
1498
+ site = HTTPClient::Site.new(urify('http://localhost:12345/foo'))
1499
+ assert_equal('http', site.scheme)
1500
+ assert_equal('localhost', site.host)
1501
+ assert_equal(12345, site.port)
1502
+ assert_equal('http://localhost:12345', site.addr)
1503
+ assert_equal('http://localhost:12345', site.to_s)
1504
+ assert_nothing_raised do
1505
+ site.inspect
1506
+ end
1507
+ #
1508
+ site1 = HTTPClient::Site.new(urify('http://localhost:12341/'))
1509
+ site2 = HTTPClient::Site.new(urify('http://localhost:12342/'))
1510
+ site3 = HTTPClient::Site.new(urify('http://localhost:12342/'))
1511
+ assert(!(site1 == site2))
1512
+ h = { site1 => 'site1', site2 => 'site2' }
1513
+ h[site3] = 'site3'
1514
+ assert_equal('site1', h[site1])
1515
+ assert_equal('site3', h[site2])
1516
+ end
1517
+
1518
+ def test_http_header
1519
+ res = @client.get(serverurl + 'hello')
1520
+ assert_equal('text/html', res.contenttype)
1521
+ assert_equal(5, res.header.get(nil).size)
1522
+ #
1523
+ res.header.delete('connection')
1524
+ assert_equal(4, res.header.get(nil).size)
1525
+ #
1526
+ res.header['foo'] = 'bar'
1527
+ assert_equal(['bar'], res.header['foo'])
1528
+ #
1529
+ assert_equal([['foo', 'bar']], res.header.get('foo'))
1530
+ res.header['foo'] = ['bar', 'bar2']
1531
+ assert_equal([['foo', 'bar'], ['foo', 'bar2']], res.header.get('foo'))
1532
+ end
1533
+
1534
+ def test_mime_type
1535
+ assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
1536
+ assert_equal('text/html', HTTP::Message.mime_type('foo.html'))
1537
+ assert_equal('text/html', HTTP::Message.mime_type('foo.htm'))
1538
+ assert_equal('text/xml', HTTP::Message.mime_type('foo.xml'))
1539
+ assert_equal('application/msword', HTTP::Message.mime_type('foo.doc'))
1540
+ assert_equal('image/png', HTTP::Message.mime_type('foo.png'))
1541
+ assert_equal('image/gif', HTTP::Message.mime_type('foo.gif'))
1542
+ assert_equal('image/jpeg', HTTP::Message.mime_type('foo.jpg'))
1543
+ assert_equal('image/jpeg', HTTP::Message.mime_type('foo.jpeg'))
1544
+ assert_equal('application/octet-stream', HTTP::Message.mime_type('foo.unknown'))
1545
+ #
1546
+ handler = lambda { |path| 'hello/world' }
1547
+ assert_nil(HTTP::Message.mime_type_handler)
1548
+ assert_nil(HTTP::Message.get_mime_type_func)
1549
+ HTTP::Message.mime_type_handler = handler
1550
+ assert_not_nil(HTTP::Message.mime_type_handler)
1551
+ assert_not_nil(HTTP::Message.get_mime_type_func)
1552
+ assert_equal('hello/world', HTTP::Message.mime_type('foo.txt'))
1553
+ HTTP::Message.mime_type_handler = nil
1554
+ assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
1555
+ HTTP::Message.set_mime_type_func(nil)
1556
+ assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
1557
+ #
1558
+ handler = lambda { |path| nil }
1559
+ HTTP::Message.mime_type_handler = handler
1560
+ assert_equal('application/octet-stream', HTTP::Message.mime_type('foo.txt'))
1561
+ end
1562
+
1563
+ def test_connect_request
1564
+ req = HTTP::Message.new_connect_request(urify('https://foo/bar'))
1565
+ assert_equal("CONNECT foo:443 HTTP/1.0\r\n\r\n", req.dump)
1566
+ req = HTTP::Message.new_connect_request(urify('https://example.com/'))
1567
+ assert_equal("CONNECT example.com:443 HTTP/1.0\r\n\r\n", req.dump)
1568
+ end
1569
+
1570
+ def test_response
1571
+ res = HTTP::Message.new_response('response')
1572
+ res.contenttype = 'text/plain'
1573
+ res.header.body_date = Time.at(946652400)
1574
+ assert_equal(
1575
+ [
1576
+ "",
1577
+ "Content-Length: 8",
1578
+ "Content-Type: text/plain",
1579
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
1580
+ "Status: 200 OK",
1581
+ "response"
1582
+ ],
1583
+ res.dump.split(/\r\n/).sort
1584
+ )
1585
+ assert_equal(['8'], res.header['Content-Length'])
1586
+ assert_equal('8', res.headers['Content-Length'])
1587
+ res.header.set('foo', 'bar')
1588
+ assert_equal(
1589
+ [
1590
+ "",
1591
+ "Content-Length: 8",
1592
+ "Content-Type: text/plain",
1593
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
1594
+ "Status: 200 OK",
1595
+ "foo: bar",
1596
+ "response"
1597
+ ],
1598
+ res.dump.split(/\r\n/).sort
1599
+ )
1600
+ # nil body
1601
+ res = HTTP::Message.new_response(nil)
1602
+ assert_equal(
1603
+ [
1604
+ "Content-Length: 0",
1605
+ "Content-Type: text/html; charset=us-ascii",
1606
+ "Status: 200 OK"
1607
+ ],
1608
+ res.dump.split(/\r\n/).sort
1609
+ )
1610
+ # for mod_ruby env
1611
+ Object.const_set('Apache', nil)
1612
+ begin
1613
+ res = HTTP::Message.new_response('response')
1614
+ assert(res.dump.split(/\r\n/).any? { |line| /^Date/ =~ line })
1615
+ #
1616
+ res = HTTP::Message.new_response('response')
1617
+ res.contenttype = 'text/plain'
1618
+ res.header.body_date = Time.at(946652400)
1619
+ res.header['Date'] = Time.at(946652400).httpdate
1620
+ assert_equal(
1621
+ [
1622
+ "",
1623
+ "Content-Length: 8",
1624
+ "Content-Type: text/plain",
1625
+ "Date: Fri, 31 Dec 1999 15:00:00 GMT",
1626
+ "HTTP/1.1 200 OK",
1627
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
1628
+ "response"
1629
+ ],
1630
+ res.dump.split(/\r\n/).sort
1631
+ )
1632
+ ensure
1633
+ Object.instance_eval { remove_const('Apache') }
1634
+ end
1635
+ end
1636
+
1637
+ def test_response_cookies
1638
+ res = HTTP::Message.new_response('response')
1639
+ res.contenttype = 'text/plain'
1640
+ res.header.body_date = Time.at(946652400)
1641
+ res.header.request_uri = 'http://www.example.com/'
1642
+ assert_nil(res.cookies)
1643
+ #
1644
+ res.header['Set-Cookie'] = [
1645
+ 'CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT',
1646
+ 'PART_NUMBER=ROCKET_LAUNCHER_0001; path=/'
1647
+ ]
1648
+ assert_equal(
1649
+ [
1650
+ "",
1651
+ "Content-Length: 8",
1652
+ "Content-Type: text/plain",
1653
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
1654
+ "Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT",
1655
+ "Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/",
1656
+ "Status: 200 OK",
1657
+ "response"
1658
+ ],
1659
+ res.dump.split(/\r\n/).sort
1660
+ )
1661
+ assert_equal(2, res.cookies.size)
1662
+ assert_equal('CUSTOMER', res.cookies[0].name)
1663
+ assert_equal('PART_NUMBER', res.cookies[1].name)
1664
+ end
1665
+
1666
+ def test_ok_response_success
1667
+ res = HTTP::Message.new_response('response')
1668
+ assert_equal(true, res.ok?)
1669
+ res.status = 404
1670
+ assert_equal(false, res.ok?)
1671
+ res.status = 500
1672
+ assert_equal(false, res.ok?)
1673
+ res.status = 302
1674
+ assert_equal(false, res.ok?)
1675
+ end
1676
+
1677
+ if !defined?(JRUBY_VERSION) and RUBY_VERSION < '1.9'
1678
+ def test_timeout_scheduler
1679
+ assert_equal('hello', @client.get_content(serverurl + 'hello'))
1680
+ status = HTTPClient.timeout_scheduler.instance_eval { @thread.kill; @thread.join; @thread.status }
1681
+ assert(!status) # dead
1682
+ assert_equal('hello', @client.get_content(serverurl + 'hello'))
1683
+ end
1684
+ end
1685
+
1686
+ def test_session_manager
1687
+ mgr = HTTPClient::SessionManager.new(@client)
1688
+ assert_nil(mgr.instance_eval { @proxy })
1689
+ assert_nil(mgr.debug_dev)
1690
+ @client.debug_dev = Object.new
1691
+ @client.proxy = 'http://myproxy:12345'
1692
+ mgr = HTTPClient::SessionManager.new(@client)
1693
+ assert_equal('http://myproxy:12345', mgr.instance_eval { @proxy }.to_s)
1694
+ assert_equal(@client.debug_dev, mgr.debug_dev)
1695
+ end
1696
+
1697
+ def create_keepalive_disconnected_thread(idx, sock)
1698
+ Thread.new {
1699
+ # return "12345" for the first connection
1700
+ sock.gets
1701
+ sock.gets
1702
+ sock.write("HTTP/1.1 200 OK\r\n")
1703
+ sock.write("Content-Length: 5\r\n")
1704
+ sock.write("\r\n")
1705
+ sock.write("12345")
1706
+ # for the next connection, close while reading the request for emulating
1707
+ # KeepAliveDisconnected
1708
+ sock.gets
1709
+ sock.close
1710
+ }
1711
+ end
1712
+
1713
+ def test_keepalive_disconnected
1714
+ client = HTTPClient.new
1715
+ server = TCPServer.open('127.0.0.1', 0)
1716
+ server.listen(30) # set enough backlogs
1717
+ endpoint = "http://127.0.0.1:#{server.addr[1]}/"
1718
+ queue = Queue.new
1719
+ Thread.new(queue) { |qs|
1720
+ Thread.abort_on_exception = true
1721
+ # want 5 requests issued
1722
+ 5.times { qs.pop }
1723
+ # emulate 10 keep-alive connections
1724
+ 10.times do |idx|
1725
+ sock = server.accept
1726
+ create_keepalive_disconnected_thread(idx, sock)
1727
+ end
1728
+ # return "23456" for the request which gets KeepAliveDisconnected
1729
+ 5.times do
1730
+ sock = server.accept
1731
+ sock.gets
1732
+ sock.gets
1733
+ sock.write("HTTP/1.1 200 OK\r\n")
1734
+ sock.write("\r\n")
1735
+ sock.write("23456")
1736
+ sock.close
1737
+ end
1738
+ # return "34567" for the rest requests
1739
+ while true
1740
+ sock = server.accept
1741
+ sock.gets
1742
+ sock.gets
1743
+ sock.write("HTTP/1.1 200 OK\r\n")
1744
+ sock.write("Connection: close\r\n")
1745
+ sock.write("Content-Length: 5\r\n")
1746
+ sock.write("\r\n")
1747
+ sock.write("34567")
1748
+ sock.close
1749
+ end
1750
+ }
1751
+ # try to allocate 10 keep-alive connections; it's a race so some
1752
+ # threads can reuse the connection so actual number of keep-alive
1753
+ # connections should be smaller than 10.
1754
+ (0...10).to_a.map {
1755
+ Thread.new(queue) { |qc|
1756
+ Thread.abort_on_exception = true
1757
+ conn = client.get_async(endpoint)
1758
+ qc.push(true)
1759
+ assert_equal("12345", conn.pop.content.read)
1760
+ }
1761
+ }.each { |th| th.join }
1762
+ # send 5 requests, some of these should get KeepAliveDesconnected
1763
+ # but should retry with new connection.
1764
+ (0...5).to_a.map {
1765
+ Thread.new {
1766
+ Thread.abort_on_exception = true
1767
+ assert_equal("23456", client.get(endpoint).content)
1768
+ }
1769
+ }.each { |th| th.join }
1770
+ # rest requests won't get KeepAliveDisconnected
1771
+ (0...10).to_a.map {
1772
+ Thread.new {
1773
+ Thread.abort_on_exception = true
1774
+ assert_equal("34567", client.get(endpoint).content)
1775
+ }
1776
+ }.each { |th| th.join }
1777
+ end
1778
+
1779
+ def create_keepalive_thread(count, sock)
1780
+ Thread.new {
1781
+ Thread.abort_on_exception = true
1782
+ count.times do
1783
+ req = sock.gets
1784
+ while line = sock.gets
1785
+ break if line.chomp.empty?
1786
+ end
1787
+ case req
1788
+ when /chunked/
1789
+ sock.write("HTTP/1.1 200 OK\r\n")
1790
+ sock.write("Transfer-Encoding: chunked\r\n")
1791
+ sock.write("\r\n")
1792
+ sock.write("1a\r\n")
1793
+ sock.write("abcdefghijklmnopqrstuvwxyz\r\n")
1794
+ sock.write("10\r\n")
1795
+ sock.write("1234567890abcdef\r\n")
1796
+ sock.write("0\r\n")
1797
+ sock.write("\r\n")
1798
+ else
1799
+ sock.write("HTTP/1.1 200 OK\r\n")
1800
+ sock.write("Content-Length: 5\r\n")
1801
+ sock.write("\r\n")
1802
+ sock.write("12345")
1803
+ end
1804
+ end
1805
+ sock.close
1806
+ }
1807
+ end
1808
+
1809
+ def test_keepalive
1810
+ server = TCPServer.open('localhost', 0)
1811
+ server_thread = Thread.new {
1812
+ Thread.abort_on_exception = true
1813
+ sock = server.accept
1814
+ create_keepalive_thread(10, sock)
1815
+ }
1816
+ url = "http://localhost:#{server.addr[1]}/"
1817
+ begin
1818
+ # content-length
1819
+ 5.times do
1820
+ assert_equal('12345', @client.get(url).body)
1821
+ end
1822
+ # chunked
1823
+ 5.times do
1824
+ assert_equal('abcdefghijklmnopqrstuvwxyz1234567890abcdef', @client.get(url + 'chunked').body)
1825
+ end
1826
+ ensure
1827
+ server.close
1828
+ server_thread.join
1829
+ end
1830
+ end
1831
+
1832
+ def test_strict_response_size_check
1833
+ @client.strict_response_size_check = false
1834
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\nContent-Length: 12345\r\n\r\nhello world"
1835
+ assert_equal('hello world', @client.get_content('http://dummy'))
1836
+
1837
+ @client.reset_all
1838
+ @client.strict_response_size_check = true
1839
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\nContent-Length: 12345\r\n\r\nhello world"
1840
+ assert_raise(HTTPClient::BadResponseError) do
1841
+ @client.get_content('http://dummy')
1842
+ end
1843
+ end
1844
+
1845
+ def test_socket_local
1846
+ @client.socket_local.host = '127.0.0.1'
1847
+ assert_equal('hello', @client.get_content(serverurl + 'hello'))
1848
+ @client.reset_all
1849
+ @client.socket_local.port = serverport
1850
+ begin
1851
+ @client.get_content(serverurl + 'hello')
1852
+ rescue Errno::EADDRINUSE, SocketError
1853
+ assert(true)
1854
+ end
1855
+ end
1856
+
1857
+ def test_body_param_order
1858
+ ary = ('b'..'d').map { |k| ['key2', k] } << ['key1', 'a'] << ['key3', 'z']
1859
+ assert_equal("key2=b&key2=c&key2=d&key1=a&key3=z", HTTP::Message.escape_query(ary))
1860
+ end
1861
+
1862
+ if RUBY_VERSION > "1.9"
1863
+ def test_charset
1864
+ body = @client.get(serverurl + 'charset').body
1865
+ assert_equal(Encoding::EUC_JP, body.encoding)
1866
+ assert_equal('あいうえお'.encode(Encoding::EUC_JP), body)
1867
+ end
1868
+ end
1869
+
1870
+ if RUBY_VERSION >= "1.9.3"
1871
+ def test_continue
1872
+ @client.debug_dev = str = ''.dup
1873
+ res = @client.get(serverurl + 'continue', :header => {:Expect => '100-continue'})
1874
+ assert_equal(200, res.status)
1875
+ assert_equal('done!', res.body)
1876
+ assert_match(/Expect: 100-continue/, str)
1877
+ end
1878
+ end
1879
+
1880
+ def test_ipv6literaladdress_in_uri
1881
+ server = TCPServer.open('::1', 0) rescue return # Skip if IPv6 is unavailable.
1882
+ server_thread = Thread.new {
1883
+ Thread.abort_on_exception = true
1884
+ sock = server.accept
1885
+ while line = sock.gets
1886
+ break if line.chomp.empty?
1887
+ end
1888
+ sock.write("HTTP/1.1 200 OK\r\n")
1889
+ sock.write("Content-Length: 5\r\n")
1890
+ sock.write("\r\n")
1891
+ sock.write("12345")
1892
+ sock.close
1893
+ }
1894
+ uri = "http://[::1]:#{server.addr[1]}/"
1895
+ begin
1896
+ assert_equal('12345', @client.get(uri).body)
1897
+ ensure
1898
+ server.close
1899
+ server_thread.kill
1900
+ server_thread.join
1901
+ end
1902
+ end
1903
+
1904
+ def test_uri_no_schema
1905
+ assert_raise(ArgumentError) do
1906
+ @client.get_content("www.example.com")
1907
+ end
1908
+ end
1909
+
1910
+ def test_tcp_keepalive
1911
+ @client.tcp_keepalive = true
1912
+ @client.get(serverurl)
1913
+
1914
+ # expecting HTTP keepalive caches the socket
1915
+ session = @client.instance_variable_get(:@session_manager).send(:get_cached_session, HTTPClient::Site.new(URI.parse(serverurl)))
1916
+ socket = session.instance_variable_get(:@socket)
1917
+
1918
+ assert_true(session.tcp_keepalive)
1919
+ assert_equal(Socket::SO_KEEPALIVE, socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE).optname)
1920
+ end
1921
+
1922
+ private
1923
+
1924
+ def check_query_get(query)
1925
+ WEBrick::HTTPUtils.parse_query(
1926
+ @client.get(serverurl + 'servlet', query).header["x-query"][0]
1927
+ )
1928
+ end
1929
+
1930
+ def check_query_post(query)
1931
+ WEBrick::HTTPUtils.parse_query(
1932
+ @client.post(serverurl + 'servlet', query).header["x-query"][0]
1933
+ )
1934
+ end
1935
+
1936
+ def setup_server
1937
+ @server = WEBrick::HTTPServer.new(
1938
+ :BindAddress => "localhost",
1939
+ :Logger => @logger,
1940
+ :Port => 0,
1941
+ :AccessLog => [],
1942
+ :DocumentRoot => File.dirname(File.expand_path(__FILE__))
1943
+ )
1944
+ @serverport = @server.config[:Port]
1945
+ [
1946
+ :hello, :sleep, :servlet_redirect, :redirect1, :redirect2, :redirect3,
1947
+ :redirect_self, :relative_redirect, :redirect_see_other, :chunked,
1948
+ :largebody, :status, :compressed, :charset, :continue
1949
+ ].each do |sym|
1950
+ @server.mount(
1951
+ "/#{sym}",
1952
+ WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
1953
+ )
1954
+ end
1955
+ @server.mount('/servlet', TestServlet.new(@server))
1956
+ @server_thread = start_server_thread(@server)
1957
+ end
1958
+
1959
+ def add_query_string(req)
1960
+ if req.query_string
1961
+ '?' + req.query_string
1962
+ else
1963
+ ''
1964
+ end
1965
+ end
1966
+
1967
+ def do_hello(req, res)
1968
+ res['content-type'] = 'text/html'
1969
+ res.body = "hello" + add_query_string(req)
1970
+ end
1971
+
1972
+ def do_sleep(req, res)
1973
+ sec = req.query['sec'].to_i
1974
+ sleep sec
1975
+ res['content-type'] = 'text/html'
1976
+ res.body = "hello" + add_query_string(req)
1977
+ end
1978
+
1979
+ def do_servlet_redirect(req, res)
1980
+ res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "servlet" + add_query_string(req))
1981
+ end
1982
+
1983
+ def do_redirect1(req, res)
1984
+ res.set_redirect(WEBrick::HTTPStatus::MovedPermanently, serverurl + "hello" + add_query_string(req))
1985
+ end
1986
+
1987
+ def do_redirect2(req, res)
1988
+ res.set_redirect(WEBrick::HTTPStatus::TemporaryRedirect, serverurl + "redirect3" + add_query_string(req))
1989
+ end
1990
+
1991
+ def do_redirect3(req, res)
1992
+ res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "hello" + add_query_string(req))
1993
+ end
1994
+
1995
+ def do_redirect_self(req, res)
1996
+ res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "redirect_self" + add_query_string(req))
1997
+ end
1998
+
1999
+ def do_relative_redirect(req, res)
2000
+ res.set_redirect(WEBrick::HTTPStatus::Found, "hello" + add_query_string(req))
2001
+ end
2002
+
2003
+ def do_redirect_see_other(req, res)
2004
+ if req.request_method == 'POST'
2005
+ res.set_redirect(WEBrick::HTTPStatus::SeeOther, serverurl + "redirect_see_other" + add_query_string(req)) # self
2006
+ else
2007
+ res.body = 'hello'
2008
+ end
2009
+ end
2010
+
2011
+ def do_chunked(req, res)
2012
+ res.chunked = true
2013
+ res['content-type'] = 'text/plain; charset=UTF-8'
2014
+ piper, pipew = IO.pipe
2015
+ res.body = piper
2016
+ pipew << req.query['msg']
2017
+ pipew.close
2018
+ end
2019
+
2020
+ def do_largebody(req, res)
2021
+ res['content-type'] = 'text/html'
2022
+ res.body = "a" * 1000 * 1000
2023
+ end
2024
+
2025
+ def do_compressed(req, res)
2026
+ res['content-type'] = 'application/octet-stream'
2027
+ if req.query['enc'] == 'gzip'
2028
+ res['content-encoding'] = 'gzip'
2029
+ res.body = GZIP_CONTENT
2030
+ elsif req.query['enc'] == 'deflate'
2031
+ res['content-encoding'] = 'deflate'
2032
+ res.body = DEFLATE_CONTENT
2033
+ elsif req.query['enc'] == 'deflate_noheader'
2034
+ res['content-encoding'] = 'deflate'
2035
+ res.body = DEFLATE_NOHEADER_CONTENT
2036
+ end
2037
+ end
2038
+
2039
+ def do_charset(req, res)
2040
+ if RUBY_VERSION > "1.9"
2041
+ res.body = 'あいうえお'.encode("euc-jp")
2042
+ res['Content-Type'] = 'text/plain; charset=euc-jp'
2043
+ else
2044
+ res.body = 'this endpoint is for 1.9 or later'
2045
+ end
2046
+ end
2047
+
2048
+ def do_status(req, res)
2049
+ res.status = req.query['status'].to_i
2050
+ end
2051
+
2052
+ def do_continue(req, res)
2053
+ req.continue
2054
+ res.body = 'done!'
2055
+ end
2056
+
2057
+ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
2058
+ def get_instance(*arg)
2059
+ self
2060
+ end
2061
+
2062
+ def do_HEAD(req, res)
2063
+ res["x-head"] = 'head' # use this for test purpose only.
2064
+ res["x-query"] = query_response(req)
2065
+ end
2066
+
2067
+ def do_GET(req, res)
2068
+ res.body = 'get'
2069
+ res['x-header'] = req['X-Header']
2070
+ res["x-query"] = query_response(req)
2071
+ end
2072
+
2073
+ def do_POST(req, res)
2074
+ res["content-type"] = "text/plain" # iso-8859-1, not US-ASCII
2075
+ res.body = 'post,' + req.body.to_s
2076
+ res["x-query"] = body_response(req)
2077
+ res["x-request-query"] = req.query_string
2078
+ end
2079
+
2080
+ def do_PATCH(req, res)
2081
+ res["x-query"] = body_response(req)
2082
+ param = WEBrick::HTTPUtils.parse_query(req.body) || {}
2083
+ res["x-size"] = (param['txt'] || '').size
2084
+ res.body = param['txt'] || 'patch'
2085
+ res["x-request-query"] = req.query_string
2086
+ end
2087
+
2088
+ def do_PUT(req, res)
2089
+ res["x-query"] = body_response(req)
2090
+ param = WEBrick::HTTPUtils.parse_query(req.body) || {}
2091
+ res["x-size"] = (param['txt'] || '').size
2092
+ res.body = param['txt'] || 'put'
2093
+ res["x-request-query"] = req.query_string
2094
+ end
2095
+
2096
+ def do_DELETE(req, res)
2097
+ res.body = 'delete'
2098
+ res["x-query"] = body_response(req)
2099
+ res["x-request-query"] = req.query_string
2100
+ end
2101
+
2102
+ def do_OPTIONS(req, res)
2103
+ res.body = 'options'
2104
+ res['x-header'] = req['X-Header']
2105
+ res['x-body'] = req.body
2106
+ end
2107
+
2108
+ def do_PROPFIND(req, res)
2109
+ res.body = 'propfind'
2110
+ end
2111
+
2112
+ def do_PROPPATCH(req, res)
2113
+ res.body = 'proppatch'
2114
+ res["x-query"] = body_response(req)
2115
+ end
2116
+
2117
+ def do_TRACE(req, res)
2118
+ # client SHOULD reflect the message received back to the client as the
2119
+ # entity-body of a 200 (OK) response. [RFC2616]
2120
+ res.body = 'trace'
2121
+ res["x-query"] = query_response(req)
2122
+ end
2123
+
2124
+ private
2125
+
2126
+ def query_response(req)
2127
+ query_escape(WEBrick::HTTPUtils.parse_query(req.query_string))
2128
+ end
2129
+
2130
+ def body_response(req)
2131
+ query_escape(WEBrick::HTTPUtils.parse_query(req.body))
2132
+ end
2133
+
2134
+ def query_escape(query)
2135
+ escaped = []
2136
+ query.sort_by { |k, v| k }.collect do |k, v|
2137
+ v.to_ary.each do |ve|
2138
+ escaped << CGI.escape(k) + '=' + CGI.escape(ve)
2139
+ end
2140
+ end
2141
+ escaped.join('&')
2142
+ end
2143
+ end
2144
+ end