net-http-persistent 2.0 → 2.1
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.
- data.tar.gz.sig +0 -0
- data/History.txt +12 -0
- data/Manifest.txt +2 -0
- data/lib/net/http/persistent.rb +32 -20
- data/lib/net/http/persistent/ssl_reuse.rb +129 -0
- data/test/test_net_http_persistent.rb +23 -2
- data/test/test_net_http_persistent_ssl_reuse.rb +100 -0
- metadata +11 -9
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/History.txt
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
=== 2.1 / 2011-09-19
|
2
|
+
|
3
|
+
* Minor Enhancement
|
4
|
+
* For HTTPS connections, SSL sessions are now reused avoiding the extra
|
5
|
+
round trips and computations of extra SSL handshakes. If you have
|
6
|
+
problems with SSL session reuse it can be disabled by
|
7
|
+
Net::HTTP::Persistent#reuse_ssl_sessions
|
8
|
+
|
9
|
+
* Bug Fixes
|
10
|
+
* The default certificate store is now used even if #verify_mode was not
|
11
|
+
set. Issue #7, Pull Request #8 by Matthew M. Boedicker
|
12
|
+
|
1
13
|
=== 2.0 / 2011-08-26
|
2
14
|
|
3
15
|
* Incompatibility
|
data/Manifest.txt
CHANGED
data/lib/net/http/persistent.rb
CHANGED
@@ -43,7 +43,7 @@ class Net::HTTP::Persistent
|
|
43
43
|
##
|
44
44
|
# The version of Net::HTTP::Persistent use are using
|
45
45
|
|
46
|
-
VERSION = '2.
|
46
|
+
VERSION = '2.1'
|
47
47
|
|
48
48
|
##
|
49
49
|
# Error class for errors raised by Net::HTTP::Persistent. Various
|
@@ -135,6 +135,15 @@ class Net::HTTP::Persistent
|
|
135
135
|
|
136
136
|
attr_reader :request_key # :nodoc:
|
137
137
|
|
138
|
+
##
|
139
|
+
# By default SSL sessions are reused to avoid extra SSL handshakes. Set
|
140
|
+
# this to false if you have problems communicating with an HTTPS server
|
141
|
+
# like:
|
142
|
+
#
|
143
|
+
# SSL_connect [...] read finished A: unexpected message (OpenSSL::SSL::SSLError)
|
144
|
+
|
145
|
+
attr_accessor :reuse_ssl_sessions
|
146
|
+
|
138
147
|
##
|
139
148
|
# An array of options for Socket#setsockopt.
|
140
149
|
#
|
@@ -230,12 +239,13 @@ class Net::HTTP::Persistent
|
|
230
239
|
key = ['net_http_persistent', name, 'requests'].compact.join '_'
|
231
240
|
@request_key = key.intern
|
232
241
|
|
233
|
-
@certificate
|
234
|
-
@ca_file
|
235
|
-
@private_key
|
236
|
-
@verify_callback
|
237
|
-
@verify_mode
|
238
|
-
@cert_store
|
242
|
+
@certificate = nil
|
243
|
+
@ca_file = nil
|
244
|
+
@private_key = nil
|
245
|
+
@verify_callback = nil
|
246
|
+
@verify_mode = OpenSSL::SSL::VERIFY_PEER
|
247
|
+
@cert_store = nil
|
248
|
+
@reuse_ssl_sessions = true
|
239
249
|
|
240
250
|
@retry_change_requests = false
|
241
251
|
end
|
@@ -258,7 +268,7 @@ class Net::HTTP::Persistent
|
|
258
268
|
end
|
259
269
|
|
260
270
|
unless connection = connections[connection_id] then
|
261
|
-
connections[connection_id] =
|
271
|
+
connections[connection_id] = http_class.new(*net_http_args)
|
262
272
|
connection = connections[connection_id]
|
263
273
|
ssl connection if uri.scheme.downcase == 'https'
|
264
274
|
end
|
@@ -314,6 +324,10 @@ class Net::HTTP::Persistent
|
|
314
324
|
rescue IOError
|
315
325
|
end
|
316
326
|
|
327
|
+
def http_class # :nodoc:
|
328
|
+
@reuse_ssl_sessions ? Net::HTTP::Persistent::SSLReuse : Net::HTTP
|
329
|
+
end
|
330
|
+
|
317
331
|
##
|
318
332
|
# Returns the HTTP protocol version for +uri+
|
319
333
|
|
@@ -522,7 +536,7 @@ class Net::HTTP::Persistent
|
|
522
536
|
def ssl connection
|
523
537
|
connection.use_ssl = true
|
524
538
|
|
525
|
-
connection.verify_mode =
|
539
|
+
connection.verify_mode = @verify_mode
|
526
540
|
|
527
541
|
if @ca_file then
|
528
542
|
connection.ca_file = @ca_file
|
@@ -535,18 +549,16 @@ class Net::HTTP::Persistent
|
|
535
549
|
connection.key = @private_key
|
536
550
|
end
|
537
551
|
|
538
|
-
connection.cert_store =
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
store.set_default_paths
|
546
|
-
store
|
547
|
-
end
|
548
|
-
end
|
552
|
+
connection.cert_store = if @cert_store then
|
553
|
+
@cert_store
|
554
|
+
else
|
555
|
+
store = OpenSSL::X509::Store.new
|
556
|
+
store.set_default_paths
|
557
|
+
store
|
558
|
+
end
|
549
559
|
end
|
550
560
|
|
551
561
|
end
|
552
562
|
|
563
|
+
require 'net/http/persistent/ssl_reuse'
|
564
|
+
|
@@ -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.session = @ssl_session if @ssl_session
|
47
|
+
s.sync_close = true
|
48
|
+
end
|
49
|
+
@socket = Net::BufferedIO.new(s)
|
50
|
+
@socket.read_timeout = @read_timeout
|
51
|
+
@socket.continue_timeout = @continue_timeout if
|
52
|
+
@socket.respond_to? :continue_timeout
|
53
|
+
@socket.debug_output = @debug_output
|
54
|
+
if use_ssl?
|
55
|
+
begin
|
56
|
+
if proxy?
|
57
|
+
@socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
|
58
|
+
@address, @port, HTTPVersion)
|
59
|
+
@socket.writeline "Host: #{@address}:#{@port}"
|
60
|
+
if proxy_user
|
61
|
+
credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
|
62
|
+
credential.delete!("\r\n")
|
63
|
+
@socket.writeline "Proxy-Authorization: Basic #{credential}"
|
64
|
+
end
|
65
|
+
@socket.writeline ''
|
66
|
+
HTTPResponse.read_new(@socket).value
|
67
|
+
end
|
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
|
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.session = @ssl_session if @ssl_session
|
99
|
+
s.sync_close = true
|
100
|
+
end
|
101
|
+
@socket = Net::BufferedIO.new(s)
|
102
|
+
@socket.read_timeout = @read_timeout
|
103
|
+
@socket.debug_output = @debug_output
|
104
|
+
if use_ssl?
|
105
|
+
if proxy?
|
106
|
+
@socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
|
107
|
+
@address, @port, HTTPVersion)
|
108
|
+
@socket.writeline "Host: #{@address}:#{@port}"
|
109
|
+
if proxy_user
|
110
|
+
credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
|
111
|
+
credential.delete!("\r\n")
|
112
|
+
@socket.writeline "Proxy-Authorization: Basic #{credential}"
|
113
|
+
end
|
114
|
+
@socket.writeline ''
|
115
|
+
HTTPResponse.read_new(@socket).value
|
116
|
+
end
|
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
|
+
|
@@ -4,10 +4,10 @@ require 'net/http/persistent'
|
|
4
4
|
require 'openssl'
|
5
5
|
require 'stringio'
|
6
6
|
|
7
|
-
class Net::HTTP
|
7
|
+
class Net::HTTP::Persistent::SSLReuse
|
8
8
|
alias orig_connect connect
|
9
9
|
|
10
|
-
def
|
10
|
+
def test_connect
|
11
11
|
unless use_ssl? then
|
12
12
|
io = Object.new
|
13
13
|
def io.setsockopt(*a) @setsockopts ||= []; @setsockopts << a end
|
@@ -29,6 +29,11 @@ class Net::HTTP
|
|
29
29
|
|
30
30
|
@socket = Net::BufferedIO.new s
|
31
31
|
end
|
32
|
+
|
33
|
+
def self.use_connect which
|
34
|
+
self.send :remove_method, :connect
|
35
|
+
self.send :alias_method, :connect, which
|
36
|
+
end
|
32
37
|
end
|
33
38
|
|
34
39
|
class TestNetHttpPersistent < MiniTest::Unit::TestCase
|
@@ -43,12 +48,16 @@ class TestNetHttpPersistent < MiniTest::Unit::TestCase
|
|
43
48
|
ENV.delete 'HTTP_PROXY_USER'
|
44
49
|
ENV.delete 'http_proxy_pass'
|
45
50
|
ENV.delete 'HTTP_PROXY_PASS'
|
51
|
+
|
52
|
+
Net::HTTP::Persistent::SSLReuse.use_connect :test_connect
|
46
53
|
end
|
47
54
|
|
48
55
|
def teardown
|
49
56
|
Thread.current.keys.each do |key|
|
50
57
|
Thread.current[key] = nil
|
51
58
|
end
|
59
|
+
|
60
|
+
Net::HTTP::Persistent::SSLReuse.use_connect :orig_connect
|
52
61
|
end
|
53
62
|
|
54
63
|
class BasicConnection
|
@@ -140,6 +149,8 @@ class TestNetHttpPersistent < MiniTest::Unit::TestCase
|
|
140
149
|
@http.read_timeout = 321
|
141
150
|
c = @http.connection_for @uri
|
142
151
|
|
152
|
+
assert_kind_of Net::HTTP::Persistent::SSLReuse, c
|
153
|
+
|
143
154
|
assert c.started?
|
144
155
|
refute c.proxy?
|
145
156
|
|
@@ -245,6 +256,15 @@ class TestNetHttpPersistent < MiniTest::Unit::TestCase
|
|
245
256
|
refute_includes conns.keys, 'example:80'
|
246
257
|
end
|
247
258
|
|
259
|
+
def test_connection_for_no_ssl_reuse
|
260
|
+
@http.reuse_ssl_sessions = false
|
261
|
+
@http.open_timeout = 123
|
262
|
+
@http.read_timeout = 321
|
263
|
+
c = @http.connection_for @uri
|
264
|
+
|
265
|
+
assert_instance_of Net::HTTP, c
|
266
|
+
end
|
267
|
+
|
248
268
|
def test_connection_for_proxy
|
249
269
|
uri = URI.parse 'http://proxy.example'
|
250
270
|
uri.user = 'johndoe'
|
@@ -759,6 +779,7 @@ class TestNetHttpPersistent < MiniTest::Unit::TestCase
|
|
759
779
|
|
760
780
|
assert c.use_ssl?
|
761
781
|
assert_equal OpenSSL::SSL::VERIFY_PEER, c.verify_mode
|
782
|
+
assert_kind_of OpenSSL::X509::Store, c.cert_store
|
762
783
|
assert_nil c.verify_callback
|
763
784
|
end
|
764
785
|
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'net/http/persistent'
|
4
|
+
require 'openssl'
|
5
|
+
require 'webrick'
|
6
|
+
require 'webrick/ssl'
|
7
|
+
|
8
|
+
##
|
9
|
+
# This test is based on (and contains verbatim code from) the Net::HTTP tests
|
10
|
+
# in ruby
|
11
|
+
|
12
|
+
class TestNetHttpPersistentSSLReuse < MiniTest::Unit::TestCase
|
13
|
+
|
14
|
+
class NullWriter
|
15
|
+
def <<(s) end
|
16
|
+
def puts(*args) end
|
17
|
+
def print(*args) end
|
18
|
+
def printf(*args) end
|
19
|
+
end
|
20
|
+
|
21
|
+
def setup
|
22
|
+
@name = OpenSSL::X509::Name.parse 'CN=localhost'
|
23
|
+
|
24
|
+
@key = OpenSSL::PKey::RSA.new 512
|
25
|
+
|
26
|
+
@cert = OpenSSL::X509::Certificate.new
|
27
|
+
@cert.version = 2
|
28
|
+
@cert.serial = 0
|
29
|
+
@cert.not_before = Time.now
|
30
|
+
@cert.not_after = Time.now + 300
|
31
|
+
@cert.public_key = @key.public_key
|
32
|
+
@cert.subject = @name
|
33
|
+
|
34
|
+
@host = 'localhost'
|
35
|
+
@port = 10082
|
36
|
+
|
37
|
+
config = {
|
38
|
+
:BindAddress => @host,
|
39
|
+
:Port => @port,
|
40
|
+
:Logger => WEBrick::Log.new(NullWriter.new),
|
41
|
+
:AccessLog => [],
|
42
|
+
:ShutDownSocketWithoutClose => true,
|
43
|
+
:ServerType => Thread,
|
44
|
+
:SSLEnable => true,
|
45
|
+
:SSLCertificate => @cert,
|
46
|
+
:SSLPrivateKey => @key,
|
47
|
+
:SSLStartImmediately => true,
|
48
|
+
}
|
49
|
+
|
50
|
+
@server = WEBrick::HTTPServer.new config
|
51
|
+
|
52
|
+
@server.mount_proc '/' do |req, res|
|
53
|
+
res.body = "ok"
|
54
|
+
end
|
55
|
+
|
56
|
+
@server.start
|
57
|
+
|
58
|
+
begin
|
59
|
+
TCPSocket.open(@host, @port).close
|
60
|
+
rescue Errno::ECONNREFUSED
|
61
|
+
sleep 0.2
|
62
|
+
n_try_max -= 1
|
63
|
+
raise 'cannot spawn server; give up' if n_try_max < 0
|
64
|
+
retry
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def teardown
|
69
|
+
if @server then
|
70
|
+
@server.shutdown
|
71
|
+
sleep 0.01 until @server.status == :Stop
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_ssl_connection_reuse
|
76
|
+
@http = Net::HTTP::Persistent::SSLReuse.new @host, @port
|
77
|
+
@http.use_ssl = true
|
78
|
+
@http.verify_callback = proc do |_, store_ctx|
|
79
|
+
store_ctx.current_cert.to_der == @cert.to_der
|
80
|
+
end
|
81
|
+
|
82
|
+
@http.start
|
83
|
+
@http.get '/'
|
84
|
+
@http.finish
|
85
|
+
|
86
|
+
@http.start
|
87
|
+
@http.get '/'
|
88
|
+
@http.finish
|
89
|
+
|
90
|
+
@http.start
|
91
|
+
@http.get '/'
|
92
|
+
|
93
|
+
socket = @http.instance_variable_get :@socket
|
94
|
+
ssl_socket = socket.io
|
95
|
+
|
96
|
+
assert ssl_socket.session_reused?
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: net-http-persistent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 1
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: "2.
|
8
|
+
- 1
|
9
|
+
version: "2.1"
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Eric Hodel
|
@@ -35,7 +35,7 @@ cert_chain:
|
|
35
35
|
x52qPcexcYZR7w==
|
36
36
|
-----END CERTIFICATE-----
|
37
37
|
|
38
|
-
date: 2011-
|
38
|
+
date: 2011-09-20 00:00:00 Z
|
39
39
|
dependencies:
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: minitest
|
@@ -43,14 +43,13 @@ dependencies:
|
|
43
43
|
requirement: &id001 !ruby/object:Gem::Requirement
|
44
44
|
none: false
|
45
45
|
requirements:
|
46
|
-
- -
|
46
|
+
- - ~>
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
hash:
|
48
|
+
hash: 5
|
49
49
|
segments:
|
50
50
|
- 2
|
51
51
|
- 3
|
52
|
-
|
53
|
-
version: 2.3.1
|
52
|
+
version: "2.3"
|
54
53
|
type: :development
|
55
54
|
version_requirements: *id001
|
56
55
|
- !ruby/object:Gem::Dependency
|
@@ -98,7 +97,9 @@ files:
|
|
98
97
|
- Rakefile
|
99
98
|
- lib/net/http/faster.rb
|
100
99
|
- lib/net/http/persistent.rb
|
100
|
+
- lib/net/http/persistent/ssl_reuse.rb
|
101
101
|
- test/test_net_http_persistent.rb
|
102
|
+
- test/test_net_http_persistent_ssl_reuse.rb
|
102
103
|
homepage: http://docs.seattlerb.org/net-http-persistent
|
103
104
|
licenses: []
|
104
105
|
|
@@ -129,9 +130,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
130
|
requirements: []
|
130
131
|
|
131
132
|
rubyforge_project: net-http-persistent
|
132
|
-
rubygems_version: 1.8.
|
133
|
+
rubygems_version: 1.8.10
|
133
134
|
signing_key:
|
134
135
|
specification_version: 3
|
135
136
|
summary: Manages persistent connections using Net::HTTP plus a speed fix for Ruby 1.8
|
136
137
|
test_files:
|
137
138
|
- test/test_net_http_persistent.rb
|
139
|
+
- test/test_net_http_persistent_ssl_reuse.rb
|
metadata.gz.sig
CHANGED
Binary file
|