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 # The current values are the default ones
[server] [server]
# CalDAV server hostname, empty for all hostnames # CalDAV server hostnames separated by a comma
# if you want to bind to multiple interfaces, seperate them with a comma # IPv4: address:port
# NOTE: IPv6 adresses are configured to only allow IPv6 connections # IPv6: [address]:port
# hence for binding to all IPv4 and IPv6 interfaces: # IPv6 adresses are configured to only allow IPv6 connections
# host = ::, 0.0.0.0 hosts = [::]:5232, 0.0.0.0:5232
host =
# CalDAV server port
port = 5232
# Daemon flag # Daemon flag
daemon = False daemon = False
# SSL flag, enable HTTPS protocol # SSL flag, enable HTTPS protocol

View File

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

View File

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

View File

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