httpclient 2.5.1 → 2.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e2df4838bec1aba63f3823f9073278dd1ca98154
4
- data.tar.gz: 13f5b992074f28118e5ed7c718af8b1454d0b6c3
3
+ metadata.gz: 96ac0d88857cdda17631e5d2f8d5055854324813
4
+ data.tar.gz: d481af07b3a540d43044a9065e47ea177d5f3aa7
5
5
  SHA512:
6
- metadata.gz: 733c0562e2187daa398245e2e7095e845d141653beef44f7064cb08696f1352c3718364638bc33aaa147962bd093a3bf85c04c4593938988da214e34a00d39d7
7
- data.tar.gz: 648e04e4c278634072da5f3cb7a4deb0708307c6631ba7c9983fb5b5bd499a9555caa1d1bdd0b17bc7691a9362232367f17e7ef4a847044336aacdb375434eeb
6
+ metadata.gz: fd7ee572d2081458053de5bcd27408848067abc83dfbf8be88b78740bac06692a504eaf16a0418e47ea1555170825a4740505581b3ddc24706f557ef1f2bc3a1
7
+ data.tar.gz: cd0a8f4fa967737b90d40bd073e0f0c7e264d2e1b5873342337abc3dc32caa9884e597d40675753d6458472f05cc560380aa955b1af62c9672013c71bf16cce8
data/lib/httpclient.rb CHANGED
@@ -324,6 +324,8 @@ class HTTPClient
324
324
  # How many times get_content and post_content follows HTTP redirect.
325
325
  # 10 by default.
326
326
  attr_accessor :follow_redirect_count
327
+ # Base url of resources.
328
+ attr_accessor :base_url
327
329
 
328
330
  # Set HTTP version as a String:: 'HTTP/1.0' or 'HTTP/1.1'
329
331
  attr_proxy(:protocol_version, true)
@@ -362,27 +364,36 @@ class HTTPClient
362
364
 
363
365
  # Creates a HTTPClient instance which manages sessions, cookies, etc.
364
366
  #
365
- # HTTPClient.new takes 3 optional arguments for proxy url string,
366
- # User-Agent String and From header String. User-Agent and From are embedded
367
- # in HTTP request Header if given. No User-Agent and From header added
368
- # without setting it explicitly.
367
+ # HTTPClient.new takes optional arguments as a Hash.
368
+ # * :proxy - proxy url string
369
+ # * :agent_name - User-Agent String
370
+ # * :from - from header String
371
+ # * :base_url - base URL of resources
372
+ # * :force_basic_auth - flag for sending Authorization header w/o gettin 401 first
373
+ # User-Agent and From are embedded in HTTP request Header if given.
374
+ # From header is not set without setting it explicitly.
369
375
  #
370
376
  # proxy = 'http://myproxy:8080'
371
377
  # agent_name = 'MyAgent/0.1'
372
378
  # from = 'from@example.com'
373
379
  # HTTPClient.new(proxy, agent_name, from)
374
380
  #
375
- # You can use a keyword argument style Hash. Keys are :proxy, :agent_name
376
- # and :from.
381
+ # After you set base_url, all resources you pass to get, post and other
382
+ # methods are recognized to be prefixed with base_url. Say base_url is
383
+ # 'https://api.example.com/v1, get('/users') is the same as
384
+ # get('https://api.example.com/v1/users') internally. You can also pass
385
+ # full URL from 'http://' even after setting base_url.
377
386
  #
378
- # HTTPClient.new(:agent_name => 'MyAgent/0.1')
379
387
  def initialize(*args)
380
- proxy, agent_name, from = keyword_argument(args, :proxy, :agent_name, :from)
388
+ proxy, agent_name, from, base_url, force_basic_auth =
389
+ keyword_argument(args, :proxy, :agent_name, :from, :base_url, :force_basic_auth)
381
390
  @proxy = nil # assigned later.
382
391
  @no_proxy = nil
383
392
  @no_proxy_regexps = []
393
+ @base_url = base_url
384
394
  @www_auth = WWWAuth.new
