rs-httpclient 3.0.0.beta1

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 (78) 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/cookie.rb +1 -0
  7. data/lib/http-access2/http.rb +1 -0
  8. data/lib/http-access2.rb +55 -0
  9. data/lib/httpclient/auth.rb +924 -0
  10. data/lib/httpclient/cacert.pem +3252 -0
  11. data/lib/httpclient/cacert1024.pem +3866 -0
  12. data/lib/httpclient/connection.rb +88 -0
  13. data/lib/httpclient/cookie.rb +220 -0
  14. data/lib/httpclient/http.rb +1082 -0
  15. data/lib/httpclient/include_client.rb +85 -0
  16. data/lib/httpclient/jruby_ssl_socket.rb +594 -0
  17. data/lib/httpclient/session.rb +960 -0
  18. data/lib/httpclient/ssl_config.rb +452 -0
  19. data/lib/httpclient/ssl_socket.rb +150 -0
  20. data/lib/httpclient/timeout.rb +140 -0
  21. data/lib/httpclient/util.rb +222 -0
  22. data/lib/httpclient/version.rb +3 -0
  23. data/lib/httpclient/webagent-cookie.rb +459 -0
  24. data/lib/httpclient.rb +1331 -0
  25. data/lib/jsonclient.rb +66 -0
  26. data/lib/oauthclient.rb +111 -0
  27. data/sample/async.rb +8 -0
  28. data/sample/auth.rb +11 -0
  29. data/sample/cookie.rb +18 -0
  30. data/sample/dav.rb +103 -0
  31. data/sample/generate_test_keys.rb +99 -0
  32. data/sample/howto.rb +49 -0
  33. data/sample/jsonclient.rb +67 -0
  34. data/sample/oauth_buzz.rb +57 -0
  35. data/sample/oauth_friendfeed.rb +59 -0
  36. data/sample/oauth_twitter.rb +61 -0
  37. data/sample/ssl/0cert.pem +22 -0
  38. data/sample/ssl/0key.pem +30 -0
  39. data/sample/ssl/1000cert.pem +19 -0
  40. data/sample/ssl/1000key.pem +18 -0
  41. data/sample/ssl/htdocs/index.html +10 -0
  42. data/sample/ssl/ssl_client.rb +22 -0
  43. data/sample/ssl/webrick_httpsd.rb +29 -0
  44. data/sample/stream.rb +21 -0
  45. data/sample/thread.rb +27 -0
  46. data/sample/wcat.rb +21 -0
  47. data/test/ca-chain.pem +40 -0
  48. data/test/ca.cert +20 -0
  49. data/test/ca.key +27 -0
  50. data/test/ca.srl +1 -0
  51. data/test/client-pass.key +30 -0
  52. data/test/client.cert +20 -0
  53. data/test/client.key +27 -0
  54. data/test/fixtures/verify.alt.cert +20 -0
  55. data/test/fixtures/verify.foo.cert +20 -0
  56. data/test/fixtures/verify.key +27 -0
  57. data/test/fixtures/verify.localhost.cert +20 -0
  58. data/test/helper.rb +129 -0
  59. data/test/htdigest +1 -0
  60. data/test/htpasswd +2 -0
  61. data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
  62. data/test/runner.rb +2 -0
  63. data/test/server.cert +20 -0
  64. data/test/server.key +27 -0
  65. data/test/sslsvr.rb +65 -0
  66. data/test/subca.cert +20 -0
  67. data/test/subca.key +27 -0
  68. data/test/subca.srl +1 -0
  69. data/test/test_auth.rb +496 -0
  70. data/test/test_cookie.rb +309 -0
  71. data/test/test_hexdump.rb +13 -0
  72. data/test/test_http-access2.rb +516 -0
  73. data/test/test_httpclient.rb +2144 -0
  74. data/test/test_include_client.rb +52 -0
  75. data/test/test_jsonclient.rb +98 -0
  76. data/test/test_ssl.rb +522 -0
  77. data/test/test_webagent-cookie.rb +465 -0
  78. metadata +130 -0
data/test/test_auth.rb ADDED
@@ -0,0 +1,496 @@
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
+
131
+ def value
132
+ @value ||= ""
133
+ end
134
+ end
135
+ end
136
+ end
137
+ def test_ntlm_auth
138
+ c = HTTPClient.new
139
+ c.set_auth("http://localhost:#{serverport}/ntlm_auth", 'admin', 'admin')
140
+ assert_equal('ntlm_auth OK', c.get_content("http://localhost:#{serverport}/ntlm_auth"))
141
+ end
142
+
143
+ def test_basic_auth
144
+ c = HTTPClient.new
145
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
146
+ assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth"))
147
+ end
148
+
149
+ def test_basic_auth_compat
150
+ c = HTTPClient.new
151
+ c.set_basic_auth("http://localhost:#{serverport}/", 'admin', 'admin')
152
+ assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth"))
153
+ end
154
+
155
+ def test_BASIC_auth
156
+ c = HTTPClient.new
157
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
158
+ begin
159
+ # WEBrick in ruby 1.8.7 uses 'BASIC' instead of 'Basic'
160
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
161
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
162
+ #
163
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
164
+ res = c.get("http://localhost:#{serverport}/basic_auth")
165
+ assert_equal('basic_auth OK', res.content)
166
+ assert_equal(200, res.status)
167
+ assert_equal(401, res.previous.status)
168
+ assert_equal(nil, res.previous.previous)
169
+ ensure
170
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
171
+ end
172
+ end
173
+
174
+ def test_BASIC_auth_force
175
+ c = HTTPClient.new
176
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
177
+ begin
178
+ # WEBrick in ruby 1.8.7 uses 'BASIC' instead of 'Basic'
179
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
180
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
181
+ #
182
+ c.force_basic_auth = true
183
+ c.debug_dev = str = ''.dup
184
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
185
+ assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth"))
186
+ assert_equal('Authorization: Basic YWRtaW46YWRtaW4='.upcase, str.split(/\r?\n/)[5].upcase)
187
+ ensure
188
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
189
+ end
190
+ end
191
+
192
+ def test_BASIC_auth_async
193
+ # async methods don't issure retry call so for successful authentication you need to set force_basic_auth flag
194
+ c = HTTPClient.new(:force_basic_auth => true)
195
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
196
+ begin
197
+ # WEBrick in ruby 1.8.7 uses 'BASIC' instead of 'Basic'
198
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
199
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
200
+ #
201
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
202
+ conn = c.get_async("http://localhost:#{serverport}/basic_auth")
203
+ assert_equal('basic_auth OK', conn.pop.body.read)
204
+ ensure
205
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
206
+ end
207
+ end
208
+
209
+ def test_BASIC_auth_nil_uri
210
+ c = HTTPClient.new
211
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
212
+ begin
213
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
214
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
215
+ c.set_auth(nil, 'admin', 'admin')
216
+ assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth"))
217
+ ensure
218
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
219
+ end
220
+ end
221
+
222
+ # To work this test consistently on CRuby you can to add 'Thread.pass' in
223
+ # @challenge iteration at BasicAuth#get like;
224
+ #
225
+ # return nil unless @challenge.find { |uri, ok|
226
+ # Thread.pass
227
+ # Util.uri_part_of(target_uri, uri) and ok
228
+ # }
229
+ def test_BASIC_auth_multi_thread
230
+ c = HTTPClient.new
231
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
232
+ begin
233
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
234
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
235
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
236
+
237
+ 20.times.map { |idx|
238
+ Thread.new(idx) { |idx2|
239
+ Thread.abort_on_exception = true
240
+ Thread.pass
241
+ assert_equal('basic_auth OK', c.get("http://localhost:#{serverport}/basic_auth?#{idx2}").body)
242
+ }
243
+ }.map { |t|
244
+ t.join
245
+ }
246
+ ensure
247
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
248
+ end
249
+ end
250
+
251
+ def test_basic_auth_reuses_credentials
252
+ c = HTTPClient.new
253
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
254
+ assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth/"))
255
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
256
+ c.debug_dev = str = ''.dup
257
+ c.get_content("http://localhost:#{serverport}/basic_auth/sub/dir/")
258
+ assert_match(/Authorization: Basic YWRtaW46YWRtaW4=/, str)
259
+ end
260
+
261
+ def test_digest_auth
262
+ c = HTTPClient.new
263
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
264
+ assert_equal('digest_auth OK', c.get_content("http://localhost:#{serverport}/digest_auth"))
265
+ end
266
+
267
+ def test_digest_auth_reuses_credentials
268
+ c = HTTPClient.new
269
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
270
+ assert_equal('digest_auth OK', c.get_content("http://localhost:#{serverport}/digest_auth/"))
271
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
272
+ c.debug_dev = str = ''.dup
273
+ c.get_content("http://localhost:#{serverport}/digest_auth/sub/dir/")
274
+ assert_match(/Authorization: Digest/, str)
275
+ end
276
+
277
+ def test_digest_auth_with_block
278
+ c = HTTPClient.new
279
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
280
+ called = false
281
+ c.get_content("http://localhost:#{serverport}/digest_auth") do |str|
282
+ assert_equal('digest_auth OK', str)
283
+ called = true
284
+ end
285
+ assert(called)
286
+ #
287
+ called = false
288
+ c.get("http://localhost:#{serverport}/digest_auth") do |str|
289
+ assert_equal('digest_auth OK', str)
290
+ called = true
291
+ end
292
+ assert(called)
293
+ end
294
+
295
+ def test_digest_auth_with_post_io
296
+ c = HTTPClient.new
297
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
298
+ post_body = StringIO.new("1234567890")
299
+ assert_equal('1234567890', c.post("http://localhost:#{serverport}/digest_auth", post_body).header['x-query'][0])
300
+ #
301
+ post_body = StringIO.new("1234567890")
302
+ post_body.read(5)
303
+ assert_equal('67890', c.post("http://localhost:#{serverport}/digest_auth", post_body).header['x-query'][0])
304
+ end
305
+
306
+ def test_digest_auth_with_querystring
307
+ c = HTTPClient.new
308
+ c.debug_dev = STDERR if $DEBUG
309
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
310
+ assert_equal('digest_auth OKbar=baz', c.get_content("http://localhost:#{serverport}/digest_auth/foo?bar=baz"))
311
+ end
312
+
313
+ def test_perfer_digest
314
+ c = HTTPClient.new
315
+ c.set_auth('http://example.com/', 'admin', 'admin')
316
+ 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"
317
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
318
+ c.debug_dev = str = ''.dup
319
+ c.get_content('http://example.com/')
320
+ assert_match(/^Authorization: Digest/, str)
321
+ end
322
+
323
+ def test_digest_sess_auth
324
+ c = HTTPClient.new
325
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
326
+ assert_equal('digest_sess_auth OK', c.get_content("http://localhost:#{serverport}/digest_sess_auth"))
327
+ end
328
+
329
+ def test_proxy_auth
330
+ c = HTTPClient.new
331
+ c.set_proxy_auth('admin', 'admin')
332
+ c.test_loopback_http_response << "HTTP/1.0 407 Unauthorized\nProxy-Authenticate: Basic realm=\"foo\"\nContent-Length: 2\n\nNG"
333
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
334
+ c.debug_dev = str = ''.dup
335
+ c.get_content('http://example.com/')
336
+ assert_match(/Proxy-Authorization: Basic YWRtaW46YWRtaW4=/, str)
337
+ end
338
+
339
+ def test_proxy_auth_force
340
+ c = HTTPClient.new
341
+ c.set_proxy_auth('admin', 'admin')
342
+ c.force_basic_auth = true
343
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
344
+ c.debug_dev = str = ''.dup
345
+ c.get_content('http://example.com/')
346
+ assert_match(/Proxy-Authorization: Basic YWRtaW46YWRtaW4=/, str)
347
+ end
348
+
349
+ def test_proxy_auth_reuses_credentials
350
+ c = HTTPClient.new
351
+ c.set_proxy_auth('admin', 'admin')
352
+ c.test_loopback_http_response << "HTTP/1.0 407 Unauthorized\nProxy-Authenticate: Basic realm=\"foo\"\nContent-Length: 2\n\nNG"
353
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
354
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
355
+ c.get_content('http://www1.example.com/')
356
+ c.debug_dev = str = ''.dup
357
+ c.get_content('http://www2.example.com/')
358
+ assert_match(/Proxy-Authorization: Basic YWRtaW46YWRtaW4=/, str)
359
+ end
360
+
361
+ def test_digest_proxy_auth_loop
362
+ c = HTTPClient.new
363
+ c.set_proxy_auth('admin', 'admin')
364
+ c.test_loopback_http_response << "HTTP/1.0 407 Unauthorized\nProxy-Authenticate: Digest realm=\"foo\", nonce=\"nonce\", stale=false\nContent-Length: 2\n\nNG"
365
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
366
+ md5 = Digest::MD5.new
367
+ ha1 = md5.hexdigest("admin:foo:admin")
368
+ ha2 = md5.hexdigest("GET:/")
369
+ response = md5.hexdigest("#{ha1}:nonce:#{ha2}")
370
+ c.debug_dev = str = ''.dup
371
+ c.get_content('http://example.com/')
372
+ assert_match(/Proxy-Authorization: Digest/, str)
373
+ assert_match(%r"response=\"#{response}\"", str)
374
+ end
375
+
376
+ def test_digest_proxy_auth
377
+ c=HTTPClient.new("http://localhost:#{proxyport}/")
378
+ c.set_proxy_auth('admin', 'admin')
379
+ c.set_auth("http://127.0.0.1:#{serverport}/", 'admin', 'admin')
380
+ assert_equal('basic_auth OK', c.get_content("http://127.0.0.1:#{serverport}/basic_auth"))
381
+ end
382
+
383
+ def test_digest_proxy_invalid_auth
384
+ c=HTTPClient.new("http://localhost:#{proxyport}/")
385
+ c.set_proxy_auth('admin', 'wrong')
386
+ c.set_auth("http://127.0.0.1:#{serverport}/", 'admin', 'admin')
387
+ assert_raises(HTTPClient::BadResponseError) do
388
+ c.get_content("http://127.0.0.1:#{serverport}/basic_auth")
389
+ end
390
+ end
391
+
392
+ def test_prefer_digest_to_basic_proxy_auth
393
+ c = HTTPClient.new
394
+ c.set_proxy_auth('admin', 'admin')
395
+ 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"
396
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
397
+ md5 = Digest::MD5.new
398
+ ha1 = md5.hexdigest("admin:foo:admin")
399
+ ha2 = md5.hexdigest("GET:/")
400
+ response = md5.hexdigest("#{ha1}:nonce:#{ha2}")
401
+ c.debug_dev = str = ''.dup
402
+ c.get_content('http://example.com/')
403
+ assert_match(/Proxy-Authorization: Digest/, str)
404
+ assert_match(%r"response=\"#{response}\"", str)
405
+ end
406
+
407
+ def test_digest_proxy_auth_reuses_credentials
408
+ c = HTTPClient.new
409
+ c.set_proxy_auth('admin', 'admin')
410
+ c.test_loopback_http_response << "HTTP/1.0 407 Unauthorized\nProxy-Authenticate: Digest realm=\"foo\", nonce=\"nonce\", stale=false\nContent-Length: 2\n\nNG"
411
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
412
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
413
+ md5 = Digest::MD5.new
414
+ ha1 = md5.hexdigest("admin:foo:admin")
415
+ ha2 = md5.hexdigest("GET:/")
416
+ response = md5.hexdigest("#{ha1}:nonce:#{ha2}")
417
+ c.get_content('http://www1.example.com/')
418
+ c.debug_dev = str = ''.dup
419
+ c.get_content('http://www2.example.com/')
420
+ assert_match(/Proxy-Authorization: Digest/, str)
421
+ assert_match(%r"response=\"#{response}\"", str)
422
+ end
423
+
424
+ def test_oauth
425
+ c = HTTPClient.new
426
+ config = HTTPClient::OAuth::Config.new(
427
+ :realm => 'http://photos.example.net/',
428
+ :consumer_key => 'dpf43f3p2l4k3l03',
429
+ :consumer_secret => 'kd94hf93k423kf44',
430
+ :token => 'nnch734d00sl2jdk',
431
+ :secret => 'pfkkdhi9sl3r4s00',
432
+ :version => '1.0',
433
+ :signature_method => 'HMAC-SHA1'
434
+ )
435
+ config.debug_timestamp = '1191242096'
436
+ config.debug_nonce = 'kllo9940pd9333jh'
437
+ c.www_auth.oauth.set_config('http://photos.example.net/', config)
438
+ c.www_auth.oauth.challenge('http://photos.example.net/')
439
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
440
+ c.debug_dev = str = ''.dup
441
+ c.get_content('http://photos.example.net/photos', [[:file, 'vacation.jpg'], [:size, 'original']])
442
+ assert(str.index(%q(GET /photos?file=vacation.jpg&size=original)))
443
+ 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")))
444
+ #
445
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
446
+ c.debug_dev = str = ''.dup
447
+ c.get_content('http://photos.example.net/photos?file=vacation.jpg&size=original')
448
+ assert(str.index(%q(GET /photos?file=vacation.jpg&size=original)))
449
+ 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")))
450
+ #
451
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
452
+ c.debug_dev = str = ''.dup
453
+ c.post_content('http://photos.example.net/photos', [[:file, 'vacation.jpg'], [:size, 'original']])
454
+ assert(str.index(%q(POST /photos)))
455
+ 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")))
456
+ end
457
+
458
+ def test_basic_auth_post_with_multipart
459
+ retry_times = 0
460
+ begin
461
+ c = HTTPClient.new
462
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
463
+ File.open(__FILE__) do |f|
464
+ # read 'f' twice for authorization negotiation
465
+ assert_equal('basic_auth OK', c.post("http://localhost:#{serverport}/basic_auth", :file => f).content)
466
+ end
467
+ rescue Errno::ECONNRESET, HTTPClient::KeepAliveDisconnected
468
+ # TODO: WEBrick server returns ECONNRESET/EPIPE before sending Unauthorized response to client?
469
+ raise if retry_times > 5
470
+ retry_times += 1
471
+ sleep 1
472
+ retry
473
+ end
474
+ end
475
+
476
+ def test_negotiate_and_basic
477
+ c = HTTPClient.new
478
+ 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)
479
+ 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)
480
+ c.test_loopback_http_response << %Q(HTTP/1.0 200 OK\r\nConnection: Keep-Alive\r\nContent-Length: 1\r\n\r\na)
481
+ c.test_loopback_http_response << %Q(HTTP/1.0 200 OK\r\nConnection: Keep-Alive\r\nContent-Length: 1\r\n\r\nb)
482
+ c.debug_dev = str = ''.dup
483
+ c.set_auth('http://www.example.org/', 'admin', 'admin')
484
+ # Do NTLM negotiation
485
+ c.get('http://www.example.org/foo')
486
+ # BasicAuth authenticator should not respond to it because NTLM
487
+ # negotiation has been finished.
488
+ assert_match(%r(Authorization: NTLM), str)
489
+ assert_not_match(%r(Authorization: Basic), str)
490
+ # ditto for other resource that is protected with NTLM
491
+ c.debug_dev = str = ''.dup
492
+ c.get('http://www.example.org/foo/subdir')
493
+ assert_not_match(%r(Authorization: NTLM), str)
494
+ assert_not_match(%r(Authorization: Basic), str)
495
+ end
496
+ end