summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBert JW Regeer <bertjw@regeer.org>2016-10-22 13:33:56 -0600
committerBert JW Regeer <bertjw@regeer.org>2016-10-22 13:33:56 -0600
commit036634350c6b03e3c4240236e311f41a4107eea1 (patch)
treec543a925e0900fbefe6b7ef91ca03a0eb6589bb8
parent52a97fe7bf71affa257b1d036388a18737b2d15f (diff)
parent57ea8381ac0e95a3feff58655fade698e70f9eee (diff)
downloadwaitress-036634350c6b03e3c4240236e311f41a4107eea1.tar.gz
Merge PR #140: Fix Windows support (IPv6/getaddrinfo)
-rw-r--r--appveyor.yml2
-rw-r--r--waitress/adjustments.py23
-rw-r--r--waitress/compat.py29
-rw-r--r--waitress/server.py11
-rw-r--r--waitress/tests/test_adjustments.py14
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]