bterlson-httpclient 2.1.4

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