httpclient 2.2.0.2 → 2.2.1

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 (51) hide show
  1. data/lib/httpclient.rb +5 -5
  2. data/lib/httpclient/cacert.p7s +12 -12
  3. data/lib/httpclient/cacert_sha1.p7s +12 -12
  4. data/lib/httpclient/http.rb +15 -8
  5. data/lib/httpclient/session.rb +6 -1
  6. data/lib/httpclient/ssl_config.rb +18 -14
  7. data/sample/async.rb +8 -0
  8. data/sample/auth.rb +11 -0
  9. data/sample/cookie.rb +18 -0
  10. data/sample/dav.rb +103 -0
  11. data/sample/howto.rb +49 -0
  12. data/sample/oauth_buzz.rb +57 -0
  13. data/sample/oauth_friendfeed.rb +59 -0
  14. data/sample/oauth_twitter.rb +61 -0
  15. data/sample/ssl/0cert.pem +22 -0
  16. data/sample/ssl/0key.pem +30 -0
  17. data/sample/ssl/1000cert.pem +19 -0
  18. data/sample/ssl/1000key.pem +18 -0
  19. data/sample/ssl/htdocs/index.html +10 -0
  20. data/sample/ssl/ssl_client.rb +22 -0
  21. data/sample/ssl/webrick_httpsd.rb +29 -0
  22. data/sample/stream.rb +21 -0
  23. data/sample/thread.rb +27 -0
  24. data/sample/wcat.rb +21 -0
  25. data/test/ca.cert +23 -0
  26. data/test/client.cert +19 -0
  27. data/test/client.key +15 -0
  28. data/test/helper.rb +99 -0
  29. data/test/htdigest +1 -0
  30. data/test/htpasswd +2 -0
  31. data/test/runner.rb +2 -0
  32. data/test/server.cert +19 -0
  33. data/test/server.key +15 -0
  34. data/test/sslsvr.rb +65 -0
  35. data/test/subca.cert +21 -0
  36. data/test/test_auth.rb +196 -0
  37. data/test/test_cookie.rb +398 -0
  38. data/test/test_http-access2.rb +497 -0
  39. data/test/test_httpclient.rb +1544 -0
  40. data/test/test_ssl.rb +215 -0
  41. metadata +53 -38
  42. data/lib/http-access2.rbc +0 -732
  43. data/lib/httpclient.rbc +0 -13559
  44. data/lib/httpclient/auth.rbc +0 -13772
  45. data/lib/httpclient/connection.rbc +0 -767
  46. data/lib/httpclient/cookie.rbc +0 -8442
  47. data/lib/httpclient/http.rbc +0 -14163
  48. data/lib/httpclient/session.rbc +0 -15846
  49. data/lib/httpclient/ssl_config.rbc +0 -5575
  50. data/lib/httpclient/timeout.rbc +0 -2411
  51. data/lib/httpclient/util.rbc +0 -1278
@@ -0,0 +1,1544 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('helper', File.dirname(__FILE__))
3
+
4
+
5
+ class TestHTTPClient < Test::Unit::TestCase
6
+ include Helper
7
+
8
+ def setup
9
+ super
10
+ setup_server
11
+ setup_client
12
+ end
13
+
14
+ def teardown
15
+ super
16
+ end
17
+
18
+ def test_initialize
19
+ setup_proxyserver
20
+ escape_noproxy do
21
+ @proxyio.string = ""
22
+ @client = HTTPClient.new(proxyurl)
23
+ assert_equal(URI.parse(proxyurl), @client.proxy)
24
+ assert_equal(200, @client.head(serverurl).status)
25
+ assert(/accept/ =~ @proxyio.string)
26
+ end
27
+ end
28
+
29
+ def test_agent_name
30
+ @client = HTTPClient.new(nil, "agent_name_foo")
31
+ str = ""
32
+ @client.debug_dev = str
33
+ @client.get(serverurl)
34
+ lines = str.split(/(?:\r?\n)+/)
35
+ assert_equal("= Request", lines[0])
36
+ assert_match(/^User-Agent: agent_name_foo/, lines[4])
37
+ end
38
+
39
+ def test_from
40
+ @client = HTTPClient.new(nil, nil, "from_bar")
41
+ str = ""
42
+ @client.debug_dev = str
43
+ @client.get(serverurl)
44
+ lines = str.split(/(?:\r?\n)+/)
45
+ assert_equal("= Request", lines[0])
46
+ assert_match(/^From: from_bar/, lines[4])
47
+ end
48
+
49
+ def test_debug_dev
50
+ str = ""
51
+ @client.debug_dev = str
52
+ assert_equal(str.object_id, @client.debug_dev.object_id)
53
+ assert(str.empty?)
54
+ @client.get(serverurl)
55
+ assert(!str.empty?)
56
+ end
57
+
58
+ def test_debug_dev_stream
59
+ str = ""
60
+ @client.debug_dev = str
61
+ conn = @client.get_async(serverurl)
62
+ Thread.pass while !conn.finished?
63
+ assert(!str.empty?)
64
+ end
65
+
66
+ def test_protocol_version_http09
67
+ @client.protocol_version = 'HTTP/0.9'
68
+ @client.debug_dev = str = ''
69
+ @client.test_loopback_http_response << "hello\nworld\n"
70
+ res = @client.get(serverurl + 'hello')
71
+ assert_equal('0.9', res.http_version)
72
+ assert_equal(nil, res.status)
73
+ assert_equal(nil, res.reason)
74
+ assert_equal("hello\nworld\n", res.content)
75
+ lines = str.split(/(?:\r?\n)+/)
76
+ assert_equal("= Request", lines[0])
77
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
78
+ assert_equal("GET /hello HTTP/0.9", lines[3])
79
+ assert_equal("Connection: close", lines[5])
80
+ assert_equal("= Response", lines[6])
81
+ assert_match(/^hello$/, lines[7])
82
+ assert_match(/^world$/, lines[8])
83
+ end
84
+
85
+ def test_protocol_version_http10
86
+ assert_equal(nil, @client.protocol_version)
87
+ @client.protocol_version = 'HTTP/1.0'
88
+ assert_equal('HTTP/1.0', @client.protocol_version)
89
+ str = ""
90
+ @client.debug_dev = str
91
+ @client.get(serverurl + 'hello')
92
+ lines = str.split(/(?:\r?\n)+/)
93
+ assert_equal("= Request", lines[0])
94
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
95
+ assert_equal("GET /hello HTTP/1.0", lines[3])
96
+ assert_equal("Connection: close", lines[5])
97
+ assert_equal("= Response", lines[6])
98
+ end
99
+
100
+ def test_host_given
101
+ str = ""
102
+ @client.debug_dev = str
103
+ @client.get(serverurl)
104
+ lines = str.split(/(?:\r?\n)+/)
105
+ assert_equal("= Request", lines[0])
106
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
107
+ assert_equal("GET / HTTP/1.1", lines[3])
108
+ assert_equal("Host: localhost:#{serverport}", lines[5])
109
+ #
110
+ @client.reset_all
111
+ str = ""
112
+ @client.debug_dev = str
113
+ @client.get(serverurl, nil, {'Host' => 'foo'})
114
+ lines = str.split(/(?:\r?\n)+/)
115
+ assert_equal("= Request", lines[0])
116
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
117
+ assert_equal("GET / HTTP/1.1", lines[3])
118
+ assert_equal("Host: foo", lines[4]) # use given param
119
+ end
120
+
121
+ def test_protocol_version_http11
122
+ assert_equal(nil, @client.protocol_version)
123
+ str = ""
124
+ @client.debug_dev = str
125
+ @client.get(serverurl)
126
+ lines = str.split(/(?:\r?\n)+/)
127
+ assert_equal("= Request", lines[0])
128
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
129
+ assert_equal("GET / HTTP/1.1", lines[3])
130
+ assert_equal("Host: localhost:#{serverport}", lines[5])
131
+ @client.protocol_version = 'HTTP/1.1'
132
+ assert_equal('HTTP/1.1', @client.protocol_version)
133
+ str = ""
134
+ @client.debug_dev = str
135
+ @client.get(serverurl)
136
+ lines = str.split(/(?:\r?\n)+/)
137
+ assert_equal("= Request", lines[0])
138
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
139
+ assert_equal("GET / HTTP/1.1", lines[3])
140
+ @client.protocol_version = 'HTTP/1.0'
141
+ str = ""
142
+ @client.debug_dev = str
143
+ @client.get(serverurl)
144
+ lines = str.split(/(?:\r?\n)+/)
145
+ assert_equal("= Request", lines[0])
146
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
147
+ assert_equal("GET / HTTP/1.0", lines[3])
148
+ end
149
+
150
+ def test_proxy
151
+ setup_proxyserver
152
+ escape_noproxy do
153
+ assert_raises(URI::InvalidURIError) do
154
+ @client.proxy = "http://"
155
+ end
156
+ @client.proxy = ""
157
+ assert_nil(@client.proxy)
158
+ @client.proxy = "http://admin:admin@foo:1234"
159
+ assert_equal(URI.parse("http://admin:admin@foo:1234"), @client.proxy)
160
+ uri = URI.parse("http://bar:2345")
161
+ @client.proxy = uri
162
+ assert_equal(uri, @client.proxy)
163
+ #
164
+ @proxyio.string = ""
165
+ @client.proxy = nil
166
+ assert_equal(200, @client.head(serverurl).status)
167
+ assert(/accept/ !~ @proxyio.string)
168
+ #
169
+ @proxyio.string = ""
170
+ @client.proxy = proxyurl
171
+ @client.debug_dev = str = ""
172
+ assert_equal(200, @client.head(serverurl).status)
173
+ assert(/accept/ =~ @proxyio.string)
174
+ assert(/Host: localhost:#{serverport}/ =~ str)
175
+ end
176
+ end
177
+
178
+ def test_host_header
179
+ @client.proxy = proxyurl
180
+ @client.debug_dev = str = ""
181
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n"
182
+ assert_equal(200, @client.head('http://www.example.com/foo').status)
183
+ # ensure no ':80' is added. some servers dislike that.
184
+ assert(/\r\nHost: www\.example\.com\r\n/ =~ str)
185
+ #
186
+ @client.debug_dev = str = ""
187
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n"
188
+ assert_equal(200, @client.head('http://www.example.com:12345/foo').status)
189
+ # ensure ':12345' exists.
190
+ assert(/\r\nHost: www\.example\.com:12345\r\n/ =~ str)
191
+ end
192
+
193
+ def test_proxy_env
194
+ setup_proxyserver
195
+ escape_env do
196
+ ENV['http_proxy'] = "http://admin:admin@foo:1234"
197
+ ENV['NO_PROXY'] = "foobar"
198
+ client = HTTPClient.new
199
+ assert_equal(URI.parse("http://admin:admin@foo:1234"), client.proxy)
200
+ assert_equal('foobar', client.no_proxy)
201
+ end
202
+ end
203
+
204
+ def test_proxy_env_cgi
205
+ setup_proxyserver
206
+ escape_env do
207
+ ENV['REQUEST_METHOD'] = 'GET' # CGI environment emulation
208
+ ENV['http_proxy'] = "http://admin:admin@foo:1234"
209
+ ENV['no_proxy'] = "foobar"
210
+ client = HTTPClient.new
211
+ assert_equal(nil, client.proxy)
212
+ ENV['CGI_HTTP_PROXY'] = "http://admin:admin@foo:1234"
213
+ client = HTTPClient.new
214
+ assert_equal(URI.parse("http://admin:admin@foo:1234"), client.proxy)
215
+ end
216
+ end
217
+
218
+ def test_empty_proxy_env
219
+ setup_proxyserver
220
+ escape_env do
221
+ ENV['http_proxy'] = ""
222
+ client = HTTPClient.new
223
+ assert_equal(nil, client.proxy)
224
+ end
225
+ end
226
+
227
+ def test_noproxy_for_localhost
228
+ @proxyio.string = ""
229
+ @client.proxy = proxyurl
230
+ assert_equal(200, @client.head(serverurl).status)
231
+ assert(/accept/ !~ @proxyio.string)
232
+ end
233
+
234
+ def test_no_proxy
235
+ setup_proxyserver
236
+ escape_noproxy do
237
+ # proxy is not set.
238
+ assert_equal(nil, @client.no_proxy)
239
+ @client.no_proxy = 'localhost'
240
+ assert_equal('localhost', @client.no_proxy)
241
+ @proxyio.string = ""
242
+ @client.proxy = nil
243
+ assert_equal(200, @client.head(serverurl).status)
244
+ assert(/accept/ !~ @proxyio.string)
245
+ #
246
+ @proxyio.string = ""
247
+ @client.proxy = proxyurl
248
+ assert_equal(200, @client.head(serverurl).status)
249
+ assert(/accept/ !~ @proxyio.string)
250
+ #
251
+ @client.no_proxy = 'foobar'
252
+ @proxyio.string = ""
253
+ @client.proxy = proxyurl
254
+ assert_equal(200, @client.head(serverurl).status)
255
+ assert(/accept/ =~ @proxyio.string)
256
+ #
257
+ @client.no_proxy = 'foobar,localhost:baz'
258
+ @proxyio.string = ""
259
+ @client.proxy = proxyurl
260
+ assert_equal(200, @client.head(serverurl).status)
261
+ assert(/accept/ !~ @proxyio.string)
262
+ #
263
+ @client.no_proxy = 'foobar,localhost:443'
264
+ @proxyio.string = ""
265
+ @client.proxy = proxyurl
266
+ assert_equal(200, @client.head(serverurl).status)
267
+ assert(/accept/ =~ @proxyio.string)
268
+ #
269
+ @client.no_proxy = "foobar,localhost:443:localhost:#{serverport},baz"
270
+ @proxyio.string = ""
271
+ @client.proxy = proxyurl
272
+ assert_equal(200, @client.head(serverurl).status)
273
+ assert(/accept/ !~ @proxyio.string)
274
+ end
275
+ end
276
+
277
+ def test_cookie_update_while_authentication
278
+ escape_noproxy do
279
+ @client.test_loopback_http_response << <<EOS
280
+ HTTP/1.0 401\r
281
+ Date: Fri, 19 Dec 2008 11:57:29 GMT\r
282
+ Content-Type: text/plain\r
283
+ Content-Length: 0\r
284
+ WWW-Authenticate: Basic realm="hello"\r
285
+ Set-Cookie: foo=bar; path=/; domain=.example.org; expires=#{Time.at(1924873200).httpdate}\r
286
+ \r
287
+ EOS
288
+ @client.test_loopback_http_response << <<EOS
289
+ HTTP/1.1 200 OK\r
290
+ Content-Length: 5\r
291
+ Connection: close\r
292
+ \r
293
+ hello
294
+ EOS
295
+ @client.debug_dev = str = ''
296
+ @client.set_auth("http://www.example.org/baz/", 'admin', 'admin')
297
+ assert_equal('hello', @client.get('http://www.example.org/baz/foo').content)
298
+ assert_match(/^Cookie: foo=bar/, str)
299
+ assert_match(/^Authorization: Basic YWRtaW46YWRtaW4=/, str)
300
+ end
301
+ end
302
+
303
+
304
+ def test_proxy_ssl
305
+ escape_noproxy do
306
+ @client.proxy = 'http://admin:admin@localhost:8080/'
307
+ # disconnected at initial 'CONNECT' so there're 2 loopback responses
308
+ @client.test_loopback_http_response << <<EOS
309
+ HTTP/1.0 407 Proxy Authentication Required\r
310
+ Date: Fri, 19 Dec 2008 11:57:29 GMT\r
311
+ Content-Type: text/plain\r
312
+ Content-Length: 0\r
313
+ Proxy-Authenticate: Basic realm="hello"\r
314
+ Proxy-Connection: close\r
315
+ \r
316
+ EOS
317
+ @client.test_loopback_http_response << <<EOS
318
+ HTTP/1.0 200 Connection established\r
319
+ \r
320
+ HTTP/1.1 200 OK\r
321
+ Content-Length: 5\r
322
+ Connection: close\r
323
+ \r
324
+ hello
325
+ EOS
326
+ assert_equal('hello', @client.get('https://localhost:17171/baz').content)
327
+ end
328
+ end
329
+
330
+ def test_loopback_response
331
+ @client.test_loopback_response << 'message body 1'
332
+ @client.test_loopback_response << 'message body 2'
333
+ assert_equal('message body 1', @client.get_content('http://somewhere'))
334
+ assert_equal('message body 2', @client.get_content('http://somewhere'))
335
+ #
336
+ @client.debug_dev = str = ''
337
+ @client.test_loopback_response << 'message body 3'
338
+ assert_equal('message body 3', @client.get_content('http://somewhere'))
339
+ assert_match(/message body 3/, str)
340
+ end
341
+
342
+ def test_loopback_response_stream
343
+ @client.test_loopback_response << 'message body 1'
344
+ @client.test_loopback_response << 'message body 2'
345
+ conn = @client.get_async('http://somewhere')
346
+ Thread.pass while !conn.finished?
347
+ assert_equal('message body 1', conn.pop.content.read)
348
+ conn = @client.get_async('http://somewhere')
349
+ Thread.pass while !conn.finished?
350
+ assert_equal('message body 2', conn.pop.content.read)
351
+ end
352
+
353
+ def test_loopback_http_response
354
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\ncontent-length: 100\n\nmessage body 1"
355
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\ncontent-length: 100\n\nmessage body 2"
356
+ assert_equal('message body 1', @client.get_content('http://somewhere'))
357
+ assert_equal('message body 2', @client.get_content('http://somewhere'))
358
+ end
359
+
360
+ def test_multiline_header
361
+ @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"
362
+ res = @client.get('http://somewhere')
363
+ assert_equal('message body 1', res.content)
364
+ assert_equal(['XXX YYY'], res.header['x-foo'])
365
+ assert_equal(['XXX YYY'], res.header['x-bar'])
366
+ end
367
+
368
+ def test_broken_header
369
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\nXXXXX\ncontent-length: 100\n\nmessage body 1"
370
+ res = @client.get('http://somewhere')
371
+ assert_equal('message body 1', res.content)
372
+ end
373
+
374
+ def test_redirect_non_https
375
+ url = serverurl + 'redirect1'
376
+ https_url = URI.parse(url)
377
+ https_url.scheme = 'https'
378
+ #
379
+ redirect_to_http = "HTTP/1.0 302 OK\nLocation: #{url}\n\n"
380
+ redirect_to_https = "HTTP/1.0 302 OK\nLocation: #{https_url}\n\n"
381
+ #
382
+ # https -> http is denied
383
+ @client.test_loopback_http_response << redirect_to_http
384
+ assert_raises(HTTPClient::BadResponseError) do
385
+ @client.get_content(https_url)
386
+ end
387
+ #
388
+ # http -> http is OK
389
+ @client.reset_all
390
+ @client.test_loopback_http_response << redirect_to_http
391
+ assert_equal('hello', @client.get_content(url))
392
+ #
393
+ # http -> https is OK
394
+ @client.reset_all
395
+ @client.test_loopback_http_response << redirect_to_https
396
+ assert_raises(OpenSSL::SSL::SSLError) do
397
+ # trying to normal endpoint with SSL -> SSL negotiation failure
398
+ @client.get_content(url)
399
+ end
400
+ #
401
+ # https -> https is OK
402
+ @client.reset_all
403
+ @client.test_loopback_http_response << redirect_to_https
404
+ assert_raises(OpenSSL::SSL::SSLError) do
405
+ # trying to normal endpoint with SSL -> SSL negotiation failure
406
+ @client.get_content(https_url)
407
+ end
408
+ #
409
+ # https -> http with strict_redirect_uri_callback
410
+ @client.redirect_uri_callback = @client.method(:strict_redirect_uri_callback)
411
+ @client.test_loopback_http_response << redirect_to_http
412
+ assert_raises(HTTPClient::BadResponseError) do
413
+ @client.get_content(https_url)
414
+ end
415
+ end
416
+
417
+ def test_redirect_relative
418
+ @client.test_loopback_http_response << "HTTP/1.0 302 OK\nLocation: hello\n\n"
419
+ assert_equal('hello', @client.get_content(serverurl + 'redirect1'))
420
+ #
421
+ @client.reset_all
422
+ @client.redirect_uri_callback = @client.method(:strict_redirect_uri_callback)
423
+ assert_equal('hello', @client.get_content(serverurl + 'redirect1'))
424
+ @client.reset_all
425
+ @client.test_loopback_http_response << "HTTP/1.0 302 OK\nLocation: hello\n\n"
426
+ begin
427
+ @client.get_content(serverurl + 'redirect1')
428
+ assert(false)
429
+ rescue HTTPClient::BadResponseError => e
430
+ assert_equal(302, e.res.status)
431
+ end
432
+ end
433
+
434
+ def test_redirect_https_relative
435
+ url = serverurl + 'redirect1'
436
+ https_url = URI.parse(url)
437
+ https_url.scheme = 'https'
438
+ @client.test_loopback_http_response << "HTTP/1.0 302 OK\nLocation: /foo\n\n"
439
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\n\nhello"
440
+ assert_equal('hello', @client.get_content(https_url))
441
+ end
442
+
443
+ def test_no_content
444
+ assert_nothing_raised do
445
+ timeout(2) do
446
+ @client.get(serverurl + 'status', :status => 101)
447
+ @client.get(serverurl + 'status', :status => 204)
448
+ @client.get(serverurl + 'status', :status => 304)
449
+ end
450
+ end
451
+ end
452
+
453
+ def test_get_content
454
+ assert_equal('hello', @client.get_content(serverurl + 'hello'))
455
+ assert_equal('hello', @client.get_content(serverurl + 'redirect1'))
456
+ assert_equal('hello', @client.get_content(serverurl + 'redirect2'))
457
+ url = serverurl.sub(/localhost/, '127.0.0.1')
458
+ assert_equal('hello', @client.get_content(url + 'hello'))
459
+ assert_equal('hello', @client.get_content(url + 'redirect1'))
460
+ assert_equal('hello', @client.get_content(url + 'redirect2'))
461
+ @client.reset(serverurl)
462
+ @client.reset(url)
463
+ @client.reset(serverurl)
464
+ @client.reset(url)
465
+ assert_raises(HTTPClient::BadResponseError) do
466
+ @client.get_content(serverurl + 'notfound')
467
+ end
468
+ assert_raises(HTTPClient::BadResponseError) do
469
+ @client.get_content(serverurl + 'redirect_self')
470
+ end
471
+ called = false
472
+ @client.redirect_uri_callback = lambda { |uri, res|
473
+ newuri = res.header['location'][0]
474
+ called = true
475
+ newuri
476
+ }
477
+ assert_equal('hello', @client.get_content(serverurl + 'relative_redirect'))
478
+ assert(called)
479
+ end
480
+
481
+ 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"
482
+ DEFLATE_CONTENT = "\x78\x9c\xcb\x48\xcd\xc9\xc9\x07\x00\x06\x2c\x02\x15"
483
+ GZIP_CONTENT.force_encoding('BINARY') if GZIP_CONTENT.respond_to?(:force_encoding)
484
+ DEFLATE_CONTENT.force_encoding('BINARY') if DEFLATE_CONTENT.respond_to?(:force_encoding)
485
+ def test_get_gzipped_content
486
+ @client.transparent_gzip_decompression = false
487
+ content = @client.get_content(serverurl + 'compressed?enc=gzip')
488
+ assert_not_equal('hello', content)
489
+ assert_equal(GZIP_CONTENT, content)
490
+ @client.transparent_gzip_decompression = true
491
+ assert_equal('hello', @client.get_content(serverurl + 'compressed?enc=gzip'))
492
+ assert_equal('hello', @client.get_content(serverurl + 'compressed?enc=deflate'))
493
+ @client.transparent_gzip_decompression = false
494
+ end
495
+
496
+ def test_get_content_with_block
497
+ @client.get_content(serverurl + 'hello') do |str|
498
+ assert_equal('hello', str)
499
+ end
500
+ @client.get_content(serverurl + 'redirect1') do |str|
501
+ assert_equal('hello', str)
502
+ end
503
+ @client.get_content(serverurl + 'redirect2') do |str|
504
+ assert_equal('hello', str)
505
+ end
506
+ end
507
+
508
+ def test_post_content
509
+ assert_equal('hello', @client.post_content(serverurl + 'hello'))
510
+ assert_equal('hello', @client.post_content(serverurl + 'redirect1'))
511
+ assert_equal('hello', @client.post_content(serverurl + 'redirect2'))
512
+ assert_raises(HTTPClient::BadResponseError) do
513
+ @client.post_content(serverurl + 'notfound')
514
+ end
515
+ assert_raises(HTTPClient::BadResponseError) do
516
+ @client.post_content(serverurl + 'redirect_self')
517
+ end
518
+ called = false
519
+ @client.redirect_uri_callback = lambda { |uri, res|
520
+ newuri = res.header['location'][0]
521
+ called = true
522
+ newuri
523
+ }
524
+ assert_equal('hello', @client.post_content(serverurl + 'relative_redirect'))
525
+ assert(called)
526
+ end
527
+
528
+ def test_post_content_io
529
+ post_body = StringIO.new("1234567890")
530
+ assert_equal('post,1234567890', @client.post_content(serverurl + 'servlet', post_body))
531
+ post_body = StringIO.new("1234567890")
532
+ assert_equal('post,1234567890', @client.post_content(serverurl + 'servlet_redirect', post_body))
533
+ #
534
+ post_body = StringIO.new("1234567890")
535
+ post_body.read(5)
536
+ assert_equal('post,67890', @client.post_content(serverurl + 'servlet_redirect', post_body))
537
+ end
538
+
539
+ def test_head
540
+ assert_equal("head", @client.head(serverurl + 'servlet').header["x-head"][0])
541
+ res = @client.head(serverurl + 'servlet', {1=>2, 3=>4})
542
+ assert_equal('1=2&3=4', res.header["x-query"][0])
543
+ end
544
+
545
+ def test_head_async
546
+ conn = @client.head_async(serverurl + 'servlet', {1=>2, 3=>4})
547
+ Thread.pass while !conn.finished?
548
+ res = conn.pop
549
+ assert_equal('1=2&3=4', res.header["x-query"][0])
550
+ end
551
+
552
+ def test_get
553
+ assert_equal("get", @client.get(serverurl + 'servlet').content)
554
+ res = @client.get(serverurl + 'servlet', {1=>2, 3=>4})
555
+ assert_equal('1=2&3=4', res.header["x-query"][0])
556
+ assert_nil(res.contenttype)
557
+ #
558
+ url = serverurl.to_s + 'servlet?5=6&7=8'
559
+ res = @client.get(url, {1=>2, 3=>4})
560
+ assert_equal('1=2&3=4&5=6&7=8', res.header["x-query"][0])
561
+ assert_nil(res.contenttype)
562
+ end
563
+
564
+ def test_get_follow_redirect
565
+ assert_equal('hello', @client.get(serverurl + 'hello', :follow_redirect => true).body)
566
+ assert_equal('hello', @client.get(serverurl + 'redirect1', :follow_redirect => true).body)
567
+ assert_equal('hello', @client.get(serverurl + 'redirect2', :follow_redirect => true).body)
568
+ end
569
+
570
+ def test_get_async
571
+ conn = @client.get_async(serverurl + 'servlet', {1=>2, 3=>4})
572
+ Thread.pass while !conn.finished?
573
+ res = conn.pop
574
+ assert_equal('1=2&3=4', res.header["x-query"][0])
575
+ end
576
+
577
+ def test_get_async_for_largebody
578
+ conn = @client.get_async(serverurl + 'largebody')
579
+ res = conn.pop
580
+ assert_equal(1000*1000, res.content.read.length)
581
+ end
582
+
583
+ def test_get_with_block
584
+ called = false
585
+ res = @client.get(serverurl + 'servlet') { |str|
586
+ assert_equal('get', str)
587
+ called = true
588
+ }
589
+ assert(called)
590
+ # res does not have a content
591
+ assert_nil(res.content)
592
+ end
593
+
594
+ def test_post
595
+ assert_equal("post", @client.post(serverurl + 'servlet').content[0, 4])
596
+ res = @client.post(serverurl + 'servlet', {1=>2, 3=>4})
597
+ assert_equal('1=2&3=4', res.header["x-query"][0])
598
+ end
599
+
600
+ def test_post_follow_redirect
601
+ assert_equal('hello', @client.post(serverurl + 'hello', :follow_redirect => true).body)
602
+ assert_equal('hello', @client.post(serverurl + 'redirect1', :follow_redirect => true).body)
603
+ assert_equal('hello', @client.post(serverurl + 'redirect2', :follow_redirect => true).body)
604
+ end
605
+
606
+ def test_post_with_content_type
607
+ ext = {'content-type' => 'application/x-www-form-urlencoded', 'hello' => 'world'}
608
+ assert_equal("post", @client.post(serverurl + 'servlet').content[0, 4], ext)
609
+ res = @client.post(serverurl + 'servlet', {1=>2, 3=>4}, ext)
610
+ assert_equal('1=2&3=4', res.header["x-query"][0])
611
+ #
612
+ ext = [['content-type', 'multipart/form-data'], ['hello', 'world']]
613
+ assert_equal("post", @client.post(serverurl + 'servlet').content[0, 4], ext)
614
+ res = @client.post(serverurl + 'servlet', {1=>2, 3=>4}, ext)
615
+ assert_match(/Content-Disposition: form-data; name="1"/, res.content)
616
+ assert_match(/Content-Disposition: form-data; name="3"/, res.content)
617
+ #
618
+ ext = {'content-type' => 'multipart/form-data; boundary=hello'}
619
+ assert_equal("post", @client.post(serverurl + 'servlet').content[0, 4], ext)
620
+ res = @client.post(serverurl + 'servlet', {1=>2, 3=>4}, ext)
621
+ assert_match(/Content-Disposition: form-data; name="1"/, res.content)
622
+ assert_match(/Content-Disposition: form-data; name="3"/, res.content)
623
+ 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)
624
+ end
625
+
626
+ def test_post_with_file
627
+ STDOUT.sync = true
628
+ File.open(__FILE__) do |file|
629
+ res = @client.post(serverurl + 'servlet', {1=>2, 3=>file})
630
+ assert_match(/^Content-Disposition: form-data; name="1"\r\n/nm, res.content)
631
+ assert_match(/^Content-Disposition: form-data; name="3";/, res.content)
632
+ assert_match(/FIND_TAG_IN_THIS_FILE/, res.content)
633
+ end
634
+ end
635
+
636
+ def test_post_with_io # streaming, but not chunked
637
+ myio = StringIO.new("X" * (HTTP::Message::Body::DEFAULT_CHUNK_SIZE + 1))
638
+ def myio.read(*args)
639
+ @called ||= 0
640
+ @called += 1
641
+ super
642
+ end
643
+ def myio.called
644
+ @called
645
+ end
646
+ @client.debug_dev = str = StringIO.new
647
+ res = @client.post(serverurl + 'servlet', {1=>2, 3=>myio})
648
+ assert_match(/\r\nContent-Disposition: form-data; name="1"\r\n/m, res.content)
649
+ assert_match(/\r\n2\r\n/m, res.content)
650
+ assert_match(/\r\nContent-Disposition: form-data; name="3"; filename=""\r\n/m, res.content)
651
+ assert_match(/\r\nContent-Length:/m, str.string)
652
+ assert_equal(3, myio.called)
653
+ end
654
+
655
+ def test_post_with_io_nosize # streaming + chunked post
656
+ myio = StringIO.new("4")
657
+ def myio.size
658
+ nil
659
+ end
660
+ @client.debug_dev = str = StringIO.new
661
+ res = @client.post(serverurl + 'servlet', {1=>2, 3=>myio})
662
+ assert_match(/\r\nContent-Disposition: form-data; name="1"\r\n/m, res.content)
663
+ assert_match(/\r\n2\r\n/m, res.content)
664
+ assert_match(/\r\nContent-Disposition: form-data; name="3"; filename=""\r\n/m, res.content)
665
+ assert_match(/\r\n4\r\n/m, res.content)
666
+ assert_match(/\r\nTransfer-Encoding: chunked\r\n/m, str.string)
667
+ end
668
+
669
+ def test_post_async
670
+ conn = @client.post_async(serverurl + 'servlet', {1=>2, 3=>4})
671
+ Thread.pass while !conn.finished?
672
+ res = conn.pop
673
+ assert_equal('1=2&3=4', res.header["x-query"][0])
674
+ end
675
+
676
+ def test_post_with_block
677
+ called = false
678
+ res = @client.post(serverurl + 'servlet') { |str|
679
+ assert_equal('post,', str)
680
+ called = true
681
+ }
682
+ assert(called)
683
+ assert_nil(res.content)
684
+ #
685
+ called = false
686
+ res = @client.post(serverurl + 'servlet', {1=>2, 3=>4}) { |str|
687
+ assert_equal('post,1=2&3=4', str)
688
+ called = true
689
+ }
690
+ assert(called)
691
+ assert_equal('1=2&3=4', res.header["x-query"][0])
692
+ assert_nil(res.content)
693
+ end
694
+
695
+ def test_post_with_custom_multipart
696
+ ext = {'content-type' => 'multipart/form-data'}
697
+ assert_equal("post", @client.post(serverurl + 'servlet').content[0, 4], ext)
698
+ body = [{ 'Content-Disposition' => 'form-data; name="1"', :content => "2"},
699
+ { 'Content-Disposition' => 'form-data; name="3"', :content => "4"}]
700
+ res = @client.post(serverurl + 'servlet', body, ext)
701
+ assert_match(/Content-Disposition: form-data; name="1"/, res.content)
702
+ assert_match(/Content-Disposition: form-data; name="3"/, res.content)
703
+ #
704
+ ext = {'content-type' => 'multipart/form-data; boundary=hello'}
705
+ assert_equal("post", @client.post(serverurl + 'servlet').content[0, 4], ext)
706
+ res = @client.post(serverurl + 'servlet', body, ext)
707
+ assert_match(/Content-Disposition: form-data; name="1"/, res.content)
708
+ assert_match(/Content-Disposition: form-data; name="3"/, res.content)
709
+ 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)
710
+ end
711
+
712
+ def test_post_with_custom_multipart_and_file
713
+ STDOUT.sync = true
714
+ File.open(__FILE__) do |file|
715
+ ext = { 'Content-Type' => 'multipart/alternative' }
716
+ body = [{ 'Content-Type' => 'text/plain', :content => "this is only a test" },
717
+ { 'Content-Type' => 'application/x-ruby', :content => file }]
718
+ res = @client.post(serverurl + 'servlet', body, ext)
719
+ assert_match(/^Content-Type: text\/plain\r\n/m, res.content)
720
+ assert_match(/^this is only a test\r\n/m, res.content)
721
+ assert_match(/^Content-Type: application\/x-ruby\r\n/m, res.content)
722
+ assert_match(/FIND_TAG_IN_THIS_FILE/, res.content)
723
+ end
724
+ end
725
+
726
+ def test_put
727
+ assert_equal("put", @client.put(serverurl + 'servlet').content)
728
+ res = @client.put(serverurl + 'servlet', {1=>2, 3=>4})
729
+ assert_equal('1=2&3=4', res.header["x-query"][0])
730
+ end
731
+
732
+ def test_put_bytesize
733
+ res = @client.put(serverurl + 'servlet', 'txt' => 'あいうえお')
734
+ assert_equal('txt=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A', res.header["x-query"][0])
735
+ assert_equal('15', res.header["x-size"][0])
736
+ end
737
+
738
+ def test_put_async
739
+ conn = @client.put_async(serverurl + 'servlet', {1=>2, 3=>4})
740
+ Thread.pass while !conn.finished?
741
+ res = conn.pop
742
+ assert_equal('1=2&3=4', res.header["x-query"][0])
743
+ end
744
+
745
+ def test_delete
746
+ assert_equal("delete", @client.delete(serverurl + 'servlet').content)
747
+ end
748
+
749
+ def test_delete_async
750
+ conn = @client.delete_async(serverurl + 'servlet')
751
+ Thread.pass while !conn.finished?
752
+ res = conn.pop
753
+ assert_equal('delete', res.content.read)
754
+ end
755
+
756
+ def test_options
757
+ assert_equal("options", @client.options(serverurl + 'servlet').content)
758
+ end
759
+
760
+ def test_options_async
761
+ conn = @client.options_async(serverurl + 'servlet')
762
+ Thread.pass while !conn.finished?
763
+ res = conn.pop
764
+ assert_equal('options', res.content.read)
765
+ end
766
+
767
+ def test_propfind
768
+ assert_equal("propfind", @client.propfind(serverurl + 'servlet').content)
769
+ end
770
+
771
+ def test_propfind_async
772
+ conn = @client.propfind_async(serverurl + 'servlet')
773
+ Thread.pass while !conn.finished?
774
+ res = conn.pop
775
+ assert_equal('propfind', res.content.read)
776
+ end
777
+
778
+ def test_proppatch
779
+ assert_equal("proppatch", @client.proppatch(serverurl + 'servlet').content)
780
+ res = @client.proppatch(serverurl + 'servlet', {1=>2, 3=>4})
781
+ assert_equal('proppatch', res.content)
782
+ assert_equal('1=2&3=4', res.header["x-query"][0])
783
+ end
784
+
785
+ def test_proppatch_async
786
+ conn = @client.proppatch_async(serverurl + 'servlet', {1=>2, 3=>4})
787
+ Thread.pass while !conn.finished?
788
+ res = conn.pop
789
+ assert_equal('proppatch', res.content.read)
790
+ assert_equal('1=2&3=4', res.header["x-query"][0])
791
+ end
792
+
793
+ def test_trace
794
+ assert_equal("trace", @client.trace(serverurl + 'servlet').content)
795
+ res = @client.trace(serverurl + 'servlet', {1=>2, 3=>4})
796
+ assert_equal('1=2&3=4', res.header["x-query"][0])
797
+ end
798
+
799
+ def test_trace_async
800
+ conn = @client.trace_async(serverurl + 'servlet', {1=>2, 3=>4})
801
+ Thread.pass while !conn.finished?
802
+ res = conn.pop
803
+ assert_equal('1=2&3=4', res.header["x-query"][0])
804
+ end
805
+
806
+ def test_chunked
807
+ assert_equal('chunked', @client.get_content(serverurl + 'chunked', { 'msg' => 'chunked' }))
808
+ end
809
+
810
+ def test_chunked_empty
811
+ assert_equal('', @client.get_content(serverurl + 'chunked', { 'msg' => '' }))
812
+ end
813
+
814
+ def test_get_query
815
+ assert_equal({'1'=>'2'}, check_query_get({1=>2}))
816
+ assert_equal({'a'=>'A', 'B'=>'b'}, check_query_get({"a"=>"A", "B"=>"b"}))
817
+ assert_equal({'&'=>'&'}, check_query_get({"&"=>"&"}))
818
+ assert_equal({'= '=>' =+'}, check_query_get({"= "=>" =+"}))
819
+ assert_equal(
820
+ ['=', '&'].sort,
821
+ check_query_get([["=", "="], ["=", "&"]])['='].to_ary.sort
822
+ )
823
+ assert_equal({'123'=>'45'}, check_query_get('123=45'))
824
+ assert_equal({'12 3'=>'45', ' '=>' '}, check_query_get('12+3=45&+=+'))
825
+ assert_equal({}, check_query_get(''))
826
+ assert_equal({'1'=>'2'}, check_query_get({1=>StringIO.new('2')}))
827
+ assert_equal({'1'=>'2', '3'=>'4'}, check_query_get(StringIO.new('3=4&1=2')))
828
+ end
829
+
830
+ def test_post_body
831
+ assert_equal({'1'=>'2'}, check_query_post({1=>2}))
832
+ assert_equal({'a'=>'A', 'B'=>'b'}, check_query_post({"a"=>"A", "B"=>"b"}))
833
+ assert_equal({'&'=>'&'}, check_query_post({"&"=>"&"}))
834
+ assert_equal({'= '=>' =+'}, check_query_post({"= "=>" =+"}))
835
+ assert_equal(
836
+ ['=', '&'].sort,
837
+ check_query_post([["=", "="], ["=", "&"]])['='].to_ary.sort
838
+ )
839
+ assert_equal({'123'=>'45'}, check_query_post('123=45'))
840
+ assert_equal({'12 3'=>'45', ' '=>' '}, check_query_post('12+3=45&+=+'))
841
+ assert_equal({}, check_query_post(''))
842
+ #
843
+ post_body = StringIO.new("foo=bar&foo=baz")
844
+ assert_equal(
845
+ ["bar", "baz"],
846
+ check_query_post(post_body)["foo"].to_ary.sort
847
+ )
848
+ end
849
+
850
+ def test_extra_headers
851
+ str = ""
852
+ @client.debug_dev = str
853
+ @client.head(serverurl, nil, {"ABC" => "DEF"})
854
+ lines = str.split(/(?:\r?\n)+/)
855
+ assert_equal("= Request", lines[0])
856
+ assert_match("ABC: DEF", lines[4])
857
+ #
858
+ str = ""
859
+ @client.debug_dev = str
860
+ @client.get(serverurl, nil, [["ABC", "DEF"], ["ABC", "DEF"]])
861
+ lines = str.split(/(?:\r?\n)+/)
862
+ assert_equal("= Request", lines[0])
863
+ assert_match("ABC: DEF", lines[4])
864
+ assert_match("ABC: DEF", lines[5])
865
+ end
866
+
867
+ def test_timeout
868
+ assert_equal(60, @client.connect_timeout)
869
+ assert_equal(120, @client.send_timeout)
870
+ assert_equal(60, @client.receive_timeout)
871
+ #
872
+ @client.connect_timeout = 1
873
+ @client.send_timeout = 2
874
+ @client.receive_timeout = 3
875
+ assert_equal(1, @client.connect_timeout)
876
+ assert_equal(2, @client.send_timeout)
877
+ assert_equal(3, @client.receive_timeout)
878
+ end
879
+
880
+ def test_connect_timeout
881
+ # ToDo
882
+ end
883
+
884
+ def test_send_timeout
885
+ # ToDo
886
+ end
887
+
888
+ def test_receive_timeout
889
+ # this test takes 2 sec
890
+ assert_equal('hello', @client.get_content(serverurl + 'sleep?sec=2'))
891
+ @client.receive_timeout = 1
892
+ assert_equal('hello', @client.get_content(serverurl + 'sleep?sec=0'))
893
+ assert_raise(HTTPClient::ReceiveTimeoutError) do
894
+ @client.get_content(serverurl + 'sleep?sec=2')
895
+ end
896
+ @client.receive_timeout = 3
897
+ assert_equal('hello', @client.get_content(serverurl + 'sleep?sec=2'))
898
+ end
899
+
900
+ def test_receive_timeout_post
901
+ # this test takes 2 sec
902
+ assert_equal('hello', @client.post(serverurl + 'sleep', :sec => 2).content)
903
+ @client.receive_timeout = 1
904
+ assert_equal('hello', @client.post(serverurl + 'sleep', :sec => 0).content)
905
+ assert_raise(HTTPClient::ReceiveTimeoutError) do
906
+ @client.post(serverurl + 'sleep', :sec => 2)
907
+ end
908
+ @client.receive_timeout = 3
909
+ assert_equal('hello', @client.post(serverurl + 'sleep', :sec => 2).content)
910
+ end
911
+
912
+ def test_async_error
913
+ assert_raise( SocketError ) do
914
+ conn = @client.get_async("http://non-existing-host/")
915
+ conn.pop
916
+ end
917
+ end
918
+
919
+ def test_reset
920
+ url = serverurl + 'servlet'
921
+ assert_nothing_raised do
922
+ 5.times do
923
+ @client.get(url)
924
+ @client.reset(url)
925
+ end
926
+ end
927
+ end
928
+
929
+ def test_reset_all
930
+ assert_nothing_raised do
931
+ 5.times do
932
+ @client.get(serverurl + 'servlet')
933
+ @client.reset_all
934
+ end
935
+ end
936
+ end
937
+
938
+ def test_cookies
939
+ cookiefile = File.join(File.dirname(File.expand_path(__FILE__)), 'test_cookies_file')
940
+ File.open(cookiefile, "wb") do |f|
941
+ f << "http://rubyforge.org/account/login.php session_ser LjEwMy45Ni40Ni0q%2A-fa0537de8cc31 2000000000 .rubyforge.org / 13\n"
942
+ end
943
+ @client.set_cookie_store(cookiefile)
944
+ cookie = @client.cookie_manager.cookies.first
945
+ url = cookie.url
946
+ assert(cookie.domain_match(url.host, cookie.domain))
947
+ #
948
+ @client.reset_all
949
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\nSet-Cookie: foo=bar; expires=#{Time.at(1924873200).gmtime.httpdate}\n\nOK"
950
+ @client.get_content('http://rubyforge.org/account/login.php')
951
+ @client.save_cookie_store
952
+ str = File.read(cookiefile)
953
+ assert_match(%r(http://rubyforge.org/account/login.php foo bar 1924873200 rubyforge.org /account 1), str)
954
+ File.unlink(cookiefile)
955
+ end
956
+
957
+ def test_eof_error_length
958
+ io = StringIO.new('')
959
+ def io.gets(*arg)
960
+ @buf ||= ["HTTP/1.0 200 OK\n", "content-length: 123\n", "\n"]
961
+ @buf.shift
962
+ end
963
+ def io.readpartial(size, buf)
964
+ @second ||= false
965
+ if !@second
966
+ @second = '1st'
967
+ buf << "abc"
968
+ buf
969
+ elsif @second == '1st'
970
+ @second = '2nd'
971
+ raise EOFError.new
972
+ else
973
+ raise Exception.new
974
+ end
975
+ end
976
+ def io.eof?
977
+ true
978
+ end
979
+ @client.test_loopback_http_response << io
980
+ assert_nothing_raised do
981
+ @client.get('http://foo/bar')
982
+ end
983
+ end
984
+
985
+ def test_eof_error_rest
986
+ io = StringIO.new('')
987
+ def io.gets(*arg)
988
+ @buf ||= ["HTTP/1.0 200 OK\n", "\n"]
989
+ @buf.shift
990
+ end
991
+ def io.readpartial(size, buf)
992
+ @second ||= false
993
+ if !@second
994
+ @second = '1st'
995
+ buf << "abc"
996
+ buf
997
+ elsif @second == '1st'
998
+ @second = '2nd'
999
+ raise EOFError.new
1000
+ else
1001
+ raise Exception.new
1002
+ end
1003
+ end
1004
+ def io.eof?
1005
+ true
1006
+ end
1007
+ @client.test_loopback_http_response << io
1008
+ assert_nothing_raised do
1009
+ @client.get('http://foo/bar')
1010
+ end
1011
+ end
1012
+
1013
+ def test_urify
1014
+ extend HTTPClient::Util
1015
+ assert_nil(urify(nil))
1016
+ uri = 'http://foo'
1017
+ assert_equal(URI.parse(uri), urify(uri))
1018
+ assert_equal(URI.parse(uri), urify(URI.parse(uri)))
1019
+ end
1020
+
1021
+ def test_connection
1022
+ c = HTTPClient::Connection.new
1023
+ assert(c.finished?)
1024
+ assert_nil(c.join)
1025
+ end
1026
+
1027
+ def test_site
1028
+ site = HTTPClient::Site.new
1029
+ assert_equal('tcp', site.scheme)
1030
+ assert_equal('0.0.0.0', site.host)
1031
+ assert_equal(0, site.port)
1032
+ assert_equal('tcp://0.0.0.0:0', site.addr)
1033
+ assert_equal('tcp://0.0.0.0:0', site.to_s)
1034
+ assert_nothing_raised do
1035
+ site.inspect
1036
+ end
1037
+ #
1038
+ site = HTTPClient::Site.new(URI.parse('http://localhost:12345/foo'))
1039
+ assert_equal('http', site.scheme)
1040
+ assert_equal('localhost', site.host)
1041
+ assert_equal(12345, site.port)
1042
+ assert_equal('http://localhost:12345', site.addr)
1043
+ assert_equal('http://localhost:12345', site.to_s)
1044
+ assert_nothing_raised do
1045
+ site.inspect
1046
+ end
1047
+ #
1048
+ site1 = HTTPClient::Site.new(URI.parse('http://localhost:12341/'))
1049
+ site2 = HTTPClient::Site.new(URI.parse('http://localhost:12342/'))
1050
+ site3 = HTTPClient::Site.new(URI.parse('http://localhost:12342/'))
1051
+ assert(!(site1 == site2))
1052
+ h = { site1 => 'site1', site2 => 'site2' }
1053
+ h[site3] = 'site3'
1054
+ assert_equal('site1', h[site1])
1055
+ assert_equal('site3', h[site2])
1056
+ end
1057
+
1058
+ def test_http_header
1059
+ res = @client.get(serverurl + 'hello')
1060
+ assert_equal('text/html', res.contenttype)
1061
+ assert_equal(5, res.header.get(nil).size)
1062
+ #
1063
+ res.header.delete('connection')
1064
+ assert_equal(4, res.header.get(nil).size)
1065
+ #
1066
+ res.header['foo'] = 'bar'
1067
+ assert_equal(['bar'], res.header['foo'])
1068
+ #
1069
+ assert_equal([['foo', 'bar']], res.header.get('foo'))
1070
+ res.header['foo'] = ['bar', 'bar2']
1071
+ assert_equal([['foo', 'bar'], ['foo', 'bar2']], res.header.get('foo'))
1072
+ end
1073
+
1074
+ def test_mime_type
1075
+ assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
1076
+ assert_equal('text/html', HTTP::Message.mime_type('foo.html'))
1077
+ assert_equal('text/html', HTTP::Message.mime_type('foo.htm'))
1078
+ assert_equal('application/msword', HTTP::Message.mime_type('foo.doc'))
1079
+ assert_equal('image/png', HTTP::Message.mime_type('foo.png'))
1080
+ assert_equal('image/gif', HTTP::Message.mime_type('foo.gif'))
1081
+ assert_equal('image/jpeg', HTTP::Message.mime_type('foo.jpg'))
1082
+ assert_equal('image/jpeg', HTTP::Message.mime_type('foo.jpeg'))
1083
+ assert_equal('application/octet-stream', HTTP::Message.mime_type('foo.unknown'))
1084
+ #
1085
+ handler = lambda { |path| 'hello/world' }
1086
+ assert_nil(HTTP::Message.mime_type_handler)
1087
+ assert_nil(HTTP::Message.get_mime_type_func)
1088
+ HTTP::Message.mime_type_handler = handler
1089
+ assert_not_nil(HTTP::Message.mime_type_handler)
1090
+ assert_not_nil(HTTP::Message.get_mime_type_func)
1091
+ assert_equal('hello/world', HTTP::Message.mime_type('foo.txt'))
1092
+ HTTP::Message.mime_type_handler = nil
1093
+ assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
1094
+ HTTP::Message.set_mime_type_func(nil)
1095
+ assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
1096
+ #
1097
+ handler = lambda { |path| nil }
1098
+ HTTP::Message.mime_type_handler = handler
1099
+ assert_equal('application/octet-stream', HTTP::Message.mime_type('foo.txt'))
1100
+ end
1101
+
1102
+ def test_connect_request
1103
+ req = HTTP::Message.new_connect_request(URI.parse('https://foo/bar'))
1104
+ assert_equal("CONNECT foo:443 HTTP/1.0\r\n\r\n", req.dump)
1105
+ req = HTTP::Message.new_connect_request(URI.parse('https://example.com/'))
1106
+ assert_equal("CONNECT example.com:443 HTTP/1.0\r\n\r\n", req.dump)
1107
+ end
1108
+
1109
+ def test_response
1110
+ res = HTTP::Message.new_response('response')
1111
+ res.contenttype = 'text/plain'
1112
+ res.header.body_date = Time.at(946652400)
1113
+ assert_equal(
1114
+ [
1115
+ "",
1116
+ "Content-Length: 8",
1117
+ "Content-Type: text/plain",
1118
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
1119
+ "Status: 200 OK",
1120
+ "response"
1121
+ ],
1122
+ res.dump.split(/\r\n/).sort
1123
+ )
1124
+ assert_equal(['8'], res.header['Content-Length'])
1125
+ assert_equal('8', res.headers['Content-Length'])
1126
+ res.header.set('foo', 'bar')
1127
+ assert_equal(
1128
+ [
1129
+ "",
1130
+ "Content-Length: 8",
1131
+ "Content-Type: text/plain",
1132
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
1133
+ "Status: 200 OK",
1134
+ "foo: bar",
1135
+ "response"
1136
+ ],
1137
+ res.dump.split(/\r\n/).sort
1138
+ )
1139
+ # nil body
1140
+ res = HTTP::Message.new_response(nil)
1141
+ assert_equal(
1142
+ [
1143
+ "Content-Length: 0",
1144
+ "Content-Type: text/html; charset=us-ascii",
1145
+ "Status: 200 OK"
1146
+ ],
1147
+ res.dump.split(/\r\n/).sort
1148
+ )
1149
+ # for mod_ruby env
1150
+ Object.const_set('Apache', nil)
1151
+ begin
1152
+ res = HTTP::Message.new_response('response')
1153
+ assert(res.dump.split(/\r\n/).any? { |line| /^Date/ =~ line })
1154
+ #
1155
+ res = HTTP::Message.new_response('response')
1156
+ res.contenttype = 'text/plain'
1157
+ res.header.body_date = Time.at(946652400)
1158
+ res.header['Date'] = Time.at(946652400).httpdate
1159
+ assert_equal(
1160
+ [
1161
+ "",
1162
+ "Content-Length: 8",
1163
+ "Content-Type: text/plain",
1164
+ "Date: Fri, 31 Dec 1999 15:00:00 GMT",
1165
+ "HTTP/1.1 200 OK",
1166
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
1167
+ "response"
1168
+ ],
1169
+ res.dump.split(/\r\n/).sort
1170
+ )
1171
+ ensure
1172
+ Object.instance_eval { remove_const('Apache') }
1173
+ end
1174
+ end
1175
+
1176
+ def test_response_cookies
1177
+ res = HTTP::Message.new_response('response')
1178
+ res.contenttype = 'text/plain'
1179
+ res.header.body_date = Time.at(946652400)
1180
+ assert_nil(res.cookies)
1181
+ #
1182
+ res.header['Set-Cookie'] = [
1183
+ 'CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT',
1184
+ 'PART_NUMBER=ROCKET_LAUNCHER_0001; path=/'
1185
+ ]
1186
+ assert_equal(
1187
+ [
1188
+ "",
1189
+ "Content-Length: 8",
1190
+ "Content-Type: text/plain",
1191
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
1192
+ "Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT",
1193
+ "Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/",
1194
+ "Status: 200 OK",
1195
+ "response"
1196
+ ],
1197
+ res.dump.split(/\r\n/).sort
1198
+ )
1199
+ assert_equal(2, res.cookies.size)
1200
+ assert_equal('CUSTOMER', res.cookies[0].name)
1201
+ assert_equal('PART_NUMBER', res.cookies[1].name)
1202
+ end
1203
+
1204
+ if !defined?(JRUBY_VERSION) and RUBY_VERSION < '1.9'
1205
+ def test_timeout_scheduler
1206
+ assert_equal('hello', @client.get_content(serverurl + 'hello'))
1207
+ status = HTTPClient.timeout_scheduler.instance_eval { @thread.kill; @thread.join; @thread.status }
1208
+ assert(!status) # dead
1209
+ assert_equal('hello', @client.get_content(serverurl + 'hello'))
1210
+ end
1211
+ end
1212
+
1213
+ def test_session_manager
1214
+ mgr = HTTPClient::SessionManager.new(@client)
1215
+ assert_nil(mgr.instance_eval { @proxy })
1216
+ assert_nil(mgr.debug_dev)
1217
+ @client.debug_dev = Object.new
1218
+ @client.proxy = 'http://myproxy:12345'
1219
+ mgr = HTTPClient::SessionManager.new(@client)
1220
+ assert_equal('http://myproxy:12345', mgr.instance_eval { @proxy }.to_s)
1221
+ assert_equal(@client.debug_dev, mgr.debug_dev)
1222
+ end
1223
+
1224
+ def create_keepalive_disconnected_thread(idx, sock)
1225
+ Thread.new {
1226
+ # return "12345" for the first connection
1227
+ sock.gets
1228
+ sock.gets
1229
+ sock.write("HTTP/1.1 200 OK\r\n")
1230
+ sock.write("Content-Length: 5\r\n")
1231
+ sock.write("\r\n")
1232
+ sock.write("12345")
1233
+ # for the next connection, close while reading the request for emulating
1234
+ # KeepAliveDisconnected
1235
+ sock.gets
1236
+ sock.close
1237
+ }
1238
+ end
1239
+
1240
+ def test_keepalive_disconnected
1241
+ client = HTTPClient.new
1242
+ server = TCPServer.open('127.0.0.1', 0)
1243
+ server.listen(30) # set enough backlogs
1244
+ endpoint = "http://127.0.0.1:#{server.addr[1]}/"
1245
+ Thread.new {
1246
+ Thread.abort_on_exception = true
1247
+ # emulate 10 keep-alive connections
1248
+ 10.times do |idx|
1249
+ sock = server.accept
1250
+ create_keepalive_disconnected_thread(idx, sock)
1251
+ end
1252
+ # return "23456" for the request which gets KeepAliveDisconnected
1253
+ 5.times do
1254
+ sock = server.accept
1255
+ sock.gets
1256
+ sock.gets
1257
+ sock.write("HTTP/1.1 200 OK\r\n")
1258
+ sock.write("\r\n")
1259
+ sock.write("23456")
1260
+ sock.close
1261
+ end
1262
+ # return "34567" for the rest requests
1263
+ while true
1264
+ sock = server.accept
1265
+ sock.gets
1266
+ sock.gets
1267
+ sock.write("HTTP/1.1 200 OK\r\n")
1268
+ sock.write("Connection: close\r\n")
1269
+ sock.write("Content-Length: 5\r\n")
1270
+ sock.write("\r\n")
1271
+ sock.write("34567")
1272
+ sock.close
1273
+ end
1274
+ }
1275
+ # allocate 10 keep-alive connections
1276
+ 10.times.to_enum.map {
1277
+ Thread.new {
1278
+ assert_equal("12345", client.get(endpoint).content)
1279
+ }
1280
+ }.each(&:join)
1281
+ # send 5 requests, which should get KeepAliveDesconnected.
1282
+ # doing these requests, rest keep-alive connections are invalidated.
1283
+ 5.times.to_enum.map {
1284
+ Thread.new {
1285
+ assert_equal("23456", client.get(endpoint).content)
1286
+ }
1287
+ }.each(&:join)
1288
+ # rest requests won't get KeepAliveDisconnected; how can I check this?
1289
+ 10.times.to_enum.map {
1290
+ Thread.new {
1291
+ assert_equal("34567", client.get(endpoint).content)
1292
+ }
1293
+ }.each(&:join)
1294
+ end
1295
+
1296
+ def create_keepalive_thread(count, sock)
1297
+ Thread.new {
1298
+ Thread.abort_on_exception = true
1299
+ count.times do
1300
+ req = sock.gets
1301
+ while line = sock.gets
1302
+ break if line.chomp.empty?
1303
+ end
1304
+ case req
1305
+ when /chunked/
1306
+ sock.write("HTTP/1.1 200 OK\r\n")
1307
+ sock.write("Transfer-Encoding: chunked\r\n")
1308
+ sock.write("\r\n")
1309
+ sock.write("1a\r\n")
1310
+ sock.write("abcdefghijklmnopqrstuvwxyz\r\n")
1311
+ sock.write("10\r\n")
1312
+ sock.write("1234567890abcdef\r\n")
1313
+ sock.write("0\r\n")
1314
+ sock.write("\r\n")
1315
+ else
1316
+ sock.write("HTTP/1.1 200 OK\r\n")
1317
+ sock.write("Content-Length: 5\r\n")
1318
+ sock.write("\r\n")
1319
+ sock.write("12345")
1320
+ end
1321
+ end
1322
+ sock.close
1323
+ }
1324
+ end
1325
+
1326
+ def test_keepalive
1327
+ server = TCPServer.open('localhost', 0)
1328
+ server_thread = Thread.new {
1329
+ Thread.abort_on_exception = true
1330
+ sock = server.accept
1331
+ create_keepalive_thread(10, sock)
1332
+ }
1333
+ url = "http://localhost:#{server.addr[1]}/"
1334
+ begin
1335
+ # content-length
1336
+ 5.times do
1337
+ assert_equal('12345', @client.get(url).body)
1338
+ end
1339
+ # chunked
1340
+ 5.times do
1341
+ assert_equal('abcdefghijklmnopqrstuvwxyz1234567890abcdef', @client.get(url + 'chunked').body)
1342
+ end
1343
+ ensure
1344
+ server.close
1345
+ server_thread.join
1346
+ end
1347
+ end
1348
+
1349
+ def test_socket_local
1350
+ @client.socket_local.host = 'localhost'
1351
+ assert_equal('hello', @client.get_content(serverurl + 'hello'))
1352
+ @client.reset_all
1353
+ @client.socket_local.port = serverport
1354
+ assert_raises(Errno::EADDRINUSE) do
1355
+ assert_equal('hello', @client.get_content(serverurl + 'hello'))
1356
+ end
1357
+ end
1358
+
1359
+ private
1360
+
1361
+ def check_query_get(query)
1362
+ WEBrick::HTTPUtils.parse_query(
1363
+ @client.get(serverurl + 'servlet', query).header["x-query"][0]
1364
+ )
1365
+ end
1366
+
1367
+ def check_query_post(query)
1368
+ WEBrick::HTTPUtils.parse_query(
1369
+ @client.post(serverurl + 'servlet', query).header["x-query"][0]
1370
+ )
1371
+ end
1372
+
1373
+ def setup_server
1374
+ @server = WEBrick::HTTPServer.new(
1375
+ :BindAddress => "localhost",
1376
+ :Logger => @logger,
1377
+ :Port => 0,
1378
+ :AccessLog => [],
1379
+ :DocumentRoot => File.dirname(File.expand_path(__FILE__))
1380
+ )
1381
+ @serverport = @server.config[:Port]
1382
+ [:hello, :sleep, :servlet_redirect, :redirect1, :redirect2, :redirect3, :redirect_self, :relative_redirect, :chunked, :largebody, :status, :compressed].each do |sym|
1383
+ @server.mount(
1384
+ "/#{sym}",
1385
+ WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
1386
+ )
1387
+ end
1388
+ @server.mount('/servlet', TestServlet.new(@server))
1389
+ @server_thread = start_server_thread(@server)
1390
+ end
1391
+
1392
+ def escape_env
1393
+ env = {}
1394
+ env.update(ENV)
1395
+ yield
1396
+ ensure
1397
+ ENV.clear
1398
+ ENV.update(env)
1399
+ end
1400
+
1401
+ def escape_noproxy
1402
+ backup = HTTPClient::NO_PROXY_HOSTS.dup
1403
+ HTTPClient::NO_PROXY_HOSTS.clear
1404
+ yield
1405
+ ensure
1406
+ HTTPClient::NO_PROXY_HOSTS.replace(backup)
1407
+ end
1408
+
1409
+ def do_hello(req, res)
1410
+ res['content-type'] = 'text/html'
1411
+ res.body = "hello"
1412
+ end
1413
+
1414
+ def do_sleep(req, res)
1415
+ sec = req.query['sec'].to_i
1416
+ sleep sec
1417
+ res['content-type'] = 'text/html'
1418
+ res.body = "hello"
1419
+ end
1420
+
1421
+ def do_servlet_redirect(req, res)
1422
+ res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "servlet")
1423
+ end
1424
+
1425
+ def do_redirect1(req, res)
1426
+ res.set_redirect(WEBrick::HTTPStatus::MovedPermanently, serverurl + "hello")
1427
+ end
1428
+
1429
+ def do_redirect2(req, res)
1430
+ res.set_redirect(WEBrick::HTTPStatus::TemporaryRedirect, serverurl + "redirect3")
1431
+ end
1432
+
1433
+ def do_redirect3(req, res)
1434
+ res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "hello")
1435
+ end
1436
+
1437
+ def do_redirect_self(req, res)
1438
+ res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "redirect_self")
1439
+ end
1440
+
1441
+ def do_relative_redirect(req, res)
1442
+ res.set_redirect(WEBrick::HTTPStatus::Found, "hello")
1443
+ end
1444
+
1445
+ def do_chunked(req, res)
1446
+ res.chunked = true
1447
+ piper, pipew = IO.pipe
1448
+ res.body = piper
1449
+ pipew << req.query['msg']
1450
+ pipew.close
1451
+ end
1452
+
1453
+ def do_largebody(req, res)
1454
+ res['content-type'] = 'text/html'
1455
+ res.body = "a" * 1000 * 1000
1456
+ end
1457
+
1458
+ def do_compressed(req, res)
1459
+ if req.query['enc'] == 'gzip'
1460
+ res['content-encoding'] = 'gzip'
1461
+ res.body = GZIP_CONTENT
1462
+ elsif req.query['enc'] == 'deflate'
1463
+ res['content-encoding'] = 'deflate'
1464
+ res.body = DEFLATE_CONTENT
1465
+ end
1466
+ end
1467
+
1468
+ def do_status(req, res)
1469
+ res.status = req.query['status'].to_i
1470
+ end
1471
+
1472
+ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
1473
+ def get_instance(*arg)
1474
+ self
1475
+ end
1476
+
1477
+ def do_HEAD(req, res)
1478
+ res["x-head"] = 'head' # use this for test purpose only.
1479
+ res["x-query"] = query_response(req)
1480
+ end
1481
+
1482
+ def do_GET(req, res)
1483
+ res.body = 'get'
1484
+ res["x-query"] = query_response(req)
1485
+ end
1486
+
1487
+ def do_POST(req, res)
1488
+ res.body = 'post,' + req.body.to_s
1489
+ res["x-query"] = body_response(req)
1490
+ end
1491
+
1492
+ def do_PUT(req, res)
1493
+ res["x-query"] = body_response(req)
1494
+ param = WEBrick::HTTPUtils.parse_query(req.body) || {}
1495
+ res["x-size"] = (param['txt'] || '').size
1496
+ res.body = param['txt'] || 'put'
1497
+ end
1498
+
1499
+ def do_DELETE(req, res)
1500
+ res.body = 'delete'
1501
+ end
1502
+
1503
+ def do_OPTIONS(req, res)
1504
+ # check RFC for legal response.
1505
+ res.body = 'options'
1506
+ end
1507
+
1508
+ def do_PROPFIND(req, res)
1509
+ res.body = 'propfind'
1510
+ end
1511
+
1512
+ def do_PROPPATCH(req, res)
1513
+ res.body = 'proppatch'
1514
+ res["x-query"] = body_response(req)
1515
+ end
1516
+
1517
+ def do_TRACE(req, res)
1518
+ # client SHOULD reflect the message received back to the client as the
1519
+ # entity-body of a 200 (OK) response. [RFC2616]
1520
+ res.body = 'trace'
1521
+ res["x-query"] = query_response(req)
1522
+ end
1523
+
1524
+ private
1525
+
1526
+ def query_response(req)
1527
+ query_escape(WEBrick::HTTPUtils.parse_query(req.query_string))
1528
+ end
1529
+
1530
+ def body_response(req)
1531
+ query_escape(WEBrick::HTTPUtils.parse_query(req.body))
1532
+ end
1533
+
1534
+ def query_escape(query)
1535
+ escaped = []
1536
+ query.sort_by { |k, v| k }.collect do |k, v|
1537
+ v.to_ary.each do |ve|
1538
+ escaped << CGI.escape(k) + '=' + CGI.escape(ve)
1539
+ end
1540
+ end
1541
+ escaped.join('&')
1542
+ end
1543
+ end
1544
+ end