summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2019-07-19 03:02:19 -0500
committerMichael Merickel <michael@merickel.org>2019-07-19 03:02:19 -0500
commita58c8889a5be154a1e616881cabfefeee337c718 (patch)
tree36806cfdb9da44d31c4a4456ca63081b80d2479c
parent9cf94669a9bb21d51741f5840b765e4ab5f94d48 (diff)
downloadwaitress-a58c8889a5be154a1e616881cabfefeee337c718.tar.gz
add proxy header tests
-rw-r--r--.coveragerc12
-rw-r--r--setup.cfg1
-rw-r--r--tox.ini11
-rw-r--r--waitress/server.py4
-rw-r--r--waitress/tests/fixtureapps/echo.py12
-rw-r--r--waitress/tests/test_functional.py56
-rw-r--r--waitress/tests/test_proxy_headers.py31
7 files changed, 105 insertions, 22 deletions
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..c2c3d96
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,12 @@
+[run]
+parallel = true
+concurrency = thread,multiprocessing
+source =
+ waitress
+
+omit =
+ waitress/tests/fixtureapps/getline.py
+
+[report]
+show_missing = true
+precision = 2
diff --git a/setup.cfg b/setup.cfg
index 4be5f9c..81cfbb1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -3,7 +3,6 @@ zip_ok = false
[nosetests]
match=^test
-where=waitress
nocapture=1
cover-package=waitress
cover-erase=1
diff --git a/tox.ini b/tox.ini
index 2533fb8..4c42a11 100644
--- a/tox.ini
+++ b/tox.ini
@@ -21,9 +21,7 @@ extras =
[py-cover]
commands =
- coverage run --source=waitress --parallel-mode {envbindir}/nosetests
- coverage combine
- coverage xml -o {envname}.xml
+ coverage run {envbindir}/nosetests waitress
extras =
testing
@@ -31,22 +29,17 @@ extras =
[testenv:py27-cover]
commands =
{[py-cover]commands}
-setenv =
- COVERAGE_FILE=.coverage.py2
[testenv:py35-cover]
commands =
{[py-cover]commands}
-setenv =
- COVERAGE_FILE=.coverage.py3
[testenv:coverage]
basepython = python3.5
commands =
- coverage erase
coverage combine
coverage xml
- coverage report --show-missing --fail-under=100 --omit=waitress/tests/fixtureapps/getline.py
+ coverage report --show-missing --fail-under=100
deps =
coverage
setenv =
diff --git a/waitress/server.py b/waitress/server.py
index bc68c77..307c377 100644
--- a/waitress/server.py
+++ b/waitress/server.py
@@ -179,15 +179,11 @@ class BaseWSGIServer(wasyncore.dispatcher, object):
adj=None, # adjustments
sockinfo=None, # opaque object
bind_socket=True,
- logger=None, # test shim
**kw
):
if adj is None:
adj = Adjustments(**kw)
- if logger is not None:
- self.logger = logger
-
if adj.trusted_proxy or adj.clear_untrusted_proxy_headers:
# wrap the application to deal with proxy headers
# we wrap it here because webtest subclasses the TcpWSGIServer
diff --git a/waitress/tests/fixtureapps/echo.py b/waitress/tests/fixtureapps/echo.py
index 8a01ea8..5509703 100644
--- a/waitress/tests/fixtureapps/echo.py
+++ b/waitress/tests/fixtureapps/echo.py
@@ -1,6 +1,18 @@
from collections import namedtuple
import json
+def app_body_only(environ, start_response): # pragma: no cover
+ cl = environ.get('CONTENT_LENGTH', None)
+ if cl is not None:
+ cl = int(cl)
+ body = environ['wsgi.input'].read(cl)
+ cl = str(len(body))
+ start_response('200 OK', [
+ ('Content-Length', cl),
+ ('Content-Type', 'text/plain'),
+ ])
+ return [body]
+
def app(environ, start_response): # pragma: no cover
cl = environ.get('CONTENT_LENGTH', None)
if cl is not None:
diff --git a/waitress/tests/test_functional.py b/waitress/tests/test_functional.py
index 6748c5f..0a39771 100644
--- a/waitress/tests/test_functional.py
+++ b/waitress/tests/test_functional.py
@@ -2,6 +2,7 @@ import errno
import logging
import multiprocessing
import os
+import signal
import socket
import string
import subprocess
@@ -28,8 +29,23 @@ def start_server(app, svr, queue, **kwargs): # pragma: no cover
"""Run a fixture application.
"""
logging.getLogger('waitress').addHandler(NullHandler())
+ try_register_coverage()
svr(app, queue, **kwargs).run()
+def try_register_coverage(): # pragma: no cover
+ # Hack around multiprocessing exiting early and not triggering coverage's
+ # atexit handler by trapping the SIGTERM and saving coverage explicitly.
+ if '_COVERAGE_RCFILE' in os.environ:
+ import coverage
+ cov = coverage.Coverage(config_file=os.getenv('_COVERAGE_RCFILE'))
+ cov.start()
+
+ def sigterm(*args):
+ cov.stop()
+ cov.save()
+ sys.exit(0)
+ signal.signal(signal.SIGTERM, sigterm)
+
class FixtureTcpWSGIServer(server.TcpWSGIServer):
"""A version of TcpWSGIServer that relays back what it's bound to.
"""
@@ -139,15 +155,21 @@ class EchoTests(object):
def setUp(self):
from waitress.tests.fixtureapps import echo
- self.start_subprocess(echo.app)
+ self.start_subprocess(
+ echo.app,
+ trusted_proxy='*',
+ trusted_proxy_count=1,
+ trusted_proxy_headers={'x-forwarded-for', 'x-forwarded-proto'},
+ clear_untrusted_proxy_headers=True,
+ )
def tearDown(self):
self.stop_subprocess()
def _read_echo(self, fp):
from waitress.tests.fixtureapps import echo
- line, headers, response_body = read_http(fp)
- return line, headers, echo.parse_response(response_body)
+ line, headers, body = read_http(fp)
+ return line, headers, echo.parse_response(body)
def test_date_and_server(self):
to_send = ("GET / HTTP/1.0\n"
@@ -416,11 +438,35 @@ class EchoTests(object):
self.assertEqual(int(response.status), 200)
self.assertEqual(response.getheader('connection'), 'close')
+ def test_proxy_headers(self):
+ to_send = (
+ "GET / HTTP/1.0\n"
+ "Content-Length: 0\n"
+ "Host: www.google.com:8080\n"
+ "X-Forwarded-For: 192.168.1.1\n"
+ "X-Forwarded-Proto: https\n"
+ "X-Forwarded-Port: 5000\n\n"
+ )
+ to_send = tobytes(to_send)
+ self.connect()
+ self.sock.send(to_send)
+ fp = self.sock.makefile('rb', 0)
+ line, headers, echo = self._read_echo(fp)
+ self.assertline(line, '200', 'OK', 'HTTP/1.0')
+ self.assertEqual(headers.get('server'), 'waitress')
+ self.assertTrue(headers.get('date'))
+ self.assertIsNone(echo.headers.get('X_FORWARDED_PORT'))
+ self.assertEqual(echo.headers['HOST'], 'www.google.com:8080')
+ self.assertEqual(echo.scheme, 'https')
+ self.assertEqual(echo.remote_addr, '192.168.1.1')
+ self.assertEqual(echo.remote_host, '192.168.1.1')
+
+
class PipeliningTests(object):
def setUp(self):
from waitress.tests.fixtureapps import echo
- self.start_subprocess(echo.app)
+ self.start_subprocess(echo.app_body_only)
def tearDown(self):
self.stop_subprocess()
@@ -459,7 +505,7 @@ class ExpectContinueTests(object):
def setUp(self):
from waitress.tests.fixtureapps import echo
- self.start_subprocess(echo.app)
+ self.start_subprocess(echo.app_body_only)
def tearDown(self):
self.stop_subprocess()
diff --git a/waitress/tests/test_proxy_headers.py b/waitress/tests/test_proxy_headers.py
index aa4c907..f3af58a 100644
--- a/waitress/tests/test_proxy_headers.py
+++ b/waitress/tests/test_proxy_headers.py
@@ -622,6 +622,34 @@ class TestProxyHeadersMiddleware(unittest.TestCase):
self.assertEqual(environ['SERVER_PORT'], '443')
self.assertEqual(environ['wsgi.url_scheme'], 'http')
+ def test_parse_forwarded_for_bad_quote(self):
+ inner = DummyApp()
+ app = self._makeOne(
+ inner,
+ trusted_proxy='*',
+ trusted_proxy_count=1,
+ trusted_proxy_headers={'x-forwarded-for'},
+ )
+ response = self._callFUT(app, headers={
+ 'X_FORWARDED_FOR': '"foo'
+ })
+ self.assertEqual(response.status, '400 Bad Request')
+ self.assertIn(b'Header "X-Forwarded-For" malformed', response.body)
+
+ def test_parse_forwarded_host_bad_quote(self):
+ inner = DummyApp()
+ app = self._makeOne(
+ inner,
+ trusted_proxy='*',
+ trusted_proxy_count=1,
+ trusted_proxy_headers={'x-forwarded-host'},
+ )
+ response = self._callFUT(app, headers={
+ 'X_FORWARDED_HOST': '"foo'
+ })
+ self.assertEqual(response.status, '400 Bad Request')
+ self.assertIn(b'Header "X-Forwarded-Host" malformed', response.body)
+
class DummyLogger(object):
def __init__(self):
@@ -630,9 +658,6 @@ class DummyLogger(object):
def warning(self, msg, *args):
self.logged.append(msg % args)
- def exception(self, msg, *args):
- self.logged.append(msg % args)
-
class DummyApp(object):
def __call__(self, environ, start_response):