RUBY-316 initial SSL support

This commit is contained in:
Kyle Banker 2011-08-26 17:35:40 -04:00
parent 8db62d2cbf
commit f00c0dfcf0
6 changed files with 58 additions and 8 deletions

View File

@ -59,6 +59,7 @@ require 'mongo/util/core_ext'
require 'mongo/util/pool'
require 'mongo/util/pool_manager'
require 'mongo/util/server_version'
require 'mongo/util/ssl_socket'
require 'mongo/util/uri_parser'
require 'mongo/collection'

View File

@ -36,7 +36,7 @@ module Mongo
RESPONSE_HEADER_SIZE = 20
attr_reader :logger, :size, :auths, :primary, :safe, :host_to_try,
:pool_size, :connect_timeout, :primary_pool
:pool_size, :connect_timeout, :primary_pool, :socket_class
# Counter for generating unique request ids.
@@current_request_id = 0
@ -73,6 +73,7 @@ module Mongo
# Disabled by default.
# @option opts [Float] :connect_timeout (nil) The number of seconds to wait before timing out a
# connection attempt.
# @option opts [Boolean] :ssl (false) If true, create the connection to the server using SSL.
#
# @example localhost, 27017
# Connection.new
@ -636,6 +637,14 @@ module Mongo
# Default maximum BSON object size
@max_bson_size = Mongo::DEFAULT_MAX_BSON_SIZE
# Determine whether to use SSL.
@ssl = opts.fetch(:ssl, false)
if @ssl
@socket_class = Mongo::SSLSocket
else
@socket_class = ::TCPSocket
end
# Authentication objects
@auths = opts.fetch(:auths, [])
@ -729,11 +738,11 @@ module Mongo
if @connect_timeout
Mongo::TimeoutHandler.timeout(@connect_timeout, OperationTimeout) do
socket = TCPSocket.new(host, port)
socket = @socket_class.new(host, port)
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
end
else
socket = TCPSocket.new(host, port)
socket = @socket_class.new(host, port)
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
end

View File

@ -31,10 +31,10 @@ module Mongo
socket = nil
if self.connection.connect_timeout
Mongo::TimeoutHandler.timeout(self.connection.connect_timeout, OperationTimeout) do
socket = TCPSocket.new(self.host, self.port)
socket = self.connection.socket_class.new(self.host, self.port)
end
else
socket = TCPSocket.new(self.host, self.port)
socket = self.connection.socket_class.new(self.host, self.port)
end
if socket.nil?

View File

@ -110,7 +110,7 @@ module Mongo
# therefore, it runs within a mutex.
def checkout_new_socket
begin
socket = TCPSocket.new(@host, @port)
socket = self.connection.socket_class.new(@host, @port)
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
rescue => ex
socket.close if socket

View File

@ -0,0 +1,38 @@
require 'openssl'
module Mongo
# A basic wrapper over Ruby's SSLSocket that initiates
# a TCP connection over SSL and then provides an basic interface
# mirroring Ruby's TCPSocket, vis., TCPSocket#send and TCPSocket#read.
class SSLSocket
def initialize(host, port)
@socket = ::TCPSocket.new(host, port)
@ssl = OpenSSL::SSL::SSLSocket.new(@socket)
@ssl.sync_close = true
@ssl.connect
end
def setsockopt(key, value, n)
@socket.setsockopt(key, value, n)
end
# Write to the SSL socket.
#
# @param buffer a buffer to send.
# @param flags socket flags. Because Ruby's SSL
def send(buffer, flags=0)
@ssl.syswrite(buffer)
end
def read(length, buffer)
@ssl.sysread(length, buffer)
end
def close
@ssl.close
end
end
end

View File

@ -7,8 +7,10 @@ class NodeTest < Test::Unit::TestCase
end
should "refuse to connect to node without 'hosts' key" do
tcp = mock()
node = Node.new(@connection, ['localhost', 27017])
TCPSocket.stubs(:new).returns(new_mock_socket)
tcp.stubs(:new).returns(new_mock_socket)
@connection.stubs(:socket_class).returns(tcp)
admin_db = new_mock_db
admin_db.stubs(:command).returns({'ok' => 1, 'ismaster' => 1})
@ -17,7 +19,7 @@ class NodeTest < Test::Unit::TestCase
@connection.expects(:log)
assert node.connect
assert node.set_config
node.set_config
end
should "load a node from an array" do