Add support for IPv6 hostnames

This commit is contained in:
Unrud 2018-09-06 09:12:53 +02:00
parent 2275ba4f93
commit dec2ad8bea
2 changed files with 57 additions and 6 deletions

View File

@ -47,6 +47,30 @@ if os.name == "posix":
else:
ParallelizationMixIn = socketserver.ThreadingMixIn
HAS_IPV6 = socket.has_ipv6
if hasattr(socket, "EAI_NONAME"):
EAI_NONAME = socket.EAI_NONAME
else:
HAS_IPV6 = False
if hasattr(socket, "EAI_ADDRFAMILY"):
EAI_ADDRFAMILY = socket.EAI_ADDRFAMILY
elif os.name == "nt":
EAI_ADDRFAMILY = None
else:
HAS_IPV6 = False
if hasattr(socket, "IPPROTO_IPV6"):
IPPROTO_IPV6 = socket.IPPROTO_IPV6
elif os.name == "nt":
IPPROTO_IPV6 = 41
else:
HAS_IPV6 = False
if hasattr(socket, "IPV6_V6ONLY"):
IPV6_V6ONLY = socket.IPV6_V6ONLY
elif os.name == "nt":
IPV6_V6ONLY = 27
else:
HAS_IPV6 = False
class ParallelHTTPServer(ParallelizationMixIn,
wsgiref.simple_server.WSGIServer):
@ -74,14 +98,18 @@ class ParallelHTTPServer(ParallelizationMixIn,
self.server_port = port
self.setup_environ()
return
ipv6 = ":" in self.server_address[0]
if ipv6 and self.address_family == socket.AF_INET:
try:
super().server_bind()
except socket.gaierror as e:
if (not HAS_IPV6 or self.address_family != socket.AF_INET or
e.errno not in (EAI_NONAME, EAI_ADDRFAMILY)):
raise
# Try again with IPv6
self.address_family = socket.AF_INET6
self.socket = socket.socket(self.address_family, self.socket_type)
if ipv6:
# Only allow IPv6 connections to the IPv6 socket
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
super().server_bind()
self.socket.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, 1)
super().server_bind()
def get_request(self):
# Set timeout for client

View File

@ -19,12 +19,14 @@ Test the internal server.
"""
import os
import shutil
import socket
import ssl
import tempfile
import threading
import time
import warnings
from urllib import request
from urllib.error import HTTPError, URLError
@ -32,6 +34,8 @@ from radicale import config, server
from .helpers import get_file_path
import pytest # isort:skip
class DisabledRedirectHandler(request.HTTPRedirectHandler):
def http_error_302(self, req, fp, code, msg, headers):
@ -66,7 +70,10 @@ class TestBaseServerRequests:
def teardown(self):
self.shutdown_socket.sendall(b" ")
self.thread.join()
try:
self.thread.join()
except RuntimeError: # Thread never started
pass
shutil.rmtree(self.colpath)
def request(self, method, path, data=None, **headers):
@ -100,3 +107,19 @@ class TestBaseServerRequests:
self.thread.start()
status, _, _ = self.request("GET", "/")
assert status == 302
def test_ipv6(self):
if not server.HAS_IPV6:
pytest.skip("IPv6 not support")
if os.name == "nt" and os.environ.get("WINE_PYTHON"):
warnings.warn("WORKAROUND: incomplete errno conversion in WINE")
server.EAI_ADDRFAMILY = -9
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as sock:
sock.setsockopt(server.IPPROTO_IPV6, server.IPV6_V6ONLY, 1)
# Find available port
sock.bind(("localhost", 0))
self.sockname = sock.getsockname()[:2]
self.configuration["server"]["hosts"] = "[%s]:%d" % self.sockname
self.thread.start()
status, _, _ = self.request("GET", "/")
assert status == 302