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.
@@ -116,7 +116,7 @@ require 'httpclient/cookie'
116
116
  # res = clnt.post(uri, body)
117
117
  # end
118
118
  #
119
- # 3. Do multipart wth custom body.
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(/([^:,]+)(?::(\d+))?/) do |host, port|
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 :read. Bear in mind that some server application does not support
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
- filtered_block = proc { |res, str|
768
- block.call(str)
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
@@ -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
- @challengeable = {}
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
- @challengeable.clear
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
- @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", '')
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
- @set == true
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
- 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)
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
- @challengeable[urify(uri)] = true
286
- true
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
- @set = true
294
- @cred = ["#{user}:#{passwd}"].pack('m').tr("\n", '')
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
- return nil unless @challengeable['challenged']
300
- @cred
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
- @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
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
- @challenge.clear
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
- @set = true
335
- if uri
336
- uri = Util.uri_dirname(uri)
337
- @auth[uri] = [user, passwd]
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
- @set == true
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
- 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)
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
- @challenge[uri] = parse_challenge_param(param_str)
366
- true
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
- @set = true
449
- @auth = [user, passwd]
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
- param = @challenge
458
- return nil unless param
459
- user, passwd = @auth
460
- return nil unless user
461
- calc_cred(req, user, passwd, param)
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
- @challenge = nil
501
+ synchronize do
502
+ @challenge = nil
503
+ end
466
504
  end
467
505
 
468
506
  def challenge(uri, param_str)
469
- @challenge = parse_challenge_param(param_str)
470
- true
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
- @challenge.clear
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
- @set = true
507
- if uri
508
- uri = Util.uri_dirname(uri)
509
- @auth[uri] = [user, passwd]
510
- else
511
- @auth_default = [user, passwd]
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
- @set == true
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
- 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)
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
- 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
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
- @challenge.clear
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
- 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)
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
- end
635
- nil
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
- 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
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
- @challengeable = {}
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
- @challengeable.clear
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
- if uri.nil?
770
- @config = config
771
- else
772
- uri = Util.uri_dirname(urify(uri))
773
- @auth[uri] = config
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
- 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
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
- return nil unless @challengeable[nil] or @challengeable.find { |uri, ok|
796
- Util.uri_part_of(target_uri, uri) and ok
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
- @challengeable[nil] = true
890
+ @config
807
891
  else
808
- @challengeable[urify(uri)] = true
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