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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +85 -0
  3. data/bin/httpclient +18 -6
  4. data/bin/jsonclient +85 -0
  5. data/lib/http-access2.rb +1 -1
  6. data/lib/httpclient.rb +262 -88
  7. data/lib/httpclient/auth.rb +269 -244
  8. data/lib/httpclient/cacert.pem +3952 -0
  9. data/lib/httpclient/cacert1024.pem +3866 -0
  10. data/lib/httpclient/connection.rb +1 -1
  11. data/lib/httpclient/cookie.rb +161 -514
  12. data/lib/httpclient/http.rb +57 -21
  13. data/lib/httpclient/include_client.rb +2 -0
  14. data/lib/httpclient/jruby_ssl_socket.rb +588 -0
  15. data/lib/httpclient/session.rb +259 -317
  16. data/lib/httpclient/ssl_config.rb +141 -188
  17. data/lib/httpclient/ssl_socket.rb +150 -0
  18. data/lib/httpclient/timeout.rb +1 -1
  19. data/lib/httpclient/util.rb +62 -1
  20. data/lib/httpclient/version.rb +1 -1
  21. data/lib/httpclient/webagent-cookie.rb +459 -0
  22. data/lib/jsonclient.rb +63 -0
  23. data/lib/oauthclient.rb +2 -1
  24. data/sample/jsonclient.rb +67 -0
  25. data/sample/oauth_twitter.rb +4 -4
  26. data/test/{ca-chain.cert → ca-chain.pem} +0 -0
  27. data/test/client-pass.key +18 -0
  28. data/test/helper.rb +10 -8
  29. data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
  30. data/test/test_auth.rb +175 -4
  31. data/test/test_cookie.rb +147 -243
  32. data/test/test_http-access2.rb +17 -16
  33. data/test/test_httpclient.rb +458 -77
  34. data/test/test_jsonclient.rb +80 -0
  35. data/test/test_ssl.rb +341 -17
  36. data/test/test_webagent-cookie.rb +465 -0
  37. metadata +57 -55
  38. data/README.txt +0 -721
  39. data/lib/httpclient/cacert.p7s +0 -1858
  40. data/lib/httpclient/cacert_sha1.p7s +0 -1858
  41. data/sample/oauth_salesforce_10.rb +0 -63
@@ -1,5 +1,5 @@
1
1
  # HTTPClient - HTTP client library.
