Skip to content

Commit 843123a

Browse files
committed
Encoding and timeout settings on underlying tcp socket confusing ssl version on server
1 parent ae1a95a commit 843123a

File tree

1 file changed

+76
-7
lines changed

1 file changed

+76
-7
lines changed

lib/mongo/socket/ssl.rb

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)