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.
- checksums.yaml +7 -0
- data/README.md +85 -0
- data/bin/httpclient +77 -0
- data/bin/jsonclient +85 -0
- data/lib/hexdump.rb +50 -0
- data/lib/http-access2.rb +6 -4
- data/lib/httpclient/auth.rb +575 -173
- data/lib/httpclient/cacert.pem +3952 -0
- data/lib/httpclient/cacert1024.pem +3866 -0
- data/lib/httpclient/connection.rb +6 -2
- data/lib/httpclient/cookie.rb +162 -504
- data/lib/httpclient/http.rb +334 -119
- data/lib/httpclient/include_client.rb +85 -0
- data/lib/httpclient/jruby_ssl_socket.rb +588 -0
- data/lib/httpclient/session.rb +385 -288
- data/lib/httpclient/ssl_config.rb +195 -155
- data/lib/httpclient/ssl_socket.rb +150 -0
- data/lib/httpclient/timeout.rb +14 -10
- data/lib/httpclient/util.rb +142 -6
- data/lib/httpclient/version.rb +3 -0
- data/lib/httpclient/webagent-cookie.rb +459 -0
- data/lib/httpclient.rb +509 -202
- data/lib/jsonclient.rb +63 -0
- data/lib/oauthclient.rb +111 -0
- data/sample/async.rb +8 -0
- data/sample/auth.rb +11 -0
- data/sample/cookie.rb +18 -0
- data/sample/dav.rb +103 -0
- data/sample/howto.rb +49 -0
- data/sample/jsonclient.rb +67 -0
- data/sample/oauth_buzz.rb +57 -0
- data/sample/oauth_friendfeed.rb +59 -0
- data/sample/oauth_twitter.rb +61 -0
- data/sample/ssl/0cert.pem +22 -0
- data/sample/ssl/0key.pem +30 -0
- data/sample/ssl/1000cert.pem +19 -0
- data/sample/ssl/1000key.pem +18 -0
- data/sample/ssl/htdocs/index.html +10 -0
- data/sample/ssl/ssl_client.rb +22 -0
- data/sample/ssl/webrick_httpsd.rb +29 -0
- data/sample/stream.rb +21 -0
- data/sample/thread.rb +27 -0
- data/sample/wcat.rb +21 -0
- data/test/ca-chain.pem +44 -0
- data/test/ca.cert +23 -0
- data/test/client-pass.key +18 -0
- data/test/client.cert +19 -0
- data/test/client.key +15 -0
- data/test/helper.rb +131 -0
- data/test/htdigest +1 -0
- data/test/htpasswd +2 -0
- data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
- data/test/runner.rb +2 -0
- data/test/server.cert +19 -0
- data/test/server.key +15 -0
- data/test/sslsvr.rb +65 -0
- data/test/subca.cert +21 -0
- data/test/test_auth.rb +492 -0
- data/test/test_cookie.rb +309 -0
- data/test/test_hexdump.rb +14 -0
- data/test/test_http-access2.rb +508 -0
- data/test/test_httpclient.rb +2145 -0
- data/test/test_include_client.rb +52 -0
- data/test/test_jsonclient.rb +80 -0
- data/test/test_ssl.rb +559 -0
- data/test/test_webagent-cookie.rb +465 -0
- metadata +85 -44
- data/lib/httpclient/auth.rb.orig +0 -513
- data/lib/httpclient/cacert.p7s +0 -1579
- data/lib/httpclient.rb.orig +0 -1020
- 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
|