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