diff options
author | Bert JW Regeer <bertjw@regeer.org> | 2016-10-22 13:33:56 -0600 |
---|---|---|
committer | Bert JW Regeer <bertjw@regeer.org> | 2016-10-22 13:33:56 -0600 |
commit | 036634350c6b03e3c4240236e311f41a4107eea1 (patch) | |
tree | c543a925e0900fbefe6b7ef91ca03a0eb6589bb8 | |
parent | 52a97fe7bf71affa257b1d036388a18737b2d15f (diff) | |
parent | 57ea8381ac0e95a3feff58655fade698e70f9eee (diff) | |
download | waitress-036634350c6b03e3c4240236e311f41a4107eea1.tar.gz |
Merge PR #140: Fix Windows support (IPv6/getaddrinfo)
-rw-r--r-- | appveyor.yml | 2 | ||||
-rw-r--r-- | waitress/adjustments.py | 23 | ||||
-rw-r--r-- | waitress/compat.py | 29 | ||||
-rw-r--r-- | waitress/server.py | 11 | ||||
-rw-r--r-- | waitress/tests/test_adjustments.py | 14 |
5 files changed, 75 insertions, 4 deletions
diff --git a/appveyor.yml b/appveyor.yml index 1350507..6505c2b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,7 @@ environment: matrix: + - PYTHON: "C:\\Python27" + TOXENV: "py27" - PYTHON: "C:\\Python35" TOXENV: "py35" diff --git a/waitress/adjustments.py b/waitress/adjustments.py index 97c47e4..1a56621 100644 --- a/waitress/adjustments.py +++ b/waitress/adjustments.py @@ -16,7 +16,12 @@ import getopt import socket -from waitress.compat import string_types +from waitress.compat import ( + PY2, + WIN, + string_types, + HAS_IPV6, + ) truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1')) @@ -224,10 +229,15 @@ class Adjustments(object): enabled_families = socket.AF_UNSPEC + if not self.ipv4 and not HAS_IPV6: # pragma: no cover + raise ValueError( + 'IPv4 is disabled but IPv6 is not available. Cowardly refusing to start.' + ) + if self.ipv4 and not self.ipv6: enabled_families = socket.AF_INET - if not self.ipv4 and self.ipv6: + if not self.ipv4 and self.ipv6 and HAS_IPV6: enabled_families = socket.AF_INET6 wanted_sockets = [] @@ -242,6 +252,15 @@ class Adjustments(object): else: (host, port) = (i, str(self.port)) + if WIN and PY2: # pragma: no cover + try: + # Try turning the port into an integer + port = int(port) + except: + raise ValueError( + 'Windows does not support service names instead of port numbers' + ) + try: if '[' in host and ']' in host: # pragma: nocover host = host.strip('[').rstrip(']') diff --git a/waitress/compat.py b/waitress/compat.py index 9e06cde..700f7a1 100644 --- a/waitress/compat.py +++ b/waitress/compat.py @@ -1,5 +1,7 @@ import sys import types +import platform +import warnings try: import urlparse @@ -7,8 +9,12 @@ except ImportError: # pragma: no cover from urllib import parse as urlparse # True if we are running on Python 3. +PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 +# True if we are running on Windows +WIN = platform.system() == 'Windows' + if PY3: # pragma: no cover string_types = str, integer_types = int, @@ -109,3 +115,26 @@ try: MAXINT = sys.maxint except AttributeError: # pragma: no cover MAXINT = sys.maxsize + + +# Fix for issue reported in https://github.com/Pylons/waitress/issues/138, +# Python on Windows may not define IPPROTO_IPV6 in socket. +import socket + +HAS_IPV6 = socket.has_ipv6 + +if hasattr(socket, 'IPPROTO_IPV6') and hasattr(socket, 'IPV6_V6ONLY'): + IPPROTO_IPV6 = socket.IPPROTO_IPV6 + IPV6_V6ONLY = socket.IPV6_V6ONLY +else: # pragma: no cover + if WIN: + IPPROTO_IPV6 = 41 + IPV6_V6ONLY = 27 + else: + warnings.warn( + 'OS does not support required IPv6 socket flags. This is requirement ' + 'for Waitress. Please open an issue at https://github.com/Pylons/waitress. ' + 'IPv6 support has been disabled.', + RuntimeWarning + ) + HAS_IPV6 = False diff --git a/waitress/server.py b/waitress/server.py index 7134f86..d3fbd79 100644 --- a/waitress/server.py +++ b/waitress/server.py @@ -22,7 +22,14 @@ from waitress import trigger from waitress.adjustments import Adjustments from waitress.channel import HTTPChannel from waitress.task import ThreadedTaskDispatcher -from waitress.utilities import cleanup_unix_socket, logging_dispatcher +from waitress.utilities import ( + cleanup_unix_socket, + logging_dispatcher, + ) +from waitress.compat import ( + IPPROTO_IPV6, + IPV6_V6ONLY, + ) def create_server(application, map=None, @@ -169,7 +176,7 @@ class BaseWSGIServer(logging_dispatcher, object): if _sock is None: self.create_socket(self.family, self.socktype) if self.family == socket.AF_INET6: # pragma: nocover - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) + self.socket.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, 1) self.set_reuse_addr() self.bind_server_socket() diff --git a/waitress/tests/test_adjustments.py b/waitress/tests/test_adjustments.py index 5c8985a..9446705 100644 --- a/waitress/tests/test_adjustments.py +++ b/waitress/tests/test_adjustments.py @@ -1,6 +1,11 @@ import sys import socket +from waitress.compat import ( + PY2, + WIN, + ) + if sys.version_info[:2] == (2, 6): # pragma: no cover import unittest2 as unittest else: # pragma: no cover @@ -181,6 +186,15 @@ class TestAdjustments(unittest.TestCase): self.assertRaises(ValueError, self._makeOne, listen='127.0.0.1:test') def test_service_port(self): + if WIN and PY2: # pragma: no cover + # On Windows and Python 2 this is broken, so we raise a ValueError + self.assertRaises( + ValueError, + self._makeOne, + listen='127.0.0.1:http', + ) + return + inst = self._makeOne(listen='127.0.0.1:http 0.0.0.0:https') bind_pairs = [sockaddr[:2] for (_, _, _, sockaddr) in inst.listen] |