385
395
  @proxy_auth = ProxyAuth.new
396
+ @www_auth.basic_auth.force_auth = @proxy_auth.basic_auth.force_auth = force_basic_auth
386
397
  @request_filter = [@proxy_auth, @www_auth]
387
398
  @debug_dev = nil
388
399
  @redirect_uri_callback = method(:default_redirect_uri_callback)
@@ -509,14 +520,14 @@ class HTTPClient
509
520
  #
510
521
  # Calling this method resets all existing sessions.
511
522
  def set_auth(domain, user, passwd)
512
- uri = urify(domain)
523
+ uri = to_resource_url(domain)
513
524
  @www_auth.set_auth(uri, user, passwd)
514
525
  reset_all
515
526
  end
516
527
 
517
528
  # Deprecated. Use set_auth instead.
518
529
  def set_basic_auth(domain, user, passwd)
519
- uri = urify(domain)
530
+ uri = to_resource_url(domain)
520
531
  @www_auth.basic_auth.set(uri, user, passwd)
521
532
  reset_all
522
533
  end
@@ -531,6 +542,14 @@ class HTTPClient
531
542
  reset_all
532
543
  end
533
544
 
545
+ # Turn on/off the BasicAuth force flag. Generally HTTP client must
546
+ # send Authorization header after it gets 401 error from server from
547
+ # security reason. But in some situation (e.g. API client) you might
548
+ # want to send Authorization from the beginning.
549
+ def force_basic_auth=(force_basic_auth)
550
+ @www_auth.basic_auth.force_auth = @proxy_auth.basic_auth.force_auth = force_basic_auth
551
+ end
552
+
534
553
  # Sets the filename where non-volatile Cookies be saved by calling
535
554
  # save_cookie_store.
536
555
  # This method tries to load and managing Cookies from the specified file.
@@ -778,7 +797,7 @@ class HTTPClient
778
797
  else
779
798
  header ||= {}
780
799
  end
781
- uri = urify(uri)
800
+ uri = to_resource_url(uri)
782
801
  if block
783
802
  if block.arity == 1
784
803
  filtered_block = proc { |res, str|
@@ -798,15 +817,13 @@ class HTTPClient
798
817
  # Sends HEAD request in async style. See request_async for arguments.
799
818
  # It immediately returns a HTTPClient::Connection instance as a result.
800
819
  def head_async(uri, *args)
801
- query, header = keyword_argument(args, :query, :header)
802
- request_async(:head, uri, query, nil, header || {})
820
+ request_async2(:head, uri, argument_to_hash(args, :query, :header))
803
821
  end
804
822
 
805
823
  # Sends GET request in async style. See request_async for arguments.
806
824
  # It immediately returns a HTTPClient::Connection instance as a result.
807
825
  def get_async(uri, *args)
808
- query, header = keyword_argument(args, :query, :header)
809
- request_async(:get, uri, query, nil, header || {})
826
+ request_async2(:get, uri, argument_to_hash(args, :query, :header))
810
827
  end
811
828
 
812
829
  # Sends POST request in async style. See request_async for arguments.
@@ -866,7 +883,7 @@ class HTTPClient
866
883
  #
867
884
  # Arguments definition is the same as request.
868
885
  def request_async(method, uri, query = nil, body = nil, header = {})
869
- uri = urify(uri)
886
+ uri = to_resource_url(uri)
870
887
  do_request_async(method, uri, query, body, header)
871
888
  end
872
889
 
@@ -881,14 +898,14 @@ class HTTPClient
881
898
  else
882
899
  header ||= {}
883
900
  end
884
- uri = urify(uri)
901
+ uri = to_resource_url(uri)
885
902
  do_request_async(method, uri, query, body, header)
886
903
  end
887
904
 
888
905
  # Resets internal session for the given URL. Keep-alive connection for the
889
906
  # site (host-port pair) is disconnected if exists.
890
907
  def reset(uri)
891
- uri = urify(uri)
908
+ uri = to_resource_url(uri)
892
909
  @session_manager.reset(uri)
893
910
  end
894
911
 
@@ -965,8 +982,8 @@ private
965
982
  retry_count -= 1
966
983
  end
967
984
  end
968
- rescue Exception
969
- conn.push $!
985
+ rescue Exception => e
986
+ conn.push e
970
987
  end
971
988
  }
