httpclient 2.1.5 → 2.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +85 -0
  3. data/bin/httpclient +77 -0
  4. data/bin/jsonclient +85 -0
  5. data/lib/hexdump.rb +50 -0
  6. data/lib/http-access2.rb +6 -4
  7. data/lib/httpclient/auth.rb +575 -173
  8. data/lib/httpclient/cacert.pem +3952 -0
  9. data/lib/httpclient/cacert1024.pem +3866 -0
  10. data/lib/httpclient/connection.rb +6 -2
  11. data/lib/httpclient/cookie.rb +162 -504
  12. data/lib/httpclient/http.rb +334 -119
  13. data/lib/httpclient/include_client.rb +85 -0
  14. data/lib/httpclient/jruby_ssl_socket.rb +588 -0
  15. data/lib/httpclient/session.rb +385 -288
  16. data/lib/httpclient/ssl_config.rb +195 -155
  17. data/lib/httpclient/ssl_socket.rb +150 -0
  18. data/lib/httpclient/timeout.rb +14 -10
  19. data/lib/httpclient/util.rb +142 -6
  20. data/lib/httpclient/version.rb +3 -0
  21. data/lib/httpclient/webagent-cookie.rb +459 -0
  22. data/lib/httpclient.rb +509 -202
  23. data/lib/jsonclient.rb +63 -0
  24. data/lib/oauthclient.rb +111 -0
  25. data/sample/async.rb +8 -0
  26. data/sample/auth.rb +11 -0
  27. data/sample/cookie.rb +18 -0
  28. data/sample/dav.rb +103 -0
  29. data/sample/howto.rb +49 -0
  30. data/sample/jsonclient.rb +67 -0
  31. data/sample/oauth_buzz.rb +57 -0
  32. data/sample/oauth_friendfeed.rb +59 -0
  33. data/sample/oauth_twitter.rb +61 -0
  34. data/sample/ssl/0cert.pem +22 -0
  35. data/sample/ssl/0key.pem +30 -0
  36. data/sample/ssl/1000cert.pem +19 -0
  37. data/sample/ssl/1000key.pem +18 -0
  38. data/sample/ssl/htdocs/index.html +10 -0
  39. data/sample/ssl/ssl_client.rb +22 -0
  40. data/sample/ssl/webrick_httpsd.rb +29 -0
  41. data/sample/stream.rb +21 -0
  42. data/sample/thread.rb +27 -0
  43. data/sample/wcat.rb +21 -0
  44. data/test/ca-chain.pem +44 -0
  45. data/test/ca.cert +23 -0
  46. data/test/client-pass.key +18 -0
  47. data/test/client.cert +19 -0
  48. data/test/client.key +15 -0
  49. data/test/helper.rb +131 -0
  50. data/test/htdigest +1 -0
  51. data/test/htpasswd +2 -0
  52. data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
  53. data/test/runner.rb +2 -0
  54. data/test/server.cert +19 -0
  55. data/test/server.key +15 -0
  56. data/test/sslsvr.rb +65 -0
  57. data/test/subca.cert +21 -0
  58. data/test/test_auth.rb +492 -0
  59. data/test/test_cookie.rb +309 -0
  60. data/test/test_hexdump.rb +14 -0
  61. data/test/test_http-access2.rb +508 -0
  62. data/test/test_httpclient.rb +2145 -0
  63. data/test/test_include_client.rb +52 -0
  64. data/test/test_jsonclient.rb +80 -0
  65. data/test/test_ssl.rb +559 -0
  66. data/test/test_webagent-cookie.rb +465 -0
  67. metadata +85 -44
  68. data/lib/httpclient/auth.rb.orig +0 -513
  69. data/lib/httpclient/cacert.p7s +0 -1579
  70. data/lib/httpclient.rb.orig +0 -1020
  71. data/lib/tags +0 -908
