httpclient 2.1.5 → 2.8.3

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 (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