From 396d7c3721bd0cd85b534ea5c0a8396f4e0c9912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Tue, 8 Feb 2011 19:27:00 +0100 Subject: [PATCH 1/3] First support for IPv6 and multiple interfaces --- radicale.py | 39 ++++++++++++++++++++++++++++++++++++--- radicale/__init__.py | 31 ++++++++++++++++++++++++------- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/radicale.py b/radicale.py index 03f6a14..c5a9025 100755 --- a/radicale.py +++ b/radicale.py @@ -38,6 +38,7 @@ arguments. import os import sys import optparse +import threading, signal import radicale @@ -97,8 +98,40 @@ if options.daemon: sys.exit() sys.stdout = sys.stderr = open(os.devnull, "w") +def exit (servers): + """Cleanly shutdown all servers. + + Might be called multiple times.""" + for s in servers: + s.shutdown() + # Launch calendar server server_class = radicale.HTTPSServer if options.ssl else radicale.HTTPServer -server = server_class( - (options.host, options.port), radicale.CalendarHTTPHandler) -server.serve_forever() +servers = [] +threads = [] + +for host in (x.strip() for x in options.host.split(',')): + try: + server = server_class( + (host, options.port), radicale.CalendarHTTPHandler) + servers.append(server) + + t = threading.Thread(target = server.serve_forever) + threads.append(t) + t.start() + except: + exit(servers) + raise + +# clean exit on SIGTERM +signal.signal(signal.SIGTERM, lambda *a: exit(servers)) + +try: + while threads: + threads[0].join(1) # try one second + if threading.active_count() <= len(threads): # one thread died + break +except KeyboardInterrupt: + pass +finally: + exit(servers) diff --git a/radicale/__init__.py b/radicale/__init__.py index ad94318..4944346 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -80,9 +80,24 @@ class HTTPServer(server.HTTPServer): # Maybe a Pylint bug, ``__init__`` calls ``server.HTTPServer.__init__`` # pylint: disable=W0231 - def __init__(self, address, handler): + def __init__(self, address, handler, bind_and_activate = True): """Create server.""" - server.HTTPServer.__init__(self, address, handler) + self.use_ipv6 = ':' in address[0] + + if self.use_ipv6: + self.address_family = socket.AF_INET6 + + # call superclass, but do NOT bind and activate, as we might change socketopts + server.HTTPServer.__init__(self, address, handler, bind_and_activate = False) + + if self.use_ipv6: + # only allow IPv6 connections to the IPv6 socket + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) + + if bind_and_activate: + self.server_bind() + self.server_activate() + self.acl = acl.load() # pylint: enable=W0231 @@ -91,22 +106,24 @@ class HTTPSServer(HTTPServer): """HTTPS server.""" PROTOCOL = "https" - def __init__(self, address, handler): + def __init__(self, address, handler, bind_and_activate = True): """Create server by wrapping HTTP socket in an SSL socket.""" # Fails with Python 2.5, import if needed # pylint: disable=F0401 import ssl # pylint: enable=F0401 - HTTPServer.__init__(self, address, handler) + HTTPServer.__init__(self, address, handler, False) self.socket = ssl.wrap_socket( - socket.socket(self.address_family, self.socket_type), + self.socket, # we can use this, it is not bound yet server_side=True, certfile=config.get("server", "certificate"), keyfile=config.get("server", "key"), ssl_version=ssl.PROTOCOL_SSLv23) - self.server_bind() - self.server_activate() + + if bind_and_activate: + self.server_bind() + self.server_activate() class CalendarHTTPHandler(server.BaseHTTPRequestHandler): From 723b2ffc29dfb73985073e7be30fd08caf25511d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Tue, 8 Feb 2011 19:29:30 +0100 Subject: [PATCH 2/3] Update config to reflect the change --- config | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config b/config index d04b3da..04f47c8 100644 --- a/config +++ b/config @@ -7,6 +7,10 @@ [server] # CalDAV server hostname, empty for all hostnames +# if you want to bind to multiple interfaces, seperate them with a comma +# NOTE: IPv6 adresses are configured to only allow IPv6 connections +# hence for binding to all IPv4 and IPv6 interfaces: +# host = ::, 0.0.0.0 host = # CalDAV server port port = 5232 From 2848dbc1a19b781af893cfece06a62ee04e00f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Tue, 8 Feb 2011 19:41:54 +0100 Subject: [PATCH 3/3] Fix comment --- radicale.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/radicale.py b/radicale.py index c5a9025..222f060 100755 --- a/radicale.py +++ b/radicale.py @@ -129,9 +129,11 @@ signal.signal(signal.SIGTERM, lambda *a: exit(servers)) try: while threads: threads[0].join(1) # try one second - if threading.active_count() <= len(threads): # one thread died + if threading.active_count() <= len(threads): + # at least one thread died -- exit all break except KeyboardInterrupt: + # no unwanted traceback :) pass finally: exit(servers)