Clean IPv6 support

This commit is contained in:
Guillaume Ayoub 2011-04-02 21:45:45 +02:00
parent d577661767
commit 9bab3cde5d
4 changed files with 48 additions and 61 deletions

13
config
View File

@ -6,14 +6,11 @@
# The current values are the default ones
[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
# CalDAV server hostnames separated by a comma
# IPv4: address:port
# IPv6: [address]:port
# IPv6 adresses are configured to only allow IPv6 connections
hosts = [::]:5232, 0.0.0.0:5232
# Daemon flag
daemon = False
# SSL flag, enable HTTPS protocol

View File

@ -38,7 +38,8 @@ arguments.
import os
import sys
import optparse
import threading, signal
import signal
import threading
import radicale
@ -56,13 +57,9 @@ parser.add_option(
"-f", "--foreground", action="store_false", dest="daemon",
help="launch in foreground (opposite of --daemon)")
parser.add_option(
"-H", "--host",
default=radicale.config.get("server", "host"),
help="set server hostname")
parser.add_option(
"-p", "--port", type="int",
default=radicale.config.getint("server", "port"),
help="set server port")
"-H", "--hosts",
default=radicale.config.get("server", "hosts"),
help="set server hostnames")
parser.add_option(
"-s", "--ssl", action="store_true",
default=radicale.config.getboolean("server", "ssl"),
@ -98,42 +95,36 @@ 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
# Launch calendar servers
servers = []
threads = []
server_class = radicale.HTTPSServer if options.ssl else radicale.HTTPServer
for host in (x.strip() for x in options.host.split(',')):
def exit():
"""Cleanly shutdown servers."""
while servers:
servers.pop().shutdown()
def serve_forever(server):
"""Serve a server forever with no traceback on keyboard interrupts."""
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
server.serve_forever()
except KeyboardInterrupt:
# No unwanted traceback
pass
finally:
exit()
# clean exit on SIGTERM
signal.signal(signal.SIGTERM, lambda *a: exit(servers))
# Clean exit on SIGTERM
signal.signal(signal.SIGTERM, lambda *_: exit())
try:
while threads:
threads[0].join(1) # try one second
if threading.active_count() <= len(threads):
# at least one thread died -- exit all
break
except KeyboardInterrupt:
# no unwanted traceback :)
pass
finally:
exit(servers)
for host in options.hosts.split(','):
address, port = host.strip().rsplit(':', 1)
address, port = address.strip('[] '), int(port)
servers.append(server_class((address, port), radicale.CalendarHTTPHandler))
for server in servers[:-1]:
# More servers to come, launch a new thread
threading.Thread(target=serve_forever, args=(server,)).start()
# Last server, no more thread
serve_forever(servers[-1])

View File

@ -85,18 +85,18 @@ class HTTPServer(server.HTTPServer):
# Maybe a Pylint bug, ``__init__`` calls ``server.HTTPServer.__init__``
# pylint: disable=W0231
def __init__(self, address, handler, bind_and_activate = True):
def __init__(self, address, handler, bind_and_activate=True):
"""Create server."""
self.use_ipv6 = ':' in address[0]
ipv6 = ":" in address[0]
if self.use_ipv6:
if 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)
# Do not bind and activate, as we might change socketopts
server.HTTPServer.__init__(self, address, handler, False)
if self.use_ipv6:
# only allow IPv6 connections to the IPv6 socket
if ipv6:
# Only allow IPv6 connections to the IPv6 socket
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
if bind_and_activate:
@ -111,7 +111,7 @@ class HTTPSServer(HTTPServer):
"""HTTPS server."""
PROTOCOL = "https"
def __init__(self, address, handler, bind_and_activate = True):
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
@ -120,7 +120,7 @@ class HTTPSServer(HTTPServer):
HTTPServer.__init__(self, address, handler, False)
self.socket = ssl.wrap_socket(
self.socket, # we can use this, it is not bound yet
self.socket,
server_side=True,
certfile=config.get("server", "certificate"),
keyfile=config.get("server", "key"),

View File

@ -39,8 +39,7 @@ except ImportError:
# Default configuration
INITIAL_CONFIG = {
"server": {
"host": "",
"port": "5232",
"hosts": "[::]:5232, 0.0.0.0:5232",
"daemon": "False",
"ssl": "False",
"certificate": "/etc/apache2/ssl/server.crt",