Skip to content

Commit d9dfb99

Browse files
committed
add vars for pyopenssl and test
1 parent b29d1ba commit d9dfb99

File tree

10 files changed

+60
-16
lines changed

10 files changed

+60
-16
lines changed

pymongo/asynchronous/encryption.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
)
8686
from pymongo.read_concern import ReadConcern
8787
from pymongo.results import BulkWriteResult, DeleteResult
88-
from pymongo.ssl_support import BLOCKING_IO_ERRORS, get_ssl_context
88+
from pymongo.ssl_support import BLOCKING_IO_ERRORS, PYBLOCKING_IO_ERRORS, get_ssl_context
8989
from pymongo.typings import _DocumentType, _DocumentTypeArg
9090
from pymongo.uri_parser_shared import parse_host
9191
from pymongo.write_concern import WriteConcern
@@ -216,7 +216,7 @@ async def kms_request(self, kms_context: MongoCryptKmsContext) -> None:
216216
raise # Propagate MongoCryptError errors directly.
217217
except Exception as exc:
218218
# Wrap I/O errors in PyMongo exceptions.
219-
if isinstance(exc, BLOCKING_IO_ERRORS):
219+
if isinstance(exc, (BLOCKING_IO_ERRORS, PYBLOCKING_IO_ERRORS)):
220220
exc = socket.timeout("timed out")
221221
# Async raises an OSError instead of returning empty bytes.
222222
if isinstance(exc, OSError):

pymongo/asynchronous/pool.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
from pymongo.server_api import _add_to_command
8686
from pymongo.server_type import SERVER_TYPE
8787
from pymongo.socket_checker import SocketChecker
88-
from pymongo.ssl_support import SSLError
88+
from pymongo.ssl_support import PYSSLError, SSLError
8989

9090
if TYPE_CHECKING:
9191
from bson import CodecOptions
@@ -637,7 +637,7 @@ async def _raise_connection_failure(self, error: BaseException) -> NoReturn:
637637
reason = ConnectionClosedReason.ERROR
638638
await self.close_conn(reason)
639639
# SSLError from PyOpenSSL inherits directly from Exception.
640-
if isinstance(error, (IOError, OSError, SSLError)):
640+
if isinstance(error, (IOError, OSError, SSLError, PYSSLError)):
641641
details = _get_timeout_details(self.opts)
642642
_raise_connection_failure(self.address, error, timeout_details=details)
643643
else:
@@ -1033,7 +1033,7 @@ async def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> A
10331033
reason=_verbose_connection_error_reason(ConnectionClosedReason.ERROR),
10341034
error=ConnectionClosedReason.ERROR,
10351035
)
1036-
if isinstance(error, (IOError, OSError, SSLError)):
1036+
if isinstance(error, (IOError, OSError, SSLError, PYSSLError)):
10371037
details = _get_timeout_details(self.opts)
10381038
_raise_connection_failure(self.address, error, timeout_details=details)
10391039

pymongo/pool_shared.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
)
3939
from pymongo.network_layer import AsyncNetworkingInterface, NetworkingInterface, PyMongoProtocol
4040
from pymongo.pool_options import PoolOptions
41-
from pymongo.ssl_support import HAS_SNI, SSLError
41+
from pymongo.ssl_support import HAS_SNI, PYSSLError, SSLError
4242

