httpclient 2.1.5 → 2.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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