972
989
  conn.async_thread = t
@@ -992,7 +1009,7 @@ private
992
1009
  end
993
1010
 
994
1011
  def follow_redirect(method, uri, query, body, header, &block)
995
- uri = urify(uri)
1012
+ uri = to_resource_url(uri)
996
1013
  if block
997
1014
  filtered_block = proc { |r, str|
998
1015
  block.call(str) if r.ok?
@@ -1201,4 +1218,13 @@ private
1201
1218
  def set_encoding(str, encoding)
1202
1219
  str.force_encoding(encoding) if encoding
1203
1220
  end
1221
+
1222
+ def to_resource_url(uri)
1223
+ u = urify(uri)
1224
+ if @base_url && u.scheme.nil? && u.host.nil?
1225
+ urify(@base_url + uri)
1226
+ else
1227
+ u
1228
+ end
1229
+ end
1204
1230
  end
@@ -217,6 +217,9 @@ class HTTPClient
217
217
  # Authentication scheme.
218
218
  attr_reader :scheme
219
219
 
220
+ # Send Authorization Header without receiving 401
221
+ attr_accessor :force_auth
222
+
220
223
  # Creates new BasicAuth filter.
221
224
  def initialize
222
225
  super
@@ -224,6 +227,7 @@ class HTTPClient
224
227
  @auth = {}
225
228
  @challenge = {}
226
229
  @scheme = "Basic"
230
+ @force_auth = false
227
231
  end
228
232
 
229
233
  # Resets challenge state. Do not send '*Authorization' header until the
@@ -259,7 +263,7 @@ class HTTPClient
259
263
  def get(req)
260
264
  target_uri = req.header.request_uri
261
265
  synchronize {
262
- return nil unless @challenge.find { |uri, ok|
266
+ return nil if !@force_auth and !@challenge.any? { |uri, ok|
263
267
  Util.uri_part_of(target_uri, uri) and ok
264
268
  }
265
269
  return @cred if @cred
@@ -36,6 +36,8 @@
36
36
  # are also provided. Widget.http_client is identical to Widget.new.http_client
37
37
  #
38
38
  #
39
+ require 'httpclient'
40
+
39
41
  class HTTPClient
40
42
  module IncludeClient
41
43
 
@@ -38,8 +38,8 @@ class HTTPClient
38
38
  # Creates a new Site based on the given URI.
39
39
  def initialize(uri = nil)
40
40
  if uri
41
- @scheme = uri.scheme
42
- @host = uri.hostname
41
+ @scheme = uri.scheme || 'tcp'
42
+ @host = uri.hostname || '0.0.0.0'
43
43
  @port = uri.port.to_i
44
44
  else
45
45
  @scheme = 'tcp'
@@ -623,10 +623,10 @@ class HTTPClient
623
623
  rescue HTTPClient::TimeoutError
624
624
  close
625
625
  raise
626
- rescue
626
+ rescue => e
627
627
  close
628
- if SSLEnabled and $!.is_a?(OpenSSL::SSL::SSLError)
629
- raise KeepAliveDisconnected.new(self, $!)
628
+ if SSLEnabled and e.is_a?(OpenSSL::SSL::SSLError)
629
+ raise KeepAliveDisconnected.new(self, e)
630
630
  else
631
631
  raise
632
632
  end
@@ -797,13 +797,13 @@ class HTTPClient
797
797
  socket = nil
798
798
  begin
799
799
  @debug_dev << "! CONNECT TO #{site.host}:#{site.port}\n" if @debug_dev
800
- clean_host = site.host.delete("[]")
801
- clean_local = @socket_local.host.delete("[]")
800
+ clean_host = site.host.delete("[]")
802
801
  if str = @test_loopback_http_response.shift
803
802
  socket = LoopBackSocket.new(clean_host, site.port, str)
804
803
  elsif @socket_local == Site::EMPTY
805
804
  socket = TCPSocket.new(clean_host, site.port)
806
805
  else
806
+ clean_local = @socket_local.host.delete("[]")
807
807
  socket = TCPSocket.new(clean_host, site.port, clean_local, @socket_local.port)
808
808
  end
809
809
  if @debug_dev
@@ -1,3 +1,3 @@
1
1
  class HTTPClient
2
- VERSION = '2.5.1'
2
+ VERSION = '2.5.2'
3
3
  end
data/test/test_auth.rb CHANGED
@@ -141,8 +141,10 @@ class TestAuth < Test::Unit::TestCase
141
141
  c = HTTPClient.new
142
142
  webrick_backup = @basic_auth.instance_eval { @auth_scheme }
143
143
  begin
144
+ # WEBrick in ruby 1.8.7 uses 'BASIC' instead of 'Basic'
144
145
  @basic_auth.instance_eval { @auth_scheme = "BASIC" }
145
146
  c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
147
+ #
146
148
  c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
147
149
  assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth"))
148
150
  ensure
@@ -150,6 +152,41 @@ class TestAuth < Test::Unit::TestCase
150
152
  end
151
153
  end
152
154
 
155
+ def test_BASIC_auth_force
156
+ c = HTTPClient.new
157
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
158
+ begin
159
+ # WEBrick in ruby 1.8.7 uses 'BASIC' instead of 'Basic'
160
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
161
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
162
+ #
163
+ c.force_basic_auth = true
164
+ c.debug_dev = str = ''
165
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
166
+ assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth"))
167
+ assert_equal('Authorization: Basic YWRtaW46YWRtaW4='.upcase, str.split(/\r?\n/)[5].upcase)
168
+ ensure
169
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
170
+ end
171
+ end
172
+
173
+ def test_BASIC_auth_async
174
+ # async methods don't issure retry call so for successful authentication you need to set force_basic_auth flag
175
+ c = HTTPClient.new(:force_basic_auth => true)
176
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
177
+ begin
178
+ # WEBrick in ruby 1.8.7 uses 'BASIC' instead of 'Basic'
179
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
180
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
181
+ #
182
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
183
+ conn = c.get_async("http://localhost:#{serverport}/basic_auth")
184
+ assert_equal('basic_auth OK', conn.pop.body.read)
185
+ ensure
186
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
187
+ end
188
+ end
189
+
153
190
  def test_BASIC_auth_nil_uri
154
191
  c = HTTPClient.new
155
192
  webrick_backup = @basic_auth.instance_eval { @auth_scheme }
@@ -118,8 +118,8 @@ class TestClient < Test::Unit::TestCase
118
118
  escape_noproxy do
119
119
  begin
120
120
  @client.proxy = "http://あ"
121
- rescue
122
- assert_match(/InvalidURIError/, $!.class.to_s)
121
+ rescue => e
122
+ assert_match(/InvalidURIError/, e.class.to_s)
123
123
  end
124
124
  @client.proxy = ""
125
125
  assert_nil(@client.proxy)
@@ -177,8 +177,8 @@ class TestHTTPClient < Test::Unit::TestCase
177
177
  escape_noproxy do
178
178
  begin
179
179
  @client.proxy = "http://あ"
180
- rescue
181
- assert_match(/InvalidURIError/, $!.class.to_s)
180
+ rescue => e
181
+ assert_match(/InvalidURIError/, e.class.to_s)
182
182
  end
183
183
  @client.proxy = ""
184
184
  assert_nil(@client.proxy)
@@ -554,6 +554,28 @@ EOS
554
554
  assert(called)
555
555
  end
556
556
 
557
+ def test_get_content_with_base_url
558
+ @client = HTTPClient.new(:base_url => serverurl[0..-1])
559
+ assert_equal('hello', @client.get_content('/hello'))
560
+ assert_equal('hello', @client.get_content('/redirect1'))
561
+ assert_equal('hello', @client.get_content('/redirect2'))
562
+ @client.reset('/')
563
+ assert_raises(HTTPClient::BadResponseError) do
564
+ @client.get_content('/notfound')
565
+ end
566
+ assert_raises(HTTPClient::BadResponseError) do
567
+ @client.get_content('/redirect_self')
568
+ end
569
+ called = false
570
+ @client.redirect_uri_callback = lambda { |uri, res|
571
+ newuri = res.header['location'][0]
572
+ called = true
573
+ newuri
574
+ }
575
+ assert_equal('hello', @client.get_content('/relative_redirect'))
576
+ assert(called)
577
+ end
578
+
557
579
  GZIP_CONTENT = "\x1f\x8b\x08\x00\x1a\x96\xe0\x4c\x00\x03\xcb\x48\xcd\xc9\xc9\x07\x00\x86\xa6\x10\x36\x05\x00\x00\x00"
558
580
  DEFLATE_CONTENT = "\x78\x9c\xcb\x48\xcd\xc9\xc9\x07\x00\x06\x2c\x02\x15"
559
581
  GZIP_CONTENT.force_encoding('BINARY') if GZIP_CONTENT.respond_to?(:force_encoding)
@@ -640,6 +662,21 @@ EOS
640
662
  assert_nil(res.contenttype)
641
663
  end
642
664
 
665
+ def test_get_with_base_url
666
+ @client = HTTPClient.new(:base_url => serverurl[0..-1])
667
+ assert_equal("get", @client.get('/servlet').content)
668
+ param = {'1'=>'2', '3'=>'4'}
669
+ res = @client.get('/servlet', param)
670
+ assert_equal(param, params(res.header["x-query"][0]))
671
+ assert_nil(res.contenttype)
672
+ #
673
+ @client.base_url = serverurl[0..-1] + '/servlet'
674
+ url = '?5=6&7=8'
675
+ res = @client.get(url, param)
676
+ assert_equal(param.merge("5"=>"6", "7"=>"8"), params(res.header["x-query"][0]))
677
+ assert_nil(res.contenttype)
678
+ end
679
+
643
680
  def test_head_follow_redirect
644
681
  expected = urify(serverurl + 'hello')
645
682
  assert_equal(expected, @client.head(serverurl + 'hello', :follow_redirect => true).header.request_uri)
@@ -661,6 +698,22 @@ EOS
661
698
  assert_equal(param, params(res.header["x-query"][0]))
662
699
  end
663
700
 
701
+ def test_get_async_with_base_url
702
+ param = {'1'=>'2', '3'=>'4'}
703
+ @client = HTTPClient.new(:base_url => serverurl)
704
+
705
+ # Use preconfigured :base_url
706
+ conn = @client.get_async('servlet', param)
707
+ Thread.pass while !conn.finished?
708
+ res = conn.pop
709
+ assert_equal(param, params(res.header["x-query"][0]))
710
+ # full URL still works
711
+ conn = @client.get_async(serverurl + 'servlet', param)
712
+ Thread.pass while !conn.finished?
713
+ res = conn.pop
714
+ assert_equal(param, params(res.header["x-query"][0]))
715
+ end
716
+
664
717
  def test_get_async_for_largebody
665
718
  conn = @client.get_async(serverurl + 'largebody')
666
719
  res = conn.pop
data/test/test_ssl.rb CHANGED
@@ -230,7 +230,7 @@ private
230
230
 
231
231
  def setup_server_with_ssl_version(ssl_version)
232
232
  logger = Logger.new(STDERR)
233
- #logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level)
233
+ logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level)
234
234
  @server = WEBrick::HTTPServer.new(
235
235
  :BindAddress => "localhost",
236
236
  :Logger => logger,
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: httpclient
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.1
4
+ version: 2.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hiroshi Nakamura
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-18 00:00:00.000000000 Z
11
+ date: 2014-10-28 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: nahi@ruby-lang.org