@@ -38,6 +38,19 @@ class SSL < Socket
38
38
# @return [ Float ] timeout The connection timeout.
39
39
attr_reader :timeout
40
40
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
+
41
54
# Establishes a socket connection.
42
55
#
43
56
# @example Connect the socket.
@@ -51,12 +64,11 @@ class SSL < Socket
51
64
# @since 2.0.0
52
65
def connect!
53
66
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 )
60
72
self
61
73
end
62
74
end
@@ -76,7 +88,58 @@ def connect!
76
88
def initialize ( host , port , timeout , family , options = { } )
77
89
@host , @port , @timeout , @options = host , port , timeout , options
78
90
@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 ) }
80
143
end
81
144
82
145
private
@@ -96,6 +159,12 @@ def create_context(options)
96
159
context
97
160
end
98
161
162
+ def read_from_socket ( length )
163
+ Timeout ::timeout ( timeout , Error ::SocketTimeoutError ) do
164
+ socket . sysread ( length ) || String . new
165
+ end
166
+ end
167
+
99
168
def verify_certificate! ( socket )
100
169
if context . verify_mode == OpenSSL ::SSL ::VERIFY_PEER
101
170
unless OpenSSL ::SSL . verify_certificate_identity ( socket . peer_cert , host )
0 commit comments