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.
@@ -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