data/test/test_auth.rb ADDED
@@ -0,0 +1,492 @@
1
+ require File.expand_path('helper', File.dirname(__FILE__))
2
+ require 'digest/md5'
3
+ require 'rack'
4
+ require 'rack/lint'
5
+ require 'rack-ntlm'
6
+
7
+ class TestAuth < Test::Unit::TestCase
8
+ include Helper
9
+
10
+ def setup
11
+ super
12
+ setup_server
13
+ end
14
+
15
+ def teardown
16
+ super
17
+ end
18
+
19
+ def setup_server
20
+ @server = WEBrick::HTTPServer.new(
21
+ :BindAddress => "localhost",
22
+ :Logger => @logger,
23
+ :Port => 0,
24
+ :AccessLog => [],
25
+ :DocumentRoot => File.dirname(File.expand_path(__FILE__))
26
+ )
27
+ @serverport = @server.config[:Port]
28
+ @server.mount(
29
+ '/basic_auth',
30
+ WEBrick::HTTPServlet::ProcHandler.new(method(:do_basic_auth).to_proc)
31
+ )
32
+ @server.mount(
33
+ '/digest_auth',
34
+ WEBrick::HTTPServlet::ProcHandler.new(method(:do_digest_auth).to_proc)
35
+ )
36
+ @server.mount(
37
+ '/digest_sess_auth',
38
+ WEBrick::HTTPServlet::ProcHandler.new(method(:do_digest_sess_auth).to_proc)
39
+ )
40
+ # NTLM endpoint
41
+ ntlm_handler = Rack::Handler::WEBrick.new(@server,
42
+ Rack::Builder.app do
43
+ use Rack::ShowExceptions
44
+ use Rack::ContentLength
45
+ use Rack::Ntlm, {:uri_pattern => /.*/, :auth => {:username => "admin", :password => "admin"}}
46
+ run lambda { |env| [200, { 'Content-Type' => 'text/html' }, ['ntlm_auth OK']] }
47
+ end
48
+ )
49
+ @server.mount(
50
+ '/ntlm_auth',
51
+ WEBrick::HTTPServlet::ProcHandler.new(Proc.new do |req, res|
52
+ ntlm_handler.service(req, res)
53
+ end)
54
+ )
55
+ # Htpasswd
56
+ htpasswd = File.join(File.dirname(__FILE__), 'htpasswd')
57
+ htpasswd_userdb = WEBrick::HTTPAuth::Htpasswd.new(htpasswd)
58
+ htdigest = File.join(File.dirname(__FILE__), 'htdigest')
59
+ htdigest_userdb = WEBrick::HTTPAuth::Htdigest.new(htdigest)
60
+ @basic_auth = WEBrick::HTTPAuth::BasicAuth.new(
61
+ :Logger => @logger,
62
+ :Realm => 'auth',
63
+ :UserDB => htpasswd_userdb
64
+ )
65
+ @digest_auth = WEBrick::HTTPAuth::DigestAuth.new(
66
+ :Logger => @logger,
67
+ :Algorithm => 'MD5',
68
+ :Realm => 'auth',
69
+ :UserDB => htdigest_userdb
70
+ )
71
+ @digest_sess_auth = WEBrick::HTTPAuth::DigestAuth.new(
72
+ :Logger => @logger,
73
+ :Algorithm => 'MD5-sess',
74
+ :Realm => 'auth',
75
+ :UserDB => htdigest_userdb
76
+ )
77
+ @server_thread = start_server_thread(@server)
78
+
79
+ @proxy_digest_auth = WEBrick::HTTPAuth::ProxyDigestAuth.new(
80
+ :Logger => @proxylogger,
81
+ :Algorithm => 'MD5',
82
+ :Realm => 'auth',
83
+ :UserDB => htdigest_userdb
84
+ )
85
+
86
+ @proxyserver = WEBrick::HTTPProxyServer.new(
87
+ :ProxyAuthProc => @proxy_digest_auth.method(:authenticate).to_proc,
88
+ :BindAddress => "localhost",
89
+ :Logger => @proxylogger,
90
+ :Port => 0,
91
+ :AccessLog => []
92
+ )
93
+ @proxyport = @proxyserver.config[:Port]
94
+ @proxyserver_thread = start_server_thread(@proxyserver)
95
+ end
96
+
97
+ def do_basic_auth(req, res)
98
+ @basic_auth.authenticate(req, res)
99
+ res['content-type'] = 'text/plain'
100
+ res.body = 'basic_auth OK'
101
+ end
102
+
103
+ def do_digest_auth(req, res)
104
+ @digest_auth.authenticate(req, res)
105
+ res['content-type'] = 'text/plain'
106
+ res['x-query'] = req.body
107
+ res.body = 'digest_auth OK' + req.query_string.to_s
108
+ end
109
+
110
+ def do_digest_sess_auth(req, res)
111
+ @digest_sess_auth.authenticate(req, res)
112
+ res['content-type'] = 'text/plain'
113
+ res['x-query'] = req.body
114
+ res.body = 'digest_sess_auth OK' + req.query_string.to_s
115
+ end
116
+
117
+ # TODO: monkey patching for rack-ntlm-test-services's incompat.
118
+ module ::Net
119
+ module NTLM
120
+ # ruby-ntlm 0.3.0 -> 0.4.0
121
+ def self.decode_utf16le(*arg)
122
+ EncodeUtil.decode_utf16le(*arg)
123
+ end
124
+ # Make it work if @value == nil
125
+ class SecurityBuffer < FieldSet
126
+ remove_method(:data_size) if method_defined?(:data_size)
127
+ def data_size
128
+ @active && @value ? @value.size : 0
129
+ end
130
+ end
131
+ end
132
+ end
133
+ def test_ntlm_auth
134
+ c = HTTPClient.new
135
+ c.set_auth("http://localhost:#{serverport}/ntlm_auth", 'admin', 'admin')
136
+ assert_equal('ntlm_auth OK', c.get_content("http://localhost:#{serverport}/ntlm_auth"))
137
+ end
138
+
139
+ def test_basic_auth
140
+ c = HTTPClient.new
141
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
142
+ assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth"))
143
+ end
144
+
145
+ def test_basic_auth_compat
146
+ c = HTTPClient.new
147
+ c.set_basic_auth("http://localhost:#{serverport}/", 'admin', 'admin')
148
+ assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth"))
149
+ end
150
+
151
+ def test_BASIC_auth
152
+ c = HTTPClient.new
153
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
154
+ begin
155
+ # WEBrick in ruby 1.8.7 uses 'BASIC' instead of 'Basic'
156
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
157
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
158
+ #
159
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
160
+ res = c.get("http://localhost:#{serverport}/basic_auth")
161
+ assert_equal('basic_auth OK', res.content)
162
+ assert_equal(200, res.status)
163
+ assert_equal(401, res.previous.status)
164
+ assert_equal(nil, res.previous.previous)
165
+ ensure
166
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
167
+ end
168
+ end
169
+
170
+ def test_BASIC_auth_force
171
+ c = HTTPClient.new
172
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
173
+ begin
174
+ # WEBrick in ruby 1.8.7 uses 'BASIC' instead of 'Basic'
175
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
176
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
177
+ #
178
+ c.force_basic_auth = true
179
+ c.debug_dev = str = ''
180
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
181
+ assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth"))
182
+ assert_equal('Authorization: Basic YWRtaW46YWRtaW4='.upcase, str.split(/\r?\n/)[5].upcase)
183
+ ensure
184
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
185
+ end
186
+ end
187
+
188
+ def test_BASIC_auth_async
189
+ # async methods don't issure retry call so for successful authentication you need to set force_basic_auth flag
190
+ c = HTTPClient.new(:force_basic_auth => true)
191
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
192
+ begin
193
+ # WEBrick in ruby 1.8.7 uses 'BASIC' instead of 'Basic'
194
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
195
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
196
+ #
197
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
198
+ conn = c.get_async("http://localhost:#{serverport}/basic_auth")
199
+ assert_equal('basic_auth OK', conn.pop.body.read)
200
+ ensure
201
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
202
+ end
203
+ end
204
+
205
+ def test_BASIC_auth_nil_uri
206
+ c = HTTPClient.new
207
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
208
+ begin
209
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
210
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
211
+ c.set_auth(nil, 'admin', 'admin')
212
+ assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth"))
213
+ ensure
214
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
215
+ end
216
+ end
217
+
218
+ # To work this test consistently on CRuby you can to add 'Thread.pass' in
219
+ # @challenge iteration at BasicAuth#get like;
220
+ #
221
+ # return nil unless @challenge.find { |uri, ok|
222
+ # Thread.pass
223
+ # Util.uri_part_of(target_uri, uri) and ok
224
+ # }
225
+ def test_BASIC_auth_multi_thread
226
+ c = HTTPClient.new
227
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
228
+ begin
229
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
230
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
231
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
232
+
233
+ 100.times.map { |idx|
234
+ Thread.new(idx) { |idx2|
235
+ Thread.abort_on_exception = true
236
+ Thread.pass
237
+ c.get("http://localhost:#{serverport}/basic_auth?#{idx2}")
238
+ }
239
+ }.map { |t|
240
+ t.join
241
+ }
242
+ ensure
243
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
244
+ end
245
+ end
246
+
247
+ def test_basic_auth_reuses_credentials
248
+ c = HTTPClient.new
249
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
250
+ assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth/"))
251
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
252
+ c.debug_dev = str = ''
253
+ c.get_content("http://localhost:#{serverport}/basic_auth/sub/dir/")
254
+ assert_match(/Authorization: Basic YWRtaW46YWRtaW4=/, str)
255
+ end
256
+
257
+ def test_digest_auth
258
+ c = HTTPClient.new
259
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
260
+ assert_equal('digest_auth OK', c.get_content("http://localhost:#{serverport}/digest_auth"))
261
+ end
262
+
263
+ def test_digest_auth_reuses_credentials
264
+ c = HTTPClient.new
265
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
266
+ assert_equal('digest_auth OK', c.get_content("http://localhost:#{serverport}/digest_auth/"))
267
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
268
+ c.debug_dev = str = ''
269
+ c.get_content("http://localhost:#{serverport}/digest_auth/sub/dir/")
270
+ assert_match(/Authorization: Digest/, str)
271
+ end
272
+
273
+ def test_digest_auth_with_block
274
+ c = HTTPClient.new
275
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
276
+ called = false
277
+ c.get_content("http://localhost:#{serverport}/digest_auth") do |str|
278
+ assert_equal('digest_auth OK', str)
279
+ called = true
280
+ end
281
+ assert(called)
282
+ #
283
+ called = false
284
+ c.get("http://localhost:#{serverport}/digest_auth") do |str|
285
+ assert_equal('digest_auth OK', str)
286
+ called = true
287
+ end
288
+ assert(called)
289
+ end
290
+
291
+ def test_digest_auth_with_post_io
292
+ c = HTTPClient.new
293
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
294
+ post_body = StringIO.new("1234567890")
295
+ assert_equal('1234567890', c.post("http://localhost:#{serverport}/digest_auth", post_body).header['x-query'][0])
296
+ #
297
+ post_body = StringIO.new("1234567890")
298
+ post_body.read(5)
299
+ assert_equal('67890', c.post("http://localhost:#{serverport}/digest_auth", post_body).header['x-query'][0])
300
+ end
301
+
302
+ def test_digest_auth_with_querystring
303
+ c = HTTPClient.new
304
+ c.debug_dev = STDERR if $DEBUG
305
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
306
+ assert_equal('digest_auth OKbar=baz', c.get_content("http://localhost:#{serverport}/digest_auth/foo?bar=baz"))
307
+ end
308
+
309
+ def test_perfer_digest
310
+ c = HTTPClient.new
311
+ c.set_auth('http://example.com/', 'admin', 'admin')
312
+ c.test_loopback_http_response << "HTTP/1.0 401 Unauthorized\nWWW-Authenticate: Basic realm=\"foo\"\nWWW-Authenticate: Digest realm=\"foo\", nonce=\"nonce\", stale=false\nContent-Length: 2\n\nNG"
313
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
314
+ c.debug_dev = str = ''
315
+ c.get_content('http://example.com/')
316
+ assert_match(/^Authorization: Digest/, str)
317
+ end
318
+
319
+ def test_digest_sess_auth
320
+ c = HTTPClient.new
321
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
322
+ assert_equal('digest_sess_auth OK', c.get_content("http://localhost:#{serverport}/digest_sess_auth"))
323
+ end
324
+
325
+ def test_proxy_auth
326
+ c = HTTPClient.new
327
+ c.set_proxy_auth('admin', 'admin')
328
+ c.test_loopback_http_response << "HTTP/1.0 407 Unauthorized\nProxy-Authenticate: Basic realm=\"foo\"\nContent-Length: 2\n\nNG"
329
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
330
+ c.debug_dev = str = ''
331
+ c.get_content('http://example.com/')
332
+ assert_match(/Proxy-Authorization: Basic YWRtaW46YWRtaW4=/, str)
333
+ end
334
+
335
+ def test_proxy_auth_force
336
+ c = HTTPClient.new
337
+ c.set_proxy_auth('admin', 'admin')
338
+ c.force_basic_auth = true
339
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
340
+ c.debug_dev = str = ''
341
+ c.get_content('http://example.com/')
342
+ assert_match(/Proxy-Authorization: Basic YWRtaW46YWRtaW4=/, str)
343
+ end
344
+
345
+ def test_proxy_auth_reuses_credentials
346
+ c = HTTPClient.new
347
+ c.set_proxy_auth('admin', 'admin')
348
+ c.test_loopback_http_response << "HTTP/1.0 407 Unauthorized\nProxy-Authenticate: Basic realm=\"foo\"\nContent-Length: 2\n\nNG"
349
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
350
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
351
+ c.get_content('http://www1.example.com/')
352
+ c.debug_dev = str = ''
353
+ c.get_content('http://www2.example.com/')
354
+ assert_match(/Proxy-Authorization: Basic YWRtaW46YWRtaW4=/, str)
355
+ end
356
+
357
+ def test_digest_proxy_auth_loop
358
+ c = HTTPClient.new
359
+ c.set_proxy_auth('admin', 'admin')
360
+ c.test_loopback_http_response << "HTTP/1.0 407 Unauthorized\nProxy-Authenticate: Digest realm=\"foo\", nonce=\"nonce\", stale=false\nContent-Length: 2\n\nNG"
361
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
362
+ md5 = Digest::MD5.new
363
+ ha1 = md5.hexdigest("admin:foo:admin")
364
+ ha2 = md5.hexdigest("GET:/")
365
+ response = md5.hexdigest("#{ha1}:nonce:#{ha2}")
366
+ c.debug_dev = str = ''
367
+ c.get_content('http://example.com/')
368
+ assert_match(/Proxy-Authorization: Digest/, str)
369
+ assert_match(%r"response=\"#{response}\"", str)
370
+ end
371
+
372
+ def test_digest_proxy_auth
373
+ c=HTTPClient.new("http://localhost:#{proxyport}/")
374
+ c.set_proxy_auth('admin', 'admin')
375
+ c.set_auth("http://127.0.0.1:#{serverport}/", 'admin', 'admin')
376
+ assert_equal('basic_auth OK', c.get_content("http://127.0.0.1:#{serverport}/basic_auth"))
377
+ end
378
+
379
+ def test_digest_proxy_invalid_auth
380
+ c=HTTPClient.new("http://localhost:#{proxyport}/")
381
+ c.set_proxy_auth('admin', 'wrong')
382
+ c.set_auth("http://127.0.0.1:#{serverport}/", 'admin', 'admin')
383
+ assert_raises(HTTPClient::BadResponseError) do
384
+ c.get_content("http://127.0.0.1:#{serverport}/basic_auth")
385
+ end
386
+ end
387
+
388
+ def test_prefer_digest_to_basic_proxy_auth
389
+ c = HTTPClient.new
390
+ c.set_proxy_auth('admin', 'admin')
391
+ c.test_loopback_http_response << "HTTP/1.0 407 Unauthorized\nProxy-Authenticate: Digest realm=\"foo\", nonce=\"nonce\", stale=false\nProxy-Authenticate: Basic realm=\"bar\"\nContent-Length: 2\n\nNG"
392
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
393
+ md5 = Digest::MD5.new
394
+ ha1 = md5.hexdigest("admin:foo:admin")
395
+ ha2 = md5.hexdigest("GET:/")
396
+ response = md5.hexdigest("#{ha1}:nonce:#{ha2}")
397
+ c.debug_dev = str = ''
398
+ c.get_content('http://example.com/')
399
+ assert_match(/Proxy-Authorization: Digest/, str)
400
+ assert_match(%r"response=\"#{response}\"", str)
401
+ end
402
+
403
+ def test_digest_proxy_auth_reuses_credentials
404
+ c = HTTPClient.new
405
+ c.set_proxy_auth('admin', 'admin')
406
+ c.test_loopback_http_response << "HTTP/1.0 407 Unauthorized\nProxy-Authenticate: Digest realm=\"foo\", nonce=\"nonce\", stale=false\nContent-Length: 2\n\nNG"
407
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
408
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
409
+ md5 = Digest::MD5.new
410
+ ha1 = md5.hexdigest("admin:foo:admin")
411
+ ha2 = md5.hexdigest("GET:/")
412
+ response = md5.hexdigest("#{ha1}:nonce:#{ha2}")
413
+ c.get_content('http://www1.example.com/')
414
+ c.debug_dev = str = ''
415
+ c.get_content('http://www2.example.com/')
416
+ assert_match(/Proxy-Authorization: Digest/, str)
417
+ assert_match(%r"response=\"#{response}\"", str)
418
+ end
419
+
420
+ def test_oauth
421
+ c = HTTPClient.new
422
+ config = HTTPClient::OAuth::Config.new(
423
+ :realm => 'http://photos.example.net/',
424
+ :consumer_key => 'dpf43f3p2l4k3l03',
425
+ :consumer_secret => 'kd94hf93k423kf44',
426
+ :token => 'nnch734d00sl2jdk',
427
+ :secret => 'pfkkdhi9sl3r4s00',
428
+ :version => '1.0',
429
+ :signature_method => 'HMAC-SHA1'
430
+ )
431
+ config.debug_timestamp = '1191242096'
432
+ config.debug_nonce = 'kllo9940pd9333jh'
433
+ c.www_auth.oauth.set_config('http://photos.example.net/', config)
434
+ c.www_auth.oauth.challenge('http://photos.example.net/')
435
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
436
+ c.debug_dev = str = ''
437
+ c.get_content('http://photos.example.net/photos', [[:file, 'vacation.jpg'], [:size, 'original']])
438
+ assert(str.index(%q(GET /photos?file=vacation.jpg&size=original)))
439
+ assert(str.index(%q(Authorization: OAuth realm="http://photos.example.net/", oauth_consumer_key="dpf43f3p2l4k3l03", oauth_nonce="kllo9940pd9333jh", oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1191242096", oauth_token="nnch734d00sl2jdk", oauth_version="1.0")))
440
+ #
441
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
442
+ c.debug_dev = str = ''
443
+ c.get_content('http://photos.example.net/photos?file=vacation.jpg&size=original')
444
+ assert(str.index(%q(GET /photos?file=vacation.jpg&size=original)))
445
+ assert(str.index(%q(Authorization: OAuth realm="http://photos.example.net/", oauth_consumer_key="dpf43f3p2l4k3l03", oauth_nonce="kllo9940pd9333jh", oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1191242096", oauth_token="nnch734d00sl2jdk", oauth_version="1.0")))
446
+ #
447
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
448
+ c.debug_dev = str = ''
449
+ c.post_content('http://photos.example.net/photos', [[:file, 'vacation.jpg'], [:size, 'original']])
450
+ assert(str.index(%q(POST /photos)))
451
+ assert(str.index(%q(Authorization: OAuth realm="http://photos.example.net/", oauth_consumer_key="dpf43f3p2l4k3l03", oauth_nonce="kllo9940pd9333jh", oauth_signature="wPkvxykrw%2BBTdCcGqKr%2B3I%2BPsiM%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1191242096", oauth_token="nnch734d00sl2jdk", oauth_version="1.0")))
452
+ end
453
+
454
+ def test_basic_auth_post_with_multipart
455
+ retry_times = 0
456
+ begin
457
+ c = HTTPClient.new
458
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
459
+ File.open(__FILE__) do |f|
460
+ # read 'f' twice for authorization negotiation
461
+ assert_equal('basic_auth OK', c.post("http://localhost:#{serverport}/basic_auth", :file => f).content)
462
+ end
463
+ rescue Errno::ECONNRESET, HTTPClient::KeepAliveDisconnected
464
+ # TODO: WEBrick server returns ECONNRESET/EPIPE before sending Unauthorized response to client?
465
+ raise if retry_times > 5
466
+ retry_times += 1
467
+ sleep 1
468
+ retry
469
+ end
470
+ end
471
+
472
+ def test_negotiate_and_basic
473
+ c = HTTPClient.new
474
+ c.test_loopback_http_response << %Q(HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: NTLM\r\nWWW-Authenticate: Basic realm="foo"\r\nConnection: Keep-Alive\r\nContent-Length: 0\r\n\r\n)
475
+ c.test_loopback_http_response << %Q(HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: NTLM TlRMTVNTUAACAAAAAAAAACgAAAABAAAAAAAAAAAAAAA=\r\nConnection: Keep-Alive\r\nContent-Length: 0\r\n\r\n)
476
+ c.test_loopback_http_response << %Q(HTTP/1.0 200 OK\r\nConnection: Keep-Alive\r\nContent-Length: 1\r\n\r\na)
477
+ c.test_loopback_http_response << %Q(HTTP/1.0 200 OK\r\nConnection: Keep-Alive\r\nContent-Length: 1\r\n\r\nb)
478
+ c.debug_dev = str = ''
479
+ c.set_auth('http://www.example.org/', 'admin', 'admin')
480
+ # Do NTLM negotiation
481
+ c.get('http://www.example.org/foo')
482
+ # BasicAuth authenticator should not respond to it because NTLM
483
+ # negotiation has been finished.
484
+ assert_match(%r(Authorization: NTLM), str)
485
+ assert_not_match(%r(Authorization: Basic), str)
486
+ # ditto for other resource that is protected with NTLM
487
+ c.debug_dev = str = ''
488
+ c.get('http://www.example.org/foo/subdir')
489
+ assert_not_match(%r(Authorization: NTLM), str)
490
+ assert_not_match(%r(Authorization: Basic), str)
491
+ end
492
+ end