2
- # Copyright (C) 2000-2009 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
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
- begin
16
- require 'net/ntlm'
17
- NTLMEnabled = true
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 for handling BasicAuth negotiation.
229
- # Used in WWWAuth and ProxyAuth.
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
- # Creates new BasicAuth filter.
237
- def initialize
238
- @cred = nil
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
- @challengeable.clear
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
- @set = true
255
- if uri.nil?
256
- @cred = ["#{user}:#{passwd}"].pack('m').tr("\n", '')
257
- else
258
- uri = Util.uri_dirname(uri)
259
- @auth[uri] = ["#{user}:#{passwd}"].pack('m').tr("\n", '')
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
- @set == true
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
- return nil unless @challengeable.find { |uri, ok|
275
- Util.uri_part_of(target_uri, uri) and ok
276
- }
277
- return @cred if @cred
278
- Util.hash_find_value(@auth) { |uri, cred|
279
- Util.uri_part_of(target_uri, uri)
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
- @challengeable[urify(uri)] = true
286
- true
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
- @set = true
294
- @cred = ["#{user}:#{passwd}"].pack('m').tr("\n", '')
308
+ synchronize do
309
+ @cred = ["#{user}:#{passwd}"].pack('m').tr("\n", '')
310
+ end
295
311
  end
296
312
 
297
313
  def get(req)
298
- target_uri = req.header.request_uri
299
- return nil unless @challengeable['challenged']
300
- @cred
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
- @challengeable['challenged'] = true
306
- true
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
- # Authentication scheme.
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
- @set = true
335
- if uri
336
- uri = Util.uri_dirname(uri)
337
- @auth[uri] = [user, passwd]
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
- @set == true
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
- param = Util.hash_find_value(@challenge) { |uri, v|
353
- Util.uri_part_of(target_uri, uri)
354
- }
355
- return nil unless param
356
- user, passwd = Util.hash_find_value(@auth) { |uri, auth_data|
357
- Util.uri_part_of(target_uri, uri)
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
- @challenge[uri] = parse_challenge_param(param_str)
366
- true
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
- @set = true
449
- @auth = [user, passwd]
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
- target_uri = req.header.request_uri
457
- param = @challenge
458
- return nil unless param
459
- user, passwd = @auth
460
- return nil unless user
461
- calc_cred(req, user, passwd, param)
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
- @challenge = nil
482
+ synchronize do
483
+ @challenge = nil
484
+ end
466
485
  end
467
486
 
468
487
  def challenge(uri, param_str)
469
- @challenge = parse_challenge_param(param_str)
470
- true
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
- # Authentication scheme.
481
- attr_reader :scheme
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
- @set = true
507
- if uri
508
- uri = Util.uri_dirname(uri)
509
- @auth[uri] = [user, passwd]
510
- else
511
- @auth_default = [user, passwd]
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
- @set == true
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
- domain_uri, param = @challenge.find { |uri, v|
526
- Util.uri_part_of(target_uri, uri)
527
- }
528
- return nil unless param
529
- user, passwd = Util.hash_find_value(@auth) { |uri, auth_data|
530
- Util.uri_part_of(target_uri, uri)
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
- return false unless NTLMEnabled
559
- if param_str.nil? or @challenge[uri].nil?
560
- c = @challenge[uri] = {}
561
- c[:state] = :init
562
- c[:authphrase] = ""
563
- else
564
- c = @challenge[uri]
565
- c[:state] = :response
566
- c[:authphrase] = param_str
567
- end
568
- true
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
- # Authentication scheme.
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
- @challenge = {}
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
- # have we marked this as set - ie that it's valid to use in this context?
611
+ # Check always (not effective but it works)
601
612
  def set?
602
- SSPIEnabled || GSSAPIEnabled
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
- domain_uri, param = @challenge.find { |uri, v|
611
- Util.uri_part_of(target_uri, uri)
612
- }
613
- return nil unless param
614
- state = param[:state]
615
- authenticator = param[:authenticator]
616
- authphrase = param[:authphrase]
617
- case state
618
- when :init
619
- if SSPIEnabled
620
- authenticator = param[:authenticator] = Win32::SSPI::NegotiateAuth.new
621
- return authenticator.get_initial_token(@scheme)
622
- else # use GSSAPI
623
- authenticator = param[:authenticator] = GSSAPI::Simple.new(domain_uri.host, 'HTTP')
624
- # Base64 encode the context token
625
- return [authenticator.init_context].pack('m').gsub(/\n/,'')
626
- end
627
- when :response
628
- @challenge.delete(domain_uri)
629
- if SSPIEnabled
630
- return authenticator.complete_authentication(authphrase)
631
- else # use GSSAPI
632
- return authenticator.init_context(authphrase.unpack('m').pop)
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
- end
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
- return false unless SSPIEnabled || GSSAPIEnabled
641
- if param_str.nil? or @challenge[uri].nil?
642
- c = @challenge[uri] = {}
643
- c[:state] = :init
644
- c[:authenticator] = nil
645
- c[:authphrase] = ""
646
- else
647
- c = @challenge[uri]
648
- c[:state] = :response
649
- c[:authphrase] = param_str
650
- end
651
- true
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 HTTPClient::Util
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
- # have we marked this as set - ie that it's valid to use in this context?
769
+ # Check always (not effective but it works)
763
770
  def set?
764
- true
771
+ !@challenge.empty?
765
772
  end
766
773
 
767
774
  # Set authentication credential.
768
775
  def set_config(uri, config)
769
- if uri.nil?
770
- @config = config
771
- else
772
- uri = Util.uri_dirname(urify(uri))
773
- @auth[uri] = config
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
- if uri.nil?
780
- @config
781
- else
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
- return nil unless @challengeable[nil] or @challengeable.find { |uri, ok|
796
- Util.uri_part_of(target_uri, uri) and ok
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
- @challengeable[nil] = true
831
+ @config
807
832
  else
808
- @challengeable[urify(uri)] = true
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