net-http-persistent-pool 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+