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 +4 -4
- data/lib/httpclient.rb +47 -21
- data/lib/httpclient/auth.rb +5 -1
- data/lib/httpclient/include_client.rb +2 -0
- data/lib/httpclient/session.rb +7 -7
- data/lib/httpclient/version.rb +1 -1
- data/test/test_auth.rb +37 -0
- data/test/test_http-access2.rb +2 -2
- data/test/test_httpclient.rb +55 -2
- data/test/test_ssl.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96ac0d88857cdda17631e5d2f8d5055854324813
|
4
|
+
data.tar.gz: d481af07b3a540d43044a9065e47ea177d5f3aa7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
366
|
-
#
|
367
|
-
#
|
368
|
-
#
|
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
|
-
#
|
376
|
-
#
|
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
|
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 =
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
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 =
|
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 =
|
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 =
|
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 =
|
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
|
data/lib/httpclient/auth.rb
CHANGED
@@ -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
|
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
|
data/lib/httpclient/session.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
data/lib/httpclient/version.rb
CHANGED
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 }
|
data/test/test_http-access2.rb
CHANGED
@@ -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/,
|
121
|
+
rescue => e
|
122
|
+
assert_match(/InvalidURIError/, e.class.to_s)
|
123
123
|
end
|
124
124
|
@client.proxy = ""
|
125
125
|
assert_nil(@client.proxy)
|
data/test/test_httpclient.rb
CHANGED
@@ -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/,
|
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
|
-
|
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.
|
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-
|
11
|
+
date: 2014-10-28 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email: nahi@ruby-lang.org
|