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.
@@ -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
+