From ed48ecc58cc1e26cacc5bf5a4b7d83c27ba27732 Mon Sep 17 00:00:00 2001 From: Silke Hofstra Date: Wed, 6 Sep 2017 17:48:49 +0200 Subject: [PATCH] Add methods for listening on multiple addresses Add listen_tcp and listen_ssl which implement Twisted's reactor.listenTCP and reactor.listenSSL for multiple addresses. Signed-off-by: Silke Hofstra --- synapse/app/_base.py | 57 +++++++++++++++++++++++++- synapse/app/homeserver.py | 84 ++++++++++++++++----------------------- 2 files changed, 90 insertions(+), 51 deletions(-) diff --git a/synapse/app/_base.py b/synapse/app/_base.py index 9477737759..0f9a57bd4c 100644 --- a/synapse/app/_base.py +++ b/synapse/app/_base.py @@ -25,7 +25,7 @@ except Exception: from daemonize import Daemonize from synapse.util import PreserveLoggingContext from synapse.util.rlimit import change_resource_limit -from twisted.internet import reactor +from twisted.internet import error, reactor def start_worker_reactor(appname, config): @@ -120,3 +120,58 @@ def quit_with_error(error_string): sys.stderr.write(" %s\n" % (line.rstrip(),)) sys.stderr.write("*" * line_length + '\n') sys.exit(1) + + +def listen_tcp(logger, bind_addresses, port, factory, backlog=50): + """ + Create a TCP socket for a port and several addresses + """ + for address in bind_addresses: + try: + reactor.listenTCP( + port, + factory, + backlog, + address + ) + except error.CannotListenError as e: + check_bind_error(logger, e, address, bind_addresses) + + +def listen_ssl(logger, bind_addresses, port, factory, context_factory, backlog=50): + """ + Create an SSL socket for a port and several addresses + """ + for address in bind_addresses: + try: + reactor.listenSSL( + port, + factory, + context_factory, + backlog, + address + ) + except error.CannotListenError as e: + check_bind_error(logger, e, address, bind_addresses) + + +def check_bind_error(logger, e, address, bind_addresses): + """ + This method checks an exception occurred while binding on 0.0.0.0. + If :: is specified in the bind addresses a warning is shown. + The exception is still raised otherwise. + + Binding on both 0.0.0.0 and :: causes an exception on Linux and macOS + because :: binds on both IPv4 and IPv6 (as per RFC 3493). + When binding on 0.0.0.0 after :: this can safely be ignored. + + Args: + logger (Logger): Logger used to log the warning. + e (Exception): Exception that was caught. + address (str): Address on which binding was attempted. + bind_addresses (list): Addresses on which the service listens. + """ + if address == '0.0.0.0' and '::' in bind_addresses: + logger.warn('Failed to listen on 0.0.0.0, continuing because listening on [::]') + else: + raise e diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index 82a4e18c67..66f206a89d 100755 --- a/synapse/app/homeserver.py +++ b/synapse/app/homeserver.py @@ -131,37 +131,31 @@ class SynapseHomeServer(HomeServer): root_resource = create_resource_tree(resources, root_resource) if tls: - for address in bind_addresses: - try: - reactor.listenSSL( - port, - SynapseSite( - "synapse.access.https.%s" % (site_tag,), - site_tag, - listener_config, - root_resource, - ), - self.tls_server_context_factory, - interface=address - ) - except error.CannotListenError as e: - check_bind_error(e, address, bind_addresses) + _base.listen_ssl( + logger, + bind_addresses, + port, + SynapseSite( + "synapse.access.https.%s" % (site_tag,), + site_tag, + listener_config, + root_resource, + ), + self.tls_server_context_factory, + ) else: - for address in bind_addresses: - try: - reactor.listenTCP( - port, - SynapseSite( - "synapse.access.http.%s" % (site_tag,), - site_tag, - listener_config, - root_resource, - ), - interface=address - ) - except error.CannotListenError as e: - check_bind_error(e, address, bind_addresses) + _base.listen_tcp( + logger, + bind_addresses, + port, + SynapseSite( + "synapse.access.http.%s" % (site_tag,), + site_tag, + listener_config, + root_resource, + ) + ) logger.info("Synapse now listening on port %d", port) def _configure_named_resource(self, name, compress=False): @@ -239,19 +233,16 @@ class SynapseHomeServer(HomeServer): elif listener["type"] == "manhole": bind_addresses = listener["bind_addresses"] - for address in bind_addresses: - try: - reactor.listenTCP( - listener["port"], - manhole( - username="matrix", - password="rabbithole", - globals={"hs": self}, - ), - interface=address - ) - except error.CannotListenError as e: - check_bind_error(e, address, bind_addresses) + _base.listen_tcp( + logger, + bind_addresses, + listener["port"], + manhole( + username="matrix", + password="rabbithole", + globals={"hs": self}, + ) + ) elif listener["type"] == "replication": bind_addresses = listener["bind_addresses"] for address in bind_addresses: @@ -264,7 +255,7 @@ class SynapseHomeServer(HomeServer): "before", "shutdown", server_listener.stopListening, ) except error.CannotListenError as e: - check_bind_error(e, address, bind_addresses) + _base.check_bind_error(logger, e, address, bind_addresses) else: logger.warn("Unrecognized listener type: %s", listener["type"]) @@ -298,13 +289,6 @@ class SynapseHomeServer(HomeServer): return db_conn -def check_bind_error(e, address, bind_addresses): - if address == '0.0.0.0' and '::' in bind_addresses: - logger.warn('Failed to listen on 0.0.0.0, continuing because listening on [::]') - else: - raise e - - def setup(config_options): """ Args: