httpclient 2.3.0.1 → 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 +18 -6
- data/bin/jsonclient +85 -0
- data/lib/http-access2.rb +1 -1
- data/lib/httpclient.rb +262 -88
- data/lib/httpclient/auth.rb +269 -244
- data/lib/httpclient/cacert.pem +3952 -0
- data/lib/httpclient/cacert1024.pem +3866 -0
- data/lib/httpclient/connection.rb +1 -1
- data/lib/httpclient/cookie.rb +161 -514
- data/lib/httpclient/http.rb +57 -21
- data/lib/httpclient/include_client.rb +2 -0
- data/lib/httpclient/jruby_ssl_socket.rb +588 -0
- data/lib/httpclient/session.rb +259 -317
- data/lib/httpclient/ssl_config.rb +141 -188
- data/lib/httpclient/ssl_socket.rb +150 -0
- data/lib/httpclient/timeout.rb +1 -1
- data/lib/httpclient/util.rb +62 -1
- data/lib/httpclient/version.rb +1 -1
- data/lib/httpclient/webagent-cookie.rb +459 -0
- data/lib/jsonclient.rb +63 -0
- data/lib/oauthclient.rb +2 -1
- data/sample/jsonclient.rb +67 -0
- data/sample/oauth_twitter.rb +4 -4
- data/test/{ca-chain.cert → ca-chain.pem} +0 -0
- data/test/client-pass.key +18 -0
- data/test/helper.rb +10 -8
- data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
- data/test/test_auth.rb +175 -4
- data/test/test_cookie.rb +147 -243
- data/test/test_http-access2.rb +17 -16
- data/test/test_httpclient.rb +458 -77
- data/test/test_jsonclient.rb +80 -0
- data/test/test_ssl.rb +341 -17
- data/test/test_webagent-cookie.rb +465 -0
- metadata +57 -55
- data/README.txt +0 -721
- data/lib/httpclient/cacert.p7s +0 -1858
- data/lib/httpclient/cacert_sha1.p7s +0 -1858
- data/sample/oauth_salesforce_10.rb +0 -63
data/lib/httpclient/auth.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# HTTPClient - HTTP client library.
|
2
|
-
# Copyright (C) 2000-
|
2
|
+
# Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
|
3
3
|
#
|
4
4
|
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
|
5
5
|
# redistribute it and/or modify it under the same terms of Ruby's license;
|
@@ -8,31 +8,14 @@
|
|
8
8
|
|
9
9
|
require 'digest/md5'
|
10
10
|
require 'httpclient/session'
|
11
|
+
require 'mutex_m'
|
11
12
|
|
12
13
|
|
13
14
|
class HTTPClient
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
rescue LoadError
|
19
|
-
NTLMEnabled = false
|
20
|
-
end
|
21
|
-
|
22
|
-
begin
|
23
|
-
require 'win32/sspi'
|
24
|
-
SSPIEnabled = true
|
25
|
-
rescue LoadError
|
26
|
-
SSPIEnabled = false
|
27
|
-
end
|
28
|
-
|
29
|
-
begin
|
30
|
-
require 'gssapi'
|
31
|
-
GSSAPIEnabled = true
|
32
|
-
rescue LoadError
|
33
|
-
GSSAPIEnabled = false
|
34
|
-
end
|
35
|
-
|
16
|
+
NTLMEnabled = false
|
17
|
+
SSPIEnabled = false
|
18
|
+
GSSAPIEnabled = false
|
36
19
|
|
37
20
|
# Common abstract class for authentication filter.
|
38
21
|
#
|
@@ -112,6 +95,13 @@ class HTTPClient
|
|
112
95
|
@authenticator.each do |auth|
|
113
96
|
next unless auth.set? # hasn't be set, don't use it
|
114
97
|
if cred = auth.get(req)
|
98
|
+
if cred == :skip
|
99
|
+
# some authenticator (NTLM and Negotiate) does not
|
100
|
+
# need to send extra header after authorization. In such case
|
101
|
+
# it should block other authenticators to respond and :skip is
|
102
|
+
# the marker for such case.
|
103
|
+
return
|
104
|
+
end
|
115
105
|
req.header.set('Authorization', auth.scheme + " " + cred)
|
116
106
|
return
|
117
107
|
end
|
@@ -196,6 +186,13 @@ class HTTPClient
|
|
196
186
|
@authenticator.each do |auth|
|
197
187
|
next unless auth.set? # hasn't be set, don't use it
|
198
188
|
if cred = auth.get(req)
|
189
|
+
if cred == :skip
|
190
|
+
# some authenticator (NTLM and Negotiate) does not
|
191
|
+
# need to send extra header after authorization. In such case
|
192
|
+
# it should block other authenticators to respond and :skip is
|
193
|
+
# the marker for such case.
|
194
|
+
return
|
195
|
+
end
|
199
196
|
req.header.set('Proxy-Authorization', auth.scheme + " " + cred)
|
200
197
|
return
|
201
198
|
end
|
@@ -225,44 +222,59 @@ class HTTPClient
|
|
225
222
|
end
|
226
223
|
end
|
227
224
|
|
228
|
-
# Authentication filter
|
229
|
-
|
230
|
-
class BasicAuth
|
225
|
+
# Authentication filter base class.
|
226
|
+
class AuthBase
|
231
227
|
include HTTPClient::Util
|
232
228
|
|
233
229
|
# Authentication scheme.
|
234
230
|
attr_reader :scheme
|
235
231
|
|
236
|
-
|
237
|
-
|
238
|
-
@
|
239
|
-
@set = false
|
240
|
-
@auth = {}
|
241
|
-
@challengeable = {}
|
242
|
-
@scheme = "Basic"
|
232
|
+
def initialize(scheme)
|
233
|
+
@scheme = scheme
|
234
|
+
@challenge = {}
|
243
235
|
end
|
244
236
|
|
245
237
|
# Resets challenge state. Do not send '*Authorization' header until the
|
246
238
|
# server sends '*Authentication' again.
|
247
239
|
def reset_challenge
|
248
|
-
|
240
|
+
synchronize do
|
241
|
+
@challenge.clear
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Authentication filter for handling BasicAuth negotiation.
|
247
|
+
# Used in WWWAuth and ProxyAuth.
|
248
|
+
class BasicAuth < AuthBase
|
249
|
+
include Mutex_m
|
250
|
+
|
251
|
+
# Send Authorization Header without receiving 401
|
252
|
+
attr_accessor :force_auth
|
253
|
+
|
254
|
+
# Creates new BasicAuth filter.
|
255
|
+
def initialize
|
256
|
+
super('Basic')
|
257
|
+
@cred = nil
|
258
|
+
@auth = {}
|
259
|
+
@force_auth = false
|
249
260
|
end
|
250
261
|
|
251
262
|
# Set authentication credential.
|
252
263
|
# uri == nil for generic purpose (allow to use user/password for any URL).
|
253
264
|
def set(uri, user, passwd)
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
265
|
+
synchronize do
|
266
|
+
if uri.nil?
|
267
|
+
@cred = ["#{user}:#{passwd}"].pack('m').tr("\n", '')
|
268
|
+
else
|
269
|
+
uri = Util.uri_dirname(uri)
|
270
|
+
@auth[uri] = ["#{user}:#{passwd}"].pack('m').tr("\n", '')
|
271
|
+
end
|
260
272
|
end
|
261
273
|
end
|
262
274
|
|
263
275
|
# have we marked this as set - ie that it's valid to use in this context?
|
264
276
|
def set?
|
265
|
-
@
|
277
|
+
@cred || @auth.any?
|
266
278
|
end
|
267
279
|
|
268
280
|
# Response handler: returns credential.
|
@@ -271,76 +283,75 @@ class HTTPClient
|
|
271
283
|
# * child page of defined credential
|
272
284
|
def get(req)
|
273
285
|
target_uri = req.header.request_uri
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
Util.
|
286
|
+
synchronize {
|
287
|
+
return nil if !@force_auth and !@challenge.any? { |uri, ok|
|
288
|
+
Util.uri_part_of(target_uri, uri) and ok
|
289
|
+
}
|
290
|
+
return @cred if @cred
|
291
|
+
Util.hash_find_value(@auth) { |uri, cred|
|
292
|
+
Util.uri_part_of(target_uri, uri)
|
293
|
+
}
|
280
294
|
}
|
281
295
|
end
|
282
296
|
|
283
297
|
# Challenge handler: remember URL for response.
|
284
298
|
def challenge(uri, param_str = nil)
|
285
|
-
|
286
|
-
|
299
|
+
synchronize {
|
300
|
+
@challenge[urify(uri)] = true
|
301
|
+
true
|
302
|
+
}
|
287
303
|
end
|
288
304
|
end
|
289
305
|
|
290
306
|
class ProxyBasicAuth < BasicAuth
|
291
|
-
|
292
307
|
def set(uri, user, passwd)
|
293
|
-
|
294
|
-
|
308
|
+
synchronize do
|
309
|
+
@cred = ["#{user}:#{passwd}"].pack('m').tr("\n", '')
|
310
|
+
end
|
295
311
|
end
|
296
312
|
|
297
313
|
def get(req)
|
298
|
-
|
299
|
-
|
300
|
-
|
314
|
+
synchronize {
|
315
|
+
return nil if !@force_auth and !@challenge['challenged']
|
316
|
+
@cred
|
317
|
+
}
|
301
318
|
end
|
302
319
|
|
303
320
|
# Challenge handler: remember URL for response.
|
304
321
|
def challenge(uri, param_str = nil)
|
305
|
-
|
306
|
-
|
322
|
+
synchronize {
|
323
|
+
@challenge['challenged'] = true
|
324
|
+
true
|
325
|
+
}
|
307
326
|
end
|
308
327
|
end
|
309
328
|
|
310
329
|
# Authentication filter for handling DigestAuth negotiation.
|
311
330
|
# Used in WWWAuth.
|
312
|
-
class DigestAuth
|
313
|
-
|
314
|
-
attr_reader :scheme
|
331
|
+
class DigestAuth < AuthBase
|
332
|
+
include Mutex_m
|
315
333
|
|
316
334
|
# Creates new DigestAuth filter.
|
317
335
|
def initialize
|
336
|
+
super('Digest')
|
318
337
|
@auth = {}
|
319
|
-
@challenge = {}
|
320
|
-
@set = false
|
321
338
|
@nonce_count = 0
|
322
|
-
@scheme = "Digest"
|
323
|
-
end
|
324
|
-
|
325
|
-
# Resets challenge state. Do not send '*Authorization' header until the
|
326
|
-
# server sends '*Authentication' again.
|
327
|
-
def reset_challenge
|
328
|
-
@challenge.clear
|
329
339
|
end
|
330
340
|
|
331
341
|
# Set authentication credential.
|
332
342
|
# uri == nil is ignored.
|
333
343
|
def set(uri, user, passwd)
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
344
|
+
synchronize do
|
345
|
+
if uri
|
346
|
+
uri = Util.uri_dirname(uri)
|
347
|
+
@auth[uri] = [user, passwd]
|
348
|
+
end
|
338
349
|
end
|
339
350
|
end
|
340
351
|
|
341
352
|
# have we marked this as set - ie that it's valid to use in this context?
|
342
353
|
def set?
|
343
|
-
@
|
354
|
+
@auth.any?
|
344
355
|
end
|
345
356
|
|
346
357
|
# Response handler: returns credential.
|
@@ -349,21 +360,25 @@ class HTTPClient
|
|
349
360
|
# * child page of defined credential
|
350
361
|
def get(req)
|
351
362
|
target_uri = req.header.request_uri
|
352
|
-
|
353
|
-
Util.
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
Util.
|
363
|
+
synchronize {
|
364
|
+
param = Util.hash_find_value(@challenge) { |uri, v|
|
365
|
+
Util.uri_part_of(target_uri, uri)
|
366
|
+
}
|
367
|
+
return nil unless param
|
368
|
+
user, passwd = Util.hash_find_value(@auth) { |uri, auth_data|
|
369
|
+
Util.uri_part_of(target_uri, uri)
|
370
|
+
}
|
371
|
+
return nil unless user
|
372
|
+
calc_cred(req, user, passwd, param)
|
358
373
|
}
|
359
|
-
return nil unless user
|
360
|
-
calc_cred(req, user, passwd, param)
|
361
374
|
end
|
362
375
|
|
363
376
|
# Challenge handler: remember URL and challenge token for response.
|
364
377
|
def challenge(uri, param_str)
|
365
|
-
|
366
|
-
|
378
|
+
synchronize {
|
379
|
+
@challenge[uri] = parse_challenge_param(param_str)
|
380
|
+
true
|
381
|
+
}
|
367
382
|
end
|
368
383
|
|
369
384
|
private
|
@@ -445,127 +460,131 @@ class HTTPClient
|
|
445
460
|
|
446
461
|
# overrides DigestAuth#set. sets default user name and password. uri is not used.
|
447
462
|
def set(uri, user, passwd)
|
448
|
-
|
449
|
-
|
463
|
+
synchronize do
|
464
|
+
@auth = [user, passwd]
|
465
|
+
end
|
450
466
|
end
|
451
467
|
|
452
468
|
# overrides DigestAuth#get. Uses default user name and password
|
453
469
|
# regardless of target uri if the proxy has required authentication
|
454
470
|
# before
|
455
471
|
def get(req)
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
472
|
+
synchronize {
|
473
|
+
param = @challenge
|
474
|
+
return nil unless param
|
475
|
+
user, passwd = @auth
|
476
|
+
return nil unless user
|
477
|
+
calc_cred(req, user, passwd, param)
|
478
|
+
}
|
462
479
|
end
|
463
480
|
|
464
481
|
def reset_challenge
|
465
|
-
|
482
|
+
synchronize do
|
483
|
+
@challenge = nil
|
484
|
+
end
|
466
485
|
end
|
467
486
|
|
468
487
|
def challenge(uri, param_str)
|
469
|
-
|
470
|
-
|
488
|
+
synchronize {
|
489
|
+
@challenge = parse_challenge_param(param_str)
|
490
|
+
true
|
491
|
+
}
|
471
492
|
end
|
472
|
-
|
473
493
|
end
|
474
494
|
|
475
495
|
# Authentication filter for handling Negotiate/NTLM negotiation.
|
476
496
|
# Used in WWWAuth and ProxyAuth.
|
477
497
|
#
|
478
498
|
# NegotiateAuth depends on 'ruby/ntlm' module.
|
479
|
-
class NegotiateAuth
|
480
|
-
|
481
|
-
|
499
|
+
class NegotiateAuth < AuthBase
|
500
|
+
include Mutex_m
|
501
|
+
|
482
502
|
# NTLM opt for ruby/ntlm. {:ntlmv2 => true} by default.
|
483
503
|
attr_reader :ntlm_opt
|
484
504
|
|
485
505
|
# Creates new NegotiateAuth filter.
|
486
506
|
def initialize(scheme = "Negotiate")
|
507
|
+
super(scheme)
|
487
508
|
@auth = {}
|
488
509
|
@auth_default = nil
|
489
|
-
@challenge = {}
|
490
|
-
@scheme = scheme
|
491
|
-
@set = false
|
492
510
|
@ntlm_opt = {
|
493
511
|
:ntlmv2 => true
|
494
512
|
}
|
495
513
|
end
|
496
514
|
|
497
|
-
# Resets challenge state. Do not send '*Authorization' header until the
|
498
|
-
# server sends '*Authentication' again.
|
499
|
-
def reset_challenge
|
500
|
-
@challenge.clear
|
501
|
-
end
|
502
|
-
|
503
515
|
# Set authentication credential.
|
504
516
|
# uri == nil for generic purpose (allow to use user/password for any URL).
|
505
517
|
def set(uri, user, passwd)
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
518
|
+
synchronize do
|
519
|
+
if uri
|
520
|
+
uri = Util.uri_dirname(uri)
|
521
|
+
@auth[uri] = [user, passwd]
|
522
|
+
else
|
523
|
+
@auth_default = [user, passwd]
|
524
|
+
end
|
512
525
|
end
|
513
526
|
end
|
514
527
|
|
515
528
|
# have we marked this as set - ie that it's valid to use in this context?
|
516
529
|
def set?
|
517
|
-
@
|
530
|
+
@auth_default || @auth.any?
|
518
531
|
end
|
519
532
|
|
520
533
|
# Response handler: returns credential.
|
521
534
|
# See ruby/ntlm for negotiation state transition.
|
522
535
|
def get(req)
|
523
|
-
return nil unless NTLMEnabled
|
524
536
|
target_uri = req.header.request_uri
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
Util.
|
537
|
+
synchronize {
|
538
|
+
_domain_uri, param = @challenge.find { |uri, v|
|
539
|
+
Util.uri_part_of(target_uri, uri)
|
540
|
+
}
|
541
|
+
return nil unless param
|
542
|
+
user, passwd = Util.hash_find_value(@auth) { |uri, auth_data|
|
543
|
+
Util.uri_part_of(target_uri, uri)
|
544
|
+
}
|
545
|
+
unless user
|
546
|
+
user, passwd = @auth_default
|
547
|
+
end
|
548
|
+
return nil unless user
|
549
|
+
Util.try_require('net/ntlm') || return
|
550
|
+
domain = nil
|
551
|
+
domain, user = user.split("\\") if user.index("\\")
|
552
|
+
state = param[:state]
|
553
|
+
authphrase = param[:authphrase]
|
554
|
+
case state
|
555
|
+
when :init
|
556
|
+
t1 = Net::NTLM::Message::Type1.new
|
557
|
+
t1.domain = domain if domain
|
558
|
+
t1.encode64
|
559
|
+
when :response
|
560
|
+
t2 = Net::NTLM::Message.decode64(authphrase)
|
561
|
+
param = {:user => user, :password => passwd}
|
562
|
+
param[:domain] = domain if domain
|
563
|
+
t3 = t2.response(param, @ntlm_opt.dup)
|
564
|
+
@challenge[target_uri][:state] = :done
|
565
|
+
t3.encode64
|
566
|
+
when :done
|
567
|
+
:skip
|
568
|
+
else
|
569
|
+
nil
|
570
|
+
end
|
531
571
|
}
|
532
|
-
unless user
|
533
|
-
user, passwd = @auth_default
|
534
|
-
end
|
535
|
-
return nil unless user
|
536
|
-
domain = nil
|
537
|
-
domain, user = user.split("\\") if user.index("\\")
|
538
|
-
state = param[:state]
|
539
|
-
authphrase = param[:authphrase]
|
540
|
-
case state
|
541
|
-
when :init
|
542
|
-
t1 = Net::NTLM::Message::Type1.new
|
543
|
-
t1.domain = domain if domain
|
544
|
-
return t1.encode64
|
545
|
-
when :response
|
546
|
-
t2 = Net::NTLM::Message.decode64(authphrase)
|
547
|
-
param = {:user => user, :password => passwd}
|
548
|
-
param[:domain] = domain if domain
|
549
|
-
t3 = t2.response(param, @ntlm_opt.dup)
|
550
|
-
@challenge.delete(domain_uri)
|
551
|
-
return t3.encode64
|
552
|
-
end
|
553
|
-
nil
|
554
572
|
end
|
555
573
|
|
556
574
|
# Challenge handler: remember URL and challenge token for response.
|
557
575
|
def challenge(uri, param_str)
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
576
|
+
synchronize {
|
577
|
+
if param_str.nil? or @challenge[uri].nil?
|
578
|
+
c = @challenge[uri] = {}
|
579
|
+
c[:state] = :init
|
580
|
+
c[:authphrase] = ""
|
581
|
+
else
|
582
|
+
c = @challenge[uri]
|
583
|
+
c[:state] = :response
|
584
|
+
c[:authphrase] = param_str
|
585
|
+
end
|
586
|
+
true
|
587
|
+
}
|
569
588
|
end
|
570
589
|
end
|
571
590
|
|
@@ -574,20 +593,12 @@ class HTTPClient
|
|
574
593
|
# Used in ProxyAuth.
|
575
594
|
#
|
576
595
|
# SSPINegotiateAuth depends on 'win32/sspi' module.
|
577
|
-
class SSPINegotiateAuth
|
578
|
-
|
579
|
-
attr_reader :scheme
|
596
|
+
class SSPINegotiateAuth < AuthBase
|
597
|
+
include Mutex_m
|
580
598
|
|
581
599
|
# Creates new SSPINegotiateAuth filter.
|
582
600
|
def initialize
|
583
|
-
|
584
|
-
@scheme = "Negotiate"
|
585
|
-
end
|
586
|
-
|
587
|
-
# Resets challenge state. Do not send '*Authorization' header until the
|
588
|
-
# server sends '*Authentication' again.
|
589
|
-
def reset_challenge
|
590
|
-
@challenge.clear
|
601
|
+
super('Negotiate')
|
591
602
|
end
|
592
603
|
|
593
604
|
# Set authentication credential.
|
@@ -597,58 +608,64 @@ class HTTPClient
|
|
597
608
|
# not supported
|
598
609
|
end
|
599
610
|
|
600
|
-
#
|
611
|
+
# Check always (not effective but it works)
|
601
612
|
def set?
|
602
|
-
|
613
|
+
!@challenge.empty?
|
603
614
|
end
|
604
615
|
|
605
616
|
# Response handler: returns credential.
|
606
617
|
# See win32/sspi for negotiation state transition.
|
607
618
|
def get(req)
|
608
|
-
return nil unless SSPIEnabled || GSSAPIEnabled
|
609
619
|
target_uri = req.header.request_uri
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
#
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
620
|
+
synchronize {
|
621
|
+
domain_uri, param = @challenge.find { |uri, v|
|
622
|
+
Util.uri_part_of(target_uri, uri)
|
623
|
+
}
|
624
|
+
return nil unless param
|
625
|
+
Util.try_require('win32/sspi') || Util.try_require('gssapi') || return
|
626
|
+
state = param[:state]
|
627
|
+
authenticator = param[:authenticator]
|
628
|
+
authphrase = param[:authphrase]
|
629
|
+
case state
|
630
|
+
when :init
|
631
|
+
if defined?(Win32::SSPI)
|
632
|
+
authenticator = param[:authenticator] = Win32::SSPI::NegotiateAuth.new
|
633
|
+
authenticator.get_initial_token(@scheme)
|
634
|
+
else # use GSSAPI
|
635
|
+
authenticator = param[:authenticator] = GSSAPI::Simple.new(domain_uri.host, 'HTTP')
|
636
|
+
# Base64 encode the context token
|
637
|
+
[authenticator.init_context].pack('m').gsub(/\n/,'')
|
638
|
+
end
|
639
|
+
when :response
|
640
|
+
@challenge[target_uri][:state] = :done
|
641
|
+
if defined?(Win32::SSPI)
|
642
|
+
authenticator.complete_authentication(authphrase)
|
643
|
+
else # use GSSAPI
|
644
|
+
authenticator.init_context(authphrase.unpack('m').pop)
|
645
|
+
end
|
646
|
+
when :done
|
647
|
+
:skip
|
648
|
+
else
|
649
|
+
nil
|
633
650
|
end
|
634
|
-
|
635
|
-
nil
|
651
|
+
}
|
636
652
|
end
|
637
653
|
|
638
654
|
# Challenge handler: remember URL and challenge token for response.
|
639
655
|
def challenge(uri, param_str)
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
656
|
+
synchronize {
|
657
|
+
if param_str.nil? or @challenge[uri].nil?
|
658
|
+
c = @challenge[uri] = {}
|
659
|
+
c[:state] = :init
|
660
|
+
c[:authenticator] = nil
|
661
|
+
c[:authphrase] = ""
|
662
|
+
else
|
663
|
+
c = @challenge[uri]
|
664
|
+
c[:state] = :response
|
665
|
+
c[:authphrase] = param_str
|
666
|
+
end
|
667
|
+
true
|
668
|
+
}
|
652
669
|
end
|
653
670
|
end
|
654
671
|
|
@@ -662,11 +679,8 @@ class HTTPClient
|
|
662
679
|
# CAUTION: This impl does NOT support OAuth Request Body Hash spec for now.
|
663
680
|
# http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html
|
664
681
|
#
|
665
|
-
class OAuth
|
666
|
-
include
|
667
|
-
|
668
|
-
# Authentication scheme.
|
669
|
-
attr_reader :scheme
|
682
|
+
class OAuth < AuthBase
|
683
|
+
include Mutex_m
|
670
684
|
|
671
685
|
class Config
|
672
686
|
include HTTPClient::Util
|
@@ -737,20 +751,13 @@ class HTTPClient
|
|
737
751
|
|
738
752
|
# Creates new DigestAuth filter.
|
739
753
|
def initialize
|
754
|
+
super('OAuth')
|
740
755
|
@config = nil # common config
|
741
756
|
@auth = {} # configs for each site
|
742
|
-
@challengeable = {}
|
743
757
|
@nonce_count = 0
|
744
758
|
@signature_handler = {
|
745
759
|
'HMAC-SHA1' => method(:sign_hmac_sha1)
|
746
760
|
}
|
747
|
-
@scheme = "OAuth"
|
748
|
-
end
|
749
|
-
|
750
|
-
# Resets challenge state. Do not send '*Authorization' header until the
|
751
|
-
# server sends '*Authentication' again.
|
752
|
-
def reset_challenge
|
753
|
-
@challengeable.clear
|
754
761
|
end
|
755
762
|
|
756
763
|
# Set authentication credential.
|
@@ -759,31 +766,28 @@ class HTTPClient
|
|
759
766
|
# not supported
|
760
767
|
end
|
761
768
|
|
762
|
-
#
|
769
|
+
# Check always (not effective but it works)
|
763
770
|
def set?
|
764
|
-
|
771
|
+
!@challenge.empty?
|
765
772
|
end
|
766
773
|
|
767
774
|
# Set authentication credential.
|
768
775
|
def set_config(uri, config)
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
776
|
+
synchronize do
|
777
|
+
if uri.nil?
|
778
|
+
@config = config
|
779
|
+
else
|
780
|
+
uri = Util.uri_dirname(urify(uri))
|
781
|
+
@auth[uri] = config
|
782
|
+
end
|
774
783
|
end
|
775
784
|
end
|
776
785
|
|
777
786
|
# Get authentication credential.
|
778
787
|
def get_config(uri = nil)
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
uri = urify(uri)
|
783
|
-
Util.hash_find_value(@auth) { |cand_uri, cred|
|
784
|
-
Util.uri_part_of(uri, cand_uri)
|
785
|
-
}
|
786
|
-
end
|
788
|
+
synchronize {
|
789
|
+
do_get_config(uri)
|
790
|
+
}
|
787
791
|
end
|
788
792
|
|
789
793
|
# Response handler: returns credential.
|
@@ -792,33 +796,54 @@ class HTTPClient
|
|
792
796
|
# * child page of defined credential
|
793
797
|
def get(req)
|
794
798
|
target_uri = req.header.request_uri
|
795
|
-
|
796
|
-
|
799
|
+
synchronize {
|
800
|
+
return nil unless @challenge[nil] or @challenge.find { |uri, ok|
|
801
|
+
Util.uri_part_of(target_uri, uri) and ok
|
802
|
+
}
|
803
|
+
config = do_get_config(target_uri) || @config
|
804
|
+
return nil unless config
|
805
|
+
calc_cred(req, config)
|
797
806
|
}
|
798
|
-
config = get_config(target_uri) || @config
|
799
|
-
return nil unless config
|
800
|
-
calc_cred(req, config)
|
801
807
|
end
|
802
808
|
|
803
809
|
# Challenge handler: remember URL for response.
|
810
|
+
#
|
811
|
+
# challenge() in OAuth handler always returns false to avoid connection
|
812
|
+
# retry which should not work in OAuth authentication context. This
|
813
|
+
# method just remember URL (nil means 'any') for the next connection.
|
814
|
+
# Normally OAuthClient handles this correctly but see how it uses when
|
815
|
+
# you need to use this class directly.
|
804
816
|
def challenge(uri, param_str = nil)
|
817
|
+
synchronize {
|
818
|
+
if uri.nil?
|
819
|
+
@challenge[nil] = true
|
820
|
+
else
|
821
|
+
@challenge[urify(uri)] = true
|
822
|
+
end
|
823
|
+
false
|
824
|
+
}
|
825
|
+
end
|
826
|
+
|
827
|
+
private
|
828
|
+
|
829
|
+
def do_get_config(uri = nil)
|
805
830
|
if uri.nil?
|
806
|
-
@
|
831
|
+
@config
|
807
832
|
else
|
808
|
-
|
833
|
+
uri = urify(uri)
|
834
|
+
Util.hash_find_value(@auth) { |cand_uri, cred|
|
835
|
+
Util.uri_part_of(uri, cand_uri)
|
836
|
+
}
|
809
837
|
end
|
810
|
-
true
|
811
838
|
end
|
812
839
|
|
813
|
-
private
|
814
|
-
|
815
840
|
def calc_cred(req, config)
|
816
841
|
header = {}
|
817
842
|
header['oauth_consumer_key'] = config.consumer_key
|
818
|
-
header['oauth_token'] = config.token
|
819
843
|
header['oauth_signature_method'] = config.signature_method
|
820
844
|
header['oauth_timestamp'] = config.debug_timestamp || Time.now.to_i.to_s
|
821
845
|
header['oauth_nonce'] = config.debug_nonce || generate_nonce()
|
846
|
+
header['oauth_token'] = config.token if config.token
|
822
847
|
header['oauth_version'] = config.version if config.version
|
823
848
|
header['oauth_callback'] = config.callback if config.callback
|
824
849
|
header['oauth_verifier'] = config.verifier if config.verifier
|