httpclient 2.3.4.1 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/{README.txt → README.md} +217 -56
- data/lib/httpclient.rb +11 -7
- data/lib/httpclient/auth.rb +242 -158
- data/lib/httpclient/cacert.p7s +3865 -1911
- data/lib/httpclient/cookie.rb +1 -1
- data/lib/httpclient/http.rb +2 -0
- data/lib/httpclient/session.rb +10 -7
- data/lib/httpclient/ssl_config.rb +12 -5
- data/lib/httpclient/version.rb +1 -1
- data/sample/api_rest.pdf +19942 -24
- data/sample/oauth_twitter.rb +4 -4
- data/sample/post.rb +11 -0
- data/test/client-pass.key +18 -0
- data/test/helper.rb +3 -3
- data/test/reports/TEST-HTTPAccess2-TestClient.xml +221 -0
- data/test/reports/TEST-TestCookie.xml +21 -0
- data/test/reports/TEST-TestCookieManager.xml +35 -0
- data/test/reports/TEST-TestHTTPClient.xml +147 -0
- data/test/reports/TEST-TestSSL.0.xml +0 -0
- data/test/reports/TEST-TestSSL.1.xml +81 -0
- data/test/reports/TEST-TestSSL.xml +0 -0
- data/test/test_auth.rb +59 -2
- data/test/test_cookie.rb +21 -0
- data/test/test_httpclient.rb +33 -1
- data/test/test_ssl.rb +1 -1
- metadata +55 -52
data/lib/httpclient.rb
CHANGED
@@ -116,7 +116,7 @@ require 'httpclient/cookie'
|
|
116
116
|
# res = clnt.post(uri, body)
|
117
117
|
# end
|
118
118
|
#
|
119
|
-
# 3. Do multipart
|
119
|
+
# 3. Do multipart with custom body.
|
120
120
|
#
|
121
121
|
# File.open('/tmp/post_data') do |file|
|
122
122
|
# body = [{ 'Content-Type' => 'application/atom+xml; charset=UTF-8',
|
@@ -483,7 +483,7 @@ class HTTPClient
|
|
483
483
|
@no_proxy = no_proxy
|
484
484
|
@no_proxy_regexps.clear
|
485
485
|
if @no_proxy
|
486
|
-
@no_proxy.scan(/([
|
486
|
+
@no_proxy.scan(/([^\s:,]+)(?::(\d+))?/) do |host, port|
|
487
487
|
if host[0] == ?.
|
488
488
|
regexp = /#{Regexp.quote(host)}\z/i
|
489
489
|
else
|
@@ -750,7 +750,7 @@ class HTTPClient
|
|
750
750
|
#
|
751
751
|
# When you pass an IO as a body, HTTPClient sends it as a HTTP request with
|
752
752
|
# chunked encoding (Transfer-Encoding: chunked in HTTP header) if IO does not
|
753
|
-
# respond to :
|
753
|
+
# respond to :size. Bear in mind that some server application does not support
|
754
754
|
# chunked request. At least cgi.rb does not support it.
|
755
755
|
def request(method, uri, *args, &block)
|
756
756
|
query, body, header, follow_redirect = keyword_argument(args, :query, :body, :header, :follow_redirect)
|
@@ -764,9 +764,13 @@ class HTTPClient
|
|
764
764
|
end
|
765
765
|
uri = urify(uri)
|
766
766
|
if block
|
767
|
-
|
768
|
-
|
769
|
-
|
767
|
+
if block.arity == 1
|
768
|
+
filtered_block = proc { |res, str|
|
769
|
+
block.call(str)
|
770
|
+
}
|
771
|
+
else
|
772
|
+
filtered_block = block
|
773
|
+
end
|
770
774
|
end
|
771
775
|
if follow_redirect
|
772
776
|
follow_redirect(method, uri, query, body, header, &block)
|
@@ -1088,7 +1092,7 @@ private
|
|
1088
1092
|
sess.get_body do |part|
|
1089
1093
|
set_encoding(part, res.body_encoding)
|
1090
1094
|
if block
|
1091
|
-
block.call(res, part)
|
1095
|
+
block.call(res, part.dup)
|
1092
1096
|
else
|
1093
1097
|
content << part
|
1094
1098
|
end
|
data/lib/httpclient/auth.rb
CHANGED
@@ -8,6 +8,7 @@
|
|
8
8
|
|
9
9
|
require 'digest/md5'
|
10
10
|
require 'httpclient/session'
|
11
|
+
require 'mutex_m'
|
11
12
|
|
12
13
|
|
13
14
|
class HTTPClient
|
@@ -229,40 +230,48 @@ class HTTPClient
|
|
229
230
|
# Used in WWWAuth and ProxyAuth.
|
230
231
|
class BasicAuth
|
231
232
|
include HTTPClient::Util
|
233
|
+
include Mutex_m
|
232
234
|
|
233
235
|
# Authentication scheme.
|
234
236
|
attr_reader :scheme
|
235
237
|
|
236
238
|
# Creates new BasicAuth filter.
|
237
239
|
def initialize
|
240
|
+
super
|
238
241
|
@cred = nil
|
239
242
|
@set = false
|
240
243
|
@auth = {}
|
241
|
-
@
|
244
|
+
@challenge = {}
|
242
245
|
@scheme = "Basic"
|
243
246
|
end
|
244
247
|
|
245
248
|
# Resets challenge state. Do not send '*Authorization' header until the
|
246
249
|
# server sends '*Authentication' again.
|
247
250
|
def reset_challenge
|
248
|
-
|
251
|
+
synchronize {
|
252
|
+
@challenge.clear
|
253
|
+
}
|
249
254
|
end
|
250
255
|
|
251
256
|
# Set authentication credential.
|
252
257
|
# uri == nil for generic purpose (allow to use user/password for any URL).
|
253
258
|
def set(uri, user, passwd)
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
259
|
+
synchronize do
|
260
|
+
if uri.nil?
|
261
|
+
@cred = ["#{user}:#{passwd}"].pack('m').tr("\n", '')
|
262
|
+
else
|
263
|
+
uri = Util.uri_dirname(uri)
|
264
|
+
@auth[uri] = ["#{user}:#{passwd}"].pack('m').tr("\n", '')
|
265
|
+
end
|
266
|
+
@set = true
|
260
267
|
end
|
261
268
|
end
|
262
269
|
|
263
270
|
# have we marked this as set - ie that it's valid to use in this context?
|
264
271
|
def set?
|
265
|
-
|
272
|
+
synchronize {
|
273
|
+
@set == true
|
274
|
+
}
|
266
275
|
end
|
267
276
|
|
268
277
|
# Response handler: returns credential.
|
@@ -271,50 +280,63 @@ class HTTPClient
|
|
271
280
|
# * child page of defined credential
|
272
281
|
def get(req)
|
273
282
|
target_uri = req.header.request_uri
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
Util.
|
283
|
+
synchronize {
|
284
|
+
return nil unless @challenge.find { |uri, ok|
|
285
|
+
Util.uri_part_of(target_uri, uri) and ok
|
286
|
+
}
|
287
|
+
return @cred if @cred
|
288
|
+
Util.hash_find_value(@auth) { |uri, cred|
|
289
|
+
Util.uri_part_of(target_uri, uri)
|
290
|
+
}
|
280
291
|
}
|
281
292
|
end
|
282
293
|
|
283
294
|
# Challenge handler: remember URL for response.
|
284
295
|
def challenge(uri, param_str = nil)
|
285
|
-
|
286
|
-
|
296
|
+
synchronize {
|
297
|
+
@challenge[urify(uri)] = true
|
298
|
+
true
|
299
|
+
}
|
287
300
|
end
|
288
301
|
end
|
289
302
|
|
290
303
|
class ProxyBasicAuth < BasicAuth
|
291
304
|
|
292
305
|
def set(uri, user, passwd)
|
293
|
-
|
294
|
-
|
306
|
+
synchronize do
|
307
|
+
@cred = ["#{user}:#{passwd}"].pack('m').tr("\n", '')
|
308
|
+
@set = true
|
309
|
+
end
|
295
310
|
end
|
296
311
|
|
297
312
|
def get(req)
|
298
313
|
target_uri = req.header.request_uri
|
299
|
-
|
300
|
-
|
314
|
+
synchronize {
|
315
|
+
return nil unless @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
331
|
class DigestAuth
|
332
|
+
include Mutex_m
|
333
|
+
|
313
334
|
# Authentication scheme.
|
314
335
|
attr_reader :scheme
|
315
336
|
|
316
337
|
# Creates new DigestAuth filter.
|
317
338
|
def initialize
|
339
|
+
super
|
318
340
|
@auth = {}
|
319
341
|
@challenge = {}
|
320
342
|
@set = false
|
@@ -325,22 +347,28 @@ class HTTPClient
|
|
325
347
|
# Resets challenge state. Do not send '*Authorization' header until the
|
326
348
|
# server sends '*Authentication' again.
|
327
349
|
def reset_challenge
|
328
|
-
|
350
|
+
synchronize do
|
351
|
+
@challenge.clear
|
352
|
+
end
|
329
353
|
end
|
330
354
|
|
331
355
|
# Set authentication credential.
|
332
356
|
# uri == nil is ignored.
|
333
357
|
def set(uri, user, passwd)
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
358
|
+
synchronize do
|
359
|
+
if uri
|
360
|
+
uri = Util.uri_dirname(uri)
|
361
|
+
@auth[uri] = [user, passwd]
|
362
|
+
end
|
363
|
+
@set = true
|
338
364
|
end
|
339
365
|
end
|
340
366
|
|
341
367
|
# have we marked this as set - ie that it's valid to use in this context?
|
342
368
|
def set?
|
343
|
-
|
369
|
+
synchronize {
|
370
|
+
@set == true
|
371
|
+
}
|
344
372
|
end
|
345
373
|
|
346
374
|
# Response handler: returns credential.
|
@@ -349,21 +377,25 @@ class HTTPClient
|
|
349
377
|
# * child page of defined credential
|
350
378
|
def get(req)
|
351
379
|
target_uri = req.header.request_uri
|
352
|
-
|
353
|
-
Util.
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
Util.
|
380
|
+
synchronize {
|
381
|
+
param = Util.hash_find_value(@challenge) { |uri, v|
|
382
|
+
Util.uri_part_of(target_uri, uri)
|
383
|
+
}
|
384
|
+
return nil unless param
|
385
|
+
user, passwd = Util.hash_find_value(@auth) { |uri, auth_data|
|
386
|
+
Util.uri_part_of(target_uri, uri)
|
387
|
+
}
|
388
|
+
return nil unless user
|
389
|
+
calc_cred(req, user, passwd, param)
|
358
390
|
}
|
359
|
-
return nil unless user
|
360
|
-
calc_cred(req, user, passwd, param)
|
361
391
|
end
|
362
392
|
|
363
393
|
# Challenge handler: remember URL and challenge token for response.
|
364
394
|
def challenge(uri, param_str)
|
365
|
-
|
366
|
-
|
395
|
+
synchronize {
|
396
|
+
@challenge[uri] = parse_challenge_param(param_str)
|
397
|
+
true
|
398
|
+
}
|
367
399
|
end
|
368
400
|
|
369
401
|
private
|
@@ -445,8 +477,10 @@ class HTTPClient
|
|
445
477
|
|
446
478
|
# overrides DigestAuth#set. sets default user name and password. uri is not used.
|
447
479
|
def set(uri, user, passwd)
|
448
|
-
|
449
|
-
|
480
|
+
synchronize do
|
481
|
+
@set = true
|
482
|
+
@auth = [user, passwd]
|
483
|
+
end
|
450
484
|
end
|
451
485
|
|
452
486
|
# overrides DigestAuth#get. Uses default user name and password
|
@@ -454,20 +488,26 @@ class HTTPClient
|
|
454
488
|
# before
|
455
489
|
def get(req)
|
456
490
|
target_uri = req.header.request_uri
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
491
|
+
synchronize {
|
492
|
+
param = @challenge
|
493
|
+
return nil unless param
|
494
|
+
user, passwd = @auth
|
495
|
+
return nil unless user
|
496
|
+
calc_cred(req, user, passwd, param)
|
497
|
+
}
|
462
498
|
end
|
463
499
|
|
464
500
|
def reset_challenge
|
465
|
-
|
501
|
+
synchronize do
|
502
|
+
@challenge = nil
|
503
|
+
end
|
466
504
|
end
|
467
505
|
|
468
506
|
def challenge(uri, param_str)
|
469
|
-
|
470
|
-
|
507
|
+
synchronize {
|
508
|
+
@challenge = parse_challenge_param(param_str)
|
509
|
+
true
|
510
|
+
}
|
471
511
|
end
|
472
512
|
|
473
513
|
end
|
@@ -477,6 +517,8 @@ class HTTPClient
|
|
477
517
|
#
|
478
518
|
# NegotiateAuth depends on 'ruby/ntlm' module.
|
479
519
|
class NegotiateAuth
|
520
|
+
include Mutex_m
|
521
|
+
|
480
522
|
# Authentication scheme.
|
481
523
|
attr_reader :scheme
|
482
524
|
# NTLM opt for ruby/ntlm. {:ntlmv2 => true} by default.
|
@@ -484,6 +526,7 @@ class HTTPClient
|
|
484
526
|
|
485
527
|
# Creates new NegotiateAuth filter.
|
486
528
|
def initialize(scheme = "Negotiate")
|
529
|
+
super()
|
487
530
|
@auth = {}
|
488
531
|
@auth_default = nil
|
489
532
|
@challenge = {}
|
@@ -497,24 +540,30 @@ class HTTPClient
|
|
497
540
|
# Resets challenge state. Do not send '*Authorization' header until the
|
498
541
|
# server sends '*Authentication' again.
|
499
542
|
def reset_challenge
|
500
|
-
|
543
|
+
synchronize do
|
544
|
+
@challenge.clear
|
545
|
+
end
|
501
546
|
end
|
502
547
|
|
503
548
|
# Set authentication credential.
|
504
549
|
# uri == nil for generic purpose (allow to use user/password for any URL).
|
505
550
|
def set(uri, user, passwd)
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
551
|
+
synchronize do
|
552
|
+
if uri
|
553
|
+
uri = Util.uri_dirname(uri)
|
554
|
+
@auth[uri] = [user, passwd]
|
555
|
+
else
|
556
|
+
@auth_default = [user, passwd]
|
557
|
+
end
|
558
|
+
@set = true
|
512
559
|
end
|
513
560
|
end
|
514
561
|
|
515
562
|
# have we marked this as set - ie that it's valid to use in this context?
|
516
563
|
def set?
|
517
|
-
|
564
|
+
synchronize {
|
565
|
+
@set == true
|
566
|
+
}
|
518
567
|
end
|
519
568
|
|
520
569
|
# Response handler: returns credential.
|
@@ -522,50 +571,54 @@ class HTTPClient
|
|
522
571
|
def get(req)
|
523
572
|
return nil unless NTLMEnabled
|
524
573
|
target_uri = req.header.request_uri
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
Util.
|
574
|
+
synchronize {
|
575
|
+
domain_uri, param = @challenge.find { |uri, v|
|
576
|
+
Util.uri_part_of(target_uri, uri)
|
577
|
+
}
|
578
|
+
return nil unless param
|
579
|
+
user, passwd = Util.hash_find_value(@auth) { |uri, auth_data|
|
580
|
+
Util.uri_part_of(target_uri, uri)
|
581
|
+
}
|
582
|
+
unless user
|
583
|
+
user, passwd = @auth_default
|
584
|
+
end
|
585
|
+
return nil unless user
|
586
|
+
domain = nil
|
587
|
+
domain, user = user.split("\\") if user.index("\\")
|
588
|
+
state = param[:state]
|
589
|
+
authphrase = param[:authphrase]
|
590
|
+
case state
|
591
|
+
when :init
|
592
|
+
t1 = Net::NTLM::Message::Type1.new
|
593
|
+
t1.domain = domain if domain
|
594
|
+
return t1.encode64
|
595
|
+
when :response
|
596
|
+
t2 = Net::NTLM::Message.decode64(authphrase)
|
597
|
+
param = {:user => user, :password => passwd}
|
598
|
+
param[:domain] = domain if domain
|
599
|
+
t3 = t2.response(param, @ntlm_opt.dup)
|
600
|
+
@challenge.delete(domain_uri)
|
601
|
+
return t3.encode64
|
602
|
+
end
|
603
|
+
nil
|
531
604
|
}
|
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
605
|
end
|
555
606
|
|
556
607
|
# Challenge handler: remember URL and challenge token for response.
|
557
608
|
def challenge(uri, param_str)
|
558
609
|
return false unless NTLMEnabled
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
610
|
+
synchronize {
|
611
|
+
if param_str.nil? or @challenge[uri].nil?
|
612
|
+
c = @challenge[uri] = {}
|
613
|
+
c[:state] = :init
|
614
|
+
c[:authphrase] = ""
|
615
|
+
else
|
616
|
+
c = @challenge[uri]
|
617
|
+
c[:state] = :response
|
618
|
+
c[:authphrase] = param_str
|
619
|
+
end
|
620
|
+
true
|
621
|
+
}
|
569
622
|
end
|
570
623
|
end
|
571
624
|
|
@@ -575,11 +628,14 @@ class HTTPClient
|
|
575
628
|
#
|
576
629
|
# SSPINegotiateAuth depends on 'win32/sspi' module.
|
577
630
|
class SSPINegotiateAuth
|
631
|
+
include Mutex_m
|
632
|
+
|
578
633
|
# Authentication scheme.
|
579
634
|
attr_reader :scheme
|
580
635
|
|
581
636
|
# Creates new SSPINegotiateAuth filter.
|
582
637
|
def initialize
|
638
|
+
super
|
583
639
|
@challenge = {}
|
584
640
|
@scheme = "Negotiate"
|
585
641
|
end
|
@@ -587,7 +643,9 @@ class HTTPClient
|
|
587
643
|
# Resets challenge state. Do not send '*Authorization' header until the
|
588
644
|
# server sends '*Authentication' again.
|
589
645
|
def reset_challenge
|
590
|
-
|
646
|
+
synchronize do
|
647
|
+
@challenge.clear
|
648
|
+
end
|
591
649
|
end
|
592
650
|
|
593
651
|
# Set authentication credential.
|
@@ -607,48 +665,52 @@ class HTTPClient
|
|
607
665
|
def get(req)
|
608
666
|
return nil unless SSPIEnabled || GSSAPIEnabled
|
609
667
|
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
|
-
|
668
|
+
synchronize {
|
669
|
+
domain_uri, param = @challenge.find { |uri, v|
|
670
|
+
Util.uri_part_of(target_uri, uri)
|
671
|
+
}
|
672
|
+
return nil unless param
|
673
|
+
state = param[:state]
|
674
|
+
authenticator = param[:authenticator]
|
675
|
+
authphrase = param[:authphrase]
|
676
|
+
case state
|
677
|
+
when :init
|
678
|
+
if SSPIEnabled
|
679
|
+
authenticator = param[:authenticator] = Win32::SSPI::NegotiateAuth.new
|
680
|
+
return authenticator.get_initial_token(@scheme)
|
681
|
+
else # use GSSAPI
|
682
|
+
authenticator = param[:authenticator] = GSSAPI::Simple.new(domain_uri.host, 'HTTP')
|
683
|
+
# Base64 encode the context token
|
684
|
+
return [authenticator.init_context].pack('m').gsub(/\n/,'')
|
685
|
+
end
|
686
|
+
when :response
|
687
|
+
@challenge.delete(domain_uri)
|
688
|
+
if SSPIEnabled
|
689
|
+
return authenticator.complete_authentication(authphrase)
|
690
|
+
else # use GSSAPI
|
691
|
+
return authenticator.init_context(authphrase.unpack('m').pop)
|
692
|
+
end
|
633
693
|
end
|
634
|
-
|
635
|
-
|
694
|
+
nil
|
695
|
+
}
|
636
696
|
end
|
637
697
|
|
638
698
|
# Challenge handler: remember URL and challenge token for response.
|
639
699
|
def challenge(uri, param_str)
|
640
700
|
return false unless SSPIEnabled || GSSAPIEnabled
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
701
|
+
synchronize {
|
702
|
+
if param_str.nil? or @challenge[uri].nil?
|
703
|
+
c = @challenge[uri] = {}
|
704
|
+
c[:state] = :init
|
705
|
+
c[:authenticator] = nil
|
706
|
+
c[:authphrase] = ""
|
707
|
+
else
|
708
|
+
c = @challenge[uri]
|
709
|
+
c[:state] = :response
|
710
|
+
c[:authphrase] = param_str
|
711
|
+
end
|
712
|
+
true
|
713
|
+
}
|
652
714
|
end
|
653
715
|
end
|
654
716
|
|
@@ -664,6 +726,7 @@ class HTTPClient
|
|
664
726
|
#
|
665
727
|
class OAuth
|
666
728
|
include HTTPClient::Util
|
729
|
+
include Mutex_m
|
667
730
|
|
668
731
|
# Authentication scheme.
|
669
732
|
attr_reader :scheme
|
@@ -737,9 +800,10 @@ class HTTPClient
|
|
737
800
|
|
738
801
|
# Creates new DigestAuth filter.
|
739
802
|
def initialize
|
803
|
+
super
|
740
804
|
@config = nil # common config
|
741
805
|
@auth = {} # configs for each site
|
742
|
-
@
|
806
|
+
@challenge = {}
|
743
807
|
@nonce_count = 0
|
744
808
|
@signature_handler = {
|
745
809
|
'HMAC-SHA1' => method(:sign_hmac_sha1)
|
@@ -750,7 +814,9 @@ class HTTPClient
|
|
750
814
|
# Resets challenge state. Do not send '*Authorization' header until the
|
751
815
|
# server sends '*Authentication' again.
|
752
816
|
def reset_challenge
|
753
|
-
|
817
|
+
synchronize do
|
818
|
+
@challenge.clear
|
819
|
+
end
|
754
820
|
end
|
755
821
|
|
756
822
|
# Set authentication credential.
|
@@ -766,24 +832,21 @@ class HTTPClient
|
|
766
832
|
|
767
833
|
# Set authentication credential.
|
768
834
|
def set_config(uri, config)
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
835
|
+
synchronize do
|
836
|
+
if uri.nil?
|
837
|
+
@config = config
|
838
|
+
else
|
839
|
+
uri = Util.uri_dirname(urify(uri))
|
840
|
+
@auth[uri] = config
|
841
|
+
end
|
774
842
|
end
|
775
843
|
end
|
776
844
|
|
777
845
|
# Get authentication credential.
|
778
846
|
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
|
847
|
+
synchronize {
|
848
|
+
do_get_config(uri)
|
849
|
+
}
|
787
850
|
end
|
788
851
|
|
789
852
|
# Response handler: returns credential.
|
@@ -792,33 +855,54 @@ class HTTPClient
|
|
792
855
|
# * child page of defined credential
|
793
856
|
def get(req)
|
794
857
|
target_uri = req.header.request_uri
|
795
|
-
|
796
|
-
|
858
|
+
synchronize {
|
859
|
+
return nil unless @challenge[nil] or @challenge.find { |uri, ok|
|
860
|
+
Util.uri_part_of(target_uri, uri) and ok
|
861
|
+
}
|
862
|
+
config = do_get_config(target_uri) || @config
|
863
|
+
return nil unless config
|
864
|
+
calc_cred(req, config)
|
797
865
|
}
|
798
|
-
config = get_config(target_uri) || @config
|
799
|
-
return nil unless config
|
800
|
-
calc_cred(req, config)
|
801
866
|
end
|
802
867
|
|
803
868
|
# Challenge handler: remember URL for response.
|
869
|
+
#
|
870
|
+
# challenge() in OAuth handler always returns false to avoid connection
|
871
|
+
# retry which should not work in OAuth authentication context. This
|
872
|
+
# method just remember URL (nil means 'any') for the next connection.
|
873
|
+
# Normally OAuthClient handles this correctly but see how it uses when
|
874
|
+
# you need to use this class directly.
|
804
875
|
def challenge(uri, param_str = nil)
|
876
|
+
synchronize {
|
877
|
+
if uri.nil?
|
878
|
+
@challenge[nil] = true
|
879
|
+
else
|
880
|
+
@challenge[urify(uri)] = true
|
881
|
+
end
|
882
|
+
false
|
883
|
+
}
|
884
|
+
end
|
885
|
+
|
886
|
+
private
|
887
|
+
|
888
|
+
def do_get_config(uri = nil)
|
805
889
|
if uri.nil?
|
806
|
-
@
|
890
|
+
@config
|
807
891
|
else
|
808
|
-
|
892
|
+
uri = urify(uri)
|
893
|
+
Util.hash_find_value(@auth) { |cand_uri, cred|
|
894
|
+
Util.uri_part_of(uri, cand_uri)
|
895
|
+
}
|
809
896
|
end
|
810
|
-
true
|
811
897
|
end
|
812
898
|
|
813
|
-
private
|
814
|
-
|
815
899
|
def calc_cred(req, config)
|
816
900
|
header = {}
|
817
901
|
header['oauth_consumer_key'] = config.consumer_key
|
818
|
-
header['oauth_token'] = config.token
|
819
902
|
header['oauth_signature_method'] = config.signature_method
|
820
903
|
header['oauth_timestamp'] = config.debug_timestamp || Time.now.to_i.to_s
|
821
904
|
header['oauth_nonce'] = config.debug_nonce || generate_nonce()
|
905
|
+
header['oauth_token'] = config.token if config.token
|
822
906
|
header['oauth_version'] = config.version if config.version
|
823
907
|
header['oauth_callback'] = config.callback if config.callback
|
824
908
|
header['oauth_verifier'] = config.verifier if config.verifier
|