httpclient 2.5.1 → 2.5.2

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