|
22 | 22 | import platform |
23 | 23 | import random |
24 | 24 | import socket |
| 25 | +import ssl |
25 | 26 | import sys |
26 | 27 | import time |
27 | 28 |
|
|
41 | 42 | from test.asynchronous.helpers import ConcurrentRunner |
42 | 43 | from test.utils_shared import delay |
43 | 44 |
|
| 45 | +try: |
| 46 | + import OpenSSL |
| 47 | + |
| 48 | + _HAVE_PYOPENSSL = True |
| 49 | +except ImportError: |
| 50 | + _HAVE_PYOPENSSL = False |
| 51 | + |
44 | 52 | _IS_SYNC = False |
45 | 53 |
|
46 | 54 |
|
@@ -651,5 +659,51 @@ async def test_max_pool_size_with_connection_failure(self): |
651 | 659 | self.assertNotIn("waiting for socket from pool", str(context.exception)) |
652 | 660 |
|
653 | 661 |
|
| 662 | +class TestPoolHandleConnectionError(unittest.TestCase): |
| 663 | + """PYTHON-5919: PyOpenSSL raises OpenSSL.SSL.SysCallError/ZeroReturnError |
| 664 | + (not ssl.SSLEOFError/ssl.SSLZeroReturnError) when the server closes the |
| 665 | + socket during the TLS handshake, e.g. when an ingress rate limiter rejects |
| 666 | + a connection. Pool._handle_connection_error must recognize these as |
| 667 | + handshake-EOF errors and still add the SystemOverloadedError label. |
| 668 | + """ |
| 669 | + |
| 670 | + def _make_pool(self): |
| 671 | + return Pool(("localhost", 27017), PoolOptions()) |
| 672 | + |
| 673 | + def test_stdlib_ssl_eof_error_is_labeled_overloaded(self): |
| 674 | + pool = self._make_pool() |
| 675 | + err = AutoReconnect("connection closed") |
| 676 | + err.__cause__ = ssl.SSLEOFError("EOF occurred in violation of protocol") |
| 677 | + pool._handle_connection_error(err) |
| 678 | + self.assertTrue(err.has_error_label("SystemOverloadedError")) |
| 679 | + |
| 680 | + @unittest.skipUnless(_HAVE_PYOPENSSL, "PyOpenSSL is not available.") |
| 681 | + def test_pyopenssl_syscall_error_is_labeled_overloaded(self): |
| 682 | + from OpenSSL.SSL import SysCallError |
| 683 | + |
| 684 | + pool = self._make_pool() |
| 685 | + err = AutoReconnect("connection closed") |
| 686 | + err.__cause__ = SysCallError(-1, "Unexpected EOF") |
| 687 | + pool._handle_connection_error(err) |
| 688 | + self.assertTrue(err.has_error_label("SystemOverloadedError")) |
| 689 | + |
| 690 | + @unittest.skipUnless(_HAVE_PYOPENSSL, "PyOpenSSL is not available.") |
| 691 | + def test_pyopenssl_zero_return_error_is_labeled_overloaded(self): |
| 692 | + from OpenSSL.SSL import ZeroReturnError |
| 693 | + |
| 694 | + pool = self._make_pool() |
| 695 | + err = AutoReconnect("connection closed") |
| 696 | + err.__cause__ = ZeroReturnError() |
| 697 | + pool._handle_connection_error(err) |
| 698 | + self.assertTrue(err.has_error_label("SystemOverloadedError")) |
| 699 | + |
| 700 | + def test_certificate_error_is_not_labeled_overloaded(self): |
| 701 | + pool = self._make_pool() |
| 702 | + err = AutoReconnect("connection closed") |
| 703 | + err.__cause__ = ssl.SSLCertVerificationError("certificate verify failed") |
| 704 | + pool._handle_connection_error(err) |
| 705 | + self.assertFalse(err.has_error_label("SystemOverloadedError")) |
| 706 | + |
| 707 | + |
654 | 708 | if __name__ == "__main__": |
655 | 709 | unittest.main() |
0 commit comments