net-http-persistent 2.9.4 → 3.0.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.
Potentially problematic release.
This version of net-http-persistent might be problematic. Click here for more details.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.travis.yml +15 -0
- data/History.txt +19 -0
- data/Manifest.txt +5 -3
- data/Rakefile +4 -1
- data/lib/net/http/persistent.rb +195 -273
- data/lib/net/http/persistent/connection.rb +40 -0
- data/lib/net/http/persistent/pool.rb +46 -0
- data/lib/net/http/persistent/timed_stack_multi.rb +69 -0
- data/test/test_net_http_persistent.rb +384 -542
- data/test/test_net_http_persistent_timed_stack_multi.rb +151 -0
- metadata +40 -27
- metadata.gz.sig +0 -0
- data/lib/net/http/faster.rb +0 -27
- data/lib/net/http/persistent/ssl_reuse.rb +0 -129
- data/test/test_net_http_persistent_ssl_reuse.rb +0 -112
@@ -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,69 @@
|
|
1
|
+
class Net::HTTP::Persistent::TimedStackMulti < ConnectionPool::TimedStack # :nodoc:
|
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
|
+
|
@@ -51,21 +51,9 @@ class Net::HTTP
|
|
51
51
|
include Net::HTTP::Persistent::TestConnect
|
52
52
|
end
|
53
53
|
|
54
|
-
class Net::HTTP::Persistent::SSLReuse
|
55
|
-
include Net::HTTP::Persistent::TestConnect
|
56
|
-
end
|
57
|
-
|
58
54
|
class TestNetHttpPersistent < Minitest::Test
|
59
55
|
|
60
|
-
RUBY_1 = RUBY_VERSION < '2'
|
61
|
-
|
62
56
|
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
57
|
@http = Net::HTTP::Persistent.new
|
70
58
|
|
71
59
|
@uri = URI.parse 'http://example.com/path'
|
@@ -80,25 +68,23 @@ class TestNetHttpPersistent < Minitest::Test
|
|
80
68
|
ENV.delete 'NO_PROXY'
|
81
69
|
|
82
70
|
Net::HTTP.use_connect :test_connect
|
83
|
-
Net::HTTP::Persistent::SSLReuse.use_connect :test_connect
|
84
71
|
end
|
85
72
|
|
86
73
|
def teardown
|
87
|
-
Thread.current.keys.each do |key|
|
88
|
-
Thread.current[key] = nil
|
89
|
-
end
|
90
|
-
|
91
74
|
Net::HTTP.use_connect :orig_connect
|
92
|
-
Net::HTTP::Persistent::SSLReuse.use_connect :orig_connect
|
93
75
|
end
|
94
76
|
|
95
77
|
class BasicConnection
|
96
|
-
attr_accessor :started, :finished, :address, :port,
|
97
|
-
:read_timeout, :open_timeout
|
78
|
+
attr_accessor :started, :finished, :address, :port, :use_ssl,
|
79
|
+
:read_timeout, :open_timeout, :keep_alive_timeout
|
80
|
+
attr_accessor :ciphers, :ssl_timeout, :ssl_version,
|
81
|
+
:verify_depth, :verify_mode, :cert_store,
|
82
|
+
:ca_file, :ca_path, :cert, :key
|
98
83
|
attr_reader :req, :debug_output
|
99
84
|
def initialize
|
100
85
|
@started, @finished = 0, 0
|
101
86
|
@address, @port = 'example.com', 80
|
87
|
+
@use_ssl = false
|
102
88
|
end
|
103
89
|
def finish
|
104
90
|
@finished += 1
|
@@ -125,21 +111,32 @@ class TestNetHttpPersistent < Minitest::Test
|
|
125
111
|
def started?
|
126
112
|
@started >= 1
|
127
113
|
end
|
114
|
+
def proxy_address
|
115
|
+
end
|
116
|
+
def proxy_port
|
117
|
+
end
|
128
118
|
end
|
129
119
|
|
130
120
|
def basic_connection
|
131
121
|
raise "#{@uri} is not HTTP" unless @uri.scheme.downcase == 'http'
|
132
122
|
|
133
|
-
|
134
|
-
|
135
|
-
|
123
|
+
net_http_args = [@uri.host, @uri.port]
|
124
|
+
|
125
|
+
connection = Net::HTTP::Persistent::Connection.allocate
|
126
|
+
connection.ssl_generation = @http.ssl_generation
|
127
|
+
connection.http = BasicConnection.new
|
128
|
+
connection.reset
|
129
|
+
|
130
|
+
@http.pool.available.push connection, connection_args: net_http_args
|
131
|
+
|
132
|
+
connection
|
136
133
|
end
|
137
134
|
|
138
135
|
def connection
|
139
|
-
|
140
|
-
|
136
|
+
connection = basic_connection
|
137
|
+
connection.last_use = Time.now
|
141
138
|
|
142
|
-
def
|
139
|
+
def (connection.http).request(req)
|
143
140
|
@req = req
|
144
141
|
r = Net::HTTPResponse.allocate
|
145
142
|
r.instance_variable_set :@header, {}
|
@@ -149,30 +146,22 @@ class TestNetHttpPersistent < Minitest::Test
|
|
149
146
|
r
|
150
147
|
end
|
151
148
|
|
152
|
-
|
149
|
+
connection
|
153
150
|
end
|
154
151
|
|
155
|
-
def
|
156
|
-
|
157
|
-
end
|
152
|
+
def ssl_connection
|
153
|
+
raise "#{@uri} is not HTTPS" unless @uri.scheme.downcase == 'https'
|
158
154
|
|
159
|
-
|
160
|
-
Thread.current[@http.request_key] ||= Hash.new 0
|
161
|
-
end
|
155
|
+
net_http_args = [@uri.host, @uri.port]
|
162
156
|
|
163
|
-
|
164
|
-
|
165
|
-
|
157
|
+
connection = Net::HTTP::Persistent::Connection.allocate
|
158
|
+
connection.ssl_generation = @http.ssl_generation
|
159
|
+
connection.http = BasicConnection.new
|
160
|
+
connection.reset
|
166
161
|
|
167
|
-
|
168
|
-
raise "#{@uri} is not HTTPS" unless @uri.scheme.downcase == 'https'
|
169
|
-
c = BasicConnection.new
|
170
|
-
ssl_conns[generation]["#{@uri.host}:#{@uri.port}"] = c
|
171
|
-
c
|
172
|
-
end
|
162
|
+
@http.pool.available.push connection, connection_args: net_http_args
|
173
163
|
|
174
|
-
|
175
|
-
Thread.current[@http.timeout_key] ||= Hash.new Net::HTTP::Persistent::EPOCH
|
164
|
+
connection
|
176
165
|
end
|
177
166
|
|
178
167
|
def test_initialize
|
@@ -188,7 +177,7 @@ class TestNetHttpPersistent < Minitest::Test
|
|
188
177
|
end
|
189
178
|
|
190
179
|
def test_initialize_name
|
191
|
-
http = Net::HTTP::Persistent.new 'name'
|
180
|
+
http = Net::HTTP::Persistent.new name: 'name'
|
192
181
|
assert_equal 'name', http.name
|
193
182
|
end
|
194
183
|
|
@@ -212,7 +201,7 @@ class TestNetHttpPersistent < Minitest::Test
|
|
212
201
|
def test_initialize_proxy
|
213
202
|
proxy_uri = URI.parse 'http://proxy.example'
|
214
203
|
|
215
|
-
http = Net::HTTP::Persistent.new
|
204
|
+
http = Net::HTTP::Persistent.new proxy: proxy_uri
|
216
205
|
|
217
206
|
assert_equal proxy_uri, http.proxy_uri
|
218
207
|
end
|
@@ -224,6 +213,13 @@ class TestNetHttpPersistent < Minitest::Test
|
|
224
213
|
assert_equal 1, @http.ssl_generation
|
225
214
|
end
|
226
215
|
|
216
|
+
def test_ca_path_equals
|
217
|
+
@http.ca_path = :ca_path
|
218
|
+
|
219
|
+
assert_equal :ca_path, @http.ca_path
|
220
|
+
assert_equal 1, @http.ssl_generation
|
221
|
+
end
|
222
|
+
|
227
223
|
def test_can_retry_eh_change_requests
|
228
224
|
post = Net::HTTP::Post.new '/'
|
229
225
|
|
@@ -234,27 +230,14 @@ class TestNetHttpPersistent < Minitest::Test
|
|
234
230
|
assert @http.can_retry? post
|
235
231
|
end
|
236
232
|
|
237
|
-
|
238
|
-
|
239
|
-
head = Net::HTTP::Head.new '/'
|
233
|
+
def test_can_retry_eh_idempotent
|
234
|
+
head = Net::HTTP::Head.new '/'
|
240
235
|
|
241
|
-
|
236
|
+
refute @http.can_retry? head
|
242
237
|
|
243
|
-
|
244
|
-
|
245
|
-
refute @http.can_retry? post
|
246
|
-
end
|
247
|
-
else
|
248
|
-
def test_can_retry_eh_idempotent
|
249
|
-
head = Net::HTTP::Head.new '/'
|
250
|
-
|
251
|
-
assert @http.can_retry? head
|
252
|
-
refute @http.can_retry? head, true
|
253
|
-
|
254
|
-
post = Net::HTTP::Post.new '/'
|
238
|
+
post = Net::HTTP::Post.new '/'
|
255
239
|
|
256
|
-
|
257
|
-
end
|
240
|
+
refute @http.can_retry? post
|
258
241
|
end
|
259
242
|
|
260
243
|
def test_cert_store_equals
|
@@ -271,168 +254,154 @@ class TestNetHttpPersistent < Minitest::Test
|
|
271
254
|
assert_equal 1, @http.ssl_generation
|
272
255
|
end
|
273
256
|
|
257
|
+
def test_ciphers_equals
|
258
|
+
@http.ciphers = :ciphers
|
259
|
+
|
260
|
+
assert_equal :ciphers, @http.ciphers
|
261
|
+
assert_equal 1, @http.ssl_generation
|
262
|
+
end
|
263
|
+
|
274
264
|
def test_connection_for
|
275
265
|
@http.open_timeout = 123
|
276
266
|
@http.read_timeout = 321
|
277
267
|
@http.idle_timeout = 42
|
278
|
-
c = @http.connection_for @uri
|
279
268
|
|
280
|
-
|
269
|
+
used = @http.connection_for @uri do |c|
|
270
|
+
assert_kind_of Net::HTTP, c.http
|
281
271
|
|
282
|
-
|
283
|
-
|
272
|
+
assert c.http.started?
|
273
|
+
refute c.http.proxy?
|
284
274
|
|
285
|
-
|
286
|
-
|
287
|
-
|
275
|
+
assert_equal 123, c.http.open_timeout
|
276
|
+
assert_equal 321, c.http.read_timeout
|
277
|
+
assert_equal 42, c.http.keep_alive_timeout
|
288
278
|
|
289
|
-
|
290
|
-
|
279
|
+
c
|
280
|
+
end
|
281
|
+
|
282
|
+
stored = @http.pool.checkout ['example.com', 80]
|
283
|
+
|
284
|
+
assert_same used, stored
|
291
285
|
end
|
292
286
|
|
293
287
|
def test_connection_for_cached
|
294
288
|
cached = basic_connection
|
295
|
-
cached.start
|
296
|
-
conns[0]['example.com:80'] = cached
|
289
|
+
cached.http.start
|
297
290
|
|
298
291
|
@http.read_timeout = 5
|
299
292
|
|
300
|
-
|
301
|
-
|
302
|
-
assert c.started?
|
293
|
+
@http.connection_for @uri do |c|
|
294
|
+
assert c.http.started?
|
303
295
|
|
304
|
-
|
296
|
+
assert_equal 5, c.http.read_timeout
|
305
297
|
|
306
|
-
|
298
|
+
assert_same cached, c
|
299
|
+
end
|
307
300
|
end
|
308
301
|
|
309
302
|
def test_connection_for_closed
|
310
303
|
cached = basic_connection
|
311
|
-
cached.start
|
304
|
+
cached.http.start
|
312
305
|
if Socket.const_defined? :TCP_NODELAY then
|
313
306
|
io = Object.new
|
314
307
|
def io.setsockopt(*a) raise IOError, 'closed stream' end
|
315
308
|
cached.instance_variable_set :@socket, Net::BufferedIO.new(io)
|
316
309
|
end
|
317
|
-
conns['example.com:80'] = cached
|
318
|
-
|
319
|
-
c = @http.connection_for @uri
|
320
|
-
|
321
|
-
assert c.started?
|
322
310
|
|
323
|
-
|
324
|
-
|
311
|
+
@http.connection_for @uri do |c|
|
312
|
+
assert c.http.started?
|
325
313
|
|
326
|
-
|
314
|
+
socket = c.http.instance_variable_get :@socket
|
327
315
|
|
328
|
-
|
329
|
-
|
316
|
+
refute_includes socket.io.instance_variables, :@setsockopt
|
317
|
+
refute_includes socket.io.instance_variables, '@setsockopt'
|
318
|
+
end
|
330
319
|
end
|
331
320
|
|
332
321
|
def test_connection_for_debug_output
|
333
322
|
io = StringIO.new
|
334
323
|
@http.debug_output = io
|
335
324
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
assert_includes conns[0].keys, 'example.com:80'
|
342
|
-
assert_same c, conns[0]['example.com:80']
|
325
|
+
@http.connection_for @uri do |c|
|
326
|
+
assert c.http.started?
|
327
|
+
assert_equal io, c.http.instance_variable_get(:@debug_output)
|
328
|
+
end
|
343
329
|
end
|
344
330
|
|
345
331
|
def test_connection_for_cached_expire_always
|
346
332
|
cached = basic_connection
|
347
|
-
cached.start
|
348
|
-
|
349
|
-
|
350
|
-
touts[cached.object_id] = Time.now # last used right now
|
333
|
+
cached.http.start
|
334
|
+
cached.requests = 10
|
335
|
+
cached.last_use = Time.now # last used right now
|
351
336
|
|
352
337
|
@http.idle_timeout = 0
|
353
338
|
|
354
|
-
|
339
|
+
@http.connection_for @uri do |c|
|
340
|
+
assert c.http.started?
|
355
341
|
|
356
|
-
|
342
|
+
assert_same cached, c
|
357
343
|
|
358
|
-
|
359
|
-
|
360
|
-
assert_equal 0, reqs[cached.object_id],
|
361
|
-
'connection reset due to timeout'
|
344
|
+
assert_equal 0, c.requests, 'connection reset due to timeout'
|
345
|
+
end
|
362
346
|
end
|
363
347
|
|
364
348
|
def test_connection_for_cached_expire_never
|
365
349
|
cached = basic_connection
|
366
|
-
cached.start
|
367
|
-
|
368
|
-
|
369
|
-
touts[cached.object_id] = Time.now # last used right now
|
350
|
+
cached.http.start
|
351
|
+
cached.requests = 10
|
352
|
+
cached.last_use = Time.now # last used right now
|
370
353
|
|
371
354
|
@http.idle_timeout = nil
|
372
355
|
|
373
|
-
|
374
|
-
|
375
|
-
assert c.started?
|
356
|
+
@http.connection_for @uri do |c|
|
357
|
+
assert c.http.started?
|
376
358
|
|
377
|
-
|
359
|
+
assert_same cached, c
|
378
360
|
|
379
|
-
|
380
|
-
|
361
|
+
assert_equal 10, c.requests, 'connection reset despite no timeout'
|
362
|
+
end
|
381
363
|
end
|
382
364
|
|
383
365
|
def test_connection_for_cached_expired
|
384
366
|
cached = basic_connection
|
385
|
-
cached.start
|
386
|
-
|
387
|
-
|
388
|
-
touts[cached.object_id] = Time.now - 3600
|
367
|
+
cached.http.start
|
368
|
+
cached.requests = 10
|
369
|
+
cached.last_use = Time.now - 3600
|
389
370
|
|
390
|
-
|
371
|
+
@http.connection_for @uri do |c|
|
372
|
+
assert c.http.started?
|
391
373
|
|
392
|
-
|
393
|
-
|
394
|
-
assert_same cached, c
|
395
|
-
assert_equal 0, reqs[cached.object_id],
|
396
|
-
'connection not reset due to timeout'
|
397
|
-
end
|
398
|
-
|
399
|
-
def test_connection_for_refused
|
400
|
-
Net::HTTP.use_connect :refused_connect
|
401
|
-
Net::HTTP::Persistent::SSLReuse.use_connect :refused_connect
|
402
|
-
|
403
|
-
e = assert_raises Net::HTTP::Persistent::Error do
|
404
|
-
@http.connection_for @uri
|
374
|
+
assert_same cached, c
|
375
|
+
assert_equal 0, cached.requests, 'connection not reset due to timeout'
|
405
376
|
end
|
406
|
-
|
407
|
-
assert_equal 'connection refused: example.com:80', e.message
|
408
377
|
end
|
409
378
|
|
410
379
|
def test_connection_for_finished_ssl
|
411
380
|
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
412
381
|
|
413
382
|
uri = URI.parse 'https://example.com/path'
|
414
|
-
c = @http.connection_for uri
|
415
383
|
|
416
|
-
|
417
|
-
|
384
|
+
@http.connection_for uri do |c|
|
385
|
+
assert c.http.started?
|
386
|
+
assert c.http.use_ssl?
|
418
387
|
|
419
|
-
|
388
|
+
@http.finish c
|
420
389
|
|
421
|
-
|
422
|
-
|
423
|
-
c2 = @http.connection_for uri
|
390
|
+
refute c.http.started?
|
391
|
+
end
|
424
392
|
|
425
|
-
|
393
|
+
@http.connection_for uri do |c2|
|
394
|
+
assert c2.http.started?
|
395
|
+
end
|
426
396
|
end
|
427
397
|
|
428
398
|
def test_connection_for_host_down
|
429
|
-
|
430
|
-
def
|
431
|
-
def
|
432
|
-
conns[0]['example.com:80'] = cached
|
399
|
+
c = basic_connection
|
400
|
+
def (c.http).start; raise Errno::EHOSTDOWN end
|
401
|
+
def (c.http).started?; false end
|
433
402
|
|
434
403
|
e = assert_raises Net::HTTP::Persistent::Error do
|
435
|
-
@http.connection_for @uri
|
404
|
+
@http.connection_for @uri do end
|
436
405
|
end
|
437
406
|
|
438
407
|
assert_equal 'host down: example.com:80', e.message
|
@@ -440,8 +409,10 @@ class TestNetHttpPersistent < Minitest::Test
|
|
440
409
|
|
441
410
|
def test_connection_for_http_class_with_fakeweb
|
442
411
|
Object.send :const_set, :FakeWeb, nil
|
443
|
-
|
444
|
-
|
412
|
+
|
413
|
+
@http.connection_for @uri do |c|
|
414
|
+
assert_instance_of Net::HTTP, c.http
|
415
|
+
end
|
445
416
|
ensure
|
446
417
|
if Object.const_defined?(:FakeWeb) then
|
447
418
|
Object.send :remove_const, :FakeWeb
|
@@ -450,8 +421,9 @@ class TestNetHttpPersistent < Minitest::Test
|
|
450
421
|
|
451
422
|
def test_connection_for_http_class_with_webmock
|
452
423
|
Object.send :const_set, :WebMock, nil
|
453
|
-
|
454
|
-
|
424
|
+
@http.connection_for @uri do |c|
|
425
|
+
assert_instance_of Net::HTTP, c.http
|
426
|
+
end
|
455
427
|
ensure
|
456
428
|
if Object.const_defined?(:WebMock) then
|
457
429
|
Object.send :remove_const, :WebMock
|
@@ -460,8 +432,9 @@ class TestNetHttpPersistent < Minitest::Test
|
|
460
432
|
|
461
433
|
def test_connection_for_http_class_with_artifice
|
462
434
|
Object.send :const_set, :Artifice, nil
|
463
|
-
|
464
|
-
|
435
|
+
@http.connection_for @uri do |c|
|
436
|
+
assert_instance_of Net::HTTP, c.http
|
437
|
+
end
|
465
438
|
ensure
|
466
439
|
if Object.const_defined?(:Artifice) then
|
467
440
|
Object.send :remove_const, :Artifice
|
@@ -469,23 +442,12 @@ class TestNetHttpPersistent < Minitest::Test
|
|
469
442
|
end
|
470
443
|
|
471
444
|
def test_connection_for_name
|
472
|
-
http = Net::HTTP::Persistent.new 'name'
|
445
|
+
http = Net::HTTP::Persistent.new name: 'name'
|
473
446
|
uri = URI.parse 'http://example/'
|
474
447
|
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
refute_includes conns.keys, 'example:80'
|
480
|
-
end
|
481
|
-
|
482
|
-
def test_connection_for_no_ssl_reuse
|
483
|
-
@http.reuse_ssl_sessions = false
|
484
|
-
@http.open_timeout = 123
|
485
|
-
@http.read_timeout = 321
|
486
|
-
c = @http.connection_for @uri
|
487
|
-
|
488
|
-
assert_instance_of Net::HTTP, c
|
448
|
+
http.connection_for uri do |c|
|
449
|
+
assert c.http.started?
|
450
|
+
end
|
489
451
|
end
|
490
452
|
|
491
453
|
def test_connection_for_proxy
|
@@ -493,16 +455,20 @@ class TestNetHttpPersistent < Minitest::Test
|
|
493
455
|
uri.user = 'johndoe'
|
494
456
|
uri.password = 'muffins'
|
495
457
|
|
496
|
-
http = Net::HTTP::Persistent.new
|
458
|
+
http = Net::HTTP::Persistent.new proxy: uri
|
497
459
|
|
498
|
-
|
460
|
+
used = http.connection_for @uri do |c|
|
461
|
+
assert c.http.started?
|
462
|
+
assert c.http.proxy?
|
499
463
|
|
500
|
-
|
501
|
-
|
464
|
+
c
|
465
|
+
end
|
466
|
+
|
467
|
+
stored = http.pool.checkout ['example.com', 80,
|
468
|
+
'proxy.example', 80,
|
469
|
+
'johndoe', 'muffins']
|
502
470
|
|
503
|
-
|
504
|
-
'example.com:80:proxy.example:80:johndoe:muffins'
|
505
|
-
assert_same c, conns[1]['example.com:80:proxy.example:80:johndoe:muffins']
|
471
|
+
assert_same used, stored
|
506
472
|
end
|
507
473
|
|
508
474
|
def test_connection_for_proxy_unescaped
|
@@ -511,25 +477,28 @@ class TestNetHttpPersistent < Minitest::Test
|
|
511
477
|
uri.password = 'muf%3Afins'
|
512
478
|
uri.freeze
|
513
479
|
|
514
|
-
http = Net::HTTP::Persistent.new
|
515
|
-
|
480
|
+
http = Net::HTTP::Persistent.new proxy: uri
|
481
|
+
|
482
|
+
http.connection_for @uri do end
|
483
|
+
|
484
|
+
stored = http.pool.checkout ['example.com', 80,
|
485
|
+
'proxy.example', 80,
|
486
|
+
'john@doe', 'muf:fins']
|
516
487
|
|
517
|
-
|
518
|
-
'example.com:80:proxy.example:80:john@doe:muf:fins'
|
488
|
+
assert stored
|
519
489
|
end
|
520
490
|
|
521
491
|
def test_connection_for_proxy_host_down
|
522
492
|
Net::HTTP.use_connect :host_down_connect
|
523
|
-
Net::HTTP::Persistent::SSLReuse.use_connect :host_down_connect
|
524
493
|
|
525
494
|
uri = URI.parse 'http://proxy.example'
|
526
495
|
uri.user = 'johndoe'
|
527
496
|
uri.password = 'muffins'
|
528
497
|
|
529
|
-
http = Net::HTTP::Persistent.new
|
498
|
+
http = Net::HTTP::Persistent.new proxy: uri
|
530
499
|
|
531
500
|
e = assert_raises Net::HTTP::Persistent::Error do
|
532
|
-
http.connection_for @uri
|
501
|
+
http.connection_for @uri do end
|
533
502
|
end
|
534
503
|
|
535
504
|
assert_equal 'host down: proxy.example:80', e.message
|
@@ -537,16 +506,15 @@ class TestNetHttpPersistent < Minitest::Test
|
|
537
506
|
|
538
507
|
def test_connection_for_proxy_refused
|
539
508
|
Net::HTTP.use_connect :refused_connect
|
540
|
-
Net::HTTP::Persistent::SSLReuse.use_connect :refused_connect
|
541
509
|
|
542
510
|
uri = URI.parse 'http://proxy.example'
|
543
511
|
uri.user = 'johndoe'
|
544
512
|
uri.password = 'muffins'
|
545
513
|
|
546
|
-
http = Net::HTTP::Persistent.new
|
514
|
+
http = Net::HTTP::Persistent.new proxy: uri
|
547
515
|
|
548
516
|
e = assert_raises Net::HTTP::Persistent::Error do
|
549
|
-
http.connection_for @uri
|
517
|
+
http.connection_for @uri do end
|
550
518
|
end
|
551
519
|
|
552
520
|
assert_equal 'connection refused: proxy.example:80', e.message
|
@@ -558,38 +526,37 @@ class TestNetHttpPersistent < Minitest::Test
|
|
558
526
|
uri.password = 'muffins'
|
559
527
|
uri.query = 'no_proxy=example.com'
|
560
528
|
|
561
|
-
http = Net::HTTP::Persistent.new
|
529
|
+
http = Net::HTTP::Persistent.new proxy: uri
|
562
530
|
|
563
|
-
|
531
|
+
http.connection_for @uri do |c|
|
532
|
+
assert c.http.started?
|
533
|
+
refute c.http.proxy?
|
534
|
+
end
|
564
535
|
|
565
|
-
|
566
|
-
refute c.proxy?
|
536
|
+
stored = http.pool.checkout ['example.com', 80]
|
567
537
|
|
568
|
-
|
569
|
-
assert_same c, conns[1]['example.com:80']
|
538
|
+
assert stored
|
570
539
|
end
|
571
540
|
|
572
541
|
def test_connection_for_refused
|
573
|
-
|
574
|
-
def cached.start; raise Errno::ECONNREFUSED end
|
575
|
-
def cached.started?; false end
|
576
|
-
conns[0]['example.com:80'] = cached
|
542
|
+
Net::HTTP.use_connect :refused_connect
|
577
543
|
|
578
544
|
e = assert_raises Net::HTTP::Persistent::Error do
|
579
|
-
@http.connection_for @uri
|
545
|
+
@http.connection_for @uri do end
|
580
546
|
end
|
581
547
|
|
582
|
-
|
548
|
+
assert_equal 'connection refused: example.com:80', e.message
|
583
549
|
end
|
584
550
|
|
585
551
|
def test_connection_for_ssl
|
586
552
|
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
587
553
|
|
588
554
|
uri = URI.parse 'https://example.com/path'
|
589
|
-
c = @http.connection_for uri
|
590
555
|
|
591
|
-
|
592
|
-
|
556
|
+
@http.connection_for uri do |c|
|
557
|
+
assert c.http.started?
|
558
|
+
assert c.http.use_ssl?
|
559
|
+
end
|
593
560
|
end
|
594
561
|
|
595
562
|
def test_connection_for_ssl_cached
|
@@ -597,11 +564,11 @@ class TestNetHttpPersistent < Minitest::Test
|
|
597
564
|
|
598
565
|
@uri = URI.parse 'https://example.com/path'
|
599
566
|
|
600
|
-
cached = ssl_connection
|
601
|
-
|
602
|
-
c = @http.connection_for @uri
|
567
|
+
cached = ssl_connection
|
603
568
|
|
604
|
-
|
569
|
+
@http.connection_for @uri do |c|
|
570
|
+
assert_same cached, c
|
571
|
+
end
|
605
572
|
end
|
606
573
|
|
607
574
|
def test_connection_for_ssl_cached_reconnect
|
@@ -611,45 +578,47 @@ class TestNetHttpPersistent < Minitest::Test
|
|
611
578
|
|
612
579
|
cached = ssl_connection
|
613
580
|
|
614
|
-
@http.
|
581
|
+
ssl_generation = @http.ssl_generation
|
615
582
|
|
616
|
-
|
583
|
+
@http.reconnect_ssl
|
617
584
|
|
618
|
-
|
585
|
+
@http.connection_for @uri do |c|
|
586
|
+
assert_same cached, c
|
587
|
+
refute_equal ssl_generation, c.ssl_generation
|
588
|
+
end
|
619
589
|
end
|
620
590
|
|
621
591
|
def test_connection_for_ssl_case
|
622
592
|
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
623
593
|
|
624
594
|
uri = URI.parse 'HTTPS://example.com/path'
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
595
|
+
@http.connection_for uri do |c|
|
596
|
+
assert c.http.started?
|
597
|
+
assert c.http.use_ssl?
|
598
|
+
end
|
629
599
|
end
|
630
600
|
|
631
601
|
def test_connection_for_timeout
|
632
602
|
cached = basic_connection
|
633
|
-
cached.start
|
634
|
-
|
635
|
-
|
636
|
-
conns[0]['example.com:80'] = cached
|
603
|
+
cached.http.start
|
604
|
+
cached.requests = 10
|
605
|
+
cached.last_use = Time.now - 6
|
637
606
|
|
638
|
-
|
607
|
+
@http.connection_for @uri do |c|
|
608
|
+
assert c.http.started?
|
609
|
+
assert_equal 0, c.requests
|
639
610
|
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
assert_same cached, c
|
611
|
+
assert_same cached, c
|
612
|
+
end
|
644
613
|
end
|
645
614
|
|
646
615
|
def test_error_message
|
647
616
|
c = basic_connection
|
648
|
-
|
649
|
-
|
617
|
+
c.last_use = Time.now - 1
|
618
|
+
c.requests = 5
|
650
619
|
|
651
|
-
message = @http.error_message
|
652
|
-
assert_match %r%after 4 requests on #{c.object_id}%, message
|
620
|
+
message = @http.error_message c
|
621
|
+
assert_match %r%after 4 requests on #{c.http.object_id}%, message
|
653
622
|
assert_match %r%, last used [\d.]+ seconds ago%, message
|
654
623
|
end
|
655
624
|
|
@@ -667,8 +636,8 @@ class TestNetHttpPersistent < Minitest::Test
|
|
667
636
|
|
668
637
|
def test_expired_eh
|
669
638
|
c = basic_connection
|
670
|
-
|
671
|
-
|
639
|
+
c.requests = 0
|
640
|
+
c.last_use = Time.now - 11
|
672
641
|
|
673
642
|
@http.idle_timeout = 0
|
674
643
|
assert @http.expired? c
|
@@ -688,41 +657,57 @@ class TestNetHttpPersistent < Minitest::Test
|
|
688
657
|
|
689
658
|
def test_expired_due_to_max_requests
|
690
659
|
c = basic_connection
|
691
|
-
|
692
|
-
|
660
|
+
c.requests = 0
|
661
|
+
c.last_use = Time.now
|
693
662
|
|
694
663
|
refute @http.expired? c
|
695
664
|
|
696
|
-
|
665
|
+
c.requests = 10
|
697
666
|
refute @http.expired? c
|
698
667
|
|
699
668
|
@http.max_requests = 10
|
700
669
|
assert @http.expired? c
|
701
670
|
|
702
|
-
|
671
|
+
c.requests = 9
|
703
672
|
refute @http.expired? c
|
704
673
|
end
|
705
674
|
|
706
675
|
def test_finish
|
707
676
|
c = basic_connection
|
708
|
-
|
677
|
+
c.requests = 5
|
709
678
|
|
710
679
|
@http.finish c
|
711
680
|
|
712
|
-
refute c.started?
|
713
|
-
assert c.finished?
|
714
|
-
|
681
|
+
refute c.http.started?
|
682
|
+
assert c.http.finished?
|
683
|
+
|
684
|
+
assert_equal 0, c.requests
|
685
|
+
assert_equal Net::HTTP::Persistent::EPOCH, c.last_use
|
715
686
|
end
|
716
687
|
|
717
688
|
def test_finish_io_error
|
718
689
|
c = basic_connection
|
719
|
-
def c.finish; @finished += 1; raise IOError end
|
720
|
-
|
690
|
+
def (c.http).finish; @finished += 1; raise IOError end
|
691
|
+
c.requests = 5
|
721
692
|
|
722
693
|
@http.finish c
|
723
694
|
|
724
|
-
refute c.started?
|
725
|
-
assert c.finished?
|
695
|
+
refute c.http.started?
|
696
|
+
assert c.http.finished?
|
697
|
+
end
|
698
|
+
|
699
|
+
def test_finish_ssl_no_session_reuse
|
700
|
+
http = Net::HTTP.new 'localhost', 443, ssl: true
|
701
|
+
http.instance_variable_set :@ssl_session, :something
|
702
|
+
|
703
|
+
c = Net::HTTP::Persistent::Connection.allocate
|
704
|
+
c.instance_variable_set :@http, http
|
705
|
+
|
706
|
+
@http.reuse_ssl_sessions = false
|
707
|
+
|
708
|
+
@http.finish c
|
709
|
+
|
710
|
+
assert_nil c.http.instance_variable_get :@ssl_session
|
726
711
|
end
|
727
712
|
|
728
713
|
def test_http_version
|
@@ -746,14 +731,6 @@ class TestNetHttpPersistent < Minitest::Test
|
|
746
731
|
refute @http.idempotent? Net::HTTP::Post.new '/'
|
747
732
|
end
|
748
733
|
|
749
|
-
def test_max_age
|
750
|
-
assert_in_delta Time.now - 5, @http.max_age
|
751
|
-
|
752
|
-
@http.idle_timeout = nil
|
753
|
-
|
754
|
-
assert_in_delta Time.now + 1, @http.max_age
|
755
|
-
end
|
756
|
-
|
757
734
|
def test_normalize_uri
|
758
735
|
assert_equal 'http://example', @http.normalize_uri('example')
|
759
736
|
assert_equal 'http://example', @http.normalize_uri('http://example')
|
@@ -775,7 +752,6 @@ class TestNetHttpPersistent < Minitest::Test
|
|
775
752
|
|
776
753
|
cached = basic_connection
|
777
754
|
cached.start
|
778
|
-
conns['example.com:80'] = cached
|
779
755
|
|
780
756
|
requests = [
|
781
757
|
Net::HTTP::Get.new((@uri + '1').request_uri),
|
@@ -935,9 +911,38 @@ class TestNetHttpPersistent < Minitest::Test
|
|
935
911
|
end
|
936
912
|
|
937
913
|
def test_reconnect_ssl
|
938
|
-
|
914
|
+
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
939
915
|
|
940
|
-
|
916
|
+
@uri = URI 'https://example.com'
|
917
|
+
now = Time.now
|
918
|
+
|
919
|
+
ssl_http = ssl_connection
|
920
|
+
|
921
|
+
def (ssl_http.http).finish
|
922
|
+
@started = 0
|
923
|
+
end
|
924
|
+
|
925
|
+
used1 = @http.connection_for @uri do |c|
|
926
|
+
c.requests = 1
|
927
|
+
c.last_use = now
|
928
|
+
c
|
929
|
+
end
|
930
|
+
|
931
|
+
assert_equal OpenSSL::SSL::VERIFY_PEER, used1.http.verify_mode
|
932
|
+
|
933
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
934
|
+
@http.reconnect_ssl
|
935
|
+
|
936
|
+
used2 = @http.connection_for @uri do |c|
|
937
|
+
c
|
938
|
+
end
|
939
|
+
|
940
|
+
assert_same used1, used2
|
941
|
+
|
942
|
+
assert_equal OpenSSL::SSL::VERIFY_NONE, used2.http.verify_mode,
|
943
|
+
'verify mode must change'
|
944
|
+
assert_equal 0, used2.requests
|
945
|
+
assert_equal Net::HTTP::Persistent::EPOCH, used2.last_use
|
941
946
|
end
|
942
947
|
|
943
948
|
def test_request
|
@@ -946,7 +951,7 @@ class TestNetHttpPersistent < Minitest::Test
|
|
946
951
|
c = connection
|
947
952
|
|
948
953
|
res = @http.request @uri
|
949
|
-
req = c.req
|
954
|
+
req = c.http.req
|
950
955
|
|
951
956
|
assert_kind_of Net::HTTPResponse, res
|
952
957
|
|
@@ -959,72 +964,51 @@ class TestNetHttpPersistent < Minitest::Test
|
|
959
964
|
assert_equal 'keep-alive', req['connection']
|
960
965
|
assert_equal '30', req['keep-alive']
|
961
966
|
|
962
|
-
assert_in_delta Time.now,
|
967
|
+
assert_in_delta Time.now, c.last_use
|
963
968
|
|
964
|
-
assert_equal 1,
|
969
|
+
assert_equal 1, c.requests
|
965
970
|
end
|
966
971
|
|
967
972
|
def test_request_ETIMEDOUT
|
968
973
|
c = basic_connection
|
969
|
-
def c.request(*a) raise Errno::ETIMEDOUT, "timed out" end
|
974
|
+
def (c.http).request(*a) raise Errno::ETIMEDOUT, "timed out" end
|
970
975
|
|
971
976
|
e = assert_raises Net::HTTP::Persistent::Error do
|
972
977
|
@http.request @uri
|
973
978
|
end
|
974
979
|
|
975
|
-
assert_equal 0,
|
980
|
+
assert_equal 0, c.requests
|
976
981
|
assert_match %r%too many connection resets%, e.message
|
977
982
|
end
|
978
983
|
|
979
984
|
def test_request_bad_response
|
980
985
|
c = basic_connection
|
981
|
-
def c.request(*a) raise Net::HTTPBadResponse end
|
986
|
+
def (c.http).request(*a) raise Net::HTTPBadResponse end
|
982
987
|
|
983
988
|
e = assert_raises Net::HTTP::Persistent::Error do
|
984
989
|
@http.request @uri
|
985
990
|
end
|
986
991
|
|
987
|
-
assert_equal 0,
|
992
|
+
assert_equal 0, c.requests
|
988
993
|
assert_match %r%too many bad responses%, e.message
|
989
994
|
end
|
990
995
|
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
r = Net::HTTPResponse.allocate
|
997
|
-
r.instance_variable_set :@header, {}
|
998
|
-
def r.http_version() '1.1' end
|
999
|
-
r
|
1000
|
-
else
|
1001
|
-
@called = true
|
1002
|
-
raise Net::HTTPBadResponse
|
1003
|
-
end
|
1004
|
-
end
|
996
|
+
def test_request_bad_response_retry
|
997
|
+
c = basic_connection
|
998
|
+
def (c.http).request(*a)
|
999
|
+
raise Net::HTTPBadResponse
|
1000
|
+
end
|
1005
1001
|
|
1002
|
+
assert_raises Net::HTTP::Persistent::Error do
|
1006
1003
|
@http.request @uri
|
1007
|
-
|
1008
|
-
assert c.finished?
|
1009
1004
|
end
|
1010
|
-
else
|
1011
|
-
def test_request_bad_response_retry
|
1012
|
-
c = basic_connection
|
1013
|
-
def c.request(*a)
|
1014
|
-
raise Net::HTTPBadResponse
|
1015
|
-
end
|
1016
|
-
|
1017
|
-
assert_raises Net::HTTP::Persistent::Error do
|
1018
|
-
@http.request @uri
|
1019
|
-
end
|
1020
1005
|
|
1021
|
-
|
1022
|
-
end
|
1006
|
+
assert c.http.finished?
|
1023
1007
|
end
|
1024
1008
|
|
1025
1009
|
def test_request_bad_response_unsafe
|
1026
1010
|
c = basic_connection
|
1027
|
-
def c.request(*a)
|
1011
|
+
def (c.http).request(*a)
|
1028
1012
|
if instance_variable_defined? :@request then
|
1029
1013
|
raise 'POST must not be retried'
|
1030
1014
|
else
|
@@ -1037,7 +1021,7 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1037
1021
|
@http.request @uri, Net::HTTP::Post.new(@uri.path)
|
1038
1022
|
end
|
1039
1023
|
|
1040
|
-
assert_equal 0,
|
1024
|
+
assert_equal 0, c.requests
|
1041
1025
|
assert_match %r%too many bad responses%, e.message
|
1042
1026
|
end
|
1043
1027
|
|
@@ -1050,7 +1034,7 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1050
1034
|
body = r.read_body
|
1051
1035
|
end
|
1052
1036
|
|
1053
|
-
req = c.req
|
1037
|
+
req = c.http.req
|
1054
1038
|
|
1055
1039
|
assert_kind_of Net::HTTPResponse, res
|
1056
1040
|
refute_nil body
|
@@ -1061,17 +1045,17 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1061
1045
|
assert_equal '30', req['keep-alive']
|
1062
1046
|
assert_match %r%test ua%, req['user-agent']
|
1063
1047
|
|
1064
|
-
assert_equal 1,
|
1048
|
+
assert_equal 1, c.requests
|
1065
1049
|
end
|
1066
1050
|
|
1067
1051
|
def test_request_close_1_0
|
1068
1052
|
c = connection
|
1069
1053
|
|
1070
|
-
class << c
|
1054
|
+
class << c.http
|
1071
1055
|
remove_method :request
|
1072
1056
|
end
|
1073
1057
|
|
1074
|
-
def c.request req
|
1058
|
+
def (c.http).request req
|
1075
1059
|
@req = req
|
1076
1060
|
r = Net::HTTPResponse.allocate
|
1077
1061
|
r.instance_variable_set :@header, {}
|
@@ -1084,7 +1068,7 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1084
1068
|
request = Net::HTTP::Get.new @uri.request_uri
|
1085
1069
|
|
1086
1070
|
res = @http.request @uri, request
|
1087
|
-
req = c.req
|
1071
|
+
req = c.http.req
|
1088
1072
|
|
1089
1073
|
assert_kind_of Net::HTTPResponse, res
|
1090
1074
|
|
@@ -1093,7 +1077,7 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1093
1077
|
assert_equal 'keep-alive', req['connection']
|
1094
1078
|
assert_equal '30', req['keep-alive']
|
1095
1079
|
|
1096
|
-
assert c.finished?
|
1080
|
+
assert c.http.finished?
|
1097
1081
|
end
|
1098
1082
|
|
1099
1083
|
def test_request_connection_close_request
|
@@ -1103,7 +1087,7 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1103
1087
|
request['connection'] = 'close'
|
1104
1088
|
|
1105
1089
|
res = @http.request @uri, request
|
1106
|
-
req = c.req
|
1090
|
+
req = c.http.req
|
1107
1091
|
|
1108
1092
|
assert_kind_of Net::HTTPResponse, res
|
1109
1093
|
|
@@ -1112,17 +1096,17 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1112
1096
|
assert_equal 'close', req['connection']
|
1113
1097
|
assert_equal nil, req['keep-alive']
|
1114
1098
|
|
1115
|
-
assert c.finished?
|
1099
|
+
assert c.http.finished?
|
1116
1100
|
end
|
1117
1101
|
|
1118
1102
|
def test_request_connection_close_response
|
1119
1103
|
c = connection
|
1120
1104
|
|
1121
|
-
class << c
|
1105
|
+
class << c.http
|
1122
1106
|
remove_method :request
|
1123
1107
|
end
|
1124
1108
|
|
1125
|
-
def c.request req
|
1109
|
+
def (c.http).request req
|
1126
1110
|
@req = req
|
1127
1111
|
r = Net::HTTPResponse.allocate
|
1128
1112
|
r.instance_variable_set :@header, {}
|
@@ -1136,7 +1120,7 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1136
1120
|
request = Net::HTTP::Get.new @uri.request_uri
|
1137
1121
|
|
1138
1122
|
res = @http.request @uri, request
|
1139
|
-
req = c.req
|
1123
|
+
req = c.http.req
|
1140
1124
|
|
1141
1125
|
assert_kind_of Net::HTTPResponse, res
|
1142
1126
|
|
@@ -1145,120 +1129,77 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1145
1129
|
assert_equal 'keep-alive', req['connection']
|
1146
1130
|
assert_equal '30', req['keep-alive']
|
1147
1131
|
|
1148
|
-
assert c.finished?
|
1132
|
+
assert c.http.finished?
|
1149
1133
|
end
|
1150
1134
|
|
1151
1135
|
def test_request_exception
|
1152
1136
|
c = basic_connection
|
1153
|
-
def c.request(*a)
|
1137
|
+
def (c.http).request(*a)
|
1138
|
+
raise Exception, "very bad things happened"
|
1139
|
+
end
|
1154
1140
|
|
1155
1141
|
assert_raises Exception do
|
1156
1142
|
@http.request @uri
|
1157
1143
|
end
|
1158
1144
|
|
1159
|
-
assert_equal 0,
|
1160
|
-
assert c.finished?
|
1145
|
+
assert_equal 0, c.requests
|
1146
|
+
assert c.http.finished?
|
1161
1147
|
end
|
1162
1148
|
|
1163
1149
|
def test_request_invalid
|
1164
1150
|
c = basic_connection
|
1165
|
-
def c.request(*a) raise Errno::EINVAL, "write" end
|
1151
|
+
def (c.http).request(*a) raise Errno::EINVAL, "write" end
|
1166
1152
|
|
1167
1153
|
e = assert_raises Net::HTTP::Persistent::Error do
|
1168
1154
|
@http.request @uri
|
1169
1155
|
end
|
1170
1156
|
|
1171
|
-
assert_equal 0,
|
1157
|
+
assert_equal 0, c.requests
|
1172
1158
|
assert_match %r%too many connection resets%, e.message
|
1173
1159
|
end
|
1174
1160
|
|
1175
|
-
def test_request_invalid_retry
|
1176
|
-
c = basic_connection
|
1177
|
-
touts[c.object_id] = Time.now
|
1178
|
-
|
1179
|
-
def c.request(*a)
|
1180
|
-
if defined? @called then
|
1181
|
-
r = Net::HTTPResponse.allocate
|
1182
|
-
r.instance_variable_set :@header, {}
|
1183
|
-
def r.http_version() '1.1' end
|
1184
|
-
r
|
1185
|
-
else
|
1186
|
-
@called = true
|
1187
|
-
raise Errno::EINVAL, "write"
|
1188
|
-
end
|
1189
|
-
end
|
1190
|
-
|
1191
|
-
@http.request @uri
|
1192
|
-
|
1193
|
-
assert c.reset?
|
1194
|
-
assert c.finished?
|
1195
|
-
end
|
1196
|
-
|
1197
1161
|
def test_request_post
|
1198
1162
|
c = connection
|
1199
1163
|
|
1200
1164
|
post = Net::HTTP::Post.new @uri.path
|
1201
1165
|
|
1202
1166
|
@http.request @uri, post
|
1203
|
-
req = c.req
|
1167
|
+
req = c.http.req
|
1204
1168
|
|
1205
1169
|
assert_same post, req
|
1206
1170
|
end
|
1207
1171
|
|
1208
1172
|
def test_request_reset
|
1209
1173
|
c = basic_connection
|
1210
|
-
def c.request(*a) raise Errno::ECONNRESET end
|
1174
|
+
def (c.http).request(*a) raise Errno::ECONNRESET end
|
1211
1175
|
|
1212
1176
|
e = assert_raises Net::HTTP::Persistent::Error do
|
1213
1177
|
@http.request @uri
|
1214
1178
|
end
|
1215
1179
|
|
1216
|
-
assert_equal 0,
|
1180
|
+
assert_equal 0, c.requests
|
1217
1181
|
assert_match %r%too many connection resets%, e.message
|
1218
1182
|
end
|
1219
1183
|
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
touts[c.object_id] = Time.now
|
1224
|
-
def c.request(*a)
|
1225
|
-
if defined? @called then
|
1226
|
-
r = Net::HTTPResponse.allocate
|
1227
|
-
r.instance_variable_set :@header, {}
|
1228
|
-
def r.http_version() '1.1' end
|
1229
|
-
r
|
1230
|
-
else
|
1231
|
-
@called = true
|
1232
|
-
raise Errno::ECONNRESET
|
1233
|
-
end
|
1234
|
-
end
|
1235
|
-
|
1236
|
-
@http.request @uri
|
1184
|
+
def test_request_reset_retry
|
1185
|
+
c = basic_connection
|
1186
|
+
c.last_use = Time.now
|
1237
1187
|
|
1238
|
-
|
1239
|
-
|
1188
|
+
def (c.http).request(*a)
|
1189
|
+
raise Errno::ECONNRESET
|
1240
1190
|
end
|
1241
|
-
else
|
1242
|
-
def test_request_reset_retry
|
1243
|
-
c = basic_connection
|
1244
|
-
touts[c.object_id] = Time.now
|
1245
1191
|
|
1246
|
-
|
1247
|
-
|
1248
|
-
end
|
1249
|
-
|
1250
|
-
assert_raises Net::HTTP::Persistent::Error do
|
1251
|
-
@http.request @uri
|
1252
|
-
end
|
1253
|
-
|
1254
|
-
refute c.reset?
|
1255
|
-
assert c.finished?
|
1192
|
+
assert_raises Net::HTTP::Persistent::Error do
|
1193
|
+
@http.request @uri
|
1256
1194
|
end
|
1195
|
+
|
1196
|
+
refute (c.http).reset?
|
1197
|
+
assert (c.http).finished?
|
1257
1198
|
end
|
1258
1199
|
|
1259
1200
|
def test_request_reset_unsafe
|
1260
1201
|
c = basic_connection
|
1261
|
-
def c.request(*a)
|
1202
|
+
def (c.http).request(*a)
|
1262
1203
|
if instance_variable_defined? :@request then
|
1263
1204
|
raise 'POST must not be retried'
|
1264
1205
|
else
|
@@ -1271,7 +1212,7 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1271
1212
|
@http.request @uri, Net::HTTP::Post.new(@uri.path)
|
1272
1213
|
end
|
1273
1214
|
|
1274
|
-
assert_equal 0,
|
1215
|
+
assert_equal 0, c.requests
|
1275
1216
|
assert_match %r%too many connection resets%, e.message
|
1276
1217
|
end
|
1277
1218
|
|
@@ -1279,15 +1220,16 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1279
1220
|
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
1280
1221
|
|
1281
1222
|
uri = URI.parse 'https://example.com/path'
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1223
|
+
@http.connection_for uri do |c|
|
1224
|
+
def (c.http).request(*)
|
1225
|
+
raise OpenSSL::SSL::SSLError, "SSL3_WRITE_PENDING:bad write retry"
|
1226
|
+
end
|
1286
1227
|
|
1287
|
-
|
1288
|
-
|
1228
|
+
e = assert_raises Net::HTTP::Persistent::Error do
|
1229
|
+
@http.request uri
|
1230
|
+
end
|
1231
|
+
assert_match %r%bad write retry%, e.message
|
1289
1232
|
end
|
1290
|
-
assert_match %r%bad write retry%, e.message
|
1291
1233
|
end
|
1292
1234
|
|
1293
1235
|
def test_request_setup
|
@@ -1308,6 +1250,22 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1308
1250
|
assert_equal '30', req['keep-alive']
|
1309
1251
|
end
|
1310
1252
|
|
1253
|
+
def test_request_string
|
1254
|
+
@http.override_headers['user-agent'] = 'test ua'
|
1255
|
+
@http.headers['accept'] = 'text/*'
|
1256
|
+
c = connection
|
1257
|
+
|
1258
|
+
res = @http.request @uri.to_s
|
1259
|
+
req = c.http.req
|
1260
|
+
|
1261
|
+
assert_kind_of Net::HTTPResponse, res
|
1262
|
+
|
1263
|
+
assert_kind_of Net::HTTP::Get, req
|
1264
|
+
assert_equal '/path', req.path
|
1265
|
+
|
1266
|
+
assert_equal 1, c.requests
|
1267
|
+
end
|
1268
|
+
|
1311
1269
|
def test_request_setup_uri
|
1312
1270
|
uri = @uri + '?a=b'
|
1313
1271
|
|
@@ -1319,8 +1277,8 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1319
1277
|
|
1320
1278
|
def test_request_failed
|
1321
1279
|
c = basic_connection
|
1322
|
-
|
1323
|
-
|
1280
|
+
c.requests = 1
|
1281
|
+
c.last_use = Time.now
|
1324
1282
|
|
1325
1283
|
original = nil
|
1326
1284
|
|
@@ -1330,7 +1288,7 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1330
1288
|
end
|
1331
1289
|
|
1332
1290
|
req = Net::HTTP::Get.new '/'
|
1333
|
-
|
1291
|
+
|
1334
1292
|
e = assert_raises Net::HTTP::Persistent::Error do
|
1335
1293
|
@http.request_failed original, req, c
|
1336
1294
|
end
|
@@ -1343,24 +1301,24 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1343
1301
|
|
1344
1302
|
def test_reset
|
1345
1303
|
c = basic_connection
|
1346
|
-
c.start
|
1347
|
-
|
1348
|
-
|
1304
|
+
c.http.start
|
1305
|
+
c.last_use = Time.now
|
1306
|
+
c.requests = 5
|
1349
1307
|
|
1350
1308
|
@http.reset c
|
1351
1309
|
|
1352
|
-
assert c.started?
|
1353
|
-
assert c.finished?
|
1354
|
-
assert c.reset?
|
1355
|
-
assert_equal 0,
|
1356
|
-
assert_equal Net::HTTP::Persistent::EPOCH,
|
1310
|
+
assert c.http.started?
|
1311
|
+
assert c.http.finished?
|
1312
|
+
assert c.http.reset?
|
1313
|
+
assert_equal 0, c.requests
|
1314
|
+
assert_equal Net::HTTP::Persistent::EPOCH, c.last_use
|
1357
1315
|
end
|
1358
1316
|
|
1359
1317
|
def test_reset_host_down
|
1360
1318
|
c = basic_connection
|
1361
|
-
|
1362
|
-
def c.start; raise Errno::EHOSTDOWN end
|
1363
|
-
|
1319
|
+
c.last_use = Time.now
|
1320
|
+
def (c.http).start; raise Errno::EHOSTDOWN end
|
1321
|
+
c.requests = 5
|
1364
1322
|
|
1365
1323
|
e = assert_raises Net::HTTP::Persistent::Error do
|
1366
1324
|
@http.reset c
|
@@ -1372,20 +1330,20 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1372
1330
|
|
1373
1331
|
def test_reset_io_error
|
1374
1332
|
c = basic_connection
|
1375
|
-
|
1376
|
-
|
1333
|
+
c.last_use = Time.now
|
1334
|
+
c.requests = 5
|
1377
1335
|
|
1378
1336
|
@http.reset c
|
1379
1337
|
|
1380
|
-
assert c.started?
|
1381
|
-
assert c.finished?
|
1338
|
+
assert c.http.started?
|
1339
|
+
assert c.http.finished?
|
1382
1340
|
end
|
1383
1341
|
|
1384
1342
|
def test_reset_refused
|
1385
1343
|
c = basic_connection
|
1386
|
-
|
1387
|
-
def c.start; raise Errno::ECONNREFUSED end
|
1388
|
-
|
1344
|
+
c.last_use = Time.now
|
1345
|
+
def (c.http).start; raise Errno::ECONNREFUSED end
|
1346
|
+
c.requests = 5
|
1389
1347
|
|
1390
1348
|
e = assert_raises Net::HTTP::Persistent::Error do
|
1391
1349
|
@http.reset c
|
@@ -1401,161 +1359,50 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1401
1359
|
|
1402
1360
|
refute @http.retry_change_requests
|
1403
1361
|
|
1404
|
-
|
1405
|
-
assert @http.can_retry?(get)
|
1406
|
-
else
|
1407
|
-
assert @http.can_retry?(get)
|
1408
|
-
end
|
1409
|
-
refute @http.can_retry?(get, true)
|
1362
|
+
refute @http.can_retry?(get)
|
1410
1363
|
refute @http.can_retry?(post)
|
1411
1364
|
|
1412
1365
|
@http.retry_change_requests = true
|
1413
1366
|
|
1414
1367
|
assert @http.retry_change_requests
|
1415
1368
|
|
1416
|
-
|
1417
|
-
assert @http.can_retry?(get)
|
1418
|
-
else
|
1419
|
-
assert @http.can_retry?(get)
|
1420
|
-
refute @http.can_retry?(get, true)
|
1421
|
-
end
|
1422
|
-
|
1369
|
+
refute @http.can_retry?(get)
|
1423
1370
|
assert @http.can_retry?(post)
|
1424
1371
|
end
|
1425
1372
|
|
1426
1373
|
def test_shutdown
|
1427
|
-
ssl_conns
|
1428
1374
|
c = connection
|
1429
|
-
rs = reqs
|
1430
|
-
ts = touts
|
1431
1375
|
|
1432
1376
|
orig = @http
|
1433
|
-
@http = Net::HTTP::Persistent.new 'name'
|
1377
|
+
@http = Net::HTTP::Persistent.new name: 'name'
|
1434
1378
|
c2 = connection
|
1435
1379
|
|
1436
1380
|
orig.shutdown
|
1437
1381
|
|
1438
1382
|
@http = orig
|
1439
1383
|
|
1440
|
-
assert c.finished?, 'last-generation connection must be finished'
|
1441
|
-
refute c2.finished?, 'present generation connection must not be finished'
|
1442
|
-
|
1443
|
-
refute_same rs, reqs
|
1444
|
-
refute_same ts, touts
|
1445
|
-
|
1446
|
-
assert_empty conns
|
1447
|
-
assert_empty ssl_conns
|
1448
|
-
|
1449
|
-
assert_empty reqs
|
1450
|
-
assert_empty touts
|
1451
|
-
end
|
1452
|
-
|
1453
|
-
def test_shutdown_in_all_threads
|
1454
|
-
conns
|
1455
|
-
ssl_conns
|
1456
|
-
|
1457
|
-
t = Thread.new do
|
1458
|
-
c = connection
|
1459
|
-
ssl_conns
|
1460
|
-
conns
|
1461
|
-
reqs
|
1462
|
-
|
1463
|
-
Thread.stop
|
1464
|
-
|
1465
|
-
c
|
1466
|
-
end
|
1467
|
-
|
1468
|
-
Thread.pass until t.status == 'sleep'
|
1469
|
-
|
1470
|
-
c = connection
|
1471
|
-
|
1472
|
-
assert_nil @http.shutdown_in_all_threads
|
1473
|
-
|
1474
|
-
assert c.finished?, 'connection in same thread must be finished'
|
1475
|
-
|
1476
|
-
assert_empty Thread.current[@http.generation_key]
|
1477
|
-
|
1478
|
-
assert_nil Thread.current[@http.request_key]
|
1479
|
-
|
1480
|
-
t.run
|
1481
|
-
assert t.value.finished?, 'connection in other thread must be finished'
|
1482
|
-
|
1483
|
-
assert_empty t[@http.generation_key]
|
1484
|
-
|
1485
|
-
assert_nil t[@http.request_key]
|
1486
|
-
end
|
1487
|
-
|
1488
|
-
def test_shutdown_no_connections
|
1489
|
-
@http.shutdown
|
1490
|
-
|
1491
|
-
assert_nil Thread.current[@http.generation_key]
|
1492
|
-
assert_nil Thread.current[@http.ssl_generation_key]
|
1493
|
-
|
1494
|
-
assert_nil Thread.current[@http.request_key]
|
1495
|
-
assert_nil Thread.current[@http.timeout_key]
|
1496
|
-
end
|
1497
|
-
|
1498
|
-
def test_shutdown_not_started
|
1499
|
-
ssl_conns
|
1500
|
-
|
1501
|
-
c = basic_connection
|
1502
|
-
def c.finish() raise IOError end
|
1503
|
-
|
1504
|
-
conns[0]["#{@uri.host}:#{@uri.port}"] = c
|
1505
|
-
|
1506
|
-
@http.shutdown
|
1507
|
-
|
1508
|
-
assert_empty Thread.current[@http.generation_key]
|
1509
|
-
assert_empty Thread.current[@http.ssl_generation_key]
|
1510
|
-
|
1511
|
-
assert_nil Thread.current[@http.request_key]
|
1512
|
-
assert_nil Thread.current[@http.timeout_key]
|
1384
|
+
assert c.http.finished?, 'last-generation connection must be finished'
|
1385
|
+
refute c2.http.finished?, 'present generation connection must not be finished'
|
1513
1386
|
end
|
1514
1387
|
|
1515
|
-
def
|
1388
|
+
def test_ssl
|
1516
1389
|
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
1517
1390
|
|
1518
|
-
@
|
1519
|
-
|
1520
|
-
@http.connection_for @uri
|
1521
|
-
|
1522
|
-
@http.shutdown
|
1523
|
-
|
1524
|
-
assert_empty ssl_conns
|
1525
|
-
end
|
1526
|
-
|
1527
|
-
def test_shutdown_thread
|
1528
|
-
t = Thread.new do
|
1529
|
-
c = connection
|
1530
|
-
conns
|
1531
|
-
ssl_conns
|
1532
|
-
|
1533
|
-
reqs
|
1534
|
-
|
1535
|
-
Thread.stop
|
1536
|
-
|
1537
|
-
c
|
1538
|
-
end
|
1539
|
-
|
1540
|
-
Thread.pass until t.status == 'sleep'
|
1541
|
-
|
1542
|
-
c = connection
|
1543
|
-
|
1544
|
-
@http.shutdown t
|
1391
|
+
@http.verify_callback = :callback
|
1392
|
+
c = Net::HTTP.new 'localhost', 80
|
1545
1393
|
|
1546
|
-
|
1394
|
+
@http.ssl c
|
1547
1395
|
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
assert_nil t[@http.request_key]
|
1553
|
-
assert_nil t[@http.timeout_key]
|
1396
|
+
assert c.use_ssl?
|
1397
|
+
assert_equal OpenSSL::SSL::VERIFY_PEER, c.verify_mode
|
1398
|
+
assert_kind_of OpenSSL::X509::Store, c.cert_store
|
1399
|
+
assert_nil c.verify_callback
|
1554
1400
|
end
|
1555
1401
|
|
1556
|
-
def
|
1402
|
+
def test_ssl_ca_file
|
1557
1403
|
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
1558
1404
|
|
1405
|
+
@http.ca_file = 'ca_file'
|
1559
1406
|
@http.verify_callback = :callback
|
1560
1407
|
c = Net::HTTP.new 'localhost', 80
|
1561
1408
|
|
@@ -1563,14 +1410,13 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1563
1410
|
|
1564
1411
|
assert c.use_ssl?
|
1565
1412
|
assert_equal OpenSSL::SSL::VERIFY_PEER, c.verify_mode
|
1566
|
-
|
1567
|
-
assert_nil c.verify_callback
|
1413
|
+
assert_equal :callback, c.verify_callback
|
1568
1414
|
end
|
1569
1415
|
|
1570
|
-
def
|
1416
|
+
def test_ssl_ca_path
|
1571
1417
|
skip 'OpenSSL is missing' unless HAVE_OPENSSL
|
1572
1418
|
|
1573
|
-
@http.
|
1419
|
+
@http.ca_path = 'ca_path'
|
1574
1420
|
@http.verify_callback = :callback
|
1575
1421
|
c = Net::HTTP.new 'localhost', 80
|
1576
1422
|
|
@@ -1667,23 +1513,11 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1667
1513
|
end
|
1668
1514
|
end
|
1669
1515
|
|
1670
|
-
def
|
1671
|
-
|
1672
|
-
|
1673
|
-
uri1 = URI.parse 'https://one.example'
|
1674
|
-
|
1675
|
-
c1 = @http.connection_for uri1
|
1676
|
-
|
1677
|
-
touts[c1.object_id] = Time.now
|
1678
|
-
reqs[c1.object_id] = 5
|
1679
|
-
|
1680
|
-
@http.reconnect_ssl
|
1681
|
-
|
1682
|
-
@http.ssl_cleanup @http.ssl_generation
|
1516
|
+
def test_ssl_timeout_equals
|
1517
|
+
@http.ssl_timeout = :ssl_timeout
|
1683
1518
|
|
1684
|
-
|
1685
|
-
|
1686
|
-
assert_empty reqs # sanity check, performed by #finish
|
1519
|
+
assert_equal :ssl_timeout, @http.ssl_timeout
|
1520
|
+
assert_equal 1, @http.ssl_generation
|
1687
1521
|
end
|
1688
1522
|
|
1689
1523
|
def test_ssl_version_equals
|
@@ -1691,10 +1525,11 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1691
1525
|
|
1692
1526
|
assert_equal :ssl_version, @http.ssl_version
|
1693
1527
|
assert_equal 1, @http.ssl_generation
|
1694
|
-
end
|
1528
|
+
end
|
1695
1529
|
|
1696
1530
|
def test_start
|
1697
1531
|
c = basic_connection
|
1532
|
+
c = c.http
|
1698
1533
|
|
1699
1534
|
@http.socket_options << [Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1]
|
1700
1535
|
@http.debug_output = $stderr
|
@@ -1722,6 +1557,13 @@ class TestNetHttpPersistent < Minitest::Test
|
|
1722
1557
|
assert_equal 1, @http.ssl_generation
|
1723
1558
|
end
|
1724
1559
|
|
1560
|
+
def test_verify_depth_equals
|
1561
|
+
@http.verify_depth = :verify_depth
|
1562
|
+
|
1563
|
+
assert_equal :verify_depth, @http.verify_depth
|
1564
|
+
assert_equal 1, @http.ssl_generation
|
1565
|
+
end
|
1566
|
+
|
1725
1567
|
def test_verify_mode_equals
|
1726
1568
|
@http.verify_mode = :verify_mode
|
1727
1569
|
|