httpclient 2.3.4.1 → 2.4.0
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.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
|