4343
if TYPE_CHECKING:
4444
from pymongo.pyopenssl_context import _sslConn
@@ -138,7 +138,7 @@ def _raise_connection_failure(
138138
msg += format_timeout_details(timeout_details)
139139
if isinstance(error, socket.timeout):
140140
raise NetworkTimeout(msg) from error
141-
elif isinstance(error, SSLError) and "timed out" in str(error):
141+
elif isinstance(error, (SSLError, PYSSLError)) and "timed out" in str(error):
142142
# Eventlet does not distinguish TLS network timeouts from other
143143
# SSLErrors (https://github.com/eventlet/eventlet/issues/692).
144144
# Luckily, we can work around this limitation because the phrase
@@ -293,7 +293,7 @@ async def _async_configured_socket(
293293
# Raise _CertificateError directly like we do after match_hostname
294294
# below.
295295
raise
296-
except (OSError, SSLError) as exc:
296+
except (OSError, SSLError, PYSSLError) as exc:
297297
sock.close()
298298
# We raise AutoReconnect for transient and permanent SSL handshake
299299
# failures alike. Permanent handshake failures, like protocol
@@ -349,7 +349,7 @@ async def _configured_protocol_interface(
349349
# Raise _CertificateError directly like we do after match_hostname
350350
# below.
351351
raise
352-
except (OSError, SSLError) as exc:
352+
except (OSError, SSLError, PYSSLError) as exc:
353353
# We raise AutoReconnect for transient and permanent SSL handshake
354354
# failures alike. Permanent handshake failures, like protocol
355355
# mismatch, will be turned into ServerSelectionTimeoutErrors later.
@@ -467,7 +467,7 @@ def _configured_socket(address: _Address, options: PoolOptions) -> Union[socket.
467467
# Raise _CertificateError directly like we do after match_hostname
468468
# below.
469469
raise
470-
except (OSError, SSLError) as exc:
470+
except (OSError, SSLError, PYSSLError) as exc:
471471
sock.close()
472472
# We raise AutoReconnect for transient and permanent SSL handshake
473473
# failures alike. Permanent handshake failures, like protocol
@@ -516,7 +516,7 @@ def _configured_socket_interface(address: _Address, options: PoolOptions) -> Net
516516
# Raise _CertificateError directly like we do after match_hostname
517517
# below.
518518
raise
519-
except (OSError, SSLError) as exc:
519+
except (OSError, SSLError, PYSSLError) as exc:
520520
sock.close()
521521
# We raise AutoReconnect for transient and permanent SSL handshake
522522
# failures alike. Permanent handshake failures, like protocol

pymongo/ssl_support.py

+14
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,20 @@
5959
BLOCKING_IO_WRITE_ERROR = _ssl.BLOCKING_IO_WRITE_ERROR
6060
BLOCKING_IO_LOOKUP_ERROR = BLOCKING_IO_READ_ERROR
6161

62+
if HAVE_PYSSL:
63+
PYSSLError = _pyssl.SSLError
64+
PYBLOCKING_IO_ERRORS = _pyssl.BLOCKING_IO_ERRORS
65+
PYBLOCKING_IO_READ_ERROR = _pyssl.BLOCKING_IO_READ_ERROR
66+
PYBLOCKING_IO_WRITE_ERROR = _pyssl.BLOCKING_IO_WRITE_ERROR
67+
PYBLOCKING_IO_LOOKUP_ERROR = BLOCKING_IO_READ_ERROR
68+
else:
69+
# just make them the same as SSL so imports won't error
70+
PYSSLError = _ssl.SSLError
71+
PYBLOCKING_IO_ERRORS = ()
72+
PYBLOCKING_IO_READ_ERROR = _ssl.BLOCKING_IO_READ_ERROR
73+
PYBLOCKING_IO_WRITE_ERROR = _ssl.BLOCKING_IO_WRITE_ERROR
74+
PYBLOCKING_IO_LOOKUP_ERROR = BLOCKING_IO_READ_ERROR
75+
6276
def get_ssl_context(
6377
certfile: Optional[str],
6478
passphrase: Optional[str],

pymongo/synchronous/encryption.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
)
8181
from pymongo.read_concern import ReadConcern
8282
from pymongo.results import BulkWriteResult, DeleteResult
83-
from pymongo.ssl_support import BLOCKING_IO_ERRORS, get_ssl_context
83+
from pymongo.ssl_support import BLOCKING_IO_ERRORS, PYBLOCKING_IO_ERRORS, get_ssl_context
8484
from pymongo.synchronous.collection import Collection
8585
from pymongo.synchronous.cursor import Cursor
8686
from pymongo.synchronous.database import Database
@@ -215,7 +215,7 @@ def kms_request(self, kms_context: MongoCryptKmsContext) -> None:
215215
raise # Propagate MongoCryptError errors directly.
216216
except Exception as exc:
217217
# Wrap I/O errors in PyMongo exceptions.
218-
if isinstance(exc, BLOCKING_IO_ERRORS):
218+
if isinstance(exc, (BLOCKING_IO_ERRORS, PYBLOCKING_IO_ERRORS)):
219219
exc = socket.timeout("timed out")
220220
# Async raises an OSError instead of returning empty bytes.
221221
if isinstance(exc, OSError):

pymongo/synchronous/pool.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
from pymongo.server_api import _add_to_command
8383
from pymongo.server_type import SERVER_TYPE
8484
from pymongo.socket_checker import SocketChecker
85-
from pymongo.ssl_support import SSLError
85+
from pymongo.ssl_support import PYSSLError, SSLError
8686
from pymongo.synchronous.client_session import _validate_session_write_concern
8787
from pymongo.synchronous.helpers import _handle_reauth
8888
from pymongo.synchronous.network import command
@@ -635,7 +635,7 @@ def _raise_connection_failure(self, error: BaseException) -> NoReturn:
635635
reason = ConnectionClosedReason.ERROR
636636
self.close_conn(reason)
637637
# SSLError from PyOpenSSL inherits directly from Exception.
638-
if isinstance(error, (IOError, OSError, SSLError)):
638+
if isinstance(error, (IOError, OSError, SSLError, PYSSLError)):
639639
details = _get_timeout_details(self.opts)
640640
_raise_connection_failure(self.address, error, timeout_details=details)
641641
else:
@@ -1029,7 +1029,7 @@ def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> Connect
10291029
reason=_verbose_connection_error_reason(ConnectionClosedReason.ERROR),
10301030
error=ConnectionClosedReason.ERROR,
10311031
)
1032-
if isinstance(error, (IOError, OSError, SSLError)):
1032+
if isinstance(error, (IOError, OSError, SSLError, PYSSLError)):
10331033
details = _get_timeout_details(self.opts)
10341034
_raise_connection_failure(self.address, error, timeout_details=details)
10351035

test/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,12 @@ def require_sync(self, func):
825825
lambda: _IS_SYNC, "This test only works with the synchronous API", func=func
826826
)
827827

828+
def require_async(self, func):
829+
"""Run a test only if using the synchronous API."""
830+
return self._require(
831+
lambda: not _IS_SYNC, "This test only works with the synchronous API", func=func
832+
)
833+
828834
def mongos_seeds(self):
829835
return ",".join("{}:{}".format(*address) for address in self.mongoses)
830836

test/asynchronous/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,12 @@ def require_sync(self, func):
827827
lambda: _IS_SYNC, "This test only works with the synchronous API", func=func
828828
)
829829

830+
def require_async(self, func):
831+
"""Run a test only if using the synchronous API."""
832+
return self._require(
833+
lambda: not _IS_SYNC, "This test only works with the asynchronous API", func=func
834+
)
835+
830836
def mongos_seeds(self):
831837
return ",".join("{}:{}".format(*address) for address in self.mongoses)
832838

test/asynchronous/test_ssl.py

+10
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,16 @@ def remove(path):
657657
) as client:
658658
self.assertTrue(await client.admin.command("ping"))
659659

660+
@async_client_context.require_async
661+
@unittest.skipUnless(_HAVE_PYOPENSSL, "PyOpenSSL is not available.")
662+
@unittest.skipUnless(HAVE_SSL, "The ssl module is not available.")
663+
async def test_pyopenssl_not_ignored_in_async(self):
664+
client = AsyncMongoClient(
665+
"mongodb://localhost:27017?tls=true&tlsAllowInvalidCertificates=true"
666+
)
667+
await client.admin.command("ping") # command doesn't matter, just needs it to connect
668+
await client.close()
669+
660670

661671
if __name__ == "__main__":
662672
unittest.main()

test/test_ssl.py

+8
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,14 @@ def remove(path):
657657
) as client:
658658
self.assertTrue(client.admin.command("ping"))
659659

660+
@client_context.require_async
661+
@unittest.skipUnless(_HAVE_PYOPENSSL, "PyOpenSSL is not available.")
662+
@unittest.skipUnless(HAVE_SSL, "The ssl module is not available.")
663+
def test_pyopenssl_not_ignored_in_async(self):
664+
client = MongoClient("mongodb://localhost:27017?tls=true&tlsAllowInvalidCertificates=true")
665+
client.admin.command("ping") # command doesn't matter, just needs it to connect
666+
client.close()
667+
660668

661669
if __name__ == "__main__":
662670
unittest.main()

0 commit comments

Comments
 (0)