bterlson-httpclient 2.1.4

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.
@@ -0,0 +1,1160 @@
1
+ require 'test/unit'
2
+ require 'httpclient'
3
+ require 'webrick'
4
+ require 'webrick/httpproxy.rb'
5
+ require 'logger'
6
+ require 'stringio'
7
+ require 'cgi'
8
+ require 'webrick/httputils'
9
+
10
+
11
+ class TestHTTPClient < Test::Unit::TestCase
12
+ Port = 17171
13
+ ProxyPort = 17172
14
+
15
+ def setup
16
+ @logger = Logger.new(STDERR)
17
+ @logger.level = Logger::Severity::FATAL
18
+ @proxyio = StringIO.new
19
+ @proxylogger = Logger.new(@proxyio)
20
+ @proxylogger.level = Logger::Severity::DEBUG
21
+ @url = "http://localhost:#{Port}/"
22
+ @proxyurl = "http://localhost:#{ProxyPort}/"
23
+ @server = @proxyserver = @client = nil
24
+ @server_thread = @proxyserver_thread = nil
25
+ setup_server
26
+ setup_client
27
+ end
28
+
29
+ def teardown
30
+ teardown_client
31
+ teardown_proxyserver if @proxyserver
32
+ teardown_server
33
+ end
34
+
35
+ def test_initialize
36
+ setup_proxyserver
37
+ escape_noproxy do
38
+ @proxyio.string = ""
39
+ @client = HTTPClient.new(@proxyurl)
40
+ assert_equal(URI.parse(@proxyurl), @client.proxy)
41
+ assert_equal(200, @client.head(@url).status)
42
+ assert(/accept/ =~ @proxyio.string)
43
+ end
44
+ end
45
+
46
+ def test_agent_name
47
+ @client = HTTPClient.new(nil, "agent_name_foo")
48
+ str = ""
49
+ @client.debug_dev = str
50
+ @client.get(@url)
51
+ lines = str.split(/(?:\r?\n)+/)
52
+ assert_equal("= Request", lines[0])
53
+ assert_match(/^User-Agent: agent_name_foo/, lines[4])
54
+ end
55
+
56
+ def test_from
57
+ @client = HTTPClient.new(nil, nil, "from_bar")
58
+ str = ""
59
+ @client.debug_dev = str
60
+ @client.get(@url)
61
+ lines = str.split(/(?:\r?\n)+/)
62
+ assert_equal("= Request", lines[0])
63
+ assert_match(/^From: from_bar/, lines[4])
64
+ end
65
+
66
+ def test_debug_dev
67
+ str = ""
68
+ @client.debug_dev = str
69
+ assert_equal(str.object_id, @client.debug_dev.object_id)
70
+ assert(str.empty?)
71
+ @client.get(@url)
72
+ assert(!str.empty?)
73
+ end
74
+
75
+ def test_debug_dev_stream
76
+ str = ""
77
+ @client.debug_dev = str
78
+ conn = @client.get_async(@url)
79
+ Thread.pass while !conn.finished?
80
+ assert(!str.empty?)
81
+ end
82
+
83
+ def test_protocol_version_http09
84
+ @client.protocol_version = 'HTTP/0.9'
85
+ @client.debug_dev = str = ''
86
+ @client.test_loopback_http_response << "hello\nworld\n"
87
+ res = @client.get(@url + 'hello')
88
+ assert_equal('0.9', res.version)
89
+ assert_equal(nil, res.status)
90
+ assert_equal(nil, res.reason)
91
+ assert_equal("hello\nworld\n", res.content)
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/0.9", lines[3])
96
+ assert_equal("Connection: close", lines[5])
97
+ assert_equal("= Response", lines[6])
98
+ assert_match(/^hello$/, lines[7])
99
+ assert_match(/^world$/, lines[8])
100
+ end
101
+
102
+ def test_protocol_version_http10
103
+ assert_equal(nil, @client.protocol_version)
104
+ @client.protocol_version = 'HTTP/1.0'
105
+ assert_equal('HTTP/1.0', @client.protocol_version)
106
+ str = ""
107
+ @client.debug_dev = str
108
+ @client.get(@url + 'hello')
109
+ lines = str.split(/(?:\r?\n)+/)
110
+ assert_equal("= Request", lines[0])
111
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
112
+ assert_equal("GET /hello HTTP/1.0", lines[3])
113
+ assert_equal("Connection: close", lines[5])
114
+ assert_equal("= Response", lines[6])
115
+ end
116
+
117
+ def test_protocol_version_http11
118
+ assert_equal(nil, @client.protocol_version)
119
+ str = ""
120
+ @client.debug_dev = str
121
+ @client.get(@url)
122
+ lines = str.split(/(?:\r?\n)+/)
123
+ assert_equal("= Request", lines[0])
124
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
125
+ assert_equal("GET / HTTP/1.1", lines[3])
126
+ assert_equal("Host: localhost:#{Port}", lines[6])
127
+ @client.protocol_version = 'HTTP/1.1'
128
+ assert_equal('HTTP/1.1', @client.protocol_version)
129
+ str = ""
130
+ @client.debug_dev = str
131
+ @client.get(@url)
132
+ lines = str.split(/(?:\r?\n)+/)
133
+ assert_equal("= Request", lines[0])
134
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
135
+ assert_equal("GET / HTTP/1.1", lines[3])
136
+ @client.protocol_version = 'HTTP/1.0'
137
+ str = ""
138
+ @client.debug_dev = str
139
+ @client.get(@url)
140
+ lines = str.split(/(?:\r?\n)+/)
141
+ assert_equal("= Request", lines[0])
142
+ assert_equal("! CONNECTION ESTABLISHED", lines[2])
143
+ assert_equal("GET / HTTP/1.0", lines[3])
144
+ end
145
+
146
+ def test_proxy
147
+ setup_proxyserver
148
+ escape_noproxy do
149
+ assert_raises(URI::InvalidURIError) do
150
+ @client.proxy = "http://"
151
+ end
152
+ assert_raises(ArgumentError) do
153
+ @client.proxy = ""
154
+ end
155
+ @client.proxy = "http://admin:admin@foo:1234"
156
+ assert_equal(URI.parse("http://admin:admin@foo:1234"), @client.proxy)
157
+ uri = URI.parse("http://bar:2345")
158
+ @client.proxy = uri
159
+ assert_equal(uri, @client.proxy)
160
+ #
161
+ @proxyio.string = ""
162
+ @client.proxy = nil
163
+ assert_equal(200, @client.head(@url).status)
164
+ assert(/accept/ !~ @proxyio.string)
165
+ #
166
+ @proxyio.string = ""
167
+ @client.proxy = @proxyurl
168
+ @client.debug_dev = str = ""
169
+ assert_equal(200, @client.head(@url).status)
170
+ assert(/accept/ =~ @proxyio.string)
171
+ assert(/Host: localhost:17171/ =~ str)
172
+ end
173
+ end
174
+
175
+ def test_host_header
176
+ @client.proxy = @proxyurl
177
+ @client.debug_dev = str = ""
178
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n"
179
+ assert_equal(200, @client.head('http://www.example.com/foo').status)
180
+ # ensure no ':80' is added. some servers dislike that.
181
+ assert(/\r\nHost: www\.example\.com\r\n/ =~ str)
182
+ #
183
+ @client.debug_dev = str = ""
184
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n"
185
+ assert_equal(200, @client.head('http://www.example.com:12345/foo').status)
186
+ # ensure ':12345' exists.
187
+ assert(/\r\nHost: www\.example\.com:12345\r\n/ =~ str)
188
+ end
189
+
190
+ def test_proxy_env
191
+ setup_proxyserver
192
+ escape_env do
193
+ ENV['http_proxy'] = "http://admin:admin@foo:1234"
194
+ ENV['NO_PROXY'] = "foobar"
195
+ client = HTTPClient.new
196
+ assert_equal(URI.parse("http://admin:admin@foo:1234"), client.proxy)
197
+ assert_equal('foobar', client.no_proxy)
198
+ end
199
+ end
200
+
201
+ def test_proxy_env_cgi
202
+ setup_proxyserver
203
+ escape_env do
204
+ ENV['REQUEST_METHOD'] = 'GET' # CGI environment emulation
205
+ ENV['http_proxy'] = "http://admin:admin@foo:1234"
206
+ ENV['no_proxy'] = "foobar"
207
+ client = HTTPClient.new
208
+ assert_equal(nil, client.proxy)
209
+ ENV['CGI_HTTP_PROXY'] = "http://admin:admin@foo:1234"
210
+ client = HTTPClient.new
211
+ assert_equal(URI.parse("http://admin:admin@foo:1234"), client.proxy)
212
+ end
213
+ end
214
+
215
+ def test_noproxy_for_localhost
216
+ @proxyio.string = ""
217
+ @client.proxy = @proxyurl
218
+ assert_equal(200, @client.head(@url).status)
219
+ assert(/accept/ !~ @proxyio.string)
220
+ end
221
+
222
+ def test_no_proxy
223
+ setup_proxyserver
224
+ escape_noproxy do
225
+ # proxy is not set.
226
+ assert_equal(nil, @client.no_proxy)
227
+ @client.no_proxy = 'localhost'
228
+ assert_equal('localhost', @client.no_proxy)
229
+ @proxyio.string = ""
230
+ @client.proxy = nil
231
+ assert_equal(200, @client.head(@url).status)
232
+ assert(/accept/ !~ @proxyio.string)
233
+ #
234
+ @proxyio.string = ""
235
+ @client.proxy = @proxyurl
236
+ assert_equal(200, @client.head(@url).status)
237
+ assert(/accept/ !~ @proxyio.string)
238
+ #
239
+ @client.no_proxy = 'foobar'
240
+ @proxyio.string = ""
241
+ @client.proxy = @proxyurl
242
+ assert_equal(200, @client.head(@url).status)
243
+ assert(/accept/ =~ @proxyio.string)
244
+ #
245
+ @client.no_proxy = 'foobar,localhost:baz'
246
+ @proxyio.string = ""
247
+ @client.proxy = @proxyurl
248
+ assert_equal(200, @client.head(@url).status)
249
+ assert(/accept/ !~ @proxyio.string)
250
+ #
251
+ @client.no_proxy = 'foobar,localhost:443'
252
+ @proxyio.string = ""
253
+ @client.proxy = @proxyurl
254
+ assert_equal(200, @client.head(@url).status)
255
+ assert(/accept/ =~ @proxyio.string)
256
+ #
257
+ @client.no_proxy = 'foobar,localhost:443:localhost:17171,baz'
258
+ @proxyio.string = ""
259
+ @client.proxy = @proxyurl
260
+ assert_equal(200, @client.head(@url).status)
261
+ assert(/accept/ !~ @proxyio.string)
262
+ end
263
+ end
264
+
265
+ def test_proxy_ssl
266
+ escape_noproxy do
267
+ @client.proxy = 'http://admin:admin@localhost:8080/'
268
+ # disconnected at initial 'CONNECT' so there're 2 loopback responses
269
+ @client.test_loopback_http_response << <<EOS
270
+ HTTP/1.0 407 Proxy Authentication Required\r
271
+ Date: Fri, 19 Dec 2008 11:57:29 GMT\r
272
+ Content-Type: text/plain\r
273
+ Content-Length: 0\r
274
+ Proxy-Authenticate: Basic realm="hello"\r
275
+ Proxy-Connection: close\r
276
+ \r
277
+ EOS
278
+ @client.test_loopback_http_response << <<EOS
279
+ HTTP/1.0 200 Connection established\r
280
+ \r
281
+ HTTP/1.1 200 OK\r
282
+ Content-Length: 5\r
283
+ Connection: close\r
284
+ \r
285
+ hello
286
+ EOS
287
+ assert_equal('hello', @client.get('https://localhost:17171/baz').content)
288
+ end
289
+ end
290
+
291
+ def test_loopback_response
292
+ @client.test_loopback_response << 'message body 1'
293
+ @client.test_loopback_response << 'message body 2'
294
+ assert_equal('message body 1', @client.get_content('http://somewhere'))
295
+ assert_equal('message body 2', @client.get_content('http://somewhere'))
296
+ #
297
+ @client.debug_dev = str = ''
298
+ @client.test_loopback_response << 'message body 3'
299
+ assert_equal('message body 3', @client.get_content('http://somewhere'))
300
+ assert_match(/message body 3/, str)
301
+ end
302
+
303
+ def test_loopback_response_stream
304
+ @client.test_loopback_response << 'message body 1'
305
+ @client.test_loopback_response << 'message body 2'
306
+ conn = @client.get_async('http://somewhere')
307
+ Thread.pass while !conn.finished?
308
+ assert_equal('message body 1', conn.pop.content.read)
309
+ conn = @client.get_async('http://somewhere')
310
+ Thread.pass while !conn.finished?
311
+ assert_equal('message body 2', conn.pop.content.read)
312
+ end
313
+
314
+ def test_loopback_http_response
315
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\ncontent-length: 100\n\nmessage body 1"
316
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\ncontent-length: 100\n\nmessage body 2"
317
+ assert_equal('message body 1', @client.get_content('http://somewhere'))
318
+ assert_equal('message body 2', @client.get_content('http://somewhere'))
319
+ end
320
+
321
+ def test_broken_header
322
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\nXXXXX\ncontent-length: 100\n\nmessage body 1"
323
+ assert_equal('message body 1', @client.get_content('http://somewhere'))
324
+ end
325
+
326
+ def test_redirect_non_https
327
+ url = @url + 'redirect1'
328
+ https_url = URI.parse(url)
329
+ https_url.scheme = 'https'
330
+ #
331
+ redirect_to_http = "HTTP/1.0 302 OK\nLocation: #{url}\n\n"
332
+ redirect_to_https = "HTTP/1.0 302 OK\nLocation: #{https_url}\n\n"
333
+ #
334
+ # https -> http is denied
335
+ @client.test_loopback_http_response << redirect_to_http
336
+ assert_raises(HTTPClient::BadResponseError) do
337
+ @client.get_content(https_url)
338
+ end
339
+ #
340
+ # http -> http is OK
341
+ @client.reset_all
342
+ @client.test_loopback_http_response << redirect_to_http
343
+ assert_equal('hello', @client.get_content(url))
344
+ #
345
+ # http -> https is OK
346
+ @client.reset_all
347
+ @client.test_loopback_http_response << redirect_to_https
348
+ assert_raises(OpenSSL::SSL::SSLError) do
349
+ # trying to normal endpoint with SSL -> SSL negotiation failure
350
+ @client.get_content(url)
351
+ end
352
+ #
353
+ # https -> https is OK
354
+ @client.reset_all
355
+ @client.test_loopback_http_response << redirect_to_https
356
+ assert_raises(OpenSSL::SSL::SSLError) do
357
+ # trying to normal endpoint with SSL -> SSL negotiation failure
358
+ @client.get_content(https_url)
359
+ end
360
+ end
361
+
362
+ def test_redirect_relative
363
+ @client.test_loopback_http_response << "HTTP/1.0 302 OK\nLocation: hello\n\n"
364
+ assert_equal('hello', @client.get_content(@url + 'redirect1'))
365
+ #
366
+ @client.reset_all
367
+ @client.redirect_uri_callback = @client.method(:strict_redirect_uri_callback)
368
+ assert_equal('hello', @client.get_content(@url + 'redirect1'))
369
+ @client.reset_all
370
+ @client.test_loopback_http_response << "HTTP/1.0 302 OK\nLocation: hello\n\n"
371
+ begin
372
+ @client.get_content(@url + 'redirect1')
373
+ assert(false)
374
+ rescue HTTPClient::BadResponseError => e
375
+ assert_equal(302, e.res.status)
376
+ end
377
+ end
378
+
379
+ def test_get_content
380
+ assert_equal('hello', @client.get_content(@url + 'hello'))
381
+ assert_equal('hello', @client.get_content(@url + 'redirect1'))
382
+ assert_equal('hello', @client.get_content(@url + 'redirect2'))
383
+ url = @url.sub(/localhost/, '127.0.0.1')
384
+ assert_equal('hello', @client.get_content(url + 'hello'))
385
+ assert_equal('hello', @client.get_content(url + 'redirect1'))
386
+ assert_equal('hello', @client.get_content(url + 'redirect2'))
387
+ @client.reset(@url)
388
+ @client.reset(url)
389
+ @client.reset(@url)
390
+ @client.reset(url)
391
+ assert_raises(HTTPClient::BadResponseError) do
392
+ @client.get_content(@url + 'notfound')
393
+ end
394
+ assert_raises(HTTPClient::BadResponseError) do
395
+ @client.get_content(@url + 'redirect_self')
396
+ end
397
+ called = false
398
+ @client.redirect_uri_callback = lambda { |uri, res|
399
+ newuri = res.header['location'][0]
400
+ called = true
401
+ newuri
402
+ }
403
+ assert_equal('hello', @client.get_content(@url + 'relative_redirect'))
404
+ assert(called)
405
+ end
406
+
407
+ def test_get_content_with_block
408
+ @client.get_content(@url + 'hello') do |str|
409
+ assert_equal('hello', str)
410
+ end
411
+ @client.get_content(@url + 'redirect1') do |str|
412
+ assert_equal('hello', str)
413
+ end
414
+ @client.get_content(@url + 'redirect2') do |str|
415
+ assert_equal('hello', str)
416
+ end
417
+ end
418
+
419
+ def test_post_content
420
+ assert_equal('hello', @client.post_content(@url + 'hello'))
421
+ assert_equal('hello', @client.post_content(@url + 'redirect1'))
422
+ assert_equal('hello', @client.post_content(@url + 'redirect2'))
423
+ assert_raises(HTTPClient::BadResponseError) do
424
+ @client.post_content(@url + 'notfound')
425
+ end
426
+ assert_raises(HTTPClient::BadResponseError) do
427
+ @client.post_content(@url + 'redirect_self')
428
+ end
429
+ called = false
430
+ @client.redirect_uri_callback = lambda { |uri, res|
431
+ newuri = res.header['location'][0]
432
+ called = true
433
+ newuri
434
+ }
435
+ assert_equal('hello', @client.post_content(@url + 'relative_redirect'))
436
+ assert(called)
437
+ end
438
+
439
+ def test_post_content_io
440
+ post_body = StringIO.new("1234567890")
441
+ assert_equal('post,1234567890', @client.post_content(@url + 'servlet', post_body))
442
+ post_body = StringIO.new("1234567890")
443
+ assert_equal('post,1234567890', @client.post_content(@url + 'servlet_redirect', post_body))
444
+ #
445
+ post_body = StringIO.new("1234567890")
446
+ post_body.read(5)
447
+ assert_equal('post,67890', @client.post_content(@url + 'servlet_redirect', post_body))
448
+ end
449
+
450
+ def test_head
451
+ assert_equal("head", @client.head(@url + 'servlet').header["x-head"][0])
452
+ res = @client.head(@url + 'servlet', {1=>2, 3=>4})
453
+ assert_equal('1=2&3=4', res.header["x-query"][0])
454
+ end
455
+
456
+ def test_head_async
457
+ conn = @client.head_async(@url + 'servlet', {1=>2, 3=>4})
458
+ Thread.pass while !conn.finished?
459
+ res = conn.pop
460
+ assert_equal('1=2&3=4', res.header["x-query"][0])
461
+ end
462
+
463
+ def test_get
464
+ assert_equal("get", @client.get(@url + 'servlet').content)
465
+ res = @client.get(@url + 'servlet', {1=>2, 3=>4})
466
+ assert_equal('1=2&3=4', res.header["x-query"][0])
467
+ assert_nil(res.contenttype)
468
+ #
469
+ url = @url.to_s + 'servlet?5=6&7=8'
470
+ res = @client.get(url, {1=>2, 3=>4})
471
+ assert_equal('1=2&3=4&5=6&7=8', res.header["x-query"][0])
472
+ assert_nil(res.contenttype)
473
+ end
474
+
475
+ def test_get_async
476
+ conn = @client.get_async(@url + 'servlet', {1=>2, 3=>4})
477
+ Thread.pass while !conn.finished?
478
+ res = conn.pop
479
+ assert_equal('1=2&3=4', res.header["x-query"][0])
480
+ end
481
+
482
+ def test_get_with_block
483
+ called = false
484
+ res = @client.get(@url + 'servlet') { |str|
485
+ assert_equal('get', str)
486
+ called = true
487
+ }
488
+ assert(called)
489
+ # res does not have a content
490
+ assert_nil(res.content)
491
+ end
492
+
493
+ def test_post
494
+ assert_equal("post", @client.post(@url + 'servlet').content[0, 4])
495
+ res = @client.post(@url + 'servlet', {1=>2, 3=>4})
496
+ assert_equal('1=2&3=4', res.header["x-query"][0])
497
+ end
498
+
499
+ def test_post_with_content_type
500
+ ext = {'content-type' => 'application/x-www-form-urlencoded', 'hello' => 'world'}
501
+ assert_equal("post", @client.post(@url + 'servlet').content[0, 4], ext)
502
+ res = @client.post(@url + 'servlet', {1=>2, 3=>4}, ext)
503
+ assert_equal('1=2&3=4', res.header["x-query"][0])
504
+ #
505
+ ext = [['content-type', 'multipart/form-data'], ['hello', 'world']]
506
+ assert_equal("post", @client.post(@url + 'servlet').content[0, 4], ext)
507
+ res = @client.post(@url + 'servlet', {1=>2, 3=>4}, ext)
508
+ assert_match(/Content-Disposition: form-data; name="1"/, res.content)
509
+ assert_match(/Content-Disposition: form-data; name="3"/, res.content)
510
+ #
511
+ ext = {'content-type' => 'multipart/form-data; boundary=hello'}
512
+ assert_equal("post", @client.post(@url + 'servlet').content[0, 4], ext)
513
+ res = @client.post(@url + 'servlet', {1=>2, 3=>4}, ext)
514
+ assert_match(/Content-Disposition: form-data; name="1"/, res.content)
515
+ assert_match(/Content-Disposition: form-data; name="3"/, res.content)
516
+ 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)
517
+ end
518
+
519
+ def test_post_with_file
520
+ STDOUT.sync = true
521
+ File.open(__FILE__) do |file|
522
+ res = @client.post(@url + 'servlet', {1=>2, 3=>file})
523
+ assert_match(/^Content-Disposition: form-data; name="1"\r\n/m, res.content)
524
+ assert_match(/^Content-Disposition: form-data; name="3";/, res.content)
525
+ # FIND_TAG_IN_THIS_FILE
526
+ assert_match(/FIND_TAG_IN_THIS_FILE/, res.content)
527
+ end
528
+ end
529
+
530
+ def test_post_with_io # chunked post
531
+ myio = StringIO.new("4")
532
+ def myio.size
533
+ nil
534
+ end
535
+ res = @client.post(@url + 'servlet', {1=>2, 3=>myio})
536
+ assert_match(/\r\nContent-Disposition: form-data; name="1"\r\n/m, res.content)
537
+ assert_match(/\r\n2\r\n/m, res.content)
538
+ assert_match(/\r\nContent-Disposition: form-data; name="3"; filename=""\r\n/m, res.content)
539
+ assert_match(/\r\n4\r\n/m, res.content)
540
+ end
541
+
542
+ def test_post_async
543
+ conn = @client.post_async(@url + 'servlet', {1=>2, 3=>4})
544
+ Thread.pass while !conn.finished?
545
+ res = conn.pop
546
+ assert_equal('1=2&3=4', res.header["x-query"][0])
547
+ end
548
+
549
+ def test_post_with_block
550
+ called = false
551
+ res = @client.post(@url + 'servlet') { |str|
552
+ assert_equal('post,', str)
553
+ called = true
554
+ }
555
+ assert(called)
556
+ assert_nil(res.content)
557
+ #
558
+ called = false
559
+ res = @client.post(@url + 'servlet', {1=>2, 3=>4}) { |str|
560
+ assert_equal('post,1=2&3=4', str)
561
+ called = true
562
+ }
563
+ assert(called)
564
+ assert_equal('1=2&3=4', res.header["x-query"][0])
565
+ assert_nil(res.content)
566
+ end
567
+
568
+ def test_put
569
+ assert_equal("put", @client.put(@url + 'servlet').content)
570
+ res = @client.put(@url + 'servlet', {1=>2, 3=>4})
571
+ assert_equal('1=2&3=4', res.header["x-query"][0])
572
+ end
573
+
574
+ def test_put_async
575
+ conn = @client.put_async(@url + 'servlet', {1=>2, 3=>4})
576
+ Thread.pass while !conn.finished?
577
+ res = conn.pop
578
+ assert_equal('1=2&3=4', res.header["x-query"][0])
579
+ end
580
+
581
+ def test_delete
582
+ assert_equal("delete", @client.delete(@url + 'servlet').content)
583
+ end
584
+
585
+ def test_delete_async
586
+ conn = @client.delete_async(@url + 'servlet')
587
+ Thread.pass while !conn.finished?
588
+ res = conn.pop
589
+ assert_equal('delete', res.content.read)
590
+ end
591
+
592
+ def test_options
593
+ assert_equal("options", @client.options(@url + 'servlet').content)
594
+ end
595
+
596
+ def test_options_async
597
+ conn = @client.options_async(@url + 'servlet')
598
+ Thread.pass while !conn.finished?
599
+ res = conn.pop
600
+ assert_equal('options', res.content.read)
601
+ end
602
+
603
+ def test_propfind
604
+ assert_equal("propfind", @client.propfind(@url + 'servlet').content)
605
+ end
606
+
607
+ def test_propfind_async
608
+ conn = @client.propfind_async(@url + 'servlet')
609
+ Thread.pass while !conn.finished?
610
+ res = conn.pop
611
+ assert_equal('propfind', res.content.read)
612
+ end
613
+
614
+ def test_proppatch
615
+ assert_equal("proppatch", @client.proppatch(@url + 'servlet').content)
616
+ res = @client.proppatch(@url + 'servlet', {1=>2, 3=>4})
617
+ assert_equal('proppatch', res.content)
618
+ assert_equal('1=2&3=4', res.header["x-query"][0])
619
+ end
620
+
621
+ def test_proppatch_async
622
+ conn = @client.proppatch_async(@url + 'servlet', {1=>2, 3=>4})
623
+ Thread.pass while !conn.finished?
624
+ res = conn.pop
625
+ assert_equal('proppatch', res.content.read)
626
+ assert_equal('1=2&3=4', res.header["x-query"][0])
627
+ end
628
+
629
+ def test_trace
630
+ assert_equal("trace", @client.trace(@url + 'servlet').content)
631
+ res = @client.trace(@url + 'servlet', {1=>2, 3=>4})
632
+ assert_equal('1=2&3=4', res.header["x-query"][0])
633
+ end
634
+
635
+ def test_trace_async
636
+ conn = @client.trace_async(@url + 'servlet', {1=>2, 3=>4})
637
+ Thread.pass while !conn.finished?
638
+ res = conn.pop
639
+ assert_equal('1=2&3=4', res.header["x-query"][0])
640
+ end
641
+
642
+ def test_chunked
643
+ assert_equal('chunked', @client.get_content(@url + 'chunked', { 'msg' => 'chunked' }))
644
+ end
645
+
646
+ def test_chunked_empty
647
+ assert_equal('', @client.get_content(@url + 'chunked', { 'msg' => '' }))
648
+ end
649
+
650
+ def test_get_query
651
+ assert_equal({'1'=>'2'}, check_query_get({1=>2}))
652
+ assert_equal({'a'=>'A', 'B'=>'b'}, check_query_get({"a"=>"A", "B"=>"b"}))
653
+ assert_equal({'&'=>'&'}, check_query_get({"&"=>"&"}))
654
+ assert_equal({'= '=>' =+'}, check_query_get({"= "=>" =+"}))
655
+ assert_equal(
656
+ ['=', '&'].sort,
657
+ check_query_get([["=", "="], ["=", "&"]])['='].to_ary.sort
658
+ )
659
+ assert_equal({'123'=>'45'}, check_query_get('123=45'))
660
+ assert_equal({'12 3'=>'45', ' '=>' '}, check_query_get('12+3=45&+=+'))
661
+ assert_equal({}, check_query_get(''))
662
+ assert_equal({'1'=>'2'}, check_query_get({1=>StringIO.new('2')}))
663
+ assert_equal({'1'=>'2', '3'=>'4'}, check_query_get(StringIO.new('3=4&1=2')))
664
+ end
665
+
666
+ def test_post_body
667
+ assert_equal({'1'=>'2'}, check_query_post({1=>2}))
668
+ assert_equal({'a'=>'A', 'B'=>'b'}, check_query_post({"a"=>"A", "B"=>"b"}))
669
+ assert_equal({'&'=>'&'}, check_query_post({"&"=>"&"}))
670
+ assert_equal({'= '=>' =+'}, check_query_post({"= "=>" =+"}))
671
+ assert_equal(
672
+ ['=', '&'].sort,
673
+ check_query_post([["=", "="], ["=", "&"]])['='].to_ary.sort
674
+ )
675
+ assert_equal({'123'=>'45'}, check_query_post('123=45'))
676
+ assert_equal({'12 3'=>'45', ' '=>' '}, check_query_post('12+3=45&+=+'))
677
+ assert_equal({}, check_query_post(''))
678
+ #
679
+ post_body = StringIO.new("foo=bar&foo=baz")
680
+ assert_equal(
681
+ ["bar", "baz"],
682
+ check_query_post(post_body)["foo"].to_ary.sort
683
+ )
684
+ end
685
+
686
+ def test_extra_headers
687
+ str = ""
688
+ @client.debug_dev = str
689
+ @client.head(@url, nil, {"ABC" => "DEF"})
690
+ lines = str.split(/(?:\r?\n)+/)
691
+ assert_equal("= Request", lines[0])
692
+ assert_match("ABC: DEF", lines[4])
693
+ #
694
+ str = ""
695
+ @client.debug_dev = str
696
+ @client.get(@url, nil, [["ABC", "DEF"], ["ABC", "DEF"]])
697
+ lines = str.split(/(?:\r?\n)+/)
698
+ assert_equal("= Request", lines[0])
699
+ assert_match("ABC: DEF", lines[4])
700
+ assert_match("ABC: DEF", lines[5])
701
+ end
702
+
703
+ def test_timeout
704
+ assert_equal(60, @client.connect_timeout)
705
+ assert_equal(120, @client.send_timeout)
706
+ assert_equal(60, @client.receive_timeout)
707
+ #
708
+ @client.connect_timeout = 1
709
+ @client.send_timeout = 2
710
+ @client.receive_timeout = 3
711
+ assert_equal(1, @client.connect_timeout)
712
+ assert_equal(2, @client.send_timeout)
713
+ assert_equal(3, @client.receive_timeout)
714
+ end
715
+
716
+ def test_connect_timeout
717
+ # ToDo
718
+ end
719
+
720
+ def test_send_timeout
721
+ # ToDo
722
+ end
723
+
724
+ def test_receive_timeout
725
+ # this test takes 2 sec
726
+ assert_equal('hello', @client.get_content(@url + 'sleep?sec=2'))
727
+ @client.receive_timeout = 1
728
+ assert_equal('hello', @client.get_content(@url + 'sleep?sec=0'))
729
+ assert_raise(HTTPClient::ReceiveTimeoutError) do
730
+ @client.get_content(@url + 'sleep?sec=2')
731
+ end
732
+ @client.receive_timeout = 3
733
+ assert_equal('hello', @client.get_content(@url + 'sleep?sec=2'))
734
+ end
735
+
736
+ def test_reset
737
+ url = @url + 'servlet'
738
+ assert_nothing_raised do
739
+ 5.times do
740
+ @client.get(url)
741
+ @client.reset(url)
742
+ end
743
+ end
744
+ end
745
+
746
+ def test_reset_all
747
+ assert_nothing_raised do
748
+ 5.times do
749
+ @client.get(@url + 'servlet')
750
+ @client.reset_all
751
+ end
752
+ end
753
+ end
754
+
755
+ def test_cookies
756
+ cookiefile = File.join(File.dirname(File.expand_path(__FILE__)), 'test_cookies_file')
757
+ File.open(cookiefile, "wb") do |f|
758
+ f << "http://rubyforge.org/account/login.php session_ser LjEwMy45Ni40Ni0q%2A-fa0537de8cc31 2000000000 .rubyforge.org / 13\n"
759
+ end
760
+ @client.set_cookie_store(cookiefile)
761
+ cookie = @client.cookie_manager.cookies.first
762
+ url = cookie.url
763
+ assert(cookie.domain_match(url.host, cookie.domain))
764
+ #
765
+ @client.reset_all
766
+ @client.test_loopback_http_response << "HTTP/1.0 200 OK\nSet-Cookie: foo=bar; expires=#{Time.mktime(2030, 12, 31).httpdate}\n\nOK"
767
+ @client.get_content('http://rubyforge.org/account/login.php')
768
+ @client.save_cookie_store
769
+ str = File.read(cookiefile)
770
+ assert_match(%r(http://rubyforge.org/account/login.php foo bar 1924873200 rubyforge.org /login.php 1), str)
771
+ end
772
+
773
+ def test_urify
774
+ extend HTTPClient::Util
775
+ assert_nil(urify(nil))
776
+ uri = 'http://foo'
777
+ assert_equal(URI.parse(uri), urify(uri))
778
+ assert_equal(URI.parse(uri), urify(URI.parse(uri)))
779
+ end
780
+
781
+ def test_connection
782
+ c = HTTPClient::Connection.new
783
+ assert(c.finished?)
784
+ assert_nil(c.join)
785
+ end
786
+
787
+ def test_site
788
+ site = HTTPClient::Site.new
789
+ assert_equal('tcp', site.scheme)
790
+ assert_equal('0.0.0.0', site.host)
791
+ assert_equal(0, site.port)
792
+ assert_equal('tcp://0.0.0.0:0', site.addr)
793
+ assert_equal('tcp://0.0.0.0:0', site.to_s)
794
+ assert_nothing_raised do
795
+ site.inspect
796
+ end
797
+ #
798
+ site = HTTPClient::Site.new(URI.parse('http://localhost:12345/foo'))
799
+ assert_equal('http', site.scheme)
800
+ assert_equal('localhost', site.host)
801
+ assert_equal(12345, site.port)
802
+ assert_equal('http://localhost:12345', site.addr)
803
+ assert_equal('http://localhost:12345', site.to_s)
804
+ assert_nothing_raised do
805
+ site.inspect
806
+ end
807
+ #
808
+ site1 = HTTPClient::Site.new(URI.parse('http://localhost:12341/'))
809
+ site2 = HTTPClient::Site.new(URI.parse('http://localhost:12342/'))
810
+ site3 = HTTPClient::Site.new(URI.parse('http://localhost:12342/'))
811
+ assert(!(site1 == site2))
812
+ h = { site1 => 'site1', site2 => 'site2' }
813
+ h[site3] = 'site3'
814
+ assert_equal('site1', h[site1])
815
+ assert_equal('site3', h[site2])
816
+ end
817
+
818
+ def test_http_header
819
+ res = @client.get(@url + 'hello')
820
+ assert_equal('text/html', res.contenttype)
821
+ assert_equal(5, res.header.get(nil).size)
822
+ #
823
+ res.header.delete('connection')
824
+ assert_equal(4, res.header.get(nil).size)
825
+ #
826
+ res.header['foo'] = 'bar'
827
+ assert_equal(['bar'], res.header['foo'])
828
+ #
829
+ assert_equal([['foo', 'bar']], res.header.get('foo'))
830
+ res.header['foo'] = ['bar', 'bar2']
831
+ assert_equal([['foo', 'bar'], ['foo', 'bar2']], res.header.get('foo'))
832
+ end
833
+
834
+ def test_mime_type
835
+ assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
836
+ assert_equal('text/html', HTTP::Message.mime_type('foo.html'))
837
+ assert_equal('text/html', HTTP::Message.mime_type('foo.htm'))
838
+ assert_equal('application/msword', HTTP::Message.mime_type('foo.doc'))
839
+ assert_equal('image/png', HTTP::Message.mime_type('foo.png'))
840
+ assert_equal('image/gif', HTTP::Message.mime_type('foo.gif'))
841
+ assert_equal('image/jpeg', HTTP::Message.mime_type('foo.jpg'))
842
+ assert_equal('image/jpeg', HTTP::Message.mime_type('foo.jpeg'))
843
+ assert_equal('application/octet-stream', HTTP::Message.mime_type('foo.unknown'))
844
+ #
845
+ handler = lambda { |path| 'hello/world' }
846
+ assert_nil(HTTP::Message.mime_type_handler)
847
+ assert_nil(HTTP::Message.get_mime_type_func)
848
+ HTTP::Message.mime_type_handler = handler
849
+ assert_not_nil(HTTP::Message.mime_type_handler)
850
+ assert_not_nil(HTTP::Message.get_mime_type_func)
851
+ assert_equal('hello/world', HTTP::Message.mime_type('foo.txt'))
852
+ HTTP::Message.mime_type_handler = nil
853
+ assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
854
+ HTTP::Message.set_mime_type_func(nil)
855
+ assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
856
+ #
857
+ handler = lambda { |path| nil }
858
+ HTTP::Message.mime_type_handler = handler
859
+ assert_equal('application/octet-stream', HTTP::Message.mime_type('foo.txt'))
860
+ end
861
+
862
+ def test_connect_request
863
+ req = HTTP::Message.new_connect_request(URI.parse('https://foo/bar'))
864
+ assert_equal("CONNECT foo:443 HTTP/1.0\r\n\r\n", req.dump)
865
+ req = HTTP::Message.new_connect_request(URI.parse('https://example.com/'))
866
+ assert_equal("CONNECT example.com:443 HTTP/1.0\r\n\r\n", req.dump)
867
+ end
868
+
869
+ def test_response
870
+ res = HTTP::Message.new_response('response')
871
+ res.contenttype = 'text/plain'
872
+ res.header.body_date = Time.mktime(2000, 1, 1)
873
+ assert_equal(
874
+ [
875
+ "",
876
+ "Content-Length: 8",
877
+ "Content-Type: text/plain",
878
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
879
+ "Status: 200 OK",
880
+ "response"
881
+ ],
882
+ res.dump.split(/\r\n/).sort
883
+ )
884
+ res.header.set('foo', 'bar')
885
+ assert_equal(
886
+ [
887
+ "",
888
+ "Content-Length: 8",
889
+ "Content-Type: text/plain",
890
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
891
+ "Status: 200 OK",
892
+ "foo: bar",
893
+ "response"
894
+ ],
895
+ res.dump.split(/\r\n/).sort
896
+ )
897
+ # nil body
898
+ res = HTTP::Message.new_response(nil)
899
+ assert_equal(
900
+ [
901
+ "Content-Length: 0",
902
+ "Content-Type: text/html; charset=us-ascii",
903
+ "Status: 200 OK"
904
+ ],
905
+ res.dump.split(/\r\n/).sort
906
+ )
907
+ # for mod_ruby env
908
+ Object.const_set('Apache', nil)
909
+ begin
910
+ res = HTTP::Message.new_response('response')
911
+ assert(res.dump.split(/\r\n/).any? { |line| /^Date/ =~ line })
912
+ #
913
+ res = HTTP::Message.new_response('response')
914
+ res.contenttype = 'text/plain'
915
+ res.header.body_date = Time.mktime(2000, 1, 1)
916
+ res.header['Date'] = Time.mktime(2000, 1, 1).httpdate
917
+ assert_equal(
918
+ [
919
+ "",
920
+ "Content-Length: 8",
921
+ "Content-Type: text/plain",
922
+ "Date: Fri, 31 Dec 1999 15:00:00 GMT",
923
+ "HTTP/1.1 200 OK",
924
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
925
+ "response"
926
+ ],
927
+ res.dump.split(/\r\n/).sort
928
+ )
929
+ ensure
930
+ Object.instance_eval { remove_const('Apache') }
931
+ end
932
+ end
933
+
934
+ def test_timeout_scheduler
935
+ assert_equal('hello', @client.get_content(@url + 'hello'))
936
+ status = HTTPClient.timeout_scheduler.instance_eval { @thread.kill; @thread.status }
937
+ assert(!status) # dead
938
+ assert_equal('hello', @client.get_content(@url + 'hello'))
939
+ end
940
+
941
+ def test_session_manager
942
+ mgr = HTTPClient::SessionManager.new(@client)
943
+ assert_nil(mgr.instance_eval { @proxy })
944
+ assert_nil(mgr.debug_dev)
945
+ @client.debug_dev = Object.new
946
+ @client.proxy = 'http://myproxy:12345'
947
+ mgr = HTTPClient::SessionManager.new(@client)
948
+ assert_equal('http://myproxy:12345', mgr.instance_eval { @proxy }.to_s)
949
+ assert_equal(@client.debug_dev, mgr.debug_dev)
950
+ end
951
+
952
+ private
953
+
954
+ def check_query_get(query)
955
+ WEBrick::HTTPUtils.parse_query(
956
+ @client.get(@url + 'servlet', query).header["x-query"][0]
957
+ )
958
+ end
959
+
960
+ def check_query_post(query)
961
+ WEBrick::HTTPUtils.parse_query(
962
+ @client.post(@url + 'servlet', query).header["x-query"][0]
963
+ )
964
+ end
965
+
966
+ def setup_server
967
+ @server = WEBrick::HTTPServer.new(
968
+ :BindAddress => "localhost",
969
+ :Logger => @logger,
970
+ :Port => Port,
971
+ :AccessLog => [],
972
+ :DocumentRoot => File.dirname(File.expand_path(__FILE__))
973
+ )
974
+ [:hello, :sleep, :servlet_redirect, :redirect1, :redirect2, :redirect3, :redirect_self, :relative_redirect, :chunked].each do |sym|
975
+ @server.mount(
976
+ "/#{sym}",
977
+ WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
978
+ )
979
+ end
980
+ @server.mount('/servlet', TestServlet.new(@server))
981
+ @server_thread = start_server_thread(@server)
982
+ end
983
+
984
+ def setup_proxyserver
985
+ @proxyserver = WEBrick::HTTPProxyServer.new(
986
+ :BindAddress => "localhost",
987
+ :Logger => @proxylogger,
988
+ :Port => ProxyPort,
989
+ :AccessLog => []
990
+ )
991
+ @proxyserver_thread = start_server_thread(@proxyserver)
992
+ end
993
+
994
+ def setup_client
995
+ @client = HTTPClient.new
996
+ end
997
+
998
+ def teardown_server
999
+ @server.shutdown
1000
+ @server_thread.kill
1001
+ @server_thread.join
1002
+ end
1003
+
1004
+ def teardown_proxyserver
1005
+ @proxyserver.shutdown
1006
+ @proxyserver_thread.kill
1007
+ @proxyserver_thread.join
1008
+ end
1009
+
1010
+ def teardown_client
1011
+ @client.reset_all
1012
+ end
1013
+
1014
+ def start_server_thread(server)
1015
+ t = Thread.new {
1016
+ Thread.current.abort_on_exception = true
1017
+ server.start
1018
+ }
1019
+ while server.status != :Running
1020
+ sleep 0.1
1021
+ unless t.alive?
1022
+ t.join
1023
+ raise
1024
+ end
1025
+ end
1026
+ t
1027
+ end
1028
+
1029
+ def escape_env
1030
+ env = {}
1031
+ env.update(ENV)
1032
+ yield
1033
+ ensure
1034
+ ENV.clear
1035
+ ENV.update(env)
1036
+ end
1037
+
1038
+ def escape_noproxy
1039
+ backup = HTTPClient::NO_PROXY_HOSTS.dup
1040
+ HTTPClient::NO_PROXY_HOSTS.clear
1041
+ yield
1042
+ ensure
1043
+ HTTPClient::NO_PROXY_HOSTS.replace(backup)
1044
+ end
1045
+
1046
+ def do_hello(req, res)
1047
+ res['content-type'] = 'text/html'
1048
+ res.body = "hello"
1049
+ end
1050
+
1051
+ def do_sleep(req, res)
1052
+ sec = req.query['sec'].to_i
1053
+ sleep sec
1054
+ res['content-type'] = 'text/html'
1055
+ res.body = "hello"
1056
+ end
1057
+
1058
+ def do_servlet_redirect(req, res)
1059
+ res.set_redirect(WEBrick::HTTPStatus::Found, @url + "servlet")
1060
+ end
1061
+
1062
+ def do_redirect1(req, res)
1063
+ res.set_redirect(WEBrick::HTTPStatus::MovedPermanently, @url + "hello")
1064
+ end
1065
+
1066
+ def do_redirect2(req, res)
1067
+ res.set_redirect(WEBrick::HTTPStatus::TemporaryRedirect, @url + "redirect3")
1068
+ end
1069
+
1070
+ def do_redirect3(req, res)
1071
+ res.set_redirect(WEBrick::HTTPStatus::Found, @url + "hello")
1072
+ end
1073
+
1074
+ def do_redirect_self(req, res)
1075
+ res.set_redirect(WEBrick::HTTPStatus::Found, @url + "redirect_self")
1076
+ end
1077
+
1078
+ def do_relative_redirect(req, res)
1079
+ res.set_redirect(WEBrick::HTTPStatus::Found, "hello")
1080
+ end
1081
+
1082
+ def do_chunked(req, res)
1083
+ res.chunked = true
1084
+ piper, pipew = IO.pipe
1085
+ res.body = piper
1086
+ pipew << req.query['msg']
1087
+ pipew.close
1088
+ end
1089
+
1090
+ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
1091
+ def get_instance(*arg)
1092
+ self
1093
+ end
1094
+
1095
+ def do_HEAD(req, res)
1096
+ res["x-head"] = 'head' # use this for test purpose only.
1097
+ res["x-query"] = query_response(req)
1098
+ end
1099
+
1100
+ def do_GET(req, res)
1101
+ res.body = 'get'
1102
+ res["x-query"] = query_response(req)
1103
+ end
1104
+
1105
+ def do_POST(req, res)
1106
+ res.body = 'post,' + req.body.to_s
1107
+ res["x-query"] = body_response(req)
1108
+ end
1109
+
1110
+ def do_PUT(req, res)
1111
+ res.body = 'put'
1112
+ res["x-query"] = body_response(req)
1113
+ end
1114
+
1115
+ def do_DELETE(req, res)
1116
+ res.body = 'delete'
1117
+ end
1118
+
1119
+ def do_OPTIONS(req, res)
1120
+ # check RFC for legal response.
1121
+ res.body = 'options'
1122
+ end
1123
+
1124
+ def do_PROPFIND(req, res)
1125
+ res.body = 'propfind'
1126
+ end
1127
+
1128
+ def do_PROPPATCH(req, res)
1129
+ res.body = 'proppatch'
1130
+ res["x-query"] = body_response(req)
1131
+ end
1132
+
1133
+ def do_TRACE(req, res)
1134
+ # client SHOULD reflect the message received back to the client as the
1135
+ # entity-body of a 200 (OK) response. [RFC2616]
1136
+ res.body = 'trace'
1137
+ res["x-query"] = query_response(req)
1138
+ end
1139
+
1140
+ private
1141
+
1142
+ def query_response(req)
1143
+ query_escape(WEBrick::HTTPUtils.parse_query(req.query_string))
1144
+ end
1145
+
1146
+ def body_response(req)
1147
+ query_escape(WEBrick::HTTPUtils.parse_query(req.body))
1148
+ end
1149
+
1150
+ def query_escape(query)
1151
+ escaped = []
1152
+ query.sort_by { |k, v| k }.collect do |k, v|
1153
+ v.to_ary.each do |ve|
1154
+ escaped << CGI.escape(k) + '=' + CGI.escape(ve)
1155
+ end
1156
+ end
1157
+ escaped.join('&')
1158
+ end
1159
+ end
1160
+ end