net-http-persistent-pool 2.10.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.
- checksums.yaml +7 -0
- data/.autotest +9 -0
- data/.gemtest +0 -0
- data/History.txt +346 -0
- data/Manifest.txt +15 -0
- data/README.rdoc +93 -0
- data/Rakefile +25 -0
- data/lib/net/http/faster.rb +27 -0
- data/lib/net/http/persistent.rb +1245 -0
- data/lib/net/http/persistent/connection.rb +40 -0
- data/lib/net/http/persistent/pool.rb +46 -0
- data/lib/net/http/persistent/ssl_reuse.rb +129 -0
- data/lib/net/http/persistent/timed_stack_multi.rb +69 -0
- data/test/test_net_http_persistent.rb +1670 -0
- data/test/test_net_http_persistent_ssl_reuse.rb +112 -0
- data/test/test_net_http_persistent_timed_stack_multi.rb +151 -0
- metadata +131 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
##
|
2
|
+
# A Net::HTTP connection wrapper that holds extra information for managing the
|
3
|
+
# connection's lifetime.
|
4
|
+
|
5
|
+
class Net::HTTP::Persistent::Connection # :nodoc:
|
6
|
+
|
7
|
+
attr_accessor :http
|
8
|
+
|
9
|
+
attr_accessor :last_use
|
10
|
+
|
11
|
+
attr_accessor :requests
|
12
|
+
|
13
|
+
attr_accessor :ssl_generation
|
14
|
+
|
15
|
+
def initialize http_class, http_args, ssl_generation
|
16
|
+
@http = http_class.new(*http_args)
|
17
|
+
@ssl_generation = ssl_generation
|
18
|
+
|
19
|
+
reset
|
20
|
+
end
|
21
|
+
|
22
|
+
def finish
|
23
|
+
@http.finish
|
24
|
+
rescue IOError
|
25
|
+
ensure
|
26
|
+
reset
|
27
|
+
end
|
28
|
+
|
29
|
+
def reset
|
30
|
+
@last_use = Net::HTTP::Persistent::EPOCH
|
31
|
+
@requests = 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def ressl ssl_generation
|
35
|
+
@ssl_generation = ssl_generation
|
36
|
+
|
37
|
+
finish
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Net::HTTP::Persistent::Pool < ConnectionPool # :nodoc:
|
2
|
+
|
3
|
+
attr_reader :available # :nodoc:
|
4
|
+
attr_reader :key # :nodoc:
|
5
|
+
|
6
|
+
def initialize(options = {}, &block)
|
7
|
+
super
|
8
|
+
|
9
|
+
@available = Net::HTTP::Persistent::TimedStackMulti.new(@size, &block)
|
10
|
+
@key = :"current-#{@available.object_id}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def checkin net_http_args
|
14
|
+
stack = Thread.current[@key][net_http_args]
|
15
|
+
|
16
|
+
raise ConnectionPool::Error, 'no connections are checked out' if
|
17
|
+
stack.empty?
|
18
|
+
|
19
|
+
conn = stack.pop
|
20
|
+
|
21
|
+
if stack.empty?
|
22
|
+
@available.push conn, connection_args: net_http_args
|
23
|
+
end
|
24
|
+
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def checkout net_http_args
|
29
|
+
stacks = Thread.current[@key] ||= Hash.new { |h, k| h[k] = [] }
|
30
|
+
stack = stacks[net_http_args]
|
31
|
+
|
32
|
+
if stack.empty? then
|
33
|
+
conn = @available.pop connection_args: net_http_args
|
34
|
+
else
|
35
|
+
conn = stack.last
|
36
|
+
end
|
37
|
+
|
38
|
+
stack.push conn
|
39
|
+
|
40
|
+
conn
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
require 'net/http/persistent/timed_stack_multi'
|
46
|
+
|
@@ -0,0 +1,129 @@
|
|
1
|
+
##
|
2
|
+
# This Net::HTTP subclass adds SSL session reuse and Server Name Indication
|
3
|
+
# (SNI) RFC 3546.
|
4
|
+
#
|
5
|
+
# DO NOT DEPEND UPON THIS CLASS
|
6
|
+
#
|
7
|
+
# This class is an implementation detail and is subject to change or removal
|
8
|
+
# at any time.
|
9
|
+
|
10
|
+
class Net::HTTP::Persistent::SSLReuse < Net::HTTP
|
11
|
+
|
12
|
+
@is_proxy_class = false
|
13
|
+
@proxy_addr = nil
|
14
|
+
@proxy_port = nil
|
15
|
+
@proxy_user = nil
|
16
|
+
@proxy_pass = nil
|
17
|
+
|
18
|
+
def initialize address, port = nil # :nodoc:
|
19
|
+
super
|
20
|
+
|
21
|
+
@ssl_session = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# From ruby trunk r33086 including http://redmine.ruby-lang.org/issues/5341
|
26
|
+
|
27
|
+
def connect # :nodoc:
|
28
|
+
D "opening connection to #{conn_address()}..."
|
29
|
+
s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }
|
30
|
+
D "opened"
|
31
|
+
if use_ssl?
|
32
|
+
ssl_parameters = Hash.new
|
33
|
+
iv_list = instance_variables
|
34
|
+
SSL_ATTRIBUTES.each do |name|
|
35
|
+
ivname = "@#{name}".intern
|
36
|
+
if iv_list.include?(ivname) and
|
37
|
+
value = instance_variable_get(ivname)
|
38
|
+
ssl_parameters[name] = value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
unless @ssl_context then
|
42
|
+
@ssl_context = OpenSSL::SSL::SSLContext.new
|
43
|
+
@ssl_context.set_params(ssl_parameters)
|
44
|
+
end
|
45
|
+
s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
|
46
|
+
s.sync_close = true
|
47
|
+
end
|
48
|
+
@socket = Net::BufferedIO.new(s)
|
49
|
+
@socket.read_timeout = @read_timeout
|
50
|
+
@socket.continue_timeout = @continue_timeout if
|
51
|
+
@socket.respond_to? :continue_timeout
|
52
|
+
@socket.debug_output = @debug_output
|
53
|
+
if use_ssl?
|
54
|
+
begin
|
55
|
+
if proxy?
|
56
|
+
@socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
|
57
|
+
@address, @port, HTTPVersion)
|
58
|
+
@socket.writeline "Host: #{@address}:#{@port}"
|
59
|
+
if proxy_user
|
60
|
+
credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
|
61
|
+
credential.delete!("\r\n")
|
62
|
+
@socket.writeline "Proxy-Authorization: Basic #{credential}"
|
63
|
+
end
|
64
|
+
@socket.writeline ''
|
65
|
+
Net::HTTPResponse.read_new(@socket).value
|
66
|
+
end
|
67
|
+
s.session = @ssl_session if @ssl_session
|
68
|
+
# Server Name Indication (SNI) RFC 3546
|
69
|
+
s.hostname = @address if s.respond_to? :hostname=
|
70
|
+
timeout(@open_timeout) { s.connect }
|
71
|
+
if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
|
72
|
+
s.post_connection_check(@address)
|
73
|
+
end
|
74
|
+
@ssl_session = s.session
|
75
|
+
rescue => exception
|
76
|
+
D "Conn close because of connect error #{exception}"
|
77
|
+
@socket.close if @socket and not @socket.closed?
|
78
|
+
raise exception
|
79
|
+
end
|
80
|
+
end
|
81
|
+
on_connect
|
82
|
+
end if RUBY_VERSION > '1.9'
|
83
|
+
|
84
|
+
##
|
85
|
+
# From ruby_1_8_7 branch r29865 including a modified
|
86
|
+
# http://redmine.ruby-lang.org/issues/5341
|
87
|
+
|
88
|
+
def connect # :nodoc:
|
89
|
+
D "opening connection to #{conn_address()}..."
|
90
|
+
s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }
|
91
|
+
D "opened"
|
92
|
+
if use_ssl?
|
93
|
+
unless @ssl_context.verify_mode
|
94
|
+
warn "warning: peer certificate won't be verified in this SSL session"
|
95
|
+
@ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
96
|
+
end
|
97
|
+
s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
|
98
|
+
s.sync_close = true
|
99
|
+
end
|
100
|
+
@socket = Net::BufferedIO.new(s)
|
101
|
+
@socket.read_timeout = @read_timeout
|
102
|
+
@socket.debug_output = @debug_output
|
103
|
+
if use_ssl?
|
104
|
+
if proxy?
|
105
|
+
@socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
|
106
|
+
@address, @port, HTTPVersion)
|
107
|
+
@socket.writeline "Host: #{@address}:#{@port}"
|
108
|
+
if proxy_user
|
109
|
+
credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
|
110
|
+
credential.delete!("\r\n")
|
111
|
+
@socket.writeline "Proxy-Authorization: Basic #{credential}"
|
112
|
+
end
|
113
|
+
@socket.writeline ''
|
114
|
+
Net::HTTPResponse.read_new(@socket).value
|
115
|
+
end
|
116
|
+
s.session = @ssl_session if @ssl_session
|
117
|
+
s.connect
|
118
|
+
if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
|
119
|
+
s.post_connection_check(@address)
|
120
|
+
end
|
121
|
+
@ssl_session = s.session
|
122
|
+
end
|
123
|
+
on_connect
|
124
|
+
end if RUBY_VERSION < '1.9'
|
125
|
+
|
126
|
+
private :connect
|
127
|
+
|
128
|
+
end
|
129
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class Net::HTTP::Persistent::TimedStackMulti < ConnectionPool::TimedStack
|
2
|
+
|
3
|
+
def initialize(size = 0, &block)
|
4
|
+
super
|
5
|
+
|
6
|
+
@enqueued = 0
|
7
|
+
@ques = Hash.new { |h, k| h[k] = [] }
|
8
|
+
@lru = {}
|
9
|
+
@key = :"connection_args-#{object_id}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def empty?
|
13
|
+
(@created - @enqueued) >= @max
|
14
|
+
end
|
15
|
+
|
16
|
+
def length
|
17
|
+
@max - @created + @enqueued
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def connection_stored? options = {} # :nodoc:
|
23
|
+
!@ques[options[:connection_args]].empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch_connection options = {} # :nodoc:
|
27
|
+
connection_args = options[:connection_args]
|
28
|
+
|
29
|
+
@enqueued -= 1
|
30
|
+
lru_update connection_args
|
31
|
+
@ques[connection_args].pop
|
32
|
+
end
|
33
|
+
|
34
|
+
def lru_update connection_args # :nodoc:
|
35
|
+
@lru.delete connection_args
|
36
|
+
@lru[connection_args] = true
|
37
|
+
end
|
38
|
+
|
39
|
+
def shutdown_connections # :nodoc:
|
40
|
+
@ques.each_key do |key|
|
41
|
+
super connection_args: key
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def store_connection obj, options = {} # :nodoc:
|
46
|
+
@ques[options[:connection_args]].push obj
|
47
|
+
@enqueued += 1
|
48
|
+
end
|
49
|
+
|
50
|
+
def try_create options = {} # :nodoc:
|
51
|
+
connection_args = options[:connection_args]
|
52
|
+
|
53
|
+
if @created >= @max && @enqueued >= 1
|
54
|
+
oldest, = @lru.first
|
55
|
+
@lru.delete oldest
|
56
|
+
@ques[oldest].pop
|
57
|
+
|
58
|
+
@created -= 1
|
59
|
+
end
|
60
|
+
|
61
|
+
if @created < @max
|
62
|
+
@created += 1
|
63
|
+
lru_update connection_args
|
64
|
+
return @create_block.call(connection_args)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
@@ -0,0 +1,1670 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'net/http/persistent'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
HAVE_OPENSSL = defined?(OpenSSL::SSL)
|
7
|
+
|
8
|
+
module Net::HTTP::Persistent::TestConnect
|
9
|
+
def self.included mod
|
10
|
+
mod.send :alias_method, :orig_connect, :connect
|
11
|
+
|
12
|
+
def mod.use_connect which
|
13
|
+
self.send :remove_method, :connect
|
14
|
+
self.send :alias_method, :connect, which
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def host_down_connect
|
19
|
+
raise Errno::EHOSTDOWN
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_connect
|
23
|
+
unless use_ssl? then
|
24
|
+
io = Object.new
|
25
|
+
def io.setsockopt(*a) @setsockopts ||= []; @setsockopts << a end
|
26
|
+
|
27
|
+
@socket = Net::BufferedIO.new io
|
28
|
+
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
32
|
+
io = open '/dev/null'
|
33
|
+
def io.setsockopt(*a) @setsockopts ||= []; @setsockopts << a end
|
34
|
+
|
35
|
+
@ssl_context ||= OpenSSL::SSL::SSLContext.new
|
36
|
+
|
37
|
+
@ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER unless
|
38
|
+
@ssl_context.verify_mode
|
39
|
+
|
40
|
+
s = OpenSSL::SSL::SSLSocket.new io, @ssl_context
|
41
|
+
|
42
|
+
@socket = Net::BufferedIO.new s
|
43
|
+
end
|
44
|
+
|
45
|
+
def refused_connect
|
46
|
+
raise Errno::ECONNREFUSED
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Net::HTTP
|
51
|
+
include Net::HTTP::Persistent::TestConnect
|
52
|
+
end
|
53
|
+
|
54
|
+
class Net::HTTP::Persistent::SSLReuse
|
55
|
+
include Net::HTTP::Persistent::TestConnect
|
56
|
+
end
|
57
|
+
|
58
|
+
class TestNetHttpPersistent < Minitest::Test
|
59
|
+
|
60
|
+
RUBY_1 = RUBY_VERSION < '2'
|
61
|
+
|
62
|
+
def setup
|
63
|
+
@http_class = if RUBY_1 and HAVE_OPENSSL then
|
64
|
+
Net::HTTP::Persistent::SSLReuse
|
65
|
+
else
|
66
|
+
Net::HTTP
|
67
|
+
end
|
68
|
+
|
69
|
+
@http = Net::HTTP::Persistent.new
|
70
|
+
|
71
|
+
@uri = URI.parse 'http://example.com/path'
|
72
|
+
|
73
|
+
ENV.delete 'http_proxy'
|
74
|
+
ENV.delete 'HTTP_PROXY'
|
75
|
+
ENV.delete 'http_proxy_user'
|
76
|
+
ENV.delete 'HTTP_PROXY_USER'
|
77
|
+
ENV.delete 'http_proxy_pass'
|
78
|
+
ENV.delete 'HTTP_PROXY_PASS'
|
79
|
+
ENV.delete 'no_proxy'
|
80
|
+
ENV.delete 'NO_PROXY'
|
81
|
+
|
82
|
+
Net::HTTP.use_connect :test_connect
|
83
|
+
Net::HTTP::Persistent::SSLReuse.use_connect :test_connect
|
84
|
+
end
|
85
|
+
|
86
|
+
def teardown
|
87
|
+
Net::HTTP.use_connect :orig_connect
|
88
|
+
Net::HTTP::Persistent::SSLReuse.use_connect :orig_connect
|
89
|
+
end
|
90
|
+
|
91
|
+
class BasicConnection
|
92
|
+
attr_accessor :started, :finished, :address, :port, :use_ssl,
|
93
|
+
:read_timeout, :open_timeout
|
94
|
+
attr_accessor :ciphers, :ssl_timeout, :ssl_version,
|
95
|
+
:verify_depth, :verify_mode, :cert_store,
|
96
|
+
:ca_file, :ca_path, :cert, :key
|
97
|
+
attr_reader :req, :debug_output
|
98
|
+
def initialize
|
99
|
+
@started, @finished = 0, 0
|
100
|
+
@address, @port = 'example.com', 80
|
101
|
+
@use_ssl = false
|
102
|
+
end
|
103
|
+
def finish
|
104
|
+
@finished += 1
|
105
|
+
@socket = nil
|
106
|
+
end
|
107
|
+
def finished?
|
108
|
+
@finished >= 1
|
109
|
+
end
|
110
|
+
def pipeline requests, &block
|
111
|
+
requests.map { |r| r.path }
|
112
|
+
end
|
113
|
+
def reset?
|
114
|
+
@started == @finished + 1
|
115
|
+
end
|
116
|
+
def set_debug_output io
|
117
|
+
@debug_output = io
|
118
|
+
end
|
119
|
+
def start
|
120
|
+
@started += 1
|
121
|
+
io = Object.new
|
122
|
+
def io.setsockopt(*a) @setsockopts ||= []; @setsockopts << a end
|
123
|
+
@socket = Net::BufferedIO.new io
|
124
|
+
end
|
125
|
+
def started?
|
126
|
+
@started >= 1
|
127
|
+
end
|
128
|
+
def proxy_address
|
129
|
+
end
|
130
|
+
def proxy_port
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def basic_connection
|
135
|
+
raise "#{@uri} is not HTTP" unless @uri.scheme.downcase == 'http'
|
136
|
+
|
137
|
+
net_http_args = [@uri.host, @uri.port]
|
138
|
+
|
139
|
+
connection = Net::HTTP::Persistent::Connection.allocate
|
140
|
+
connection.ssl_generation = @http.ssl_generation
|
141
|
+
connection.http = BasicConnection.new
|
142
|
+
connection.reset
|
143
|
+
|
144
|
+
@http.pool.available.push connection, connection_args: net_http_args
|
145
|
+
|
146
|
+
connection
|
147
|
+
end
|
148
|
+
|
149
|
+
def connection
|
150
|
+
connection = basic_connection
|
151
|
+
connection.last_use = Time.now
|
152
|
+
|
153
|
+
def (connection.http).request(req)
|
154
|
+
@req = req
|
155
|
+
r = Net::HTTPResponse.allocate
|
156
|
+
r.instance_variable_set :@header, {}
|
157
|
+
def r.http_version() '1.1' end
|
158
|
+
def r.read_body() :read_body end
|
159
|
+
yield r if block_given?
|
160
|
+
r
|
161
|
+
end
|
162
|
+
|
163
|
+
connection
|
164
|
+
end
|
165
|
+
|
166
|
+
def ssl_connection
|
167
|
+
raise "#{@uri} is not HTTPS" unless @uri.scheme.downcase == 'https'
|
168
|
+
|
169
|
+
net_http_args = [@uri.host, @uri.port]
|
170
|
+
|
171
|
+
connection = Net::HTTP::Persistent::Connection.allocate
|
172
|
+
connection.ssl_generation = @http.ssl_generation
|
173
|
+
connection.http = BasicConnection.new
|
174
|
+
connection.reset
|
175
|
+
|
176
|
+
@http.pool.available.push connection, connection_args: net_http_args
|
177
|
+
|
178
|
+
connection
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_initialize
|
182
|
+
assert_nil @http.proxy_uri
|
183
|
+
|
184
|
+
assert_empty @http.no_proxy
|
185
|
+
|
186
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
187
|
+
|
188
|
+
ssl_session_exists = OpenSSL::SSL.const_defined? :Session
|
189
|
+
|
190
|
+
assert_equal ssl_session_exists, @http.reuse_ssl_sessions
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_initialize_name
|
194
|
+
http = Net::HTTP::Persistent.new 'name'
|
195
|
+
assert_equal 'name', http.name
|
196
|
+
end
|
197
|
+
|
198
|
+
def test_initialize_no_ssl_session
|
199
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
200
|
+
|
201
|
+
skip "OpenSSL::SSL::Session does not exist on #{RUBY_PLATFORM}" unless
|
202
|
+
OpenSSL::SSL.const_defined? :Session
|
203
|
+
|
204
|
+
ssl_session = OpenSSL::SSL::Session
|
205
|
+
|
206
|
+
OpenSSL::SSL.send :remove_const, :Session
|
207
|
+
|
208
|
+
http = Net::HTTP::Persistent.new
|
209
|
+
|
210
|
+
refute http.reuse_ssl_sessions
|
211
|
+
ensure
|
212
|
+
OpenSSL::SSL.const_set :Session, ssl_session if ssl_session
|
213
|
+
end
|
214
|
+
|
215
|
+
def test_initialize_proxy
|
216
|
+
proxy_uri = URI.parse 'http://proxy.example'
|
217
|
+
|
218
|
+
http = Net::HTTP::Persistent.new nil, proxy_uri
|
219
|
+
|
220
|
+
assert_equal proxy_uri, http.proxy_uri
|
221
|
+
end
|
222
|
+
|
223
|
+
def test_ca_file_equals
|
224
|
+
@http.ca_file = :ca_file
|
225
|
+
|
226
|
+
assert_equal :ca_file, @http.ca_file
|
227
|
+
assert_equal 1, @http.ssl_generation
|
228
|
+
end
|
229
|
+
|
230
|
+
def test_ca_path_equals
|
231
|
+
@http.ca_path = :ca_path
|
232
|
+
|
233
|
+
assert_equal :ca_path, @http.ca_path
|
234
|
+
assert_equal 1, @http.ssl_generation
|
235
|
+
end
|
236
|
+
|
237
|
+
def test_can_retry_eh_change_requests
|
238
|
+
post = Net::HTTP::Post.new '/'
|
239
|
+
|
240
|
+
refute @http.can_retry? post
|
241
|
+
|
242
|
+
@http.retry_change_requests = true
|
243
|
+
|
244
|
+
assert @http.can_retry? post
|
245
|
+
end
|
246
|
+
|
247
|
+
if RUBY_1 then
|
248
|
+
def test_can_retry_eh_idempotent
|
249
|
+
head = Net::HTTP::Head.new '/'
|
250
|
+
|
251
|
+
assert @http.can_retry? head
|
252
|
+
|
253
|
+
post = Net::HTTP::Post.new '/'
|
254
|
+
|
255
|
+
refute @http.can_retry? post
|
256
|
+
end
|
257
|
+
else
|
258
|
+
def test_can_retry_eh_idempotent
|
259
|
+
head = Net::HTTP::Head.new '/'
|
260
|
+
|
261
|
+
assert @http.can_retry? head
|
262
|
+
refute @http.can_retry? head, true
|
263
|
+
|
264
|
+
post = Net::HTTP::Post.new '/'
|
265
|
+
|
266
|
+
refute @http.can_retry? post
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def test_cert_store_equals
|
271
|
+
@http.cert_store = :cert_store
|
272
|
+
|
273
|
+
assert_equal :cert_store, @http.cert_store
|
274
|
+
assert_equal 1, @http.ssl_generation
|
275
|
+
end
|
276
|
+
|
277
|
+
def test_certificate_equals
|
278
|
+
@http.certificate = :cert
|
279
|
+
|
280
|
+
assert_equal :cert, @http.certificate
|
281
|
+
assert_equal 1, @http.ssl_generation
|
282
|
+
end
|
283
|
+
|
284
|
+
def test_ciphers_equals
|
285
|
+
@http.ciphers = :ciphers
|
286
|
+
|
287
|
+
assert_equal :ciphers, @http.ciphers
|
288
|
+
assert_equal 1, @http.ssl_generation
|
289
|
+
end
|
290
|
+
|
291
|
+
def test_connection_for
|
292
|
+
@http.open_timeout = 123
|
293
|
+
@http.read_timeout = 321
|
294
|
+
@http.idle_timeout = 42
|
295
|
+
|
296
|
+
used = @http.connection_for @uri do |c|
|
297
|
+
assert_kind_of @http_class, c.http
|
298
|
+
|
299
|
+
assert c.http.started?
|
300
|
+
refute c.http.proxy?
|
301
|
+
|
302
|
+
assert_equal 123, c.http.open_timeout
|
303
|
+
assert_equal 321, c.http.read_timeout
|
304
|
+
assert_equal 42, c.http.keep_alive_timeout unless RUBY_1
|
305
|
+
|
306
|
+
c
|
307
|
+
end
|
308
|
+
|
309
|
+
stored = @http.pool.checkout ['example.com', 80]
|
310
|
+
|
311
|
+
assert_same used, stored
|
312
|
+
end
|
313
|
+
|
314
|
+
def test_connection_for_cached
|
315
|
+
cached = basic_connection
|
316
|
+
cached.http.start
|
317
|
+
|
318
|
+
@http.read_timeout = 5
|
319
|
+
|
320
|
+
@http.connection_for @uri do |c|
|
321
|
+
assert c.http.started?
|
322
|
+
|
323
|
+
assert_equal 5, c.http.read_timeout
|
324
|
+
|
325
|
+
assert_same cached, c
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def test_connection_for_closed
|
330
|
+
cached = basic_connection
|
331
|
+
cached.http.start
|
332
|
+
if Socket.const_defined? :TCP_NODELAY then
|
333
|
+
io = Object.new
|
334
|
+
def io.setsockopt(*a) raise IOError, 'closed stream' end
|
335
|
+
cached.instance_variable_set :@socket, Net::BufferedIO.new(io)
|
336
|
+
end
|
337
|
+
|
338
|
+
@http.connection_for @uri do |c|
|
339
|
+
assert c.http.started?
|
340
|
+
|
341
|
+
socket = c.http.instance_variable_get :@socket
|
342
|
+
|
343
|
+
refute_includes socket.io.instance_variables, :@setsockopt
|
344
|
+
refute_includes socket.io.instance_variables, '@setsockopt'
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
def test_connection_for_debug_output
|
349
|
+
io = StringIO.new
|
350
|
+
@http.debug_output = io
|
351
|
+
|
352
|
+
@http.connection_for @uri do |c|
|
353
|
+
assert c.http.started?
|
354
|
+
assert_equal io, c.http.instance_variable_get(:@debug_output)
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
def test_connection_for_cached_expire_always
|
359
|
+
cached = basic_connection
|
360
|
+
cached.http.start
|
361
|
+
cached.requests = 10
|
362
|
+
cached.last_use = Time.now # last used right now
|
363
|
+
|
364
|
+
@http.idle_timeout = 0
|
365
|
+
|
366
|
+
@http.connection_for @uri do |c|
|
367
|
+
assert c.http.started?
|
368
|
+
|
369
|
+
assert_same cached, c
|
370
|
+
|
371
|
+
assert_equal 0, c.requests, 'connection reset due to timeout'
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
def test_connection_for_cached_expire_never
|
376
|
+
cached = basic_connection
|
377
|
+
cached.http.start
|
378
|
+
cached.requests = 10
|
379
|
+
cached.last_use = Time.now # last used right now
|
380
|
+
|
381
|
+
@http.idle_timeout = nil
|
382
|
+
|
383
|
+
@http.connection_for @uri do |c|
|
384
|
+
assert c.http.started?
|
385
|
+
|
386
|
+
assert_same cached, c
|
387
|
+
|
388
|
+
assert_equal 10, c.requests, 'connection reset despite no timeout'
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
def test_connection_for_cached_expired
|
393
|
+
cached = basic_connection
|
394
|
+
cached.http.start
|
395
|
+
cached.requests = 10
|
396
|
+
cached.last_use = Time.now - 3600
|
397
|
+
|
398
|
+
@http.connection_for @uri do |c|
|
399
|
+
assert c.http.started?
|
400
|
+
|
401
|
+
assert_same cached, c
|
402
|
+
assert_equal 0, cached.requests, 'connection not reset due to timeout'
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def test_connection_for_finished_ssl
|
407
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
408
|
+
|
409
|
+
uri = URI.parse 'https://example.com/path'
|
410
|
+
|
411
|
+
@http.connection_for uri do |c|
|
412
|
+
assert c.http.started?
|
413
|
+
assert c.http.use_ssl?
|
414
|
+
|
415
|
+
@http.finish c
|
416
|
+
|
417
|
+
refute c.http.started?
|
418
|
+
end
|
419
|
+
|
420
|
+
@http.connection_for uri do |c2|
|
421
|
+
assert c2.http.started?
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
def test_connection_for_host_down
|
426
|
+
c = basic_connection
|
427
|
+
def (c.http).start; raise Errno::EHOSTDOWN end
|
428
|
+
def (c.http).started?; false end
|
429
|
+
|
430
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
431
|
+
@http.connection_for @uri do end
|
432
|
+
end
|
433
|
+
|
434
|
+
assert_equal 'host down: example.com:80', e.message
|
435
|
+
end
|
436
|
+
|
437
|
+
def test_connection_for_http_class_with_fakeweb
|
438
|
+
Object.send :const_set, :FakeWeb, nil
|
439
|
+
|
440
|
+
@http.connection_for @uri do |c|
|
441
|
+
assert_instance_of Net::HTTP, c.http
|
442
|
+
end
|
443
|
+
ensure
|
444
|
+
if Object.const_defined?(:FakeWeb) then
|
445
|
+
Object.send :remove_const, :FakeWeb
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
def test_connection_for_http_class_with_webmock
|
450
|
+
Object.send :const_set, :WebMock, nil
|
451
|
+
@http.connection_for @uri do |c|
|
452
|
+
assert_instance_of Net::HTTP, c.http
|
453
|
+
end
|
454
|
+
ensure
|
455
|
+
if Object.const_defined?(:WebMock) then
|
456
|
+
Object.send :remove_const, :WebMock
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def test_connection_for_http_class_with_artifice
|
461
|
+
Object.send :const_set, :Artifice, nil
|
462
|
+
@http.connection_for @uri do |c|
|
463
|
+
assert_instance_of Net::HTTP, c.http
|
464
|
+
end
|
465
|
+
ensure
|
466
|
+
if Object.const_defined?(:Artifice) then
|
467
|
+
Object.send :remove_const, :Artifice
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
def test_connection_for_name
|
472
|
+
http = Net::HTTP::Persistent.new 'name'
|
473
|
+
uri = URI.parse 'http://example/'
|
474
|
+
|
475
|
+
http.connection_for uri do |c|
|
476
|
+
assert c.http.started?
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
def test_connection_for_no_ssl_reuse
|
481
|
+
@http.reuse_ssl_sessions = false
|
482
|
+
@http.open_timeout = 123
|
483
|
+
@http.read_timeout = 321
|
484
|
+
|
485
|
+
@http.connection_for @uri do |c|
|
486
|
+
assert_instance_of Net::HTTP, c.http
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
def test_connection_for_proxy
|
491
|
+
uri = URI.parse 'http://proxy.example'
|
492
|
+
uri.user = 'johndoe'
|
493
|
+
uri.password = 'muffins'
|
494
|
+
|
495
|
+
http = Net::HTTP::Persistent.new nil, uri
|
496
|
+
|
497
|
+
used = http.connection_for @uri do |c|
|
498
|
+
assert c.http.started?
|
499
|
+
assert c.http.proxy?
|
500
|
+
|
501
|
+
c
|
502
|
+
end
|
503
|
+
|
504
|
+
stored = http.pool.checkout ['example.com', 80,
|
505
|
+
'proxy.example', 80,
|
506
|
+
'johndoe', 'muffins']
|
507
|
+
|
508
|
+
assert_same used, stored
|
509
|
+
end
|
510
|
+
|
511
|
+
def test_connection_for_proxy_unescaped
|
512
|
+
uri = URI.parse 'http://proxy.example'
|
513
|
+
uri.user = 'john%40doe'
|
514
|
+
uri.password = 'muf%3Afins'
|
515
|
+
uri.freeze
|
516
|
+
|
517
|
+
http = Net::HTTP::Persistent.new nil, uri
|
518
|
+
|
519
|
+
http.connection_for @uri do end
|
520
|
+
|
521
|
+
stored = http.pool.checkout ['example.com', 80,
|
522
|
+
'proxy.example', 80,
|
523
|
+
'john@doe', 'muf:fins']
|
524
|
+
|
525
|
+
assert stored
|
526
|
+
end
|
527
|
+
|
528
|
+
def test_connection_for_proxy_host_down
|
529
|
+
Net::HTTP.use_connect :host_down_connect
|
530
|
+
Net::HTTP::Persistent::SSLReuse.use_connect :host_down_connect
|
531
|
+
|
532
|
+
uri = URI.parse 'http://proxy.example'
|
533
|
+
uri.user = 'johndoe'
|
534
|
+
uri.password = 'muffins'
|
535
|
+
|
536
|
+
http = Net::HTTP::Persistent.new nil, uri
|
537
|
+
|
538
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
539
|
+
http.connection_for @uri do end
|
540
|
+
end
|
541
|
+
|
542
|
+
assert_equal 'host down: proxy.example:80', e.message
|
543
|
+
end
|
544
|
+
|
545
|
+
def test_connection_for_proxy_refused
|
546
|
+
Net::HTTP.use_connect :refused_connect
|
547
|
+
Net::HTTP::Persistent::SSLReuse.use_connect :refused_connect
|
548
|
+
|
549
|
+
uri = URI.parse 'http://proxy.example'
|
550
|
+
uri.user = 'johndoe'
|
551
|
+
uri.password = 'muffins'
|
552
|
+
|
553
|
+
http = Net::HTTP::Persistent.new nil, uri
|
554
|
+
|
555
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
556
|
+
http.connection_for @uri do end
|
557
|
+
end
|
558
|
+
|
559
|
+
assert_equal 'connection refused: proxy.example:80', e.message
|
560
|
+
end
|
561
|
+
|
562
|
+
def test_connection_for_no_proxy
|
563
|
+
uri = URI.parse 'http://proxy.example'
|
564
|
+
uri.user = 'johndoe'
|
565
|
+
uri.password = 'muffins'
|
566
|
+
uri.query = 'no_proxy=example.com'
|
567
|
+
|
568
|
+
http = Net::HTTP::Persistent.new nil, uri
|
569
|
+
|
570
|
+
http.connection_for @uri do |c|
|
571
|
+
assert c.http.started?
|
572
|
+
refute c.http.proxy?
|
573
|
+
end
|
574
|
+
|
575
|
+
stored = http.pool.checkout ['example.com', 80]
|
576
|
+
|
577
|
+
assert stored
|
578
|
+
end
|
579
|
+
|
580
|
+
def test_connection_for_refused
|
581
|
+
Net::HTTP.use_connect :refused_connect
|
582
|
+
Net::HTTP::Persistent::SSLReuse.use_connect :refused_connect
|
583
|
+
|
584
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
585
|
+
@http.connection_for @uri do end
|
586
|
+
end
|
587
|
+
|
588
|
+
assert_equal 'connection refused: example.com:80', e.message
|
589
|
+
end
|
590
|
+
|
591
|
+
def test_connection_for_ssl
|
592
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
593
|
+
|
594
|
+
uri = URI.parse 'https://example.com/path'
|
595
|
+
|
596
|
+
@http.connection_for uri do |c|
|
597
|
+
assert c.http.started?
|
598
|
+
assert c.http.use_ssl?
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
def test_connection_for_ssl_cached
|
603
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
604
|
+
|
605
|
+
@uri = URI.parse 'https://example.com/path'
|
606
|
+
|
607
|
+
cached = ssl_connection
|
608
|
+
|
609
|
+
@http.connection_for @uri do |c|
|
610
|
+
assert_same cached, c
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
def test_connection_for_ssl_cached_reconnect
|
615
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
616
|
+
|
617
|
+
@uri = URI.parse 'https://example.com/path'
|
618
|
+
|
619
|
+
cached = ssl_connection
|
620
|
+
|
621
|
+
ssl_generation = @http.ssl_generation
|
622
|
+
|
623
|
+
@http.reconnect_ssl
|
624
|
+
|
625
|
+
@http.connection_for @uri do |c|
|
626
|
+
assert_same cached, c
|
627
|
+
refute_equal ssl_generation, c.ssl_generation
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
def test_connection_for_ssl_case
|
632
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
633
|
+
|
634
|
+
uri = URI.parse 'HTTPS://example.com/path'
|
635
|
+
@http.connection_for uri do |c|
|
636
|
+
assert c.http.started?
|
637
|
+
assert c.http.use_ssl?
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
def test_connection_for_timeout
|
642
|
+
cached = basic_connection
|
643
|
+
cached.http.start
|
644
|
+
cached.requests = 10
|
645
|
+
cached.last_use = Time.now - 6
|
646
|
+
|
647
|
+
@http.connection_for @uri do |c|
|
648
|
+
assert c.http.started?
|
649
|
+
assert_equal 0, c.requests
|
650
|
+
|
651
|
+
assert_same cached, c
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
def test_error_message
|
656
|
+
c = basic_connection
|
657
|
+
c.last_use = Time.now - 1
|
658
|
+
c.requests = 5
|
659
|
+
|
660
|
+
message = @http.error_message c
|
661
|
+
assert_match %r%after 4 requests on #{c.http.object_id}%, message
|
662
|
+
assert_match %r%, last used [\d.]+ seconds ago%, message
|
663
|
+
end
|
664
|
+
|
665
|
+
def test_escape
|
666
|
+
assert_nil @http.escape nil
|
667
|
+
|
668
|
+
assert_equal '+%3F', @http.escape(' ?')
|
669
|
+
end
|
670
|
+
|
671
|
+
def test_unescape
|
672
|
+
assert_nil @http.unescape nil
|
673
|
+
|
674
|
+
assert_equal ' ?', @http.unescape('+%3F')
|
675
|
+
end
|
676
|
+
|
677
|
+
def test_expired_eh
|
678
|
+
c = basic_connection
|
679
|
+
c.requests = 0
|
680
|
+
c.last_use = Time.now - 11
|
681
|
+
|
682
|
+
@http.idle_timeout = 0
|
683
|
+
assert @http.expired? c
|
684
|
+
|
685
|
+
@http.idle_timeout = 10
|
686
|
+
assert @http.expired? c
|
687
|
+
|
688
|
+
@http.idle_timeout = 11
|
689
|
+
assert @http.expired? c
|
690
|
+
|
691
|
+
@http.idle_timeout = 12
|
692
|
+
refute @http.expired? c
|
693
|
+
|
694
|
+
@http.idle_timeout = nil
|
695
|
+
refute @http.expired? c
|
696
|
+
end
|
697
|
+
|
698
|
+
def test_expired_due_to_max_requests
|
699
|
+
c = basic_connection
|
700
|
+
c.requests = 0
|
701
|
+
c.last_use = Time.now
|
702
|
+
|
703
|
+
refute @http.expired? c
|
704
|
+
|
705
|
+
c.requests = 10
|
706
|
+
refute @http.expired? c
|
707
|
+
|
708
|
+
@http.max_requests = 10
|
709
|
+
assert @http.expired? c
|
710
|
+
|
711
|
+
c.requests = 9
|
712
|
+
refute @http.expired? c
|
713
|
+
end
|
714
|
+
|
715
|
+
def test_finish
|
716
|
+
c = basic_connection
|
717
|
+
c.requests = 5
|
718
|
+
|
719
|
+
@http.finish c
|
720
|
+
|
721
|
+
refute c.http.started?
|
722
|
+
assert c.http.finished?
|
723
|
+
|
724
|
+
assert_equal 0, c.requests
|
725
|
+
assert_equal Net::HTTP::Persistent::EPOCH, c.last_use
|
726
|
+
end
|
727
|
+
|
728
|
+
def test_finish_io_error
|
729
|
+
c = basic_connection
|
730
|
+
def (c.http).finish; @finished += 1; raise IOError end
|
731
|
+
c.requests = 5
|
732
|
+
|
733
|
+
@http.finish c
|
734
|
+
|
735
|
+
refute c.http.started?
|
736
|
+
assert c.http.finished?
|
737
|
+
end
|
738
|
+
|
739
|
+
def test_http_version
|
740
|
+
assert_nil @http.http_version @uri
|
741
|
+
|
742
|
+
connection
|
743
|
+
|
744
|
+
@http.request @uri
|
745
|
+
|
746
|
+
assert_equal '1.1', @http.http_version(@uri)
|
747
|
+
end
|
748
|
+
|
749
|
+
def test_idempotent_eh
|
750
|
+
assert @http.idempotent? Net::HTTP::Delete.new '/'
|
751
|
+
assert @http.idempotent? Net::HTTP::Get.new '/'
|
752
|
+
assert @http.idempotent? Net::HTTP::Head.new '/'
|
753
|
+
assert @http.idempotent? Net::HTTP::Options.new '/'
|
754
|
+
assert @http.idempotent? Net::HTTP::Put.new '/'
|
755
|
+
assert @http.idempotent? Net::HTTP::Trace.new '/'
|
756
|
+
|
757
|
+
refute @http.idempotent? Net::HTTP::Post.new '/'
|
758
|
+
end
|
759
|
+
|
760
|
+
def test_max_age
|
761
|
+
assert_in_delta Time.now - 5, @http.max_age
|
762
|
+
|
763
|
+
@http.idle_timeout = nil
|
764
|
+
|
765
|
+
assert_in_delta Time.now + 1, @http.max_age
|
766
|
+
end
|
767
|
+
|
768
|
+
def test_normalize_uri
|
769
|
+
assert_equal 'http://example', @http.normalize_uri('example')
|
770
|
+
assert_equal 'http://example', @http.normalize_uri('http://example')
|
771
|
+
assert_equal 'https://example', @http.normalize_uri('https://example')
|
772
|
+
end
|
773
|
+
|
774
|
+
def test_override_haeders
|
775
|
+
assert_empty @http.override_headers
|
776
|
+
|
777
|
+
@http.override_headers['User-Agent'] = 'MyCustomAgent'
|
778
|
+
|
779
|
+
expected = { 'User-Agent' => 'MyCustomAgent' }
|
780
|
+
|
781
|
+
assert_equal expected, @http.override_headers
|
782
|
+
end
|
783
|
+
|
784
|
+
def test_pipeline
|
785
|
+
skip 'net-http-pipeline not installed' unless defined?(Net::HTTP::Pipeline)
|
786
|
+
|
787
|
+
cached = basic_connection
|
788
|
+
cached.start
|
789
|
+
|
790
|
+
requests = [
|
791
|
+
Net::HTTP::Get.new((@uri + '1').request_uri),
|
792
|
+
Net::HTTP::Get.new((@uri + '2').request_uri),
|
793
|
+
]
|
794
|
+
|
795
|
+
responses = @http.pipeline @uri, requests
|
796
|
+
|
797
|
+
assert_equal 2, responses.length
|
798
|
+
assert_equal '/1', responses.first
|
799
|
+
assert_equal '/2', responses.last
|
800
|
+
end
|
801
|
+
|
802
|
+
def test_private_key_equals
|
803
|
+
@http.private_key = :private_key
|
804
|
+
|
805
|
+
assert_equal :private_key, @http.private_key
|
806
|
+
assert_equal 1, @http.ssl_generation
|
807
|
+
end
|
808
|
+
|
809
|
+
def test_proxy_equals_env
|
810
|
+
ENV['http_proxy'] = 'proxy.example'
|
811
|
+
|
812
|
+
@http.proxy = :ENV
|
813
|
+
|
814
|
+
assert_equal URI.parse('http://proxy.example'), @http.proxy_uri
|
815
|
+
|
816
|
+
assert_equal 1, @http.generation, 'generation'
|
817
|
+
assert_equal 1, @http.ssl_generation, 'ssl_generation'
|
818
|
+
end
|
819
|
+
|
820
|
+
def test_proxy_equals_nil
|
821
|
+
@http.proxy = nil
|
822
|
+
|
823
|
+
assert_equal nil, @http.proxy_uri
|
824
|
+
|
825
|
+
assert_equal 1, @http.generation, 'generation'
|
826
|
+
assert_equal 1, @http.ssl_generation, 'ssl_generation'
|
827
|
+
end
|
828
|
+
|
829
|
+
def test_proxy_equals_uri
|
830
|
+
proxy_uri = URI.parse 'http://proxy.example'
|
831
|
+
|
832
|
+
@http.proxy = proxy_uri
|
833
|
+
|
834
|
+
assert_equal proxy_uri, @http.proxy_uri
|
835
|
+
end
|
836
|
+
|
837
|
+
def test_proxy_from_env
|
838
|
+
ENV['http_proxy'] = 'proxy.example'
|
839
|
+
ENV['http_proxy_user'] = 'johndoe'
|
840
|
+
ENV['http_proxy_pass'] = 'muffins'
|
841
|
+
ENV['NO_PROXY'] = 'localhost,example.com'
|
842
|
+
|
843
|
+
uri = @http.proxy_from_env
|
844
|
+
|
845
|
+
expected = URI.parse 'http://proxy.example'
|
846
|
+
expected.user = 'johndoe'
|
847
|
+
expected.password = 'muffins'
|
848
|
+
expected.query = 'no_proxy=localhost%2Cexample.com'
|
849
|
+
|
850
|
+
assert_equal expected, uri
|
851
|
+
end
|
852
|
+
|
853
|
+
def test_proxy_from_env_lower
|
854
|
+
ENV['http_proxy'] = 'proxy.example'
|
855
|
+
ENV['http_proxy_user'] = 'johndoe'
|
856
|
+
ENV['http_proxy_pass'] = 'muffins'
|
857
|
+
ENV['no_proxy'] = 'localhost,example.com'
|
858
|
+
|
859
|
+
uri = @http.proxy_from_env
|
860
|
+
|
861
|
+
expected = URI.parse 'http://proxy.example'
|
862
|
+
expected.user = 'johndoe'
|
863
|
+
expected.password = 'muffins'
|
864
|
+
expected.query = 'no_proxy=localhost%2Cexample.com'
|
865
|
+
|
866
|
+
assert_equal expected, uri
|
867
|
+
end
|
868
|
+
|
869
|
+
def test_proxy_from_env_nil
|
870
|
+
uri = @http.proxy_from_env
|
871
|
+
|
872
|
+
assert_nil uri
|
873
|
+
|
874
|
+
ENV['http_proxy'] = ''
|
875
|
+
|
876
|
+
uri = @http.proxy_from_env
|
877
|
+
|
878
|
+
assert_nil uri
|
879
|
+
end
|
880
|
+
|
881
|
+
def test_proxy_from_env_no_proxy_star
|
882
|
+
uri = @http.proxy_from_env
|
883
|
+
|
884
|
+
assert_nil uri
|
885
|
+
|
886
|
+
ENV['http_proxy'] = 'proxy.example'
|
887
|
+
ENV['no_proxy'] = '*'
|
888
|
+
|
889
|
+
uri = @http.proxy_from_env
|
890
|
+
|
891
|
+
assert_nil uri
|
892
|
+
end
|
893
|
+
|
894
|
+
def test_proxy_bypass
|
895
|
+
ENV['http_proxy'] = 'proxy.example'
|
896
|
+
ENV['no_proxy'] = 'localhost,example.com:80'
|
897
|
+
|
898
|
+
@http.proxy = :ENV
|
899
|
+
|
900
|
+
assert @http.proxy_bypass? 'localhost', 80
|
901
|
+
assert @http.proxy_bypass? 'localhost', 443
|
902
|
+
assert @http.proxy_bypass? 'LOCALHOST', 80
|
903
|
+
assert @http.proxy_bypass? 'example.com', 80
|
904
|
+
refute @http.proxy_bypass? 'example.com', 443
|
905
|
+
assert @http.proxy_bypass? 'www.example.com', 80
|
906
|
+
refute @http.proxy_bypass? 'www.example.com', 443
|
907
|
+
assert @http.proxy_bypass? 'endingexample.com', 80
|
908
|
+
refute @http.proxy_bypass? 'example.org', 80
|
909
|
+
end
|
910
|
+
|
911
|
+
def test_proxy_bypass_space
|
912
|
+
ENV['http_proxy'] = 'proxy.example'
|
913
|
+
ENV['no_proxy'] = 'localhost, example.com'
|
914
|
+
|
915
|
+
@http.proxy = :ENV
|
916
|
+
|
917
|
+
assert @http.proxy_bypass? 'example.com', 80
|
918
|
+
refute @http.proxy_bypass? 'example.org', 80
|
919
|
+
end
|
920
|
+
|
921
|
+
def test_proxy_bypass_trailing
|
922
|
+
ENV['http_proxy'] = 'proxy.example'
|
923
|
+
ENV['no_proxy'] = 'localhost,example.com,'
|
924
|
+
|
925
|
+
@http.proxy = :ENV
|
926
|
+
|
927
|
+
assert @http.proxy_bypass? 'example.com', 80
|
928
|
+
refute @http.proxy_bypass? 'example.org', 80
|
929
|
+
end
|
930
|
+
|
931
|
+
def test_proxy_bypass_double_comma
|
932
|
+
ENV['http_proxy'] = 'proxy.example'
|
933
|
+
ENV['no_proxy'] = 'localhost,,example.com'
|
934
|
+
|
935
|
+
@http.proxy = :ENV
|
936
|
+
|
937
|
+
assert @http.proxy_bypass? 'example.com', 80
|
938
|
+
refute @http.proxy_bypass? 'example.org', 80
|
939
|
+
end
|
940
|
+
|
941
|
+
def test_reconnect
|
942
|
+
result = @http.reconnect
|
943
|
+
|
944
|
+
assert_equal 1, result
|
945
|
+
end
|
946
|
+
|
947
|
+
def test_reconnect_ssl
|
948
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
949
|
+
|
950
|
+
@uri = URI 'https://example.com'
|
951
|
+
now = Time.now
|
952
|
+
|
953
|
+
ssl_http = ssl_connection
|
954
|
+
|
955
|
+
def (ssl_http.http).finish
|
956
|
+
@started = 0
|
957
|
+
end
|
958
|
+
|
959
|
+
used1 = @http.connection_for @uri do |c|
|
960
|
+
c.requests = 1
|
961
|
+
c.last_use = now
|
962
|
+
c
|
963
|
+
end
|
964
|
+
|
965
|
+
assert_equal OpenSSL::SSL::VERIFY_PEER, used1.http.verify_mode
|
966
|
+
|
967
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
968
|
+
@http.reconnect_ssl
|
969
|
+
|
970
|
+
used2 = @http.connection_for @uri do |c|
|
971
|
+
c
|
972
|
+
end
|
973
|
+
|
974
|
+
assert_same used1, used2
|
975
|
+
|
976
|
+
assert_equal OpenSSL::SSL::VERIFY_NONE, used2.http.verify_mode,
|
977
|
+
'verify mode must change'
|
978
|
+
assert_equal 0, used2.requests
|
979
|
+
assert_equal Net::HTTP::Persistent::EPOCH, used2.last_use
|
980
|
+
end
|
981
|
+
|
982
|
+
def test_request
|
983
|
+
@http.override_headers['user-agent'] = 'test ua'
|
984
|
+
@http.headers['accept'] = 'text/*'
|
985
|
+
c = connection
|
986
|
+
|
987
|
+
res = @http.request @uri
|
988
|
+
req = c.http.req
|
989
|
+
|
990
|
+
assert_kind_of Net::HTTPResponse, res
|
991
|
+
|
992
|
+
assert_kind_of Net::HTTP::Get, req
|
993
|
+
assert_equal '/path', req.path
|
994
|
+
|
995
|
+
assert_equal 'test ua', req['user-agent']
|
996
|
+
assert_match %r%text/\*%, req['accept']
|
997
|
+
|
998
|
+
assert_equal 'keep-alive', req['connection']
|
999
|
+
assert_equal '30', req['keep-alive']
|
1000
|
+
|
1001
|
+
assert_in_delta Time.now, c.last_use
|
1002
|
+
|
1003
|
+
assert_equal 1, c.requests
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
def test_request_ETIMEDOUT
|
1007
|
+
c = basic_connection
|
1008
|
+
def (c.http).request(*a) raise Errno::ETIMEDOUT, "timed out" end
|
1009
|
+
|
1010
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
1011
|
+
@http.request @uri
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
assert_equal 0, c.requests
|
1015
|
+
assert_match %r%too many connection resets%, e.message
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
def test_request_bad_response
|
1019
|
+
c = basic_connection
|
1020
|
+
def (c.http).request(*a) raise Net::HTTPBadResponse end
|
1021
|
+
|
1022
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
1023
|
+
@http.request @uri
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
assert_equal 0, c.requests
|
1027
|
+
assert_match %r%too many bad responses%, e.message
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
if RUBY_1 then
|
1031
|
+
def test_request_bad_response_retry
|
1032
|
+
c = basic_connection
|
1033
|
+
def (c.http).request(*a)
|
1034
|
+
if defined? @called then
|
1035
|
+
r = Net::HTTPResponse.allocate
|
1036
|
+
r.instance_variable_set :@header, {}
|
1037
|
+
def r.http_version() '1.1' end
|
1038
|
+
r
|
1039
|
+
else
|
1040
|
+
@called = true
|
1041
|
+
raise Net::HTTPBadResponse
|
1042
|
+
end
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
@http.request @uri
|
1046
|
+
|
1047
|
+
assert c.http.finished?
|
1048
|
+
end
|
1049
|
+
else
|
1050
|
+
def test_request_bad_response_retry
|
1051
|
+
c = basic_connection
|
1052
|
+
def (c.http).request(*a)
|
1053
|
+
raise Net::HTTPBadResponse
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
assert_raises Net::HTTP::Persistent::Error do
|
1057
|
+
@http.request @uri
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
assert c.http.finished?
|
1061
|
+
end
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
def test_request_bad_response_unsafe
|
1065
|
+
c = basic_connection
|
1066
|
+
def (c.http).request(*a)
|
1067
|
+
if instance_variable_defined? :@request then
|
1068
|
+
raise 'POST must not be retried'
|
1069
|
+
else
|
1070
|
+
@request = true
|
1071
|
+
raise Net::HTTPBadResponse
|
1072
|
+
end
|
1073
|
+
end
|
1074
|
+
|
1075
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
1076
|
+
@http.request @uri, Net::HTTP::Post.new(@uri.path)
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
assert_equal 0, c.requests
|
1080
|
+
assert_match %r%too many bad responses%, e.message
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
def test_request_block
|
1084
|
+
@http.headers['user-agent'] = 'test ua'
|
1085
|
+
c = connection
|
1086
|
+
body = nil
|
1087
|
+
|
1088
|
+
res = @http.request @uri do |r|
|
1089
|
+
body = r.read_body
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
req = c.http.req
|
1093
|
+
|
1094
|
+
assert_kind_of Net::HTTPResponse, res
|
1095
|
+
refute_nil body
|
1096
|
+
|
1097
|
+
assert_kind_of Net::HTTP::Get, req
|
1098
|
+
assert_equal '/path', req.path
|
1099
|
+
assert_equal 'keep-alive', req['connection']
|
1100
|
+
assert_equal '30', req['keep-alive']
|
1101
|
+
assert_match %r%test ua%, req['user-agent']
|
1102
|
+
|
1103
|
+
assert_equal 1, c.requests
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
def test_request_close_1_0
|
1107
|
+
c = connection
|
1108
|
+
|
1109
|
+
class << c.http
|
1110
|
+
remove_method :request
|
1111
|
+
end
|
1112
|
+
|
1113
|
+
def (c.http).request req
|
1114
|
+
@req = req
|
1115
|
+
r = Net::HTTPResponse.allocate
|
1116
|
+
r.instance_variable_set :@header, {}
|
1117
|
+
def r.http_version() '1.0' end
|
1118
|
+
def r.read_body() :read_body end
|
1119
|
+
yield r if block_given?
|
1120
|
+
r
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
request = Net::HTTP::Get.new @uri.request_uri
|
1124
|
+
|
1125
|
+
res = @http.request @uri, request
|
1126
|
+
req = c.http.req
|
1127
|
+
|
1128
|
+
assert_kind_of Net::HTTPResponse, res
|
1129
|
+
|
1130
|
+
assert_kind_of Net::HTTP::Get, req
|
1131
|
+
assert_equal '/path', req.path
|
1132
|
+
assert_equal 'keep-alive', req['connection']
|
1133
|
+
assert_equal '30', req['keep-alive']
|
1134
|
+
|
1135
|
+
assert c.http.finished?
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
def test_request_connection_close_request
|
1139
|
+
c = connection
|
1140
|
+
|
1141
|
+
request = Net::HTTP::Get.new @uri.request_uri
|
1142
|
+
request['connection'] = 'close'
|
1143
|
+
|
1144
|
+
res = @http.request @uri, request
|
1145
|
+
req = c.http.req
|
1146
|
+
|
1147
|
+
assert_kind_of Net::HTTPResponse, res
|
1148
|
+
|
1149
|
+
assert_kind_of Net::HTTP::Get, req
|
1150
|
+
assert_equal '/path', req.path
|
1151
|
+
assert_equal 'close', req['connection']
|
1152
|
+
assert_equal nil, req['keep-alive']
|
1153
|
+
|
1154
|
+
assert c.http.finished?
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
def test_request_connection_close_response
|
1158
|
+
c = connection
|
1159
|
+
|
1160
|
+
class << c.http
|
1161
|
+
remove_method :request
|
1162
|
+
end
|
1163
|
+
|
1164
|
+
def (c.http).request req
|
1165
|
+
@req = req
|
1166
|
+
r = Net::HTTPResponse.allocate
|
1167
|
+
r.instance_variable_set :@header, {}
|
1168
|
+
r['connection'] = 'close'
|
1169
|
+
def r.http_version() '1.1' end
|
1170
|
+
def r.read_body() :read_body end
|
1171
|
+
yield r if block_given?
|
1172
|
+
r
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
request = Net::HTTP::Get.new @uri.request_uri
|
1176
|
+
|
1177
|
+
res = @http.request @uri, request
|
1178
|
+
req = c.http.req
|
1179
|
+
|
1180
|
+
assert_kind_of Net::HTTPResponse, res
|
1181
|
+
|
1182
|
+
assert_kind_of Net::HTTP::Get, req
|
1183
|
+
assert_equal '/path', req.path
|
1184
|
+
assert_equal 'keep-alive', req['connection']
|
1185
|
+
assert_equal '30', req['keep-alive']
|
1186
|
+
|
1187
|
+
assert c.http.finished?
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
def test_request_exception
|
1191
|
+
c = basic_connection
|
1192
|
+
def (c.http).request(*a)
|
1193
|
+
raise Exception, "very bad things happened"
|
1194
|
+
end
|
1195
|
+
|
1196
|
+
assert_raises Exception do
|
1197
|
+
@http.request @uri
|
1198
|
+
end
|
1199
|
+
|
1200
|
+
assert_equal 0, c.requests
|
1201
|
+
assert c.http.finished?
|
1202
|
+
end
|
1203
|
+
|
1204
|
+
def test_request_invalid
|
1205
|
+
c = basic_connection
|
1206
|
+
def (c.http).request(*a) raise Errno::EINVAL, "write" end
|
1207
|
+
|
1208
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
1209
|
+
@http.request @uri
|
1210
|
+
end
|
1211
|
+
|
1212
|
+
assert_equal 0, c.requests
|
1213
|
+
assert_match %r%too many connection resets%, e.message
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
def test_request_invalid_retry
|
1217
|
+
c = basic_connection
|
1218
|
+
c.last_use = Time.now
|
1219
|
+
|
1220
|
+
def (c.http).request(*a)
|
1221
|
+
if defined? @called then
|
1222
|
+
r = Net::HTTPResponse.allocate
|
1223
|
+
r.instance_variable_set :@header, {}
|
1224
|
+
def r.http_version() '1.1' end
|
1225
|
+
r
|
1226
|
+
else
|
1227
|
+
@called = true
|
1228
|
+
raise Errno::EINVAL, "write"
|
1229
|
+
end
|
1230
|
+
end
|
1231
|
+
|
1232
|
+
@http.request @uri
|
1233
|
+
|
1234
|
+
assert c.http.reset?
|
1235
|
+
assert c.http.finished?
|
1236
|
+
end
|
1237
|
+
|
1238
|
+
def test_request_post
|
1239
|
+
c = connection
|
1240
|
+
|
1241
|
+
post = Net::HTTP::Post.new @uri.path
|
1242
|
+
|
1243
|
+
@http.request @uri, post
|
1244
|
+
req = c.http.req
|
1245
|
+
|
1246
|
+
assert_same post, req
|
1247
|
+
end
|
1248
|
+
|
1249
|
+
def test_request_reset
|
1250
|
+
c = basic_connection
|
1251
|
+
def (c.http).request(*a) raise Errno::ECONNRESET end
|
1252
|
+
|
1253
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
1254
|
+
@http.request @uri
|
1255
|
+
end
|
1256
|
+
|
1257
|
+
assert_equal 0, c.requests
|
1258
|
+
assert_match %r%too many connection resets%, e.message
|
1259
|
+
end
|
1260
|
+
|
1261
|
+
if RUBY_1 then
|
1262
|
+
def test_request_reset_retry
|
1263
|
+
c = basic_connection
|
1264
|
+
c.last_use = Time.now
|
1265
|
+
def (c.http).request(*a)
|
1266
|
+
if defined? @called then
|
1267
|
+
r = Net::HTTPResponse.allocate
|
1268
|
+
r.instance_variable_set :@header, {}
|
1269
|
+
def r.http_version() '1.1' end
|
1270
|
+
r
|
1271
|
+
else
|
1272
|
+
@called = true
|
1273
|
+
raise Errno::ECONNRESET
|
1274
|
+
end
|
1275
|
+
end
|
1276
|
+
|
1277
|
+
@http.request @uri
|
1278
|
+
|
1279
|
+
assert c.http.reset?
|
1280
|
+
assert c.http.finished?
|
1281
|
+
end
|
1282
|
+
else
|
1283
|
+
def test_request_reset_retry
|
1284
|
+
c = basic_connection
|
1285
|
+
c.last_use = Time.now
|
1286
|
+
|
1287
|
+
def (c.http).request(*a)
|
1288
|
+
raise Errno::ECONNRESET
|
1289
|
+
end
|
1290
|
+
|
1291
|
+
assert_raises Net::HTTP::Persistent::Error do
|
1292
|
+
@http.request @uri
|
1293
|
+
end
|
1294
|
+
|
1295
|
+
refute (c.http).reset?
|
1296
|
+
assert (c.http).finished?
|
1297
|
+
end
|
1298
|
+
end
|
1299
|
+
|
1300
|
+
def test_request_reset_unsafe
|
1301
|
+
c = basic_connection
|
1302
|
+
def (c.http).request(*a)
|
1303
|
+
if instance_variable_defined? :@request then
|
1304
|
+
raise 'POST must not be retried'
|
1305
|
+
else
|
1306
|
+
@request = true
|
1307
|
+
raise Errno::ECONNRESET
|
1308
|
+
end
|
1309
|
+
end
|
1310
|
+
|
1311
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
1312
|
+
@http.request @uri, Net::HTTP::Post.new(@uri.path)
|
1313
|
+
end
|
1314
|
+
|
1315
|
+
assert_equal 0, c.requests
|
1316
|
+
assert_match %r%too many connection resets%, e.message
|
1317
|
+
end
|
1318
|
+
|
1319
|
+
def test_request_ssl_error
|
1320
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
1321
|
+
|
1322
|
+
uri = URI.parse 'https://example.com/path'
|
1323
|
+
@http.connection_for uri do |c|
|
1324
|
+
def (c.http).request(*)
|
1325
|
+
raise OpenSSL::SSL::SSLError, "SSL3_WRITE_PENDING:bad write retry"
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
1329
|
+
@http.request uri
|
1330
|
+
end
|
1331
|
+
assert_match %r%bad write retry%, e.message
|
1332
|
+
end
|
1333
|
+
end
|
1334
|
+
|
1335
|
+
def test_request_setup
|
1336
|
+
@http.override_headers['user-agent'] = 'test ua'
|
1337
|
+
@http.headers['accept'] = 'text/*'
|
1338
|
+
|
1339
|
+
input = Net::HTTP::Post.new '/path'
|
1340
|
+
|
1341
|
+
req = @http.request_setup input
|
1342
|
+
|
1343
|
+
assert_same input, req
|
1344
|
+
assert_equal '/path', req.path
|
1345
|
+
|
1346
|
+
assert_equal 'test ua', req['user-agent']
|
1347
|
+
assert_match %r%text/\*%, req['accept']
|
1348
|
+
|
1349
|
+
assert_equal 'keep-alive', req['connection']
|
1350
|
+
assert_equal '30', req['keep-alive']
|
1351
|
+
end
|
1352
|
+
|
1353
|
+
def test_request_setup_uri
|
1354
|
+
uri = @uri + '?a=b'
|
1355
|
+
|
1356
|
+
req = @http.request_setup uri
|
1357
|
+
|
1358
|
+
assert_kind_of Net::HTTP::Get, req
|
1359
|
+
assert_equal '/path?a=b', req.path
|
1360
|
+
end
|
1361
|
+
|
1362
|
+
def test_request_failed
|
1363
|
+
c = basic_connection
|
1364
|
+
c.requests = 1
|
1365
|
+
c.last_use = Time.now
|
1366
|
+
|
1367
|
+
original = nil
|
1368
|
+
|
1369
|
+
begin
|
1370
|
+
raise 'original'
|
1371
|
+
rescue => original
|
1372
|
+
end
|
1373
|
+
|
1374
|
+
req = Net::HTTP::Get.new '/'
|
1375
|
+
|
1376
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
1377
|
+
@http.request_failed original, req, c
|
1378
|
+
end
|
1379
|
+
|
1380
|
+
assert_match "too many connection resets (due to original - RuntimeError)",
|
1381
|
+
e.message
|
1382
|
+
|
1383
|
+
assert_equal original.backtrace, e.backtrace
|
1384
|
+
end
|
1385
|
+
|
1386
|
+
def test_reset
|
1387
|
+
c = basic_connection
|
1388
|
+
c.http.start
|
1389
|
+
c.last_use = Time.now
|
1390
|
+
c.requests = 5
|
1391
|
+
|
1392
|
+
@http.reset c
|
1393
|
+
|
1394
|
+
assert c.http.started?
|
1395
|
+
assert c.http.finished?
|
1396
|
+
assert c.http.reset?
|
1397
|
+
assert_equal 0, c.requests
|
1398
|
+
assert_equal Net::HTTP::Persistent::EPOCH, c.last_use
|
1399
|
+
end
|
1400
|
+
|
1401
|
+
def test_reset_host_down
|
1402
|
+
c = basic_connection
|
1403
|
+
c.last_use = Time.now
|
1404
|
+
def (c.http).start; raise Errno::EHOSTDOWN end
|
1405
|
+
c.requests = 5
|
1406
|
+
|
1407
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
1408
|
+
@http.reset c
|
1409
|
+
end
|
1410
|
+
|
1411
|
+
assert_match %r%host down%, e.message
|
1412
|
+
assert_match __FILE__, e.backtrace.first
|
1413
|
+
end
|
1414
|
+
|
1415
|
+
def test_reset_io_error
|
1416
|
+
c = basic_connection
|
1417
|
+
c.last_use = Time.now
|
1418
|
+
c.requests = 5
|
1419
|
+
|
1420
|
+
@http.reset c
|
1421
|
+
|
1422
|
+
assert c.http.started?
|
1423
|
+
assert c.http.finished?
|
1424
|
+
end
|
1425
|
+
|
1426
|
+
def test_reset_refused
|
1427
|
+
c = basic_connection
|
1428
|
+
c.last_use = Time.now
|
1429
|
+
def (c.http).start; raise Errno::ECONNREFUSED end
|
1430
|
+
c.requests = 5
|
1431
|
+
|
1432
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
1433
|
+
@http.reset c
|
1434
|
+
end
|
1435
|
+
|
1436
|
+
assert_match %r%connection refused%, e.message
|
1437
|
+
assert_match __FILE__, e.backtrace.first
|
1438
|
+
end
|
1439
|
+
|
1440
|
+
def test_retry_change_requests_equals
|
1441
|
+
get = Net::HTTP::Get.new('/')
|
1442
|
+
post = Net::HTTP::Post.new('/')
|
1443
|
+
|
1444
|
+
refute @http.retry_change_requests
|
1445
|
+
|
1446
|
+
if RUBY_1 then
|
1447
|
+
assert @http.can_retry?(get)
|
1448
|
+
else
|
1449
|
+
assert @http.can_retry?(get)
|
1450
|
+
end
|
1451
|
+
refute @http.can_retry?(get, true)
|
1452
|
+
refute @http.can_retry?(post)
|
1453
|
+
|
1454
|
+
@http.retry_change_requests = true
|
1455
|
+
|
1456
|
+
assert @http.retry_change_requests
|
1457
|
+
|
1458
|
+
if RUBY_1 then
|
1459
|
+
assert @http.can_retry?(get)
|
1460
|
+
else
|
1461
|
+
assert @http.can_retry?(get)
|
1462
|
+
refute @http.can_retry?(get, true)
|
1463
|
+
end
|
1464
|
+
|
1465
|
+
assert @http.can_retry?(post)
|
1466
|
+
end
|
1467
|
+
|
1468
|
+
def test_shutdown
|
1469
|
+
c = connection
|
1470
|
+
|
1471
|
+
orig = @http
|
1472
|
+
@http = Net::HTTP::Persistent.new 'name'
|
1473
|
+
c2 = connection
|
1474
|
+
|
1475
|
+
orig.shutdown
|
1476
|
+
|
1477
|
+
@http = orig
|
1478
|
+
|
1479
|
+
assert c.http.finished?, 'last-generation connection must be finished'
|
1480
|
+
refute c2.http.finished?, 'present generation connection must not be finished'
|
1481
|
+
end
|
1482
|
+
|
1483
|
+
def test_ssl
|
1484
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
1485
|
+
|
1486
|
+
@http.verify_callback = :callback
|
1487
|
+
c = Net::HTTP.new 'localhost', 80
|
1488
|
+
|
1489
|
+
@http.ssl c
|
1490
|
+
|
1491
|
+
assert c.use_ssl?
|
1492
|
+
assert_equal OpenSSL::SSL::VERIFY_PEER, c.verify_mode
|
1493
|
+
assert_kind_of OpenSSL::X509::Store, c.cert_store
|
1494
|
+
assert_nil c.verify_callback
|
1495
|
+
end
|
1496
|
+
|
1497
|
+
def test_ssl_ca_file
|
1498
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
1499
|
+
|
1500
|
+
@http.ca_file = 'ca_file'
|
1501
|
+
@http.verify_callback = :callback
|
1502
|
+
c = Net::HTTP.new 'localhost', 80
|
1503
|
+
|
1504
|
+
@http.ssl c
|
1505
|
+
|
1506
|
+
assert c.use_ssl?
|
1507
|
+
assert_equal OpenSSL::SSL::VERIFY_PEER, c.verify_mode
|
1508
|
+
assert_equal :callback, c.verify_callback
|
1509
|
+
end
|
1510
|
+
|
1511
|
+
def test_ssl_ca_path
|
1512
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
1513
|
+
|
1514
|
+
@http.ca_path = 'ca_path'
|
1515
|
+
@http.verify_callback = :callback
|
1516
|
+
c = Net::HTTP.new 'localhost', 80
|
1517
|
+
|
1518
|
+
@http.ssl c
|
1519
|
+
|
1520
|
+
assert c.use_ssl?
|
1521
|
+
assert_equal OpenSSL::SSL::VERIFY_PEER, c.verify_mode
|
1522
|
+
assert_equal :callback, c.verify_callback
|
1523
|
+
end
|
1524
|
+
|
1525
|
+
def test_ssl_cert_store
|
1526
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
1527
|
+
|
1528
|
+
store = OpenSSL::X509::Store.new
|
1529
|
+
@http.cert_store = store
|
1530
|
+
|
1531
|
+
c = Net::HTTP.new 'localhost', 80
|
1532
|
+
|
1533
|
+
@http.ssl c
|
1534
|
+
|
1535
|
+
assert c.use_ssl?
|
1536
|
+
assert_equal store, c.cert_store
|
1537
|
+
end
|
1538
|
+
|
1539
|
+
def test_ssl_cert_store_default
|
1540
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
1541
|
+
|
1542
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
1543
|
+
|
1544
|
+
c = Net::HTTP.new 'localhost', 80
|
1545
|
+
|
1546
|
+
@http.ssl c
|
1547
|
+
|
1548
|
+
assert c.use_ssl?
|
1549
|
+
assert c.cert_store
|
1550
|
+
end
|
1551
|
+
|
1552
|
+
def test_ssl_certificate
|
1553
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
1554
|
+
|
1555
|
+
@http.certificate = :cert
|
1556
|
+
@http.private_key = :key
|
1557
|
+
c = Net::HTTP.new 'localhost', 80
|
1558
|
+
|
1559
|
+
@http.ssl c
|
1560
|
+
|
1561
|
+
assert c.use_ssl?
|
1562
|
+
assert_equal :cert, c.cert
|
1563
|
+
assert_equal :key, c.key
|
1564
|
+
end
|
1565
|
+
|
1566
|
+
def test_ssl_verify_mode
|
1567
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
1568
|
+
|
1569
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
1570
|
+
c = Net::HTTP.new 'localhost', 80
|
1571
|
+
|
1572
|
+
@http.ssl c
|
1573
|
+
|
1574
|
+
assert c.use_ssl?
|
1575
|
+
assert_equal OpenSSL::SSL::VERIFY_NONE, c.verify_mode
|
1576
|
+
end
|
1577
|
+
|
1578
|
+
def test_ssl_warning
|
1579
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
1580
|
+
|
1581
|
+
begin
|
1582
|
+
orig_verify_peer = OpenSSL::SSL::VERIFY_PEER
|
1583
|
+
OpenSSL::SSL.send :remove_const, :VERIFY_PEER
|
1584
|
+
OpenSSL::SSL.send :const_set, :VERIFY_PEER, OpenSSL::SSL::VERIFY_NONE
|
1585
|
+
|
1586
|
+
c = Net::HTTP.new 'localhost', 80
|
1587
|
+
|
1588
|
+
out, err = capture_io do
|
1589
|
+
@http.ssl c
|
1590
|
+
end
|
1591
|
+
|
1592
|
+
assert_empty out
|
1593
|
+
|
1594
|
+
assert_match %r%localhost:80%, err
|
1595
|
+
assert_match %r%I_KNOW_THAT_OPENSSL%, err
|
1596
|
+
|
1597
|
+
Object.send :const_set, :I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG, nil
|
1598
|
+
|
1599
|
+
assert_silent do
|
1600
|
+
@http.ssl c
|
1601
|
+
end
|
1602
|
+
ensure
|
1603
|
+
OpenSSL::SSL.send :remove_const, :VERIFY_PEER
|
1604
|
+
OpenSSL::SSL.send :const_set, :VERIFY_PEER, orig_verify_peer
|
1605
|
+
if Object.const_defined?(:I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG) then
|
1606
|
+
Object.send :remove_const, :I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG
|
1607
|
+
end
|
1608
|
+
end
|
1609
|
+
end
|
1610
|
+
|
1611
|
+
def test_ssl_timeout_equals
|
1612
|
+
@http.ssl_timeout = :ssl_timeout
|
1613
|
+
|
1614
|
+
assert_equal :ssl_timeout, @http.ssl_timeout
|
1615
|
+
assert_equal 1, @http.ssl_generation
|
1616
|
+
end
|
1617
|
+
|
1618
|
+
def test_ssl_version_equals
|
1619
|
+
@http.ssl_version = :ssl_version
|
1620
|
+
|
1621
|
+
assert_equal :ssl_version, @http.ssl_version
|
1622
|
+
assert_equal 1, @http.ssl_generation
|
1623
|
+
end unless RUBY_1
|
1624
|
+
|
1625
|
+
def test_start
|
1626
|
+
c = basic_connection
|
1627
|
+
c = c.http
|
1628
|
+
|
1629
|
+
@http.socket_options << [Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1]
|
1630
|
+
@http.debug_output = $stderr
|
1631
|
+
@http.open_timeout = 6
|
1632
|
+
|
1633
|
+
@http.start c
|
1634
|
+
|
1635
|
+
assert_equal $stderr, c.debug_output
|
1636
|
+
assert_equal 6, c.open_timeout
|
1637
|
+
|
1638
|
+
socket = c.instance_variable_get :@socket
|
1639
|
+
|
1640
|
+
expected = []
|
1641
|
+
expected << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if
|
1642
|
+
Socket.const_defined? :TCP_NODELAY
|
1643
|
+
expected << [Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1]
|
1644
|
+
|
1645
|
+
assert_equal expected, socket.io.instance_variable_get(:@setsockopts)
|
1646
|
+
end
|
1647
|
+
|
1648
|
+
def test_verify_callback_equals
|
1649
|
+
@http.verify_callback = :verify_callback
|
1650
|
+
|
1651
|
+
assert_equal :verify_callback, @http.verify_callback
|
1652
|
+
assert_equal 1, @http.ssl_generation
|
1653
|
+
end
|
1654
|
+
|
1655
|
+
def test_verify_depth_equals
|
1656
|
+
@http.verify_depth = :verify_depth
|
1657
|
+
|
1658
|
+
assert_equal :verify_depth, @http.verify_depth
|
1659
|
+
assert_equal 1, @http.ssl_generation
|
1660
|
+
end
|
1661
|
+
|
1662
|
+
def test_verify_mode_equals
|
1663
|
+
@http.verify_mode = :verify_mode
|
1664
|
+
|
1665
|
+
assert_equal :verify_mode, @http.verify_mode
|
1666
|
+
assert_equal 1, @http.ssl_generation
|
1667
|
+
end
|
1668
|
+
|
1669
|
+
end
|
1670
|
+
|