Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial try for SSL session reuse

Should have tests.
  • Loading branch information...
commit 7fc04933961ea3ea5a2aa595172ca7cd29a718f5 1 parent fb3794b
@nahi authored
Showing with 74 additions and 6 deletions.
  1. +21 −6 lib/httpclient/session.rb
  2. +53 −0 lib/httpclient/ssl_config.rb
View
27 lib/httpclient/session.rb
@@ -300,6 +300,18 @@ def ssl_connect(hostname = nil)
@ssl_socket.connect
end
+ def session_reused?
+ @ssl_socket.session_reused? if @ssl_socket.respond_to?(:session_reused?)
+ end
+
+ def session=(session)
+ @ssl_socket.session = session if @ssl_socket.respond_to?(:session=)
+ end
+
+ def session
+ @ssl_socket.session if @ssl_socket.respond_to?(:session)
+ end
+
def post_connection_check(host)
verify_mode = @context.verify_mode || OpenSSL::SSL::VERIFY_NONE
if verify_mode == OpenSSL::SSL::VERIFY_NONE
@@ -376,16 +388,13 @@ def check_mask(value, mask)
end
def create_openssl_socket(socket)
- ssl_socket = nil
- if OpenSSL::SSL.const_defined?("SSLContext")
- ctx = OpenSSL::SSL::SSLContext.new
- @context.set_context(ctx)
- ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ctx)
+ if ctx = @context.ssl_context
+ OpenSSL::SSL::SSLSocket.new(socket, ctx)
else
ssl_socket = OpenSSL::SSL::SSLSocket.new(socket)
@context.set_context(ssl_socket)
+ ssl_socket
end
- ssl_socket
end
def debug(str)
@@ -736,9 +745,15 @@ def connect
else
@socket = create_ssl_socket(@socket)
connect_ssl_proxy(@socket, URI.parse(@dest.to_s)) if @proxy
+ if cached_session = @ssl_config.get_ssl_session(@dest)
+ @socket.session = cached_session
+ end
@socket.ssl_connect(@dest.host)
@socket.post_connection_check(@dest)
@ssl_peer_cert = @socket.peer_cert
+ if !cached_session or @socket.session != cached_session
+ @ssl_config.add_ssl_session(@dest, @socket.session)
+ end
end
end
# Use Ruby internal buffering instead of passing data immediately
View
53 lib/httpclient/ssl_config.rb
@@ -69,6 +69,33 @@ class SSLConfig
# For server side configuration. Ignore this.
attr_reader :client_ca # :nodoc:
+ # external ssl session cache
+ class SSLSessionCache
+ def initialize
+ @pool = {}
+ end
+
+ def get(site)
+ if sessions = @pool[site]
+ sess = sessions.first
+ p [:get, sess]
+ sess
+ end
+ end
+
+ def add(site, session)
+ p [:add, session]
+ puts session.to_text
+ (@pool[site] ||= []) << session
+ end
+
+ def remove(session)
+ @pool.each do |site, sessions|
+ p [:remove, sessions.delete(session)]
+ end
+ end
+ end
+
# Creates a SSLConfig.
def initialize(client)
return unless SSLEnabled
@@ -83,6 +110,7 @@ def initialize(client)
@options = defined?(SSL::OP_ALL) ? SSL::OP_ALL | SSL::OP_NO_SSLv2 : nil
@ciphers = "ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH"
@cacerts_loaded = false
+ @ssl_session_cache = SSLSessionCache.new
end
# Sets certificate (OpenSSL::X509::Certificate) for SSL client
@@ -238,6 +266,18 @@ def client_ca=(client_ca) # :nodoc:
end
# interfaces for SSLSocketWrap.
+ def ssl_context
+ @ssl_context ||= create_context
+ end
+
+ def add_ssl_session(site, session)
+ @ssl_session_cache.add(site, session)
+ end
+
+ def get_ssl_session(site)
+ @ssl_session_cache.get(site)
+ end
+
def set_context(ctx) # :nodoc:
load_trust_ca unless @cacerts_loaded
@cacerts_loaded = true
@@ -253,6 +293,12 @@ def set_context(ctx) # :nodoc:
ctx.timeout = @timeout
ctx.options = @options
ctx.ciphers = @ciphers
+ ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
+ ctx.session_cache_size = 2
+ ctx.session_id_context = "TODO"
+ ctx.session_remove_cb = proc do |ctx, sess|
+ @ssl_session_cache.remove(sess)
+ end
end
# post connection check proc for ruby < 1.8.5.
@@ -354,6 +400,13 @@ def sample_verify_callback(is_ok, ctx)
def change_notify
@client.reset_all
+ @ssl_context = nil
+ end
+
+ def create_context
+ ctx = OpenSSL::SSL::SSLContext.new
+ set_context(ctx)
+ ctx
end
def load_cacerts(cert_store)
Please sign in to comment.
Something went wrong with that request. Please try again.