Skip to content

Commit c2d6343

Browse files
authored
PYTHON-2163 Suppress ragged EOFs when using pyOpenSSL to match the stdlib (#453)
Wrap pyOpenSSL connection errors with AutoReconnect.
1 parent a075eb7 commit c2d6343

File tree

2 files changed

+30
-10
lines changed

2 files changed

+30
-10
lines changed

pymongo/pool.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,7 @@ def _configured_socket(address, options):
10071007
# Raise CertificateError directly like we do after match_hostname
10081008
# below.
10091009
raise
1010-
except IOError as exc:
1010+
except (IOError, OSError, _SSLError) as exc:
10111011
sock.close()
10121012
# We raise AutoReconnect for transient and permanent SSL handshake
10131013
# failures alike. Permanent handshake failures, like protocol
@@ -1176,15 +1176,17 @@ def connect(self, all_credentials=None):
11761176
if self.enabled_for_cmap:
11771177
listeners.publish_connection_created(self.address, conn_id)
11781178

1179-
sock = None
11801179
try:
11811180
sock = _configured_socket(self.address, self.opts)
1182-
except socket.error as error:
1181+
except Exception as error:
11831182
if self.enabled_for_cmap:
11841183
listeners.publish_connection_closed(
11851184
self.address, conn_id, ConnectionClosedReason.ERROR)
11861185

1187-
_raise_connection_failure(self.address, error)
1186+
if isinstance(error, (IOError, OSError, _SSLError)):
1187+
_raise_connection_failure(self.address, error)
1188+
1189+
raise
11881190

11891191
sock_info = SocketInfo(sock, self, self.address, conn_id)
11901192
if self.handshake:

pymongo/pyopenssl_context.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,20 @@ def _is_ip_address(address):
8383
_SSL.WantReadError, _SSL.WantWriteError, _SSL.WantX509LookupError)
8484

8585

86+
def _ragged_eof(exc):
87+
"""Return True if the OpenSSL.SSL.SysCallError is a ragged EOF."""
88+
return exc.args == (-1, 'Unexpected EOF')
89+
90+
8691
# https://github.com/pyca/pyopenssl/issues/168
8792
# https://github.com/pyca/pyopenssl/issues/176
8893
# https://docs.python.org/3/library/ssl.html#notes-on-non-blocking-sockets
8994
class _sslConn(_SSL.Connection):
9095

91-
def __init__(self, *args, **kwargs):
96+
def __init__(self, ctx, sock, suppress_ragged_eofs):
9297
self.socket_checker = _SocketChecker()
93-
super(_sslConn, self).__init__(*args, **kwargs)
98+
self.suppress_ragged_eofs = suppress_ragged_eofs
99+
super(_sslConn, self).__init__(ctx, sock)
94100

95101
def _call(self, call, *args, **kwargs):
96102
timeout = self.gettimeout()
@@ -110,10 +116,22 @@ def do_handshake(self, *args, **kwargs):
110116
return self._call(super(_sslConn, self).do_handshake, *args, **kwargs)
111117

112118
def recv(self, *args, **kwargs):
113-
return self._call(super(_sslConn, self).recv, *args, **kwargs)
119+
try:
120+
return self._call(super(_sslConn, self).recv, *args, **kwargs)
121+
except _SSL.SysCallError as exc:
122+
# Suppress ragged EOFs to match the stdlib.
123+
if self.suppress_ragged_eofs and _ragged_eof(exc):
124+
return b""
125+
raise
114126

115127
def recv_into(self, *args, **kwargs):
116-
return self._call(super(_sslConn, self).recv_into, *args, **kwargs)
128+
try:
129+
return self._call(super(_sslConn, self).recv_into, *args, **kwargs)
130+
except _SSL.SysCallError as exc:
131+
# Suppress ragged EOFs to match the stdlib.
132+
if self.suppress_ragged_eofs and _ragged_eof(exc):
133+
return 0
134+
raise
117135

118136
def sendall(self, buf, flags=0):
119137
view = memoryview(buf)
@@ -266,12 +284,12 @@ def set_default_verify_paths(self):
266284

267285
def wrap_socket(self, sock, server_side=False,
268286
do_handshake_on_connect=True,
269-
suppress_ragged_eofs=True, # TODO: Add support to _sslConn.
287+
suppress_ragged_eofs=True,
270288
server_hostname=None, session=None):
271289
"""Wrap an existing Python socket sock and return a TLS socket
272290
object.
273291
"""
274-
ssl_conn = _sslConn(self._ctx, sock)
292+
ssl_conn = _sslConn(self._ctx, sock, suppress_ragged_eofs)
275293
if session:
276294
ssl_conn.set_session(session)
277295
if server_side is True:

0 commit comments

Comments
 (0)