From f137d8424e84591f37532d7e7c2dd7e5daedcd42 Mon Sep 17 00:00:00 2001 From: Alex Zorin Date: Sat, 19 Jun 2021 09:55:02 +1000 Subject: [PATCH] acme.standalone: expose original socket.error --- acme/acme/standalone.py | 12 ++++++++++-- acme/tests/standalone_test.py | 16 +++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/acme/acme/standalone.py b/acme/acme/standalone.py index eda45304c..e2672fb94 100644 --- a/acme/acme/standalone.py +++ b/acme/acme/standalone.py @@ -8,6 +8,7 @@ import socket import socketserver import threading from typing import List +from typing import Optional from acme import challenges from acme import crypto_util @@ -66,6 +67,9 @@ class BaseDualNetworkedServers: self.threads: List[threading.Thread] = [] self.servers: List[socketserver.BaseServer] = [] + # Preserve socket error for re-raising, if no servers can be started + last_socket_err: Optional[socket.error] = None + # Must try True first. # Ubuntu, for example, will fail to bind to IPv4 if we've already bound # to IPv6. But that's ok, since it will accept IPv4 connections on the IPv6 @@ -82,7 +86,8 @@ class BaseDualNetworkedServers: logger.debug( "Successfully bound to %s:%s using %s", new_address[0], new_address[1], "IPv6" if ip_version else "IPv4") - except socket.error: + except socket.error as e: + last_socket_err = e if self.servers: # Already bound using IPv6. logger.debug( @@ -101,7 +106,10 @@ class BaseDualNetworkedServers: # bind to the same port for both servers. port = server.socket.getsockname()[1] if not self.servers: - raise socket.error("Could not bind to IPv4 or IPv6.") + if last_socket_err: + raise last_socket_err + else: # pragma: no cover + raise socket.error("Could not bind to IPv4 or IPv6.") def serve_forever(self): """Wraps socketserver.TCPServer.serve_forever""" diff --git a/acme/tests/standalone_test.py b/acme/tests/standalone_test.py index 17d73dba8..e0aa5aa22 100644 --- a/acme/tests/standalone_test.py +++ b/acme/tests/standalone_test.py @@ -190,12 +190,18 @@ class BaseDualNetworkedServersTest(unittest.TestCase): @mock.patch("socket.socket.bind") def test_fail_to_bind(self, mock_bind): - mock_bind.side_effect = socket.error + from errno import EADDRINUSE from acme.standalone import BaseDualNetworkedServers - self.assertRaises(socket.error, BaseDualNetworkedServers, - BaseDualNetworkedServersTest.SingleProtocolServer, - ('', 0), - socketserver.BaseRequestHandler) + + mock_bind.side_effect = socket.error(EADDRINUSE, "Fake addr in use error") + + with self.assertRaises(socket.error) as em: + BaseDualNetworkedServers( + BaseDualNetworkedServersTest.SingleProtocolServer, + ('', 0), socketserver.BaseRequestHandler) + + self.assertEqual(em.exception.errno, EADDRINUSE) + def test_ports_equal(self): from acme.standalone import BaseDualNetworkedServers