httpclient 2.2.0.2 → 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
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