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 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
@@ -6,4 +6,6 @@ README.txt
6
6
  Rakefile
7
7
  lib/net/http/faster.rb
8
8
  lib/net/http/persistent.rb
9
+ lib/net/http/persistent/ssl_reuse.rb
9
10
  test/test_net_http_persistent.rb
11
+ test/test_net_http_persistent_ssl_reuse.rb
@@ -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.0'
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 = nil
234
- @ca_file = nil
235
- @private_key = nil
236
- @verify_callback = nil
237
- @verify_mode = nil
238
- @cert_store = nil
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] = Net::HTTP.new(*net_http_args)
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 = OpenSSL::SSL::VERIFY_PEER unless @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 = @cert_store if @cert_store
539
-
540
- if @verify_mode then
541
- connection.verify_mode = @verify_mode
542
-
543
- connection.cert_store ||= begin
544
- store = OpenSSL::X509::Store.new
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 connect
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: 3
4
+ hash: 1
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
- - 0
9
- version: "2.0"
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-08-27 00:00:00 Z
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: 1
48
+ hash: 5
49
49
  segments:
50
50
  - 2
51
51
  - 3
52
- - 1
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.9
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