softwaregravy-net-http-persistent 2.6

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,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,1302 @@
1
+ require 'rubygems'
2
+ require 'minitest/autorun'
3
+ require 'net/http/persistent'
4
+ require 'openssl'
5
+ require 'stringio'
6
+
7
+ class Net::HTTP::Persistent::SSLReuse
8
+ alias orig_connect connect
9
+
10
+ def test_connect
11
+ unless use_ssl? then
12
+ io = Object.new
13
+ def io.setsockopt(*a) @setsockopts ||= []; @setsockopts << a end
14
+
15
+ @socket = Net::BufferedIO.new io
16
+
17
+ return
18
+ end
19
+
20
+ io = open '/dev/null'
21
+ def io.setsockopt(*a) @setsockopts ||= []; @setsockopts << a end
22
+
23
+ @ssl_context ||= OpenSSL::SSL::SSLContext.new
24
+
25
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER unless
26
+ @ssl_context.verify_mode
27
+
28
+ s = OpenSSL::SSL::SSLSocket.new io, @ssl_context
29
+
30
+ @socket = Net::BufferedIO.new s
31
+ end
32
+
33
+ def self.use_connect which
34
+ self.send :remove_method, :connect
35
+ self.send :alias_method, :connect, which
36
+ end
37
+ end
38
+
39
+ class TestNetHttpPersistent < MiniTest::Unit::TestCase
40
+
41
+ def setup
42
+ @http = Net::HTTP::Persistent.new
43
+ @uri = URI.parse 'http://example.com/path'
44
+
45
+ ENV.delete 'http_proxy'
46
+ ENV.delete 'HTTP_PROXY'
47
+ ENV.delete 'http_proxy_user'
48
+ ENV.delete 'HTTP_PROXY_USER'
49
+ ENV.delete 'http_proxy_pass'
50
+ ENV.delete 'HTTP_PROXY_PASS'
51
+
52
+ Net::HTTP::Persistent::SSLReuse.use_connect :test_connect
53
+ end
54
+
55
+ def teardown
56
+ Thread.current.keys.each do |key|
57
+ Thread.current[key] = nil
58
+ end
59
+
60
+ Net::HTTP::Persistent::SSLReuse.use_connect :orig_connect
61
+ end
62
+
63
+ class BasicConnection
64
+ attr_accessor :started, :finished, :address, :port
65
+ attr_reader :req
66
+ def initialize
67
+ @started, @finished = 0, 0
68
+ @address, @port = 'example.com', 80
69
+ end
70
+ def finish
71
+ @finished += 1
72
+ @socket = nil
73
+ end
74
+ def finished?
75
+ @finished >= 1
76
+ end
77
+ def pipeline requests, &block
78
+ requests.map { |r| r.path }
79
+ end
80
+ def reset?
81
+ @started == @finished + 1
82
+ end
83
+ def start
84
+ @started += 1
85
+ io = Object.new
86
+ def io.setsockopt(*a) @setsockopts ||= []; @setsockopts << a end
87
+ @socket = Net::BufferedIO.new io
88
+ end
89
+ def started?
90
+ @started >= 1
91
+ end
92
+ end
93
+
94
+ def basic_connection
95
+ raise "#{@uri} is not HTTP" unless @uri.scheme.downcase == 'http'
96
+
97
+ c = BasicConnection.new
98
+ conns[0]["#{@uri.host}:#{@uri.port}"] = c
99
+ c
100
+ end
101
+
102
+ def connection
103
+ c = basic_connection
104
+ touts[c.object_id] = Time.now
105
+
106
+ def c.request(req)
107
+ @req = req
108
+ r = Net::HTTPResponse.allocate
109
+ r.instance_variable_set :@header, {}
110
+ def r.http_version() '1.1' end
111
+ def r.read_body() :read_body end
112
+ yield r if block_given?
113
+ r
114
+ end
115
+
116
+ c
117
+ end
118
+
119
+ def conns
120
+ Thread.current[@http.generation_key] ||= Hash.new { |h,k| h[k] = {} }
121
+ end
122
+
123
+ def reqs
124
+ Thread.current[@http.request_key] ||= Hash.new 0
125
+ end
126
+
127
+ def ssl_conns
128
+ Thread.current[@http.ssl_generation_key] ||= Hash.new { |h,k| h[k] = {} }
129
+ end
130
+
131
+ def ssl_connection generation = 0
132
+ raise "#{@uri} is not HTTPS" unless @uri.scheme.downcase == 'https'
133
+ c = BasicConnection.new
134
+ ssl_conns[generation]["#{@uri.host}:#{@uri.port}"] = c
135
+ c
136
+ end
137
+
138
+ def touts
139
+ Thread.current[@http.timeout_key] ||= Hash.new Net::HTTP::Persistent::EPOCH
140
+ end
141
+
142
+ def test_initialize
143
+ assert_nil @http.proxy_uri
144
+ end
145
+
146
+ def test_initialize_name
147
+ http = Net::HTTP::Persistent.new 'name'
148
+ assert_equal 'name', http.name
149
+ end
150
+
151
+ def test_initialize_proxy
152
+ proxy_uri = URI.parse 'http://proxy.example'
153
+
154
+ http = Net::HTTP::Persistent.new nil, proxy_uri
155
+
156
+ assert_equal proxy_uri, http.proxy_uri
157
+ end
158
+
159
+ def test_ca_file_equals
160
+ @http.ca_file = :ca_file
161
+
162
+ assert_equal :ca_file, @http.ca_file
163
+ assert_equal 1, @http.ssl_generation
164
+ end
165
+
166
+ def test_cert_store_equals
167
+ @http.cert_store = :cert_store
168
+
169
+ assert_equal :cert_store, @http.cert_store
170
+ assert_equal 1, @http.ssl_generation
171
+ end
172
+
173
+ def test_certificate_equals
174
+ @http.certificate = :cert
175
+
176
+ assert_equal :cert, @http.certificate
177
+ assert_equal 1, @http.ssl_generation
178
+ end
179
+
180
+ def test_connection_for
181
+ @http.open_timeout = 123
182
+ @http.read_timeout = 321
183
+ c = @http.connection_for @uri
184
+
185
+ assert_kind_of Net::HTTP::Persistent::SSLReuse, c
186
+
187
+ assert c.started?
188
+ refute c.proxy?
189
+
190
+ assert_equal 123, c.open_timeout
191
+ assert_equal 321, c.read_timeout
192
+
193
+ assert_includes conns[0].keys, 'example.com:80'
194
+ assert_same c, conns[0]['example.com:80']
195
+
196
+ socket = c.instance_variable_get :@socket
197
+ expected = if Socket.const_defined? :TCP_NODELAY then
198
+ [[Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1]]
199
+ else
200
+ []
201
+ end
202
+
203
+ assert_equal expected, socket.io.instance_variable_get(:@setsockopts)
204
+ end
205
+
206
+ def test_connection_for_cached
207
+ cached = basic_connection
208
+ cached.start
209
+ conns[0]['example.com:80'] = cached
210
+
211
+ c = @http.connection_for @uri
212
+
213
+ assert c.started?
214
+
215
+ assert_same cached, c
216
+ end
217
+
218
+ def test_connection_for_closed
219
+ cached = basic_connection
220
+ cached.start
221
+ if Socket.const_defined? :TCP_NODELAY then
222
+ io = Object.new
223
+ def io.setsockopt(*a) raise IOError, 'closed stream' end
224
+ cached.instance_variable_set :@socket, Net::BufferedIO.new(io)
225
+ end
226
+ conns['example.com:80'] = cached
227
+
228
+ c = @http.connection_for @uri
229
+
230
+ assert c.started?
231
+
232
+ assert_includes conns.keys, 'example.com:80'
233
+ assert_same c, conns[0]['example.com:80']
234
+
235
+ socket = c.instance_variable_get :@socket
236
+
237
+ refute_includes socket.io.instance_variables, :@setsockopt
238
+ refute_includes socket.io.instance_variables, '@setsockopt'
239
+ end
240
+
241
+ def test_connection_for_debug_output
242
+ io = StringIO.new
243
+ @http.debug_output = io
244
+
245
+ c = @http.connection_for @uri
246
+
247
+ assert c.started?
248
+ assert_equal io, c.instance_variable_get(:@debug_output)
249
+
250
+ assert_includes conns[0].keys, 'example.com:80'
251
+ assert_same c, conns[0]['example.com:80']
252
+ end
253
+
254
+ def test_connection_for_cached_expire_always
255
+ cached = basic_connection
256
+ cached.start
257
+ conns[0]['example.com:80'] = cached
258
+ reqs[cached.object_id] = 10
259
+ touts[cached.object_id] = Time.now # last used right now
260
+
261
+ @http.idle_timeout = 0
262
+
263
+ c = @http.connection_for @uri
264
+
265
+ assert c.started?
266
+
267
+ assert_same cached, c
268
+
269
+ assert_equal 0, reqs[cached.object_id],
270
+ 'connection reset due to timeout'
271
+ end
272
+
273
+ def test_connection_for_cached_expire_never
274
+ cached = basic_connection
275
+ cached.start
276
+ conns[0]['example.com:80'] = cached
277
+ reqs[cached.object_id] = 10
278
+ touts[cached.object_id] = Time.now # last used right now
279
+
280
+ @http.idle_timeout = nil
281
+
282
+ c = @http.connection_for @uri
283
+
284
+ assert c.started?
285
+
286
+ assert_same cached, c
287
+
288
+ assert_equal 10, reqs[cached.object_id],
289
+ 'connection reset despite no timeout'
290
+ end
291
+
292
+ def test_connection_for_cached_expired
293
+ cached = basic_connection
294
+ cached.start
295
+ conns[0]['example.com:80'] = cached
296
+ reqs[cached.object_id] = 10
297
+ touts[cached.object_id] = Time.now - 3600
298
+
299
+ c = @http.connection_for @uri
300
+
301
+ assert c.started?
302
+
303
+ assert_same cached, c
304
+ assert_equal 0, reqs[cached.object_id],
305
+ 'connection not reset due to timeout'
306
+ end
307
+
308
+ def test_connection_for_finished_ssl
309
+ uri = URI.parse 'https://example.com/path'
310
+ c = @http.connection_for uri
311
+
312
+ assert c.started?
313
+ assert c.use_ssl?
314
+
315
+ @http.finish c
316
+
317
+ refute c.started?
318
+
319
+ c2 = @http.connection_for uri
320
+
321
+ assert c2.started?
322
+ end
323
+
324
+ def test_connection_for_host_down
325
+ cached = basic_connection
326
+ def cached.start; raise Errno::EHOSTDOWN end
327
+ def cached.started?; false end
328
+ conns[0]['example.com:80'] = cached
329
+
330
+ e = assert_raises Net::HTTP::Persistent::Error do
331
+ @http.connection_for @uri
332
+ end
333
+
334
+ assert_match %r%host down%, e.message
335
+ end
336
+
337
+ def test_connection_for_http_class_with_fakeweb
338
+ Object.send :const_set, :FakeWeb, nil
339
+ c = @http.connection_for @uri
340
+ assert_instance_of Net::HTTP, c
341
+ ensure
342
+ if Object.const_defined?(:FakeWeb) then
343
+ Object.send :remove_const, :FakeWeb
344
+ end
345
+ end
346
+
347
+ def test_connection_for_http_class_with_webmock
348
+ Object.send :const_set, :WebMock, nil
349
+ c = @http.connection_for @uri
350
+ assert_instance_of Net::HTTP, c
351
+ ensure
352
+ if Object.const_defined?(:WebMock) then
353
+ Object.send :remove_const, :WebMock
354
+ end
355
+ end
356
+
357
+ def test_connection_for_name
358
+ http = Net::HTTP::Persistent.new 'name'
359
+ uri = URI.parse 'http://example/'
360
+
361
+ c = http.connection_for uri
362
+
363
+ assert c.started?
364
+
365
+ refute_includes conns.keys, 'example:80'
366
+ end
367
+
368
+ def test_connection_for_no_ssl_reuse
369
+ @http.reuse_ssl_sessions = false
370
+ @http.open_timeout = 123
371
+ @http.read_timeout = 321
372
+ c = @http.connection_for @uri
373
+
374
+ assert_instance_of Net::HTTP, c
375
+ end
376
+
377
+ def test_connection_for_proxy
378
+ uri = URI.parse 'http://proxy.example'
379
+ uri.user = 'johndoe'
380
+ uri.password = 'muffins'
381
+
382
+ http = Net::HTTP::Persistent.new nil, uri
383
+
384
+ c = http.connection_for @uri
385
+
386
+ assert c.started?
387
+ assert c.proxy?
388
+
389
+ assert_includes conns[1].keys,
390
+ 'example.com:80:proxy.example:80:johndoe:muffins'
391
+ assert_same c, conns[1]['example.com:80:proxy.example:80:johndoe:muffins']
392
+ end
393
+
394
+ def test_connection_for_refused
395
+ cached = basic_connection
396
+ def cached.start; raise Errno::ECONNREFUSED end
397
+ def cached.started?; false end
398
+ conns[0]['example.com:80'] = cached
399
+
400
+ e = assert_raises Net::HTTP::Persistent::Error do
401
+ @http.connection_for @uri
402
+ end
403
+
404
+ assert_match %r%connection refused%, e.message
405
+ end
406
+
407
+ def test_connection_for_socket_options
408
+ @http.socket_options << [Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1]
409
+ c = @http.connection_for @uri
410
+
411
+ socket = c.instance_variable_get :@socket
412
+
413
+ expected = []
414
+ expected << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if
415
+ Socket.const_defined? :TCP_NODELAY
416
+ expected << [Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1]
417
+
418
+ assert_equal expected, socket.io.instance_variable_get(:@setsockopts)
419
+ end
420
+
421
+ def test_connection_for_ssl
422
+ uri = URI.parse 'https://example.com/path'
423
+ c = @http.connection_for uri
424
+
425
+ assert c.started?
426
+ assert c.use_ssl?
427
+ end
428
+
429
+ def test_connection_for_ssl_cached
430
+ @uri = URI.parse 'https://example.com/path'
431
+
432
+ cached = ssl_connection 0
433
+
434
+ c = @http.connection_for @uri
435
+
436
+ assert_same cached, c
437
+ end
438
+
439
+ def test_connection_for_ssl_cached_reconnect
440
+ @uri = URI.parse 'https://example.com/path'
441
+
442
+ cached = ssl_connection
443
+
444
+ @http.reconnect_ssl
445
+
446
+ c = @http.connection_for @uri
447
+
448
+ refute_same cached, c
449
+ end
450
+
451
+ def test_connection_for_ssl_case
452
+ uri = URI.parse 'HTTPS://example.com/path'
453
+ c = @http.connection_for uri
454
+
455
+ assert c.started?
456
+ assert c.use_ssl?
457
+ end
458
+
459
+ def test_connection_for_timeout
460
+ cached = basic_connection
461
+ cached.start
462
+ reqs[cached.object_id] = 10
463
+ touts[cached.object_id] = Time.now - 6
464
+ conns[0]['example.com:80'] = cached
465
+
466
+ c = @http.connection_for @uri
467
+
468
+ assert c.started?
469
+ assert_equal 0, reqs[c.object_id]
470
+
471
+ assert_same cached, c
472
+ end
473
+
474
+ def test_error_message
475
+ c = basic_connection
476
+ touts[c.object_id] = Time.now - 1
477
+ reqs[c.object_id] = 5
478
+
479
+ message = @http.error_message(c)
480
+ assert_match %r%after 4 requests on #{c.object_id}%, message
481
+ assert_match %r%, last used [\d.]+ seconds ago%, message
482
+ end
483
+
484
+ def test_escape
485
+ assert_nil @http.escape nil
486
+
487
+ assert_equal '+%3F', @http.escape(' ?')
488
+ end
489
+
490
+ def test_expired_eh
491
+ c = basic_connection
492
+ touts[c.object_id] = Time.now - 11
493
+
494
+ @http.idle_timeout = 0
495
+ assert @http.expired? c
496
+
497
+ @http.idle_timeout = 10
498
+ assert @http.expired? c
499
+
500
+ @http.idle_timeout = 11
501
+ assert @http.expired? c
502
+
503
+ @http.idle_timeout = 12
504
+ refute @http.expired? c
505
+
506
+ @http.idle_timeout = nil
507
+ refute @http.expired? c
508
+ end
509
+
510
+ def test_finish
511
+ c = basic_connection
512
+ reqs[c.object_id] = 5
513
+
514
+ @http.finish c
515
+
516
+ refute c.started?
517
+ assert c.finished?
518
+ assert_equal 0, reqs[c.object_id]
519
+ end
520
+
521
+ def test_finish_io_error
522
+ c = basic_connection
523
+ def c.finish; @finished += 1; raise IOError end
524
+ reqs[c.object_id] = 5
525
+
526
+ @http.finish c
527
+
528
+ refute c.started?
529
+ assert c.finished?
530
+ end
531
+
532
+ def test_http_version
533
+ assert_nil @http.http_version @uri
534
+
535
+ connection
536
+
537
+ @http.request @uri
538
+
539
+ assert_equal '1.1', @http.http_version(@uri)
540
+ end
541
+
542
+ def test_idempotent_eh
543
+ assert @http.idempotent? Net::HTTP::Delete.new '/'
544
+ assert @http.idempotent? Net::HTTP::Get.new '/'
545
+ assert @http.idempotent? Net::HTTP::Head.new '/'
546
+ assert @http.idempotent? Net::HTTP::Options.new '/'
547
+ assert @http.idempotent? Net::HTTP::Put.new '/'
548
+ assert @http.idempotent? Net::HTTP::Trace.new '/'
549
+
550
+ refute @http.idempotent? Net::HTTP::Post.new '/'
551
+ end
552
+
553
+ def test_max_age
554
+ assert_in_delta Time.now - 5, @http.max_age
555
+
556
+ @http.idle_timeout = nil
557
+
558
+ assert_in_delta Time.now + 1, @http.max_age
559
+ end
560
+
561
+ def test_normalize_uri
562
+ assert_equal 'http://example', @http.normalize_uri('example')
563
+ assert_equal 'http://example', @http.normalize_uri('http://example')
564
+ assert_equal 'https://example', @http.normalize_uri('https://example')
565
+ end
566
+
567
+ def test_override_haeders
568
+ assert_empty @http.override_headers
569
+
570
+ @http.override_headers['User-Agent'] = 'MyCustomAgent'
571
+
572
+ expected = { 'User-Agent' => 'MyCustomAgent' }
573
+
574
+ assert_equal expected, @http.override_headers
575
+ end
576
+
577
+ def test_pipeline
578
+ skip 'net-http-pipeline not installed' unless defined?(Net::HTTP::Pipeline)
579
+
580
+ cached = basic_connection
581
+ cached.start
582
+ conns['example.com:80'] = cached
583
+
584
+ requests = [
585
+ Net::HTTP::Get.new((@uri + '1').request_uri),
586
+ Net::HTTP::Get.new((@uri + '2').request_uri),
587
+ ]
588
+
589
+ responses = @http.pipeline @uri, requests
590
+
591
+ assert_equal 2, responses.length
592
+ assert_equal '/1', responses.first
593
+ assert_equal '/2', responses.last
594
+ end
595
+
596
+ def test_private_key_equals
597
+ @http.private_key = :private_key
598
+
599
+ assert_equal :private_key, @http.private_key
600
+ assert_equal 1, @http.ssl_generation
601
+ end
602
+
603
+ def test_proxy_equals_env
604
+ ENV['HTTP_PROXY'] = 'proxy.example'
605
+
606
+ @http.proxy = :ENV
607
+
608
+ assert_equal URI.parse('http://proxy.example'), @http.proxy_uri
609
+
610
+ assert_equal 1, @http.generation, 'generation'
611
+ assert_equal 1, @http.ssl_generation, 'ssl_generation'
612
+ end
613
+
614
+ def test_proxy_equals_uri
615
+ proxy_uri = URI.parse 'http://proxy.example'
616
+
617
+ @http.proxy = proxy_uri
618
+
619
+ assert_equal proxy_uri, @http.proxy_uri
620
+ end
621
+
622
+ def test_proxy_from_env
623
+ ENV['HTTP_PROXY'] = 'proxy.example'
624
+ ENV['HTTP_PROXY_USER'] = 'johndoe'
625
+ ENV['HTTP_PROXY_PASS'] = 'muffins'
626
+
627
+ uri = @http.proxy_from_env
628
+
629
+ expected = URI.parse 'http://proxy.example'
630
+ expected.user = 'johndoe'
631
+ expected.password = 'muffins'
632
+
633
+ assert_equal expected, uri
634
+ end
635
+
636
+ def test_proxy_from_env_lower
637
+ ENV['http_proxy'] = 'proxy.example'
638
+ ENV['http_proxy_user'] = 'johndoe'
639
+ ENV['http_proxy_pass'] = 'muffins'
640
+
641
+ uri = @http.proxy_from_env
642
+
643
+ expected = URI.parse 'http://proxy.example'
644
+ expected.user = 'johndoe'
645
+ expected.password = 'muffins'
646
+
647
+ assert_equal expected, uri
648
+ end
649
+
650
+ def test_proxy_from_env_nil
651
+ uri = @http.proxy_from_env
652
+
653
+ assert_nil uri
654
+
655
+ ENV['HTTP_PROXY'] = ''
656
+
657
+ uri = @http.proxy_from_env
658
+
659
+ assert_nil uri
660
+ end
661
+
662
+ def test_reconnect
663
+ result = @http.reconnect
664
+
665
+ assert_equal 1, result
666
+ end
667
+
668
+ def test_reconnect_ssl
669
+ result = @http.reconnect_ssl
670
+
671
+ assert_equal 1, result
672
+ end
673
+
674
+ def test_request
675
+ @http.override_headers['user-agent'] = 'test ua'
676
+ @http.headers['accept'] = 'text/*'
677
+ c = connection
678
+
679
+ res = @http.request @uri
680
+ req = c.req
681
+
682
+ assert_kind_of Net::HTTPResponse, res
683
+
684
+ assert_kind_of Net::HTTP::Get, req
685
+ assert_equal '/path', req.path
686
+
687
+ assert_equal 'test ua', req['user-agent']
688
+ assert_match %r%text/\*%, req['accept']
689
+
690
+ assert_equal 'keep-alive', req['connection']
691
+ assert_equal '30', req['keep-alive']
692
+
693
+ assert_in_delta Time.now, touts[c.object_id]
694
+
695
+ assert_equal 1, reqs[c.object_id]
696
+ end
697
+
698
+ def test_request_bad_response
699
+ c = basic_connection
700
+ def c.request(*a) raise Net::HTTPBadResponse end
701
+
702
+ e = assert_raises Net::HTTP::Persistent::Error do
703
+ @http.request @uri
704
+ end
705
+
706
+ assert_equal 0, reqs[c.object_id]
707
+ assert_match %r%too many bad responses%, e.message
708
+ end
709
+
710
+ def test_request_bad_response_retry
711
+ c = basic_connection
712
+ def c.request(*a)
713
+ if defined? @called then
714
+ r = Net::HTTPResponse.allocate
715
+ r.instance_variable_set :@header, {}
716
+ def r.http_version() '1.1' end
717
+ r
718
+ else
719
+ @called = true
720
+ raise Net::HTTPBadResponse
721
+ end
722
+ end
723
+
724
+ @http.request @uri
725
+
726
+ assert c.finished?
727
+ end
728
+
729
+ def test_request_bad_response_unsafe
730
+ c = basic_connection
731
+ def c.request(*a)
732
+ if instance_variable_defined? :@request then
733
+ raise 'POST must not be retried'
734
+ else
735
+ @request = true
736
+ raise Net::HTTPBadResponse
737
+ end
738
+ end
739
+
740
+ e = assert_raises Net::HTTP::Persistent::Error do
741
+ @http.request @uri, Net::HTTP::Post.new(@uri.path)
742
+ end
743
+
744
+ assert_equal 0, reqs[c.object_id]
745
+ assert_match %r%too many bad responses%, e.message
746
+ end
747
+
748
+ def test_request_block
749
+ @http.headers['user-agent'] = 'test ua'
750
+ c = connection
751
+ body = nil
752
+
753
+ res = @http.request @uri do |r|
754
+ body = r.read_body
755
+ end
756
+
757
+ req = c.req
758
+
759
+ assert_kind_of Net::HTTPResponse, res
760
+ refute_nil body
761
+
762
+ assert_kind_of Net::HTTP::Get, req
763
+ assert_equal '/path', req.path
764
+ assert_equal 'keep-alive', req['connection']
765
+ assert_equal '30', req['keep-alive']
766
+ assert_match %r%test ua%, req['user-agent']
767
+
768
+ assert_equal 1, reqs[c.object_id]
769
+ end
770
+
771
+ def test_request_close_1_0
772
+ c = connection
773
+
774
+ class << c
775
+ remove_method :request
776
+ end
777
+
778
+ def c.request req
779
+ @req = req
780
+ r = Net::HTTPResponse.allocate
781
+ r.instance_variable_set :@header, {}
782
+ def r.http_version() '1.0' end
783
+ def r.read_body() :read_body end
784
+ yield r if block_given?
785
+ r
786
+ end
787
+
788
+ request = Net::HTTP::Get.new @uri.request_uri
789
+
790
+ res = @http.request @uri, request
791
+ req = c.req
792
+
793
+ assert_kind_of Net::HTTPResponse, res
794
+
795
+ assert_kind_of Net::HTTP::Get, req
796
+ assert_equal '/path', req.path
797
+ assert_equal 'keep-alive', req['connection']
798
+ assert_equal '30', req['keep-alive']
799
+
800
+ assert c.finished?
801
+ end
802
+
803
+ def test_request_connection_close_request
804
+ c = connection
805
+
806
+ request = Net::HTTP::Get.new @uri.request_uri
807
+ request['connection'] = 'close'
808
+
809
+ res = @http.request @uri, request
810
+ req = c.req
811
+
812
+ assert_kind_of Net::HTTPResponse, res
813
+
814
+ assert_kind_of Net::HTTP::Get, req
815
+ assert_equal '/path', req.path
816
+ assert_equal 'close', req['connection']
817
+ assert_equal nil, req['keep-alive']
818
+
819
+ assert c.finished?
820
+ end
821
+
822
+ def test_request_connection_close_response
823
+ c = connection
824
+
825
+ class << c
826
+ remove_method :request
827
+ end
828
+
829
+ def c.request req
830
+ @req = req
831
+ r = Net::HTTPResponse.allocate
832
+ r.instance_variable_set :@header, {}
833
+ r['connection'] = 'close'
834
+ def r.http_version() '1.1' end
835
+ def r.read_body() :read_body end
836
+ yield r if block_given?
837
+ r
838
+ end
839
+
840
+ request = Net::HTTP::Get.new @uri.request_uri
841
+
842
+ res = @http.request @uri, request
843
+ req = c.req
844
+
845
+ assert_kind_of Net::HTTPResponse, res
846
+
847
+ assert_kind_of Net::HTTP::Get, req
848
+ assert_equal '/path', req.path
849
+ assert_equal 'keep-alive', req['connection']
850
+ assert_equal '30', req['keep-alive']
851
+
852
+ assert c.finished?
853
+ end
854
+
855
+ def test_request_invalid
856
+ c = basic_connection
857
+ def c.request(*a) raise Errno::EINVAL, "write" end
858
+
859
+ e = assert_raises Net::HTTP::Persistent::Error do
860
+ @http.request @uri
861
+ end
862
+
863
+ assert_equal 0, reqs[c.object_id]
864
+ assert_match %r%too many connection resets%, e.message
865
+ end
866
+
867
+ def test_request_invalid_retry
868
+ c = basic_connection
869
+ touts[c.object_id] = Time.now
870
+
871
+ def c.request(*a)
872
+ if defined? @called then
873
+ r = Net::HTTPResponse.allocate
874
+ r.instance_variable_set :@header, {}
875
+ def r.http_version() '1.1' end
876
+ r
877
+ else
878
+ @called = true
879
+ raise Errno::EINVAL, "write"
880
+ end
881
+ end
882
+
883
+ @http.request @uri
884
+
885
+ assert c.reset?
886
+ assert c.finished?
887
+ end
888
+
889
+ def test_request_post
890
+ c = connection
891
+
892
+ post = Net::HTTP::Post.new @uri.path
893
+
894
+ @http.request @uri, post
895
+ req = c.req
896
+
897
+ assert_same post, req
898
+ end
899
+
900
+ def test_request_reset
901
+ c = basic_connection
902
+ def c.request(*a) raise Errno::ECONNRESET end
903
+
904
+ e = assert_raises Net::HTTP::Persistent::Error do
905
+ @http.request @uri
906
+ end
907
+
908
+ assert_equal 0, reqs[c.object_id]
909
+ assert_match %r%too many connection resets%, e.message
910
+ end
911
+
912
+ def test_request_reset_retry
913
+ c = basic_connection
914
+ touts[c.object_id] = Time.now
915
+ def c.request(*a)
916
+ if defined? @called then
917
+ r = Net::HTTPResponse.allocate
918
+ r.instance_variable_set :@header, {}
919
+ def r.http_version() '1.1' end
920
+ r
921
+ else
922
+ @called = true
923
+ raise Errno::ECONNRESET
924
+ end
925
+ end
926
+
927
+ @http.request @uri
928
+
929
+ assert c.reset?
930
+ assert c.finished?
931
+ end
932
+
933
+ def test_request_reset_unsafe
934
+ c = basic_connection
935
+ def c.request(*a)
936
+ if instance_variable_defined? :@request then
937
+ raise 'POST must not be retried'
938
+ else
939
+ @request = true
940
+ raise Errno::ECONNRESET
941
+ end
942
+ end
943
+
944
+ e = assert_raises Net::HTTP::Persistent::Error do
945
+ @http.request @uri, Net::HTTP::Post.new(@uri.path)
946
+ end
947
+
948
+ assert_equal 0, reqs[c.object_id]
949
+ assert_match %r%too many connection resets%, e.message
950
+ end
951
+
952
+ def test_request_ssl_error
953
+ uri = URI.parse 'https://example.com/path'
954
+ c = @http.connection_for uri
955
+ def c.request(*)
956
+ raise OpenSSL::SSL::SSLError, "SSL3_WRITE_PENDING:bad write retry"
957
+ end
958
+
959
+ e = assert_raises Net::HTTP::Persistent::Error do
960
+ @http.request uri
961
+ end
962
+ assert_match %r%bad write retry%, e.message
963
+ end
964
+
965
+ def test_reset
966
+ c = basic_connection
967
+ c.start
968
+ touts[c.object_id] = Time.now
969
+ reqs[c.object_id] = 5
970
+
971
+ @http.reset c
972
+
973
+ assert c.started?
974
+ assert c.finished?
975
+ assert c.reset?
976
+ assert_equal 0, reqs[c.object_id]
977
+ assert_equal Net::HTTP::Persistent::EPOCH, touts[c.object_id]
978
+ end
979
+
980
+ def test_reset_host_down
981
+ c = basic_connection
982
+ touts[c.object_id] = Time.now
983
+ def c.start; raise Errno::EHOSTDOWN end
984
+ reqs[c.object_id] = 5
985
+
986
+ e = assert_raises Net::HTTP::Persistent::Error do
987
+ @http.reset c
988
+ end
989
+
990
+ assert_match %r%host down%, e.message
991
+ end
992
+
993
+ def test_reset_io_error
994
+ c = basic_connection
995
+ touts[c.object_id] = Time.now
996
+ reqs[c.object_id] = 5
997
+
998
+ @http.reset c
999
+
1000
+ assert c.started?
1001
+ assert c.finished?
1002
+ end
1003
+
1004
+ def test_reset_refused
1005
+ c = basic_connection
1006
+ touts[c.object_id] = Time.now
1007
+ def c.start; raise Errno::ECONNREFUSED end
1008
+ reqs[c.object_id] = 5
1009
+
1010
+ e = assert_raises Net::HTTP::Persistent::Error do
1011
+ @http.reset c
1012
+ end
1013
+
1014
+ assert_match %r%connection refused%, e.message
1015
+ end
1016
+
1017
+ def test_retry_change_requests_equals
1018
+ get = Net::HTTP::Get.new('/')
1019
+ post = Net::HTTP::Post.new('/')
1020
+
1021
+ refute @http.retry_change_requests
1022
+
1023
+ assert @http.can_retry?(get)
1024
+ refute @http.can_retry?(post)
1025
+
1026
+ @http.retry_change_requests = true
1027
+
1028
+ assert @http.retry_change_requests
1029
+
1030
+ assert @http.can_retry?(get)
1031
+ assert @http.can_retry?(post)
1032
+ end
1033
+
1034
+ def test_shutdown
1035
+ ssl_conns
1036
+ c = connection
1037
+ rs = reqs
1038
+ ts = touts
1039
+
1040
+ orig = @http
1041
+ @http = Net::HTTP::Persistent.new 'name'
1042
+ c2 = connection
1043
+
1044
+ orig.shutdown
1045
+
1046
+ @http = orig
1047
+
1048
+ assert c.finished?, 'last-generation connection must be finished'
1049
+ refute c2.finished?, 'present generation connection must not be finished'
1050
+
1051
+ refute_same rs, reqs
1052
+ refute_same ts, touts
1053
+
1054
+ assert_empty conns
1055
+ assert_empty ssl_conns
1056
+
1057
+ assert_empty reqs
1058
+ assert_empty touts
1059
+ end
1060
+
1061
+ def test_shutdown_in_all_threads
1062
+ conns
1063
+ ssl_conns
1064
+
1065
+ t = Thread.new do
1066
+ c = connection
1067
+ ssl_conns
1068
+ conns
1069
+ reqs
1070
+
1071
+ Thread.stop
1072
+
1073
+ c
1074
+ end
1075
+
1076
+ Thread.pass until t.status == 'sleep'
1077
+
1078
+ c = connection
1079
+
1080
+ assert_nil @http.shutdown_in_all_threads
1081
+
1082
+ assert c.finished?, 'connection in same thread must be finished'
1083
+
1084
+ assert_empty Thread.current[@http.generation_key]
1085
+
1086
+ assert_nil Thread.current[@http.request_key]
1087
+
1088
+ t.run
1089
+ assert t.value.finished?, 'connection in other thread must be finished'
1090
+
1091
+ assert_empty t[@http.generation_key]
1092
+
1093
+ assert_nil t[@http.request_key]
1094
+ end
1095
+
1096
+ def test_shutdown_no_connections
1097
+ conns
1098
+ ssl_conns
1099
+
1100
+ @http.shutdown
1101
+
1102
+ assert_empty Thread.current[@http.generation_key]
1103
+ assert_empty Thread.current[@http.ssl_generation_key]
1104
+
1105
+ assert_nil Thread.current[@http.request_key]
1106
+ assert_nil Thread.current[@http.timeout_key]
1107
+ end
1108
+
1109
+ def test_shutdown_not_started
1110
+ ssl_conns
1111
+
1112
+ c = basic_connection
1113
+ def c.finish() raise IOError end
1114
+
1115
+ conns[0]["#{@uri.host}:#{@uri.port}"] = c
1116
+
1117
+ @http.shutdown
1118
+
1119
+ assert_empty Thread.current[@http.generation_key]
1120
+ assert_empty Thread.current[@http.ssl_generation_key]
1121
+
1122
+ assert_nil Thread.current[@http.request_key]
1123
+ assert_nil Thread.current[@http.timeout_key]
1124
+ end
1125
+
1126
+ def test_shutdown_ssl
1127
+ @uri = URI 'https://example'
1128
+
1129
+ @http.connection_for @uri
1130
+
1131
+ @http.shutdown
1132
+
1133
+ assert_empty ssl_conns
1134
+ end
1135
+
1136
+ def test_shutdown_thread
1137
+ t = Thread.new do
1138
+ c = connection
1139
+ conns
1140
+ ssl_conns
1141
+
1142
+ reqs
1143
+
1144
+ Thread.stop
1145
+
1146
+ c
1147
+ end
1148
+
1149
+ Thread.pass until t.status == 'sleep'
1150
+
1151
+ c = connection
1152
+
1153
+ @http.shutdown t
1154
+
1155
+ refute c.finished?
1156
+
1157
+ t.run
1158
+ assert t.value.finished?
1159
+ assert_empty t[@http.generation_key]
1160
+ assert_empty t[@http.ssl_generation_key]
1161
+ assert_nil t[@http.request_key]
1162
+ assert_nil t[@http.timeout_key]
1163
+ end
1164
+
1165
+ def test_ssl
1166
+ @http.verify_callback = :callback
1167
+ c = Net::HTTP.new 'localhost', 80
1168
+
1169
+ @http.ssl c
1170
+
1171
+ assert c.use_ssl?
1172
+ assert_equal OpenSSL::SSL::VERIFY_PEER, c.verify_mode
1173
+ assert_kind_of OpenSSL::X509::Store, c.cert_store
1174
+ assert_nil c.verify_callback
1175
+ end
1176
+
1177
+ def test_ssl_ca_file
1178
+ @http.ca_file = 'ca_file'
1179
+ @http.verify_callback = :callback
1180
+ c = Net::HTTP.new 'localhost', 80
1181
+
1182
+ @http.ssl c
1183
+
1184
+ assert c.use_ssl?
1185
+ assert_equal OpenSSL::SSL::VERIFY_PEER, c.verify_mode
1186
+ assert_equal :callback, c.verify_callback
1187
+ end
1188
+
1189
+ def test_ssl_cert_store
1190
+ store = OpenSSL::X509::Store.new
1191
+ @http.cert_store = store
1192
+
1193
+ c = Net::HTTP.new 'localhost', 80
1194
+
1195
+ @http.ssl c
1196
+
1197
+ assert c.use_ssl?
1198
+ assert_equal store, c.cert_store
1199
+ end
1200
+
1201
+ def test_ssl_cert_store_default
1202
+ @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
1203
+
1204
+ c = Net::HTTP.new 'localhost', 80
1205
+
1206
+ @http.ssl c
1207
+
1208
+ assert c.use_ssl?
1209
+ assert c.cert_store
1210
+ end
1211
+
1212
+ def test_ssl_certificate
1213
+ @http.certificate = :cert
1214
+ @http.private_key = :key
1215
+ c = Net::HTTP.new 'localhost', 80
1216
+
1217
+ @http.ssl c
1218
+
1219
+ assert c.use_ssl?
1220
+ assert_equal :cert, c.cert
1221
+ assert_equal :key, c.key
1222
+ end
1223
+
1224
+ def test_ssl_verify_mode
1225
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
1226
+ c = Net::HTTP.new 'localhost', 80
1227
+
1228
+ @http.ssl c
1229
+
1230
+ assert c.use_ssl?
1231
+ assert_equal OpenSSL::SSL::VERIFY_NONE, c.verify_mode
1232
+ end
1233
+
1234
+ def test_ssl_warning
1235
+ orig_verify_peer = OpenSSL::SSL::VERIFY_PEER
1236
+ OpenSSL::SSL.send :remove_const, :VERIFY_PEER
1237
+ OpenSSL::SSL.send :const_set, :VERIFY_PEER, OpenSSL::SSL::VERIFY_NONE
1238
+
1239
+ c = Net::HTTP.new 'localhost', 80
1240
+
1241
+ out, err = capture_io do
1242
+ @http.ssl c
1243
+ end
1244
+
1245
+ assert_empty out
1246
+
1247
+ assert_match %r%localhost:80%, err
1248
+ assert_match %r%I_KNOW_THAT_OPENSSL%, err
1249
+
1250
+ Object.send :const_set, :I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG, nil
1251
+
1252
+ assert_silent do
1253
+ @http.ssl c
1254
+ end
1255
+ ensure
1256
+ OpenSSL::SSL.send :remove_const, :VERIFY_PEER
1257
+ OpenSSL::SSL.send :const_set, :VERIFY_PEER, orig_verify_peer
1258
+ if Object.const_defined?(:I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG) then
1259
+ Object.send :remove_const, :I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG
1260
+ end
1261
+ end
1262
+
1263
+ def test_ssl_cleanup
1264
+ uri1 = URI.parse 'https://one.example'
1265
+
1266
+ c1 = @http.connection_for uri1
1267
+
1268
+ touts[c1.object_id] = Time.now
1269
+ reqs[c1.object_id] = 5
1270
+
1271
+ @http.reconnect_ssl
1272
+
1273
+ @http.ssl_cleanup @http.ssl_generation
1274
+
1275
+ assert_empty ssl_conns
1276
+ assert_empty touts
1277
+ assert_empty reqs # sanity check, performed by #finish
1278
+ end
1279
+
1280
+ def test_ssl_version_equals
1281
+ @http.ssl_version = :ssl_version
1282
+
1283
+ assert_equal :ssl_version, @http.ssl_version
1284
+ assert_equal 1, @http.ssl_generation
1285
+ end if RUBY_VERSION > '1.9'
1286
+
1287
+ def test_verify_callback_equals
1288
+ @http.verify_callback = :verify_callback
1289
+
1290
+ assert_equal :verify_callback, @http.verify_callback
1291
+ assert_equal 1, @http.ssl_generation
1292
+ end
1293
+
1294
+ def test_verify_mode_equals
1295
+ @http.verify_mode = :verify_mode
1296
+
1297
+ assert_equal :verify_mode, @http.verify_mode
1298
+ assert_equal 1, @http.ssl_generation
1299
+ end
1300
+
1301
+ end
1302
+