@@ -38,6 +38,19 @@ class SSL < Socket
3838 # @return [ Float ] timeout The connection timeout.
3939 attr_reader :timeout
4040
41+ # Close the socket.
42+ #
43+ # @example Close the socket.
44+ # socket.close
45+ #
46+ # @return [ true ] Always true.
47+ #
48+ # @since 2.0.0
49+ def close
50+ socket . sysclose rescue true
51+ true
52+ end
53+
4154 # Establishes a socket connection.
4255 #
4356 # @example Connect the socket.
@@ -51,12 +64,11 @@ class SSL < Socket
5164 # @since 2.0.0
5265 def connect!
5366 Timeout . timeout ( timeout , Error ::SocketTimeoutError ) do
54- socket . setsockopt ( IPPROTO_TCP , TCP_NODELAY , 1 )
55- socket . connect ( ::Socket . pack_sockaddr_in ( port , host ) )
56- ssl_socket = OpenSSL ::SSL ::SSLSocket . new ( socket , context )
57- ssl_socket . sync_close = true
58- ssl_socket . connect
59- verify_certificate! ( ssl_socket )
67+ @tcp_socket . connect ( ::Socket . pack_sockaddr_in ( port , host ) )
68+ @socket = OpenSSL ::SSL ::SSLSocket . new ( @tcp_socket , context )
69+ @socket . sync_close = true
70+ @socket . connect
71+ verify_certificate! ( @socket )
6072 self
6173 end
6274 end
@@ -76,7 +88,58 @@ def connect!
7688 def initialize ( host , port , timeout , family , options = { } )
7789 @host , @port , @timeout , @options = host , port , timeout , options
7890 @context = create_context ( options )
79- super ( family )
91+ @family = family
92+ @tcp_socket = ::Socket . new ( family , SOCK_STREAM , 0 )
93+ @tcp_socket . setsockopt ( IPPROTO_TCP , TCP_NODELAY , 1 )
94+ end
95+
96+ # Will read all data from the socket for the provided number of bytes.
97+ # If less data is returned than requested, an exception will be raised.
98+ #
99+ # @example Read all the requested data from the socket.
100+ # socket.read(4096)
101+ #
102+ # @param [ Integer ] length The number of bytes to read.
103+ #
104+ # @raise [ Mongo::SocketError ] If not all data is returned.
105+ #
106+ # @return [ Object ] The data from the socket.
107+ #
108+ # @since 2.0.0
109+ def read ( length )
110+ handle_errors do
111+ data = read_from_socket ( length )
112+ while data . length < length
113+ data << read_from_socket ( length - data . length )
114+ end
115+ data
116+ end
117+ end
118+
119+ # Read a single byte from the socket.
120+ #
121+ # @example Read a single byte.
122+ # socket.readbyte
123+ #
124+ # @return [ Object ] The read byte.
125+ #
126+ # @since 2.0.0
127+ def readbyte
128+ handle_errors { read_from_socket ( 1 ) }
129+ end
130+
131+ # Writes data to the socket instance.
132+ #
133+ # @example Write to the socket.
134+ # socket.write(data)
135+ #
136+ # @param [ Object ] data The data to be written.
137+ #
138+ # @return [ Integer ] The length of bytes written to the socket.
139+ #
140+ # @since 2.0.0
141+ def write ( data )
142+ handle_errors { socket . syswrite ( data ) }
80143 end
81144
82145 private
@@ -96,6 +159,12 @@ def create_context(options)
96159 context
97160 end
98161
162+ def read_from_socket ( length )
163+ Timeout ::timeout ( timeout , Error ::SocketTimeoutError ) do
164+ socket . sysread ( length ) || String . new
165+ end
166+ end
167+
99168 def verify_certificate! ( socket )
100169 if context . verify_mode == OpenSSL ::SSL ::VERIFY_PEER
101170 unless OpenSSL ::SSL . verify_certificate_identity ( socket . peer_cert , host )
0 commit comments