lib/socketry/ssl/socket.rb in socketry-0.3.0 vs lib/socketry/ssl/socket.rb in socketry-0.4.0
- old
+ new
@@ -24,13 +24,14 @@
)
raise TypeError, "invalid SSL context (#{ssl_context.class})" unless ssl_context.is_a?(OpenSSL::SSL::SSLContext)
raise TypeError, "expected Hash, got #{ssl_params.class}" if ssl_params && !ssl_params.is_a?(Hash)
@ssl_socket_class = ssl_socket_class
+
@ssl_context = ssl_context
@ssl_context.set_params(ssl_params) if ssl_params && !ssl_params.empty?
- @ssl_context.freeze
+
@ssl_socket = nil
super(**args)
end
@@ -39,27 +40,29 @@
# @param remote_addr [String] DNS name or IP address of the host to connect to
# @param remote_port [Fixnum] TCP port to connect to
# @param local_addr [String] DNS name or IP address to bind to locally
# @param local_port [Fixnum] Local TCP port to bind to
# @param timeout [Numeric] Number of seconds to wait before aborting connect
- # @param socket_class [Class] Custom low-level socket class
+ # @param enable_sni [true, false] (default: true) Enables Server Name Indication (SNI)
+ # @param verify_hostname [true, false] (default: true) Ensure server's hostname matches cert
# @raise [Socketry::AddressError] an invalid address was given
# @raise [Socketry::TimeoutError] connect operation timed out
# @raise [Socketry::SSL::Error] an error occurred negotiating an SSL connection
# @return [self]
def connect(
remote_addr,
remote_port,
local_addr: nil,
local_port: nil,
timeout: Socketry::Timeout::DEFAULT_TIMEOUTS[:connect],
+ enable_sni: true,
verify_hostname: true
)
super(remote_addr, remote_port, local_addr: local_addr, local_port: local_port, timeout: timeout)
@ssl_socket = OpenSSL::SSL::SSLSocket.new(@socket, @ssl_context)
- @ssl_socket.hostname = remote_addr
+ @ssl_socket.hostname = remote_addr if enable_sni
begin
@ssl_socket.connect_nonblock
rescue IO::WaitReadable
retry if @socket.wait_readable(timeout)
@@ -108,53 +111,50 @@
# @param size [Fixnum] number of bytes to attempt to read
# @param outbuf [String, NilClass] an optional buffer into which data should be read
# @raise [Socketry::Error] an I/O operation failed
# @return [String, :wait_readable] data read, or :wait_readable if operation would block
def read_nonblock(size, outbuf: nil)
- ensure_connected
case outbuf
when String
- @ssl_socket.read_nonblock(size, outbuf, exception: false)
+ perform { @ssl_socket.read_nonblock(size, outbuf, exception: false) }
when NilClass
- @ssl_socket.read_nonblock(size, exception: false)
+ perform { @ssl_socket.read_nonblock(size, exception: false) }
else raise TypeError, "unexpected outbuf class: #{outbuf.class}"
end
- # Some buggy Rubies continue to raise exceptions in these cases
- rescue IO::WaitReadable
- :wait_readable
- # Due to SSL, we may need to write to complete a read (e.g. renegotiation)
- rescue IO::WaitWritable
- :wait_writable
- rescue => ex
- # TODO: more specific exceptions
- raise Socketry::Error, ex.message, ex.backtrace
end
# Perform a non-blocking write operation
#
# @param data [String] number of bytes to attempt to read
# @raise [Socketry::Error] an I/O operation failed
# @return [Fixnum, :wait_writable] number of bytes written, or :wait_writable if op would block
def write_nonblock(data)
+ perform { @ssl_socket.write_nonblock(data, exception: false) }
+ end
+
+ # Close the socket
+ #
+ # @return [true, false] true if the socket was open, false if closed
+ def close
+ @ssl_socket.close rescue nil
+ super
+ end
+
+ private
+
+ # Perform a non-blocking I/O operation
+ def perform
ensure_connected
- @ssl_socket.write_nonblock(data, exception: false)
+ yield
# Some buggy Rubies continue to raise this exception
- rescue IO::WaitWriteable
+ rescue IO::WaitWritable
:wait_writable
- # Due to SSL, we may need to write to complete a read (e.g. renegotiation)
+ # Due to SSL, we may need to write to complete a read (e.g. handshaking, renegotiation)
rescue IO::WaitReadable
:wait_readable
rescue => ex
# TODO: more specific exceptions
raise Socketry::Error, ex.message, ex.backtrace
- end
-
- # Close the socket
- #
- # @return [true, false] true if the socket was open, false if closed
- def close
- @ssl_socket.close
- super
end
end
end
end