summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBert JW Regeer <bertjw@regeer.org>2019-12-12 11:45:25 -0800
committerBert JW Regeer <bertjw@regeer.org>2019-12-12 11:45:25 -0800
commit0a05a2f5932d87e5f4e150edc00b4e9a62c87791 (patch)
treeb721e9d73c601033fe0c465dd68ad87795b81301
parent0b3e3cf815050c40cf13998150f21bc63212c65e (diff)
downloadwaitress-0a05a2f5932d87e5f4e150edc00b4e9a62c87791.tar.gz
Blacken the entire code base
-rw-r--r--docs/conf.py115
-rw-r--r--setup.py79
-rw-r--r--waitress/__init__.py28
-rw-r--r--waitress/__main__.py1
-rw-r--r--waitress/adjustments.py237
-rw-r--r--waitress/buffers.py41
-rw-r--r--waitress/channel.py74
-rw-r--r--waitress/compat.py83
-rw-r--r--waitress/parser.py109
-rw-r--r--waitress/proxy_headers.py88
-rw-r--r--waitress/receiver.py35
-rw-r--r--waitress/runner.py42
-rw-r--r--waitress/server.py166
-rw-r--r--waitress/task.py257
-rw-r--r--waitress/tests/fixtureapps/badcl.py11
-rw-r--r--waitress/tests/fixtureapps/echo.py71
-rw-r--r--waitress/tests/fixtureapps/error.py25
-rw-r--r--waitress/tests/fixtureapps/filewrapper.py70
-rw-r--r--waitress/tests/fixtureapps/getline.py8
-rw-r--r--waitress/tests/fixtureapps/nocl.py23
-rw-r--r--waitress/tests/fixtureapps/runner.py5
-rw-r--r--waitress/tests/fixtureapps/sleepy.py14
-rw-r--r--waitress/tests/fixtureapps/toolarge.py7
-rw-r--r--waitress/tests/fixtureapps/writecb.py20
-rw-r--r--waitress/tests/test_adjustments.py315
-rw-r--r--waitress/tests/test_buffers.py194
-rw-r--r--waitress/tests/test_channel.py165
-rw-r--r--waitress/tests/test_compat.py14
-rw-r--r--waitress/tests/test_functional.py873
-rw-r--r--waitress/tests/test_init.py7
-rw-r--r--waitress/tests/test_parser.py255
-rw-r--r--waitress/tests/test_proxy_headers.py705
-rw-r--r--waitress/tests/test_receiver.py52
-rw-r--r--waitress/tests/test_regression.py11
-rw-r--r--waitress/tests/test_runner.py124
-rw-r--r--waitress/tests/test_server.py171
-rw-r--r--waitress/tests/test_task.py613
-rw-r--r--waitress/tests/test_trigger.py12
-rw-r--r--waitress/tests/test_utilities.py54
-rw-r--r--waitress/tests/test_wasyncore.py442
-rw-r--r--waitress/trigger.py27
-rw-r--r--waitress/utilities.py192
-rw-r--r--waitress/wasyncore.py191
43 files changed, 3245 insertions, 2781 deletions
diff --git a/docs/conf.py b/docs/conf.py
index c04eaa3..cf0ff9b 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -15,7 +15,7 @@
# If your extensions are in another directory, add it here. If the
# directory is relative to the documentation root, use os.path.abspath to
# make it absolute, like shown here.
-#sys.path.append(os.path.abspath('some/directory'))
+# sys.path.append(os.path.abspath('some/directory'))
import datetime
import pkg_resources
@@ -27,68 +27,70 @@ import pylons_sphinx_themes
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
- 'sphinx.ext.autodoc',
- 'sphinx.ext.intersphinx',
+ "sphinx.ext.autodoc",
+ "sphinx.ext.intersphinx",
]
intersphinx_mapping = {
- 'python': ('https://docs.python.org/3/', None),
+ "python": ("https://docs.python.org/3/", None),
}
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
# The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
# The master toctree document.
-master_doc = 'index'
+master_doc = "index"
# General substitutions.
-project = 'waitress'
+project = "waitress"
thisyear = datetime.datetime.now().year
-copyright = '2012-%s, Agendaless Consulting <chrism@plope.com>' % thisyear
+copyright = "2012-%s, Agendaless Consulting <chrism@plope.com>" % thisyear
# The default replacements for |version| and |release|, also used in various
# other places throughout the built documents.
#
# The short X.Y version.
-version = pkg_resources.get_distribution('waitress').version
+version = pkg_resources.get_distribution("waitress").version
# The full version, including alpha/beta/rc tags.
release = version
# There are two options for replacing |today|: either, you set today to
# some non-false value, then it is used:
-#today = ''
+# today = ''
# Else, today_fmt is used as the format for a strftime call.
-today_fmt = '%B %d, %Y'
+today_fmt = "%B %d, %Y"
# List of documents that shouldn't be included in the build.
-#unused_docs = []
+# unused_docs = []
# List of directories, relative to source directories, that shouldn't be
# searched for source files.
-#exclude_dirs = []
-exclude_patterns = ['_themes/README.rst',]
+# exclude_dirs = []
+exclude_patterns = [
+ "_themes/README.rst",
+]
# The reST default role (used for this markup: `text`) to use for all
# documents.
-#default_role = None
+# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
add_module_names = False
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
-#show_authors = False
+# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
# Do not use smart quotes.
smartquotes = False
@@ -98,9 +100,9 @@ smartquotes = False
# -----------------------
# Add and use Pylons theme
-html_theme = 'pylons'
+html_theme = "pylons"
html_theme_path = pylons_sphinx_themes.get_html_themes_path()
-html_theme_options = dict(github_url='https://github.com/Pylons/waitress')
+html_theme_options = dict(github_url="https://github.com/Pylons/waitress")
# The style sheet to use for HTML and HTML Help pages. A file of that name
# must exist either in Sphinx' static/ path, or in one of the custom paths
@@ -109,11 +111,11 @@ html_theme_options = dict(github_url='https://github.com/Pylons/waitress')
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
-#html_title = None
+# html_title = None
# A shorter title for the navigation bar. Default is the same as
# html_title.
-#html_short_title = None
+# html_short_title = None
# The name of an image file (within the static path) to place at the top of
# the sidebar.
@@ -122,92 +124,99 @@ html_theme_options = dict(github_url='https://github.com/Pylons/waitress')
# The name of an image file (within the static path) to use as favicon of
# the docs. This file should be a Windows icon file (.ico) being 16x16 or
# 32x32 pixels large.
-#html_favicon = None
+# html_favicon = None
# Add any paths that contain custom static files (such as style sheets)
# here, relative to this directory. They are copied after the builtin
# static files, so a file named "default.css" will overwrite the builtin
# "default.css".
-#html_static_path = ['.static']
+# html_static_path = ['.static']
# If not '', a 'Last updated on:' timestamp is inserted at every page
# bottom, using the given strftime format.
-html_last_updated_fmt = '%b %d, %Y'
+html_last_updated_fmt = "%b %d, %Y"
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
# If false, no module index is generated.
-#html_use_modindex = True
+# html_use_modindex = True
# If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
# If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
# If true, the reST sources are included in the HTML build as
# _sources/<name>.
-#html_copy_source = True
+# html_copy_source = True
# If true, an OpenSearch description file will be output, and all pages
# will contain a <link> tag referring to it. The value of this option must
# be the base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = ''
+# html_file_suffix = ''
# Output file base name for HTML help builder.
-htmlhelp_basename = 'waitress'
+htmlhelp_basename = "waitress"
# Control display of sidebars
-html_sidebars = {'**': [
- 'localtoc.html',
- 'ethicalads.html',
- 'relations.html',
- 'sourcelink.html',
- 'searchbox.html',
-]}
+html_sidebars = {
+ "**": [
+ "localtoc.html",
+ "ethicalads.html",
+ "relations.html",
+ "sourcelink.html",
+ "searchbox.html",
+ ]
+}
# Options for LaTeX output
# ------------------------
# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
+# latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
+# latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, document class [howto/manual]).
latex_documents = [
- ('index', 'waitress.tex', 'waitress Documentation',
- 'Pylons Project Developers', 'manual'),
+ (
+ "index",
+ "waitress.tex",
+ "waitress Documentation",
+ "Pylons Project Developers",
+ "manual",
+ ),
]
# The name of an image file (relative to this directory) to place at the
# top of the title page.
-#latex_logo = '.static/logo_hi.gif'
+# latex_logo = '.static/logo_hi.gif'
# For "manual" documents, if this is true, then toplevel headings are
# parts, not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
+# latex_preamble = ''
# Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
# If false, no module index is generated.
-#latex_use_modindex = True
+# latex_use_modindex = True
diff --git a/setup.py b/setup.py
index 9a07479..13a9d58 100644
--- a/setup.py
+++ b/setup.py
@@ -16,66 +16,63 @@ from setuptools import setup, find_packages
here = os.path.abspath(os.path.dirname(__file__))
try:
- README = open(os.path.join(here, 'README.rst')).read()
- CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
+ README = open(os.path.join(here, "README.rst")).read()
+ CHANGES = open(os.path.join(here, "CHANGES.txt")).read()
except IOError:
- README = CHANGES = ''
+ README = CHANGES = ""
docs_extras = [
- 'Sphinx>=1.8.1',
- 'docutils',
- 'pylons-sphinx-themes>=1.0.9',
+ "Sphinx>=1.8.1",
+ "docutils",
+ "pylons-sphinx-themes>=1.0.9",
]
testing_extras = [
- 'nose',
- 'coverage',
+ "nose",
+ "coverage",
]
setup(
- name='waitress',
- version='1.3.1',
- author='Zope Foundation and Contributors',
- author_email='zope-dev@zope.org',
+ name="waitress",
+ version="1.3.1",
+ author="Zope Foundation and Contributors",
+ author_email="zope-dev@zope.org",
maintainer="Pylons Project",
maintainer_email="pylons-discuss@googlegroups.com",
- description='Waitress WSGI server',
- long_description=README + '\n\n' + CHANGES,
- license='ZPL 2.1',
- keywords='waitress wsgi server http',
+ description="Waitress WSGI server",
+ long_description=README + "\n\n" + CHANGES,
+ license="ZPL 2.1",
+ keywords="waitress wsgi server http",
classifiers=[
- 'Development Status :: 5 - Production/Stable',
- 'Environment :: Web Environment',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: Zope Public License',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
- 'Programming Language :: Python :: 3.6',
- 'Programming Language :: Python :: 3.7',
- 'Programming Language :: Python :: Implementation :: CPython',
- 'Programming Language :: Python :: Implementation :: PyPy',
- 'Natural Language :: English',
- 'Operating System :: OS Independent',
- 'Topic :: Internet :: WWW/HTTP',
- 'Topic :: Internet :: WWW/HTTP :: WSGI',
+ "Development Status :: 5 - Production/Stable",
+ "Environment :: Web Environment",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: Zope Public License",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 2",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.4",
+ "Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: 3.6",
+ "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: Implementation :: CPython",
+ "Programming Language :: Python :: Implementation :: PyPy",
+ "Natural Language :: English",
+ "Operating System :: OS Independent",
+ "Topic :: Internet :: WWW/HTTP",
+ "Topic :: Internet :: WWW/HTTP :: WSGI",
],
- url='https://github.com/Pylons/waitress',
+ url="https://github.com/Pylons/waitress",
packages=find_packages(),
- extras_require={
- 'testing': testing_extras,
- 'docs': docs_extras,
- },
+ extras_require={"testing": testing_extras, "docs": docs_extras,},
include_package_data=True,
- test_suite='waitress',
+ test_suite="waitress",
zip_safe=False,
entry_points="""
[paste.server_runner]
main = waitress:serve_paste
[console_scripts]
waitress-serve = waitress.runner:run
- """
+ """,
)
diff --git a/waitress/__init__.py b/waitress/__init__.py
index 775fe3a..e6e5911 100644
--- a/waitress/__init__.py
+++ b/waitress/__init__.py
@@ -1,41 +1,45 @@
from waitress.server import create_server
import logging
+
def serve(app, **kw):
- _server = kw.pop('_server', create_server) # test shim
- _quiet = kw.pop('_quiet', False) # test shim
- _profile = kw.pop('_profile', False) # test shim
- if not _quiet: # pragma: no cover
+ _server = kw.pop("_server", create_server) # test shim
+ _quiet = kw.pop("_quiet", False) # test shim
+ _profile = kw.pop("_profile", False) # test shim
+ if not _quiet: # pragma: no cover
# idempotent if logging has already been set up
logging.basicConfig()
server = _server(app, **kw)
- if not _quiet: # pragma: no cover
- server.print_listen('Serving on http://{}:{}')
- if _profile: # pragma: no cover
- profile('server.run()', globals(), locals(), (), False)
+ if not _quiet: # pragma: no cover
+ server.print_listen("Serving on http://{}:{}")
+ if _profile: # pragma: no cover
+ profile("server.run()", globals(), locals(), (), False)
else:
server.run()
+
def serve_paste(app, global_conf, **kw):
serve(app, **kw)
return 0
-def profile(cmd, globals, locals, sort_order, callers): # pragma: no cover
+
+def profile(cmd, globals, locals, sort_order, callers): # pragma: no cover
# runs a command under the profiler and print profiling output at shutdown
import os
import profile
import pstats
import tempfile
+
fd, fn = tempfile.mkstemp()
try:
profile.runctx(cmd, globals, locals, fn)
stats = pstats.Stats(fn)
stats.strip_dirs()
# calls,time,cumulative and cumulative,calls,time are useful
- stats.sort_stats(*(sort_order or ('cumulative', 'calls', 'time')))
+ stats.sort_stats(*(sort_order or ("cumulative", "calls", "time")))
if callers:
- stats.print_callers(.3)
+ stats.print_callers(0.3)
else:
- stats.print_stats(.3)
+ stats.print_stats(0.3)
finally:
os.remove(fn)
diff --git a/waitress/__main__.py b/waitress/__main__.py
index e484f40..9bcd07e 100644
--- a/waitress/__main__.py
+++ b/waitress/__main__.py
@@ -1,2 +1,3 @@
from waitress.runner import run # pragma nocover
+
run() # pragma nocover
diff --git a/waitress/adjustments.py b/waitress/adjustments.py
index f33c570..93439ea 100644
--- a/waitress/adjustments.py
+++ b/waitress/adjustments.py
@@ -23,15 +23,15 @@ from .compat import (
WIN,
string_types,
HAS_IPV6,
- )
+)
-truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1'))
+truthy = frozenset(("t", "true", "y", "yes", "on", "1"))
KNOWN_PROXY_HEADERS = frozenset(
- header.lower().replace('_', '-')
- for header in PROXY_HEADERS
+ header.lower().replace("_", "-") for header in PROXY_HEADERS
)
+
def asbool(s):
""" Return the boolean value ``True`` if the case-lowered value of string
input ``s`` is any of ``t``, ``true``, ``y``, ``on``, or ``1``, otherwise
@@ -45,15 +45,18 @@ def asbool(s):
s = str(s).strip()
return s.lower() in truthy
+
def asoctal(s):
"""Convert the given octal string to an actual number."""
return int(s, 8)
+
def aslist_cronly(value):
if isinstance(value, string_types):
value = filter(None, [x.strip() for x in value.splitlines()])
return list(value)
+
def aslist(value):
""" Return a list of strings, separating the input based on newlines
and, if flatten=True (the default), also split on spaces within
@@ -65,82 +68,90 @@ def aslist(value):
result.extend(subvalues)
return result
+
def asset(value):
return set(aslist(value))
+
def slash_fixed_str(s):
s = s.strip()
if s:
# always have a leading slash, replace any number of leading slashes
# with a single slash, and strip any trailing slashes
- s = '/' + s.lstrip('/').rstrip('/')
+ s = "/" + s.lstrip("/").rstrip("/")
return s
+
def str_iftruthy(s):
return str(s) if s else None
+
def as_socket_list(sockets):
"""Checks if the elements in the list are of type socket and
removes them if not."""
return [sock for sock in sockets if isinstance(sock, socket.socket)]
+
class _str_marker(str):
pass
+
class _int_marker(int):
pass
+
class _bool_marker(object):
pass
+
class Adjustments(object):
"""This class contains tunable parameters.
"""
_params = (
- ('host', str),
- ('port', int),
- ('ipv4', asbool),
- ('ipv6', asbool),
- ('listen', aslist),
- ('threads', int),
- ('trusted_proxy', str_iftruthy),
- ('trusted_proxy_count', int),
- ('trusted_proxy_headers', asset),
- ('log_untrusted_proxy_headers', asbool),
- ('clear_untrusted_proxy_headers', asbool),
- ('url_scheme', str),
- ('url_prefix', slash_fixed_str),
- ('backlog', int),
- ('recv_bytes', int),
- ('send_bytes', int),
- ('outbuf_overflow', int),
- ('outbuf_high_watermark', int),
- ('inbuf_overflow', int),
- ('connection_limit', int),
- ('cleanup_interval', int),
- ('channel_timeout', int),
- ('log_socket_errors', asbool),
- ('max_request_header_size', int),
- ('max_request_body_size', int),
- ('expose_tracebacks', asbool),
- ('ident', str_iftruthy),
- ('asyncore_loop_timeout', int),
- ('asyncore_use_poll', asbool),
- ('unix_socket', str),
- ('unix_socket_perms', asoctal),
- ('sockets', as_socket_list),
+ ("host", str),
+ ("port", int),
+ ("ipv4", asbool),
+ ("ipv6", asbool),
+ ("listen", aslist),
+ ("threads", int),
+ ("trusted_proxy", str_iftruthy),
+ ("trusted_proxy_count", int),
+ ("trusted_proxy_headers", asset),
+ ("log_untrusted_proxy_headers", asbool),
+ ("clear_untrusted_proxy_headers", asbool),
+ ("url_scheme", str),
+ ("url_prefix", slash_fixed_str),
+ ("backlog", int),
+ ("recv_bytes", int),
+ ("send_bytes", int),
+ ("outbuf_overflow", int),
+ ("outbuf_high_watermark", int),
+ ("inbuf_overflow", int),
+ ("connection_limit", int),
+ ("cleanup_interval", int),
+ ("channel_timeout", int),
+ ("log_socket_errors", asbool),
+ ("max_request_header_size", int),
+ ("max_request_body_size", int),
+ ("expose_tracebacks", asbool),
+ ("ident", str_iftruthy),
+ ("asyncore_loop_timeout", int),
+ ("asyncore_use_poll", asbool),
+ ("unix_socket", str),
+ ("unix_socket_perms", asoctal),
+ ("sockets", as_socket_list),
)
_param_map = dict(_params)
# hostname or IP address to listen on
- host = _str_marker('0.0.0.0')
+ host = _str_marker("0.0.0.0")
# TCP port to listen on
port = _int_marker(8080)
- listen = ['{}:{}'.format(host, port)]
+ listen = ["{}:{}".format(host, port)]
# number of threads available for tasks
threads = 4
@@ -178,14 +189,14 @@ class Adjustments(object):
clear_untrusted_proxy_headers = _bool_marker
# default ``wsgi.url_scheme`` value
- url_scheme = 'http'
+ url_scheme = "http"
# default ``SCRIPT_NAME`` value, also helps reset ``PATH_INFO``
# when nonempty
- url_prefix = ''
+ url_prefix = ""
# server identity (sent in Server: header)
- ident = 'waitress'
+ ident = "waitress"
# backlog is the value waitress passes to pass to socket.listen() This is
# the maximum number of incoming TCP connections that will wait in an OS
@@ -277,44 +288,44 @@ class Adjustments(object):
def __init__(self, **kw):
- if 'listen' in kw and ('host' in kw or 'port' in kw):
- raise ValueError('host or port may not be set if listen is set.')
+ if "listen" in kw and ("host" in kw or "port" in kw):
+ raise ValueError("host or port may not be set if listen is set.")
- if 'listen' in kw and 'sockets' in kw:
- raise ValueError('socket may not be set if listen is set.')
+ if "listen" in kw and "sockets" in kw:
+ raise ValueError("socket may not be set if listen is set.")
- if 'sockets' in kw and ('host' in kw or 'port' in kw):
- raise ValueError('host or port may not be set if sockets is set.')
+ if "sockets" in kw and ("host" in kw or "port" in kw):
+ raise ValueError("host or port may not be set if sockets is set.")
- if 'sockets' in kw and 'unix_socket' in kw:
- raise ValueError('unix_socket may not be set if sockets is set')
+ if "sockets" in kw and "unix_socket" in kw:
+ raise ValueError("unix_socket may not be set if sockets is set")
- if 'unix_socket' in kw and ('host' in kw or 'port' in kw):
- raise ValueError('unix_socket may not be set if host or port is set')
+ if "unix_socket" in kw and ("host" in kw or "port" in kw):
+ raise ValueError("unix_socket may not be set if host or port is set")
- if 'unix_socket' in kw and 'listen' in kw:
- raise ValueError('unix_socket may not be set if listen is set')
+ if "unix_socket" in kw and "listen" in kw:
+ raise ValueError("unix_socket may not be set if listen is set")
- if 'send_bytes' in kw:
+ if "send_bytes" in kw:
warnings.warn(
- 'send_bytes will be removed in a future release',
- DeprecationWarning,
+ "send_bytes will be removed in a future release", DeprecationWarning,
)
for k, v in kw.items():
if k not in self._param_map:
- raise ValueError('Unknown adjustment %r' % k)
+ raise ValueError("Unknown adjustment %r" % k)
setattr(self, k, self._param_map[k](v))
- if (not isinstance(self.host, _str_marker) or
- not isinstance(self.port, _int_marker)):
- self.listen = ['{}:{}'.format(self.host, self.port)]
+ if not isinstance(self.host, _str_marker) or not isinstance(
+ self.port, _int_marker
+ ):
+ self.listen = ["{}:{}".format(self.host, self.port)]
enabled_families = socket.AF_UNSPEC
- if not self.ipv4 and not HAS_IPV6: # pragma: no cover
+ 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.'
+ "IPv4 is disabled but IPv6 is not available. Cowardly refusing to start."
)
if self.ipv4 and not self.ipv6:
@@ -326,30 +337,30 @@ class Adjustments(object):
wanted_sockets = []
hp_pairs = []
for i in self.listen:
- if ':' in i:
+ if ":" in i:
(host, port) = i.rsplit(":", 1)
# IPv6 we need to make sure that we didn't split on the address
- if ']' in port: # pragma: nocover
+ if "]" in port: # pragma: nocover
(host, port) = (i, str(self.port))
else:
(host, port) = (i, str(self.port))
- if WIN and PY2: # pragma: no cover
+ if WIN and PY2: # pragma: no cover
try:
# Try turning the port into an integer
port = int(port)
except Exception:
raise ValueError(
- 'Windows does not support service names instead of port numbers'
+ "Windows does not support service names instead of port numbers"
)
try:
- if '[' in host and ']' in host: # pragma: nocover
- host = host.strip('[').rstrip(']')
+ if "[" in host and "]" in host: # pragma: nocover
+ host = host.strip("[").rstrip("]")
- if host == '*':
+ if host == "*":
host = None
for s in socket.getaddrinfo(
@@ -358,7 +369,7 @@ class Adjustments(object):
enabled_families,
socket.SOCK_STREAM,
socket.IPPROTO_TCP,
- socket.AI_PASSIVE
+ socket.AI_PASSIVE,
):
(family, socktype, proto, _, sockaddr) = s
@@ -372,19 +383,18 @@ class Adjustments(object):
# two different zone-indices (which makes no sense what so
# ever...) yet treats them equally when we attempt to bind().
if (
- sockaddr[1] == 0 or
- (sockaddr[0].split('%', 1)[0], sockaddr[1]) not in hp_pairs
+ sockaddr[1] == 0
+ or (sockaddr[0].split("%", 1)[0], sockaddr[1]) not in hp_pairs
):
wanted_sockets.append((family, socktype, proto, sockaddr))
- hp_pairs.append((sockaddr[0].split('%', 1)[0], sockaddr[1]))
+ hp_pairs.append((sockaddr[0].split("%", 1)[0], sockaddr[1]))
except Exception:
- raise ValueError('Invalid host/port specified.')
+ raise ValueError("Invalid host/port specified.")
if self.trusted_proxy_count is not None and self.trusted_proxy is None:
raise ValueError(
- "trusted_proxy_count has no meaning without setting "
- "trusted_proxy"
+ "trusted_proxy_count has no meaning without setting " "trusted_proxy"
)
elif self.trusted_proxy_count is None:
@@ -392,8 +402,7 @@ class Adjustments(object):
if self.trusted_proxy_headers and self.trusted_proxy is None:
raise ValueError(
- "trusted_proxy_headers has no meaning without setting "
- "trusted_proxy"
+ "trusted_proxy_headers has no meaning without setting " "trusted_proxy"
)
if self.trusted_proxy_headers:
@@ -405,12 +414,13 @@ class Adjustments(object):
if unknown_values:
raise ValueError(
"Received unknown trusted_proxy_headers value (%s) expected one "
- "of %s" % (", ".join(unknown_values), ", ".join(KNOWN_PROXY_HEADERS))
+ "of %s"
+ % (", ".join(unknown_values), ", ".join(KNOWN_PROXY_HEADERS))
)
if (
- 'forwarded' in self.trusted_proxy_headers and
- self.trusted_proxy_headers - {'forwarded'}
+ "forwarded" in self.trusted_proxy_headers
+ and self.trusted_proxy_headers - {"forwarded"}
):
raise ValueError(
"The Forwarded proxy header and the "
@@ -420,19 +430,19 @@ class Adjustments(object):
elif self.trusted_proxy is not None:
warnings.warn(
- 'No proxy headers were marked as trusted, but trusted_proxy was set. '
- 'Implicitly trusting X-Forwarded-Proto for backwards compatibility. '
- 'This will be removed in future versions of waitress.',
- DeprecationWarning
+ "No proxy headers were marked as trusted, but trusted_proxy was set. "
+ "Implicitly trusting X-Forwarded-Proto for backwards compatibility. "
+ "This will be removed in future versions of waitress.",
+ DeprecationWarning,
)
- self.trusted_proxy_headers = {'x-forwarded-proto'}
+ self.trusted_proxy_headers = {"x-forwarded-proto"}
if self.clear_untrusted_proxy_headers is _bool_marker:
warnings.warn(
- 'In future versions of Waitress clear_untrusted_proxy_headers will be '
- 'set to True by default. You may opt-out by setting this value to '
- 'False, or opt-in explicitly by setting this to True.',
- DeprecationWarning
+ "In future versions of Waitress clear_untrusted_proxy_headers will be "
+ "set to True by default. You may opt-out by setting this value to "
+ "False, or opt-in explicitly by setting this to True.",
+ DeprecationWarning,
)
self.clear_untrusted_proxy_headers = False
@@ -447,35 +457,35 @@ class Adjustments(object):
dictionary suitable for passing into __init__, where __init__ does the
casting.
"""
- long_opts = ['help', 'call']
+ long_opts = ["help", "call"]
for opt, cast in cls._params:
- opt = opt.replace('_', '-')
+ opt = opt.replace("_", "-")
if cast is asbool:
long_opts.append(opt)
- long_opts.append('no-' + opt)
+ long_opts.append("no-" + opt)
else:
- long_opts.append(opt + '=')
+ long_opts.append(opt + "=")
kw = {
- 'help': False,
- 'call': False,
+ "help": False,
+ "call": False,
}
- opts, args = getopt.getopt(argv, '', long_opts)
+ opts, args = getopt.getopt(argv, "", long_opts)
for opt, value in opts:
- param = opt.lstrip('-').replace('-', '_')
+ param = opt.lstrip("-").replace("-", "_")
- if param == 'listen':
- kw['listen'] = '{} {}'.format(kw.get('listen', ''), value)
+ if param == "listen":
+ kw["listen"] = "{} {}".format(kw.get("listen", ""), value)
continue
- if param.startswith('no_'):
+ if param.startswith("no_"):
param = param[3:]
- kw[param] = 'false'
- elif param in ('help', 'call'):
+ kw[param] = "false"
+ elif param in ("help", "call"):
kw[param] = True
elif cls._param_map[param] is asbool:
- kw[param] = 'true'
+ kw[param] = "true"
else:
kw[param] = value
@@ -487,16 +497,19 @@ class Adjustments(object):
has_inet_socket = False
has_unsupported_socket = False
for sock in sockets:
- if (sock.family == socket.AF_INET or sock.family == socket.AF_INET6) and \
- sock.type == socket.SOCK_STREAM:
+ if (
+ sock.family == socket.AF_INET or sock.family == socket.AF_INET6
+ ) and sock.type == socket.SOCK_STREAM:
has_inet_socket = True
- elif hasattr(socket, 'AF_UNIX') and \
- sock.family == socket.AF_UNIX and \
- sock.type == socket.SOCK_STREAM:
+ elif (
+ hasattr(socket, "AF_UNIX")
+ and sock.family == socket.AF_UNIX
+ and sock.type == socket.SOCK_STREAM
+ ):
has_unix_socket = True
else:
has_unsupported_socket = True
if has_unix_socket and has_inet_socket:
- raise ValueError('Internet and UNIX sockets may not be mixed.')
+ raise ValueError("Internet and UNIX sockets may not be mixed.")
if has_unsupported_socket:
- raise ValueError('Only Internet or UNIX stream sockets may be used.')
+ raise ValueError("Only Internet or UNIX stream sockets may be used.")
diff --git a/waitress/buffers.py b/waitress/buffers.py
index aa11b70..04f6b42 100644
--- a/waitress/buffers.py
+++ b/waitress/buffers.py
@@ -16,11 +16,12 @@
from io import BytesIO
# copy_bytes controls the size of temp. strings for shuffling data around.
-COPY_BYTES = 1 << 18 # 256K
+COPY_BYTES = 1 << 18 # 256K
# The maximum number of bytes to buffer in a simple string.
STRBUF_LIMIT = 8192
+
class FileBasedBuffer(object):
remain = 0
@@ -46,7 +47,7 @@ class FileBasedBuffer(object):
def __nonzero__(self):
return True
- __bool__ = __nonzero__ # py3
+ __bool__ = __nonzero__ # py3
def append(self, s):
file = self.file
@@ -73,8 +74,8 @@ class FileBasedBuffer(object):
def skip(self, numbytes, allow_prune=0):
if self.remain < numbytes:
- raise ValueError("Can't skip %d bytes in buffer of %d bytes" % (
- numbytes, self.remain)
+ raise ValueError(
+ "Can't skip %d bytes in buffer of %d bytes" % (numbytes, self.remain)
)
self.file.seek(numbytes, 1)
self.remain = self.remain - numbytes
@@ -104,21 +105,22 @@ class FileBasedBuffer(object):
return self.file
def close(self):
- if hasattr(self.file, 'close'):
+ if hasattr(self.file, "close"):
self.file.close()
self.remain = 0
-class TempfileBasedBuffer(FileBasedBuffer):
+class TempfileBasedBuffer(FileBasedBuffer):
def __init__(self, from_buffer=None):
FileBasedBuffer.__init__(self, self.newfile(), from_buffer)
def newfile(self):
from tempfile import TemporaryFile
- return TemporaryFile('w+b')
-class BytesIOBasedBuffer(FileBasedBuffer):
+ return TemporaryFile("w+b")
+
+class BytesIOBasedBuffer(FileBasedBuffer):
def __init__(self, from_buffer=None):
if from_buffer is not None:
FileBasedBuffer.__init__(self, BytesIO(), from_buffer)
@@ -129,17 +131,19 @@ class BytesIOBasedBuffer(FileBasedBuffer):
def newfile(self):
return BytesIO()
+
def _is_seekable(fp):
- if hasattr(fp, 'seekable'):
+ if hasattr(fp, "seekable"):
return fp.seekable()
- return hasattr(fp, 'seek') and hasattr(fp, 'tell')
+ return hasattr(fp, "seek") and hasattr(fp, "tell")
+
class ReadOnlyFileBasedBuffer(FileBasedBuffer):
# used as wsgi.file_wrapper
def __init__(self, file, block_size=32768):
self.file = file
- self.block_size = block_size # for __iter__
+ self.block_size = block_size # for __iter__
def prepare(self, size=None):
if _is_seekable(self.file):
@@ -168,7 +172,7 @@ class ReadOnlyFileBasedBuffer(FileBasedBuffer):
file.seek(read_pos)
return res
- def __iter__(self): # called by task if self.filelike has no seek/tell
+ def __iter__(self): # called by task if self.filelike has no seek/tell
return self
def next(self):
@@ -177,11 +181,12 @@ class ReadOnlyFileBasedBuffer(FileBasedBuffer):
raise StopIteration
return val
- __next__ = next # py3
+ __next__ = next # py3
def append(self, s):
raise NotImplementedError
+
class OverflowableBuffer(object):
"""
This buffer implementation has four stages:
@@ -194,7 +199,7 @@ class OverflowableBuffer(object):
overflowed = False
buf = None
- strbuf = b'' # Bytes-based buffer.
+ strbuf = b"" # Bytes-based buffer.
def __init__(self, overflow):
# overflow is the maximum to be stored in a StringIO buffer.
@@ -214,7 +219,7 @@ class OverflowableBuffer(object):
# OverflowError on Python 2
return self.__len__() > 0
- __bool__ = __nonzero__ # py3
+ __bool__ = __nonzero__ # py3
def _create_buffer(self):
strbuf = self.strbuf
@@ -225,7 +230,7 @@ class OverflowableBuffer(object):
buf = self.buf
if strbuf:
buf.append(self.strbuf)
- self.strbuf = b''
+ self.strbuf = b""
return buf
def _set_small_buffer(self):
@@ -268,7 +273,7 @@ class OverflowableBuffer(object):
# We could slice instead of converting to
# a buffer, but that would eat up memory in
# large transfers.
- self.strbuf = b''
+ self.strbuf = b""
return
buf = self._create_buffer()
buf.skip(numbytes, allow_prune)
@@ -280,7 +285,7 @@ class OverflowableBuffer(object):
"""
buf = self.buf
if buf is None:
- self.strbuf = b''
+ self.strbuf = b""
return
buf.prune()
if self.overflowed:
diff --git a/waitress/channel.py b/waitress/channel.py
index 26862dc..a8bc76f 100644
--- a/waitress/channel.py
+++ b/waitress/channel.py
@@ -32,9 +32,11 @@ from waitress.utilities import InternalServerError
from . import wasyncore
+
class ClientDisconnected(Exception):
""" Raised when attempting to write to a closed socket."""
+
class HTTPChannel(wasyncore.dispatcher, object):
"""
Setting self.requests = [somerequest] prevents more requests from being
@@ -47,27 +49,22 @@ class HTTPChannel(wasyncore.dispatcher, object):
error_task_class = ErrorTask
parser_class = HTTPRequestParser
- request = None # A request parser instance
- last_activity = 0 # Time of last activity
- will_close = False # set to True to close the socket.
- close_when_flushed = False # set to True to close the socket when flushed
- requests = () # currently pending requests
- sent_continue = False # used as a latch after sending 100 continue
- total_outbufs_len = 0 # total bytes ready to send
- current_outbuf_count = 0 # total bytes written to current outbuf
+ request = None # A request parser instance
+ last_activity = 0 # Time of last activity
+ will_close = False # set to True to close the socket.
+ close_when_flushed = False # set to True to close the socket when flushed
+ requests = () # currently pending requests
+ sent_continue = False # used as a latch after sending 100 continue
+ total_outbufs_len = 0 # total bytes ready to send
+ current_outbuf_count = 0 # total bytes written to current outbuf
#
# ASYNCHRONOUS METHODS (including __init__)
#
def __init__(
- self,
- server,
- sock,
- addr,
- adj,
- map=None,
- ):
+ self, server, sock, addr, adj, map=None,
+ ):
self.server = server
self.adj = adj
self.outbufs = [OverflowableBuffer(adj.outbuf_overflow)]
@@ -88,11 +85,7 @@ class HTTPChannel(wasyncore.dispatcher, object):
# if there's data in the out buffer or we've been instructed to close
# the channel (possibly by our server maintenance logic), run
# handle_write
- return (
- self.total_outbufs_len
- or self.will_close
- or self.close_when_flushed
- )
+ return self.total_outbufs_len or self.will_close or self.close_when_flushed
def handle_write(self):
# Precondition: there's data in the out buffer to be sent, or
@@ -125,10 +118,10 @@ class HTTPChannel(wasyncore.dispatcher, object):
flush()
except socket.error:
if self.adj.log_socket_errors:
- self.logger.exception('Socket error')
+ self.logger.exception("Socket error")
self.will_close = True
except Exception:
- self.logger.exception('Unexpected exception when flushing')
+ self.logger.exception("Unexpected exception when flushing")
self.will_close = True
if self.close_when_flushed and not self.total_outbufs_len:
@@ -151,7 +144,7 @@ class HTTPChannel(wasyncore.dispatcher, object):
data = self.recv(self.adj.recv_bytes)
except socket.error:
if self.adj.log_socket_errors:
- self.logger.exception('Socket error')
+ self.logger.exception("Socket error")
self.handle_close()
return
if data:
@@ -180,7 +173,7 @@ class HTTPChannel(wasyncore.dispatcher, object):
if not self.sent_continue:
# there's no current task, so we don't need to try to
# lock the outbuf to append to it.
- outbuf_payload = b'HTTP/1.1 100 Continue\r\n\r\n'
+ outbuf_payload = b"HTTP/1.1 100 Continue\r\n\r\n"
self.outbufs[-1].append(outbuf_payload)
self.current_outbuf_count += len(outbuf_payload)
self.total_outbufs_len += len(outbuf_payload)
@@ -247,8 +240,7 @@ class HTTPChannel(wasyncore.dispatcher, object):
try:
toclose.close()
except Exception:
- self.logger.exception(
- 'Unexpected error when closing an outbuf')
+ self.logger.exception("Unexpected error when closing an outbuf")
else:
# caught up, done flushing for now
dobreak = True
@@ -269,7 +261,8 @@ class HTTPChannel(wasyncore.dispatcher, object):
outbuf.close()
except Exception:
self.logger.exception(
- 'Unknown exception while trying to close outbuf')
+ "Unknown exception while trying to close outbuf"
+ )
self.total_outbufs_len = 0
self.connected = False
self.outbuf_lock.notify()
@@ -288,7 +281,7 @@ class HTTPChannel(wasyncore.dispatcher, object):
This hook keeps track of closed channels.
"""
- fd = self._fileno # next line sets this to None
+ fd = self._fileno # next line sets this to None
wasyncore.dispatcher.del_channel(self, map)
ac = self.server.active_channels
if fd in ac:
@@ -337,8 +330,8 @@ class HTTPChannel(wasyncore.dispatcher, object):
if self.total_outbufs_len > self.adj.outbuf_high_watermark:
with self.outbuf_lock:
while (
- self.connected and
- self.total_outbufs_len > self.adj.outbuf_high_watermark
+ self.connected
+ and self.total_outbufs_len > self.adj.outbuf_high_watermark
):
self.server.pull_trigger()
self.outbuf_lock.wait()
@@ -355,18 +348,22 @@ class HTTPChannel(wasyncore.dispatcher, object):
try:
task.service()
except ClientDisconnected:
- self.logger.info('Client disconnected while serving %s' %
- task.request.path)
+ self.logger.info(
+ "Client disconnected while serving %s" % task.request.path
+ )
task.close_on_finish = True
except Exception:
- self.logger.exception('Exception while serving %s' %
- task.request.path)
+ self.logger.exception(
+ "Exception while serving %s" % task.request.path
+ )
if not task.wrote_header:
if self.adj.expose_tracebacks:
body = traceback.format_exc()
else:
- body = ('The server encountered an unexpected '
- 'internal server error')
+ body = (
+ "The server encountered an unexpected "
+ "internal server error"
+ )
req_version = request.version
req_headers = request.headers
request = self.parser_class(self.adj)
@@ -375,13 +372,12 @@ class HTTPChannel(wasyncore.dispatcher, object):
# HTTP 1.1 requirements
request.version = req_version
try:
- request.headers['CONNECTION'] = req_headers[
- 'CONNECTION']
+ request.headers["CONNECTION"] = req_headers["CONNECTION"]
except KeyError:
pass
task = self.error_task_class(self, request)
try:
- task.service() # must not fail
+ task.service() # must not fail
except ClientDisconnected:
task.close_on_finish = True
else:
diff --git a/waitress/compat.py b/waitress/compat.py
index 913ee5c..fe72a76 100644
--- a/waitress/compat.py
+++ b/waitress/compat.py
@@ -6,68 +6,79 @@ import warnings
try:
import urlparse
-except ImportError: # pragma: no cover
+except ImportError: # pragma: no cover
from urllib import parse as urlparse
try:
import fcntl
-except ImportError: # pragma: no cover
- fcntl = None # windows
+except ImportError: # pragma: no cover
+ fcntl = None # windows
# 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'
+WIN = platform.system() == "Windows"
-if PY3: # pragma: no cover
- string_types = str,
- integer_types = int,
- class_types = type,
+if PY3: # pragma: no cover
+ string_types = (str,)
+ integer_types = (int,)
+ class_types = (type,)
text_type = str
binary_type = bytes
long = int
else:
- string_types = basestring,
+ string_types = (basestring,)
integer_types = (int, long)
class_types = (type, types.ClassType)
text_type = unicode
binary_type = str
long = long
-if PY3: # pragma: no cover
+if PY3: # pragma: no cover
from urllib.parse import unquote_to_bytes
+
def unquote_bytes_to_wsgi(bytestring):
- return unquote_to_bytes(bytestring).decode('latin-1')
+ return unquote_to_bytes(bytestring).decode("latin-1")
+
+
else:
from urlparse import unquote as unquote_to_bytes
+
def unquote_bytes_to_wsgi(bytestring):
return unquote_to_bytes(bytestring)
-def text_(s, encoding='latin-1', errors='strict'):
+
+def text_(s, encoding="latin-1", errors="strict"):
""" If ``s`` is an instance of ``binary_type``, return
``s.decode(encoding, errors)``, otherwise return ``s``"""
if isinstance(s, binary_type):
return s.decode(encoding, errors)
- return s # pragma: no cover
+ return s # pragma: no cover
+
+
+if PY3: # pragma: no cover
-if PY3: # pragma: no cover
def tostr(s):
if isinstance(s, text_type):
- s = s.encode('latin-1')
- return str(s, 'latin-1', 'strict')
+ s = s.encode("latin-1")
+ return str(s, "latin-1", "strict")
def tobytes(s):
- return bytes(s, 'latin-1')
+ return bytes(s, "latin-1")
+
+
else:
tostr = str
def tobytes(s):
return s
-if PY3: # pragma: no cover
+
+if PY3: # pragma: no cover
import builtins
+
exec_ = getattr(builtins, "exec")
def reraise(tp, value, tb=None):
@@ -79,7 +90,8 @@ if PY3: # pragma: no cover
del builtins
-else: # pragma: no cover
+else: # pragma: no cover
+
def exec_(code, globs=None, locs=None):
"""Execute code in a namespace."""
if globs is None:
@@ -92,23 +104,25 @@ else: # pragma: no cover
locs = globs
exec("""exec code in globs, locs""")
- exec_("""def reraise(tp, value, tb=None):
+ exec_(
+ """def reraise(tp, value, tb=None):
raise tp, value, tb
-""")
+"""
+ )
try:
from StringIO import StringIO as NativeIO
-except ImportError: # pragma: no cover
+except ImportError: # pragma: no cover
from io import StringIO as NativeIO
try:
import httplib
-except ImportError: # pragma: no cover
+except ImportError: # pragma: no cover
from http import client as httplib
try:
MAXINT = sys.maxint
-except AttributeError: # pragma: no cover
+except AttributeError: # pragma: no cover
MAXINT = sys.maxsize
@@ -118,45 +132,48 @@ import socket
HAS_IPV6 = socket.has_ipv6
-if hasattr(socket, 'IPPROTO_IPV6') and hasattr(socket, 'IPV6_V6ONLY'):
+if hasattr(socket, "IPPROTO_IPV6") and hasattr(socket, "IPV6_V6ONLY"):
IPPROTO_IPV6 = socket.IPPROTO_IPV6
IPV6_V6ONLY = socket.IPV6_V6ONLY
-else: # pragma: no cover
+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
+ "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
-def set_nonblocking(fd): # pragma: no cover
+
+def set_nonblocking(fd): # pragma: no cover
if PY3 and sys.version_info[1] >= 5:
os.set_blocking(fd, False)
elif fcntl is None:
- raise RuntimeError('no fcntl module present')
+ raise RuntimeError("no fcntl module present")
else:
flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0)
flags = flags | os.O_NONBLOCK
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
+
if PY3:
ResourceWarning = ResourceWarning
else:
ResourceWarning = UserWarning
+
def qualname(cls):
if PY3:
return cls.__qualname__
return cls.__name__
+
try:
import thread
except ImportError:
# py3
import _thread as thread
-
diff --git a/waitress/parser.py b/waitress/parser.py
index 6ee700e..815a389 100644
--- a/waitress/parser.py
+++ b/waitress/parser.py
@@ -39,26 +39,29 @@ from waitress.utilities import (
BadRequest,
)
+
class ParsingError(Exception):
pass
+
class HTTPRequestParser(object):
"""A structure that collects the HTTP request.
Once the stream is completed, the instance is passed to
a server task constructor.
"""
- completed = False # Set once request is completed.
- empty = False # Set if no request was made.
+
+ completed = False # Set once request is completed.
+ empty = False # Set if no request was made.
expect_continue = False # client sent "Expect: 100-continue" header
- headers_finished = False # True when headers have been read
- header_plus = b''
+ headers_finished = False # True when headers have been read
+ header_plus = b""
chunked = False
content_length = 0
header_bytes_received = 0
body_bytes_received = 0
body_rcv = None
- version = '1.0'
+ version = "1.0"
error = None
connection_close = False
@@ -81,7 +84,7 @@ class HTTPRequestParser(object):
body have been received.
"""
if self.completed:
- return 0 # Can't consume any more.
+ return 0 # Can't consume any more.
datalen = len(data)
br = self.body_rcv
if br is None:
@@ -114,7 +117,8 @@ class HTTPRequestParser(object):
# is too large
if self.content_length >= max_body:
self.error = RequestEntityTooLarge(
- 'exceeds max_body of %s' % max_body)
+ "exceeds max_body of %s" % max_body
+ )
self.completed = True
self.headers_finished = True
return consumed
@@ -127,9 +131,10 @@ class HTTPRequestParser(object):
# on our own. we disregard the incoming(?) requests HTTP
# version and just use 1.0. IOW someone just sent garbage
# over the wire
- self.parse_header(b'GET / HTTP/1.0\n')
+ self.parse_header(b"GET / HTTP/1.0\n")
self.error = RequestHeaderFieldsTooLarge(
- 'exceeds max_header of %s' % max_header)
+ "exceeds max_header of %s" % max_header
+ )
self.completed = True
self.header_plus = s
return datalen
@@ -140,8 +145,7 @@ class HTTPRequestParser(object):
max_body = self.adj.max_request_body_size
if self.body_bytes_received >= max_body:
# this will only be raised during t-e: chunked requests
- self.error = RequestEntityTooLarge(
- 'exceeds max_body of %s' % max_body)
+ self.error = RequestEntityTooLarge("exceeds max_body of %s" % max_body)
self.completed = True
elif br.error:
# garbage in chunked encoding input probably
@@ -157,7 +161,7 @@ class HTTPRequestParser(object):
# TRANSFER_ENCODING header in parse_header, so this will
# appear to the client to be an entirely non-chunked HTTP
# request with a valid content-length.
- self.headers['CONTENT_LENGTH'] = str(br.__len__())
+ self.headers["CONTENT_LENGTH"] = str(br.__len__())
return consumed
def parse_header(self, header_plus):
@@ -165,33 +169,33 @@ class HTTPRequestParser(object):
Parses the header_plus block of text (the headers plus the
first line of the request).
"""
- index = header_plus.find(b'\n')
+ index = header_plus.find(b"\n")
if index >= 0:
first_line = header_plus[:index].rstrip()
- header = header_plus[index + 1:]
+ header = header_plus[index + 1 :]
else:
first_line = header_plus.rstrip()
- header = b''
+ header = b""
- self.first_line = first_line # for testing
+ self.first_line = first_line # for testing
lines = get_header_lines(header)
headers = self.headers
for line in lines:
- index = line.find(b':')
+ index = line.find(b":")
if index > 0:
key = line[:index]
- if b'_' in key:
+ if b"_" in key:
continue
- value = line[index + 1:].strip()
- key1 = tostr(key.upper().replace(b'-', b'_'))
+ value = line[index + 1 :].strip()
+ key1 = tostr(key.upper().replace(b"-", b"_"))
# If a header already exists, we append subsequent values
# seperated by a comma. Applications already need to handle
# the comma seperated values, as HTTP front ends might do
# the concatenation for you (behavior specified in RFC2616).
try:
- headers[key1] += tostr(b', ' + value)
+ headers[key1] += tostr(b", " + value)
except KeyError:
headers[key1] = tostr(value)
# else there's garbage in the headers?
@@ -202,35 +206,38 @@ class HTTPRequestParser(object):
command = tostr(command)
self.command = command
self.version = version
- (self.proxy_scheme,
- self.proxy_netloc,
- self.path,
- self.query, self.fragment) = split_uri(uri)
+ (
+ self.proxy_scheme,
+ self.proxy_netloc,
+ self.path,
+ self.query,
+ self.fragment,
+ ) = split_uri(uri)
self.url_scheme = self.adj.url_scheme
- connection = headers.get('CONNECTION', '')
+ connection = headers.get("CONNECTION", "")
- if version == '1.0':
- if connection.lower() != 'keep-alive':
+ if version == "1.0":
+ if connection.lower() != "keep-alive":
self.connection_close = True
- if version == '1.1':
+ if version == "1.1":
# since the server buffers data from chunked transfers and clients
# never need to deal with chunked requests, downstream clients
# should not see the HTTP_TRANSFER_ENCODING header; we pop it
# here
- te = headers.pop('TRANSFER_ENCODING', '')
- if te.lower() == 'chunked':
+ te = headers.pop("TRANSFER_ENCODING", "")
+ if te.lower() == "chunked":
self.chunked = True
buf = OverflowableBuffer(self.adj.inbuf_overflow)
self.body_rcv = ChunkedReceiver(buf)
- expect = headers.get('EXPECT', '').lower()
- self.expect_continue = expect == '100-continue'
- if connection.lower() == 'close':
+ expect = headers.get("EXPECT", "").lower()
+ self.expect_continue = expect == "100-continue"
+ if connection.lower() == "close":
self.connection_close = True
if not self.chunked:
try:
- cl = int(headers.get('CONTENT_LENGTH', 0))
+ cl = int(headers.get("CONTENT_LENGTH", 0))
except ValueError:
cl = 0
self.content_length = cl
@@ -250,11 +257,12 @@ class HTTPRequestParser(object):
if body_rcv is not None:
body_rcv.getbuf().close()
+
def split_uri(uri):
# urlsplit handles byte input by returning bytes on py3, so
# scheme, netloc, path, query, and fragment are bytes
- scheme = netloc = path = query = fragment = b''
+ scheme = netloc = path = query = fragment = b""
# urlsplit below will treat this as a scheme-less netloc, thereby losing
# the original intent of the request. Here we shamelessly stole 4 lines of
@@ -263,19 +271,19 @@ def split_uri(uri):
# https://github.com/python/cpython/blob/8c9e9b0cd5b24dfbf1424d1f253d02de80e8f5ef/Lib/urllib/parse.py#L465-L468
# and https://github.com/Pylons/waitress/issues/260
- if uri[:2] == b'//':
+ if uri[:2] == b"//":
path = uri
- if b'#' in path:
- path, fragment = path.split(b'#', 1)
+ if b"#" in path:
+ path, fragment = path.split(b"#", 1)
- if b'?' in path:
- path, query = path.split(b'?', 1)
+ if b"?" in path:
+ path, query = path.split(b"?", 1)
else:
try:
scheme, netloc, path, query, fragment = urlparse.urlsplit(uri)
except UnicodeError:
- raise ParsingError('Bad URI')
+ raise ParsingError("Bad URI")
return (
tostr(scheme),
@@ -285,14 +293,15 @@ def split_uri(uri):
tostr(fragment),
)
+
def get_header_lines(header):
"""
Splits the header into lines, putting multi-line headers together.
"""
r = []
- lines = header.split(b'\n')
+ lines = header.split(b"\n")
for line in lines:
- if line.startswith((b' ', b'\t')):
+ if line.startswith((b" ", b"\t")):
if not r:
# https://corte.si/posts/code/pathod/pythonservers/index.html
raise ParsingError('Malformed header line "%s"' % tostr(line))
@@ -301,19 +310,21 @@ def get_header_lines(header):
r.append(line)
return r
+
first_line_re = re.compile(
- b'([^ ]+) '
- b'((?:[^ :?#]+://[^ ?#/]*(?:[0-9]{1,5})?)?[^ ]+)'
- b'(( HTTP/([0-9.]+))$|$)'
+ b"([^ ]+) "
+ b"((?:[^ :?#]+://[^ ?#/]*(?:[0-9]{1,5})?)?[^ ]+)"
+ b"(( HTTP/([0-9.]+))$|$)"
)
+
def crack_first_line(line):
m = first_line_re.match(line)
if m is not None and m.end() == len(line):
if m.group(3):
version = m.group(5)
else:
- version = b''
+ version = b""
method = m.group(1)
# the request methods that are currently defined are all uppercase:
@@ -330,4 +341,4 @@ def crack_first_line(line):
uri = m.group(2)
return method, uri, version
else:
- return b'', b'', b''
+ return b"", b"", b""
diff --git a/waitress/proxy_headers.py b/waitress/proxy_headers.py
index 132fea8..1df8b8e 100644
--- a/waitress/proxy_headers.py
+++ b/waitress/proxy_headers.py
@@ -3,16 +3,18 @@ from collections import namedtuple
from .utilities import logger, undquote, BadRequest
-PROXY_HEADERS = frozenset({
- 'X_FORWARDED_FOR',
- 'X_FORWARDED_HOST',
- 'X_FORWARDED_PROTO',
- 'X_FORWARDED_PORT',
- 'X_FORWARDED_BY',
- 'FORWARDED',
-})
+PROXY_HEADERS = frozenset(
+ {
+ "X_FORWARDED_FOR",
+ "X_FORWARDED_HOST",
+ "X_FORWARDED_PROTO",
+ "X_FORWARDED_PORT",
+ "X_FORWARDED_BY",
+ "FORWARDED",
+ }
+)
-Forwarded = namedtuple('Forwarded', ['by', 'for_', 'host', 'proto'])
+Forwarded = namedtuple("Forwarded", ["by", "for_", "host", "proto"])
class MalformedProxyHeader(Exception):
@@ -34,8 +36,8 @@ def proxy_headers_middleware(
):
def translate_proxy_headers(environ, start_response):
untrusted_headers = PROXY_HEADERS
- remote_peer = environ['REMOTE_ADDR']
- if trusted_proxy == '*' or remote_peer == trusted_proxy:
+ remote_peer = environ["REMOTE_ADDR"]
+ if trusted_proxy == "*" or remote_peer == trusted_proxy:
try:
untrusted_headers = parse_proxy_headers(
environ,
@@ -46,28 +48,27 @@ def proxy_headers_middleware(
except MalformedProxyHeader as ex:
logger.warning(
'Malformed proxy header "%s" from "%s": %s value: %s',
- ex.header, remote_peer, ex.reason, ex.value)
+ ex.header,
+ remote_peer,
+ ex.reason,
+ ex.value,
+ )
error = BadRequest('Header "{0}" malformed.'.format(ex.header))
return error.wsgi_response(environ, start_response)
# Clear out the untrusted proxy headers
if clear_untrusted:
clear_untrusted_headers(
- environ,
- untrusted_headers,
- log_warning=log_untrusted,
- logger=logger,
+ environ, untrusted_headers, log_warning=log_untrusted, logger=logger,
)
return app(environ, start_response)
+
return translate_proxy_headers
def parse_proxy_headers(
- environ,
- trusted_proxy_count,
- trusted_proxy_headers,
- logger=logger,
+ environ, trusted_proxy_count, trusted_proxy_headers, logger=logger,
):
if trusted_proxy_headers is None:
trusted_proxy_headers = set()
@@ -78,14 +79,9 @@ def parse_proxy_headers(
untrusted_headers = set(PROXY_HEADERS)
def raise_for_multiple_values():
- raise ValueError(
- 'Unspecified behavior for multiple values found in header',
- )
+ raise ValueError("Unspecified behavior for multiple values found in header",)
- if (
- "x-forwarded-for" in trusted_proxy_headers
- and "HTTP_X_FORWARDED_FOR" in environ
- ):
+ if "x-forwarded-for" in trusted_proxy_headers and "HTTP_X_FORWARDED_FOR" in environ:
try:
forwarded_for = []
@@ -110,9 +106,7 @@ def parse_proxy_headers(
untrusted_headers.remove("X_FORWARDED_FOR")
except Exception as ex:
raise MalformedProxyHeader(
- "X-Forwarded-For",
- str(ex),
- environ['HTTP_X_FORWARDED_FOR'],
+ "X-Forwarded-For", str(ex), environ["HTTP_X_FORWARDED_FOR"],
)
if (
@@ -133,35 +127,29 @@ def parse_proxy_headers(
untrusted_headers.remove("X_FORWARDED_HOST")
except Exception as ex:
raise MalformedProxyHeader(
- "X-Forwarded-Host",
- str(ex),
- environ['HTTP_X_FORWARDED_HOST'],
+ "X-Forwarded-Host", str(ex), environ["HTTP_X_FORWARDED_HOST"],
)
if "x-forwarded-proto" in trusted_proxy_headers:
try:
forwarded_proto = undquote(environ.get("HTTP_X_FORWARDED_PROTO", ""))
- if ',' in forwarded_proto:
+ if "," in forwarded_proto:
raise_for_multiple_values()
untrusted_headers.remove("X_FORWARDED_PROTO")
except Exception as ex:
raise MalformedProxyHeader(
- "X-Forwarded-Proto",
- str(ex),
- environ['HTTP_X_FORWARDED_PROTO'],
+ "X-Forwarded-Proto", str(ex), environ["HTTP_X_FORWARDED_PROTO"],
)
if "x-forwarded-port" in trusted_proxy_headers:
try:
forwarded_port = undquote(environ.get("HTTP_X_FORWARDED_PORT", ""))
- if ',' in forwarded_port:
+ if "," in forwarded_port:
raise_for_multiple_values()
untrusted_headers.remove("X_FORWARDED_PORT")
except Exception as ex:
raise MalformedProxyHeader(
- "X-Forwarded-Port",
- str(ex),
- environ['HTTP_X_FORWARDED_PORT'],
+ "X-Forwarded-Port", str(ex), environ["HTTP_X_FORWARDED_PORT"],
)
if "x-forwarded-by" in trusted_proxy_headers:
@@ -197,10 +185,10 @@ def parse_proxy_headers(
raise ValueError('Invalid forwarded-pair missing "="')
if token.strip() != token:
- raise ValueError('Token may not be surrounded by whitespace')
+ raise ValueError("Token may not be surrounded by whitespace")
if value.strip() != value:
- raise ValueError('Value may not be surrounded by whitespace')
+ raise ValueError("Value may not be surrounded by whitespace")
if token == "by":
forwarded_by = undquote(value)
@@ -224,7 +212,7 @@ def parse_proxy_headers(
)
except Exception as ex:
raise MalformedProxyHeader(
- "Forwarded", str(ex), environ['HTTP_FORWARDED'],
+ "Forwarded", str(ex), environ["HTTP_FORWARDED"],
)
proxies = proxies[-trusted_proxy_count:]
@@ -294,17 +282,11 @@ def parse_proxy_headers(
environ["HTTP_HOST"] = "{}:{}".format(
forwarded_host, forwarded_port
)
- elif (
- forwarded_port == "80"
- and environ["wsgi.url_scheme"] != "http"
- ):
+ elif forwarded_port == "80" and environ["wsgi.url_scheme"] != "http":
environ["HTTP_HOST"] = "{}:{}".format(
forwarded_host, forwarded_port
)
- elif (
- forwarded_port == "443"
- and environ["wsgi.url_scheme"] != "https"
- ):
+ elif forwarded_port == "443" and environ["wsgi.url_scheme"] != "https":
environ["HTTP_HOST"] = "{}:{}".format(
forwarded_host, forwarded_port
)
@@ -336,7 +318,7 @@ def clear_untrusted_headers(
untrusted_headers_removed = [
header
for header in untrusted_headers
- if environ.pop('HTTP_' + header, False) is not False
+ if environ.pop("HTTP_" + header, False) is not False
]
if log_warning and untrusted_headers_removed:
diff --git a/waitress/receiver.py b/waitress/receiver.py
index 594ae97..380b8fd 100644
--- a/waitress/receiver.py
+++ b/waitress/receiver.py
@@ -18,6 +18,7 @@ from waitress.utilities import find_double_newline
from waitress.utilities import BadRequest
+
class FixedStreamReceiver(object):
# See IStreamConsumer
@@ -30,12 +31,12 @@ class FixedStreamReceiver(object):
def __len__(self):
return self.buf.__len__()
-
+
def received(self, data):
- 'See IStreamConsumer'
+ "See IStreamConsumer"
rm = self.remain
if rm < 1:
- self.completed = True # Avoid any chance of spinning
+ self.completed = True # Avoid any chance of spinning
return 0
datalen = len(data)
if rm <= datalen:
@@ -54,12 +55,13 @@ class FixedStreamReceiver(object):
def getbuf(self):
return self.buf
+
class ChunkedReceiver(object):
chunk_remainder = 0
- control_line = b''
+ control_line = b""
all_chunks_received = False
- trailer = b''
+ trailer = b""
completed = False
error = None
@@ -89,28 +91,27 @@ class ChunkedReceiver(object):
elif not self.all_chunks_received:
# Receive a control line.
s = self.control_line + s
- pos = s.find(b'\n')
+ pos = s.find(b"\n")
if pos < 0:
# Control line not finished.
self.control_line = s
- s = ''
+ s = ""
else:
# Control line finished.
line = s[:pos]
- s = s[pos + 1:]
- self.control_line = b''
+ s = s[pos + 1 :]
+ self.control_line = b""
line = line.strip()
if line:
# Begin a new chunk.
- semi = line.find(b';')
+ semi = line.find(b";")
if semi >= 0:
# discard extension info.
line = line[:semi]
try:
- sz = int(line.strip(), 16) # hexadecimal
- except ValueError: # garbage in input
- self.error = BadRequest(
- 'garbage in chunked encoding input')
+ sz = int(line.strip(), 16) # hexadecimal
+ except ValueError: # garbage in input
+ self.error = BadRequest("garbage in chunked encoding input")
sz = 0
if sz > 0:
# Start a new chunk.
@@ -122,11 +123,11 @@ class ChunkedReceiver(object):
else:
# Receive the trailer.
trailer = self.trailer + s
- if trailer.startswith(b'\r\n'):
+ if trailer.startswith(b"\r\n"):
# No trailer.
self.completed = True
return orig_size - (len(trailer) - 2)
- elif trailer.startswith(b'\n'):
+ elif trailer.startswith(b"\n"):
# No trailer.
self.completed = True
return orig_size - (len(trailer) - 1)
@@ -134,7 +135,7 @@ class ChunkedReceiver(object):
if pos < 0:
# Trailer not finished.
self.trailer = trailer
- s = b''
+ s = b""
else:
# Finished the trailer.
self.completed = True
diff --git a/waitress/runner.py b/waitress/runner.py
index 863ab7f..2495084 100644
--- a/waitress/runner.py
+++ b/waitress/runner.py
@@ -172,7 +172,8 @@ Tuning options:
"""
-RUNNER_PATTERN = re.compile(r"""
+RUNNER_PATTERN = re.compile(
+ r"""
^
(?P<module>
[a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)*
@@ -182,13 +183,17 @@ RUNNER_PATTERN = re.compile(r"""
[a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)*
)
$
- """, re.I | re.X)
+ """,
+ re.I | re.X,
+)
+
def match(obj_name):
matches = RUNNER_PATTERN.match(obj_name)
if not matches:
raise ValueError("Malformed application '{0}'".format(obj_name))
- return matches.group('module'), matches.group('object')
+ return matches.group("module"), matches.group("object")
+
def resolve(module_name, object_name):
"""Resolve a named object in a module."""
@@ -202,34 +207,35 @@ def resolve(module_name, object_name):
# but I've yet to go over the commits. I know, however, that the NEWS
# file makes no mention of such a change to the behaviour of
# ``__import__``.
- segments = [str(segment) for segment in object_name.split('.')]
+ segments = [str(segment) for segment in object_name.split(".")]
obj = __import__(module_name, fromlist=segments[:1])
for segment in segments:
obj = getattr(obj, segment)
return obj
-def show_help(stream, name, error=None): # pragma: no cover
+
+def show_help(stream, name, error=None): # pragma: no cover
if error is not None:
- print('Error: {0}\n'.format(error), file=stream)
+ print("Error: {0}\n".format(error), file=stream)
print(HELP.format(name), file=stream)
+
def show_exception(stream):
exc_type, exc_value = sys.exc_info()[:2]
- args = getattr(exc_value, 'args', None)
+ args = getattr(exc_value, "args", None)
print(
- (
- 'There was an exception ({0}) importing your module.\n'
- ).format(
+ ("There was an exception ({0}) importing your module.\n").format(
exc_type.__name__,
),
- file=stream
+ file=stream,
)
if args:
- print('It had these arguments: ', file=stream)
+ print("It had these arguments: ", file=stream)
for idx, arg in enumerate(args, start=1):
- print('{0}. {1}\n'.format(idx, arg), file=stream)
+ print("{0}. {1}\n".format(idx, arg), file=stream)
else:
- print('It had no arguments.', file=stream)
+ print("It had no arguments.", file=stream)
+
def run(argv=sys.argv, _serve=serve):
"""Command line runner."""
@@ -241,12 +247,12 @@ def run(argv=sys.argv, _serve=serve):
show_help(sys.stderr, name, str(exc))
return 1
- if kw['help']:
+ if kw["help"]:
show_help(sys.stdout, name)
return 0
if len(args) != 1:
- show_help(sys.stderr, name, 'Specify one application only')
+ show_help(sys.stderr, name, "Specify one application only")
return 1
try:
@@ -270,11 +276,11 @@ def run(argv=sys.argv, _serve=serve):
show_help(sys.stderr, name, "Bad object name '{0}'".format(obj_name))
show_exception(sys.stderr)
return 1
- if kw['call']:
+ if kw["call"]:
app = app()
# These arguments are specific to the runner, not waitress itself.
- del kw['call'], kw['help']
+ del kw["call"], kw["help"]
_serve(app, **kw)
return 0
diff --git a/waitress/server.py b/waitress/server.py
index 307c377..ae56699 100644
--- a/waitress/server.py
+++ b/waitress/server.py
@@ -26,17 +26,19 @@ from waitress.utilities import cleanup_unix_socket
from waitress.compat import (
IPPROTO_IPV6,
IPV6_V6ONLY,
- )
+)
from . import wasyncore
from .proxy_headers import proxy_headers_middleware
-def create_server(application,
- map=None,
- _start=True, # test shim
- _sock=None, # test shim
- _dispatcher=None, # test shim
- **kw # adjustments
- ):
+
+def create_server(
+ application,
+ map=None,
+ _start=True, # test shim
+ _sock=None, # test shim
+ _dispatcher=None, # test shim
+ **kw # adjustments
+):
"""
if __name__ == '__main__':
server = create_server(app)
@@ -45,11 +47,11 @@ def create_server(application,
if application is None:
raise ValueError(
'The "app" passed to ``create_server`` was ``None``. You forgot '
- 'to return a WSGI app within your application.'
- )
+ "to return a WSGI app within your application."
+ )
adj = Adjustments(**kw)
- if map is None: # pragma: nocover
+ if map is None: # pragma: nocover
map = {}
dispatcher = _dispatcher
@@ -57,7 +59,7 @@ def create_server(application,
dispatcher = ThreadedTaskDispatcher()
dispatcher.set_thread_count(adj.threads)
- if adj.unix_socket and hasattr(socket, 'AF_UNIX'):
+ if adj.unix_socket and hasattr(socket, "AF_UNIX"):
sockinfo = (socket.AF_UNIX, socket.SOCK_STREAM, None, None)
return UnixWSGIServer(
application,
@@ -66,7 +68,8 @@ def create_server(application,
_sock,
dispatcher=dispatcher,
adj=adj,
- sockinfo=sockinfo)
+ sockinfo=sockinfo,
+ )
effective_listen = []
last_serv = None
@@ -82,8 +85,11 @@ def create_server(application,
_sock,
dispatcher=dispatcher,
adj=adj,
- sockinfo=sockinfo)
- effective_listen.append((last_serv.effective_host, last_serv.effective_port))
+ sockinfo=sockinfo,
+ )
+ effective_listen.append(
+ (last_serv.effective_host, last_serv.effective_port)
+ )
for sock in adj.sockets:
sockinfo = (sock.family, sock.type, sock.proto, sock.getsockname())
@@ -96,9 +102,12 @@ def create_server(application,
dispatcher=dispatcher,
adj=adj,
bind_socket=False,
- sockinfo=sockinfo)
- effective_listen.append((last_serv.effective_host, last_serv.effective_port))
- elif hasattr(socket, 'AF_UNIX') and sock.family == socket.AF_UNIX:
+ sockinfo=sockinfo,
+ )
+ effective_listen.append(
+ (last_serv.effective_host, last_serv.effective_port)
+ )
+ elif hasattr(socket, "AF_UNIX") and sock.family == socket.AF_UNIX:
last_serv = UnixWSGIServer(
application,
map,
@@ -107,8 +116,11 @@ def create_server(application,
dispatcher=dispatcher,
adj=adj,
bind_socket=False,
- sockinfo=sockinfo)
- effective_listen.append((last_serv.effective_host, last_serv.effective_port))
+ sockinfo=sockinfo,
+ )
+ effective_listen.append(
+ (last_serv.effective_host, last_serv.effective_port)
+ )
# We are running a single server, so we can just return the last server,
# saves us from having to create one more object
@@ -126,25 +138,22 @@ def create_server(application,
# the serve() API to call .run() which starts the wasyncore loop, and catches
# SystemExit/KeyboardInterrupt so that it can atempt to cleanly shut down.
class MultiSocketServer(object):
- asyncore = wasyncore # test shim
-
- def __init__(self,
- map=None,
- adj=None,
- effective_listen=None,
- dispatcher=None,
- ):
+ asyncore = wasyncore # test shim
+
+ def __init__(
+ self, map=None, adj=None, effective_listen=None, dispatcher=None,
+ ):
self.adj = adj
self.map = map
self.effective_listen = effective_listen
self.task_dispatcher = dispatcher
- def print_listen(self, format_str): # pragma: nocover
+ def print_listen(self, format_str): # pragma: nocover
for l in self.effective_listen:
l = list(l)
- if ':' in l[0]:
- l[0] = '[{}]'.format(l[0])
+ if ":" in l[0]:
+ l[0] = "[{}]".format(l[0])
print(format_str.format(*l))
@@ -167,20 +176,21 @@ class BaseWSGIServer(wasyncore.dispatcher, object):
channel_class = HTTPChannel
next_channel_cleanup = 0
- socketmod = socket # test shim
- asyncore = wasyncore # test shim
-
- def __init__(self,
- application,
- map=None,
- _start=True, # test shim
- _sock=None, # test shim
- dispatcher=None, # dispatcher
- adj=None, # adjustments
- sockinfo=None, # opaque object
- bind_socket=True,
- **kw
- ):
+ socketmod = socket # test shim
+ asyncore = wasyncore # test shim
+
+ def __init__(
+ self,
+ application,
+ map=None,
+ _start=True, # test shim
+ _sock=None, # test shim
+ dispatcher=None, # dispatcher
+ adj=None, # adjustments
+ sockinfo=None, # opaque object
+ bind_socket=True,
+ **kw
+ ):
if adj is None:
adj = Adjustments(**kw)
@@ -220,7 +230,7 @@ class BaseWSGIServer(wasyncore.dispatcher, object):
self.asyncore.dispatcher.__init__(self, _sock, map=map)
if _sock is None:
self.create_socket(self.family, self.socktype)
- if self.family == socket.AF_INET6: # pragma: nocover
+ if self.family == socket.AF_INET6: # pragma: nocover
self.socket.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, 1)
self.set_reuse_addr()
@@ -235,25 +245,25 @@ class BaseWSGIServer(wasyncore.dispatcher, object):
self.accept_connections()
def bind_server_socket(self):
- raise NotImplementedError # pragma: no cover
+ raise NotImplementedError # pragma: no cover
def get_server_name(self, ip):
"""Given an IP or hostname, try to determine the server name."""
if not ip:
- raise ValueError('Requires an IP to get the server name')
+ raise ValueError("Requires an IP to get the server name")
server_name = str(ip)
# If we are bound to all IP's, just return the current hostname, only
# fall-back to "localhost" if we fail to get the hostname
- if server_name == '0.0.0.0' or server_name == '::':
+ if server_name == "0.0.0.0" or server_name == "::":
try:
return str(self.socketmod.gethostname())
except (socket.error, UnicodeDecodeError): # pragma: no cover
# We also deal with UnicodeDecodeError in case of Windows with
# non-ascii hostname
- return 'localhost'
+ return "localhost"
# Now let's try and convert the IP address to a proper hostname
try:
@@ -265,17 +275,17 @@ class BaseWSGIServer(wasyncore.dispatcher, object):
# If it contains an IPv6 literal, make sure to surround it with
# brackets
- if ':' in server_name and '[' not in server_name:
- server_name = '[{}]'.format(server_name)
+ if ":" in server_name and "[" not in server_name:
+ server_name = "[{}]".format(server_name)
return server_name
def getsockname(self):
- raise NotImplementedError # pragma: no cover
+ raise NotImplementedError # pragma: no cover
def accept_connections(self):
self.accepting = True
- self.socket.listen(self.adj.backlog) # Get around asyncore NT limit
+ self.socket.listen(self.adj.backlog) # Get around asyncore NT limit
def add_task(self, task):
self.task_dispatcher.add_task(task)
@@ -285,7 +295,7 @@ class BaseWSGIServer(wasyncore.dispatcher, object):
if now >= self.next_channel_cleanup:
self.next_channel_cleanup = now + self.adj.cleanup_interval
self.maintenance(now)
- return (self.accepting and len(self._map) < self.adj.connection_limit)
+ return self.accepting and len(self._map) < self.adj.connection_limit
def writable(self):
return False
@@ -308,8 +318,7 @@ class BaseWSGIServer(wasyncore.dispatcher, object):
# address family is unknown. We don't want the whole server
# to shut down because of this.
if self.adj.log_socket_errors:
- self.logger.warning('server accept() threw an exception',
- exc_info=True)
+ self.logger.warning("server accept() threw an exception", exc_info=True)
return
self.set_socket_options(conn)
addr = self.fix_addr(addr)
@@ -345,7 +354,7 @@ class BaseWSGIServer(wasyncore.dispatcher, object):
if (not channel.requests) and channel.last_activity < cutoff:
channel.will_close = True
- def print_listen(self, format_str): # pragma: nocover
+ def print_listen(self, format_str): # pragma: nocover
print(format_str.format(self.effective_host, self.effective_port))
def close(self):
@@ -354,7 +363,6 @@ class BaseWSGIServer(wasyncore.dispatcher, object):
class TcpWSGIServer(BaseWSGIServer):
-
def bind_server_socket(self):
(_, _, _, sockaddr) = self.sockinfo
self.bind(sockaddr)
@@ -362,10 +370,9 @@ class TcpWSGIServer(BaseWSGIServer):
def getsockname(self):
try:
return self.socketmod.getnameinfo(
- self.socket.getsockname(),
- self.socketmod.NI_NUMERICSERV
+ self.socket.getsockname(), self.socketmod.NI_NUMERICSERV
)
- except: # pragma: no cover
+ except: # pragma: no cover
# This only happens on Linux because a DNS issue is considered a
# temporary failure that will raise (even when NI_NAMEREQD is not
# set). Instead we try again, but this time we just ask for the
@@ -373,7 +380,7 @@ class TcpWSGIServer(BaseWSGIServer):
# better than nothing.
return self.socketmod.getnameinfo(
self.socket.getsockname(),
- self.socketmod.NI_NUMERICHOST | self.socketmod.NI_NUMERICSERV
+ self.socketmod.NI_NUMERICHOST | self.socketmod.NI_NUMERICSERV,
)
def set_socket_options(self, conn):
@@ -381,19 +388,20 @@ class TcpWSGIServer(BaseWSGIServer):
conn.setsockopt(level, optname, value)
-if hasattr(socket, 'AF_UNIX'):
+if hasattr(socket, "AF_UNIX"):
class UnixWSGIServer(BaseWSGIServer):
-
- def __init__(self,
- application,
- map=None,
- _start=True, # test shim
- _sock=None, # test shim
- dispatcher=None, # dispatcher
- adj=None, # adjustments
- sockinfo=None, # opaque object
- **kw):
+ def __init__(
+ self,
+ application,
+ map=None,
+ _start=True, # test shim
+ _sock=None, # test shim
+ dispatcher=None, # dispatcher
+ adj=None, # adjustments
+ sockinfo=None, # opaque object
+ **kw
+ ):
if sockinfo is None:
sockinfo = (socket.AF_UNIX, socket.SOCK_STREAM, None, None)
@@ -405,7 +413,8 @@ if hasattr(socket, 'AF_UNIX'):
dispatcher=dispatcher,
adj=adj,
sockinfo=sockinfo,
- **kw)
+ **kw
+ )
def bind_server_socket(self):
cleanup_unix_socket(self.adj.unix_socket)
@@ -414,13 +423,14 @@ if hasattr(socket, 'AF_UNIX'):
os.chmod(self.adj.unix_socket, self.adj.unix_socket_perms)
def getsockname(self):
- return ('unix', self.socket.getsockname())
+ return ("unix", self.socket.getsockname())
def fix_addr(self, addr):
- return ('localhost', None)
+ return ("localhost", None)
def get_server_name(self, ip):
- return 'localhost'
+ return "localhost"
+
# Compatibility alias.
WSGIServer = TcpWSGIServer
diff --git a/waitress/task.py b/waitress/task.py
index 78d8b83..6866372 100644
--- a/waitress/task.py
+++ b/waitress/task.py
@@ -27,25 +27,28 @@ from .utilities import (
)
rename_headers = { # or keep them without the HTTP_ prefix added
- 'CONTENT_LENGTH': 'CONTENT_LENGTH',
- 'CONTENT_TYPE': 'CONTENT_TYPE',
+ "CONTENT_LENGTH": "CONTENT_LENGTH",
+ "CONTENT_TYPE": "CONTENT_TYPE",
}
-hop_by_hop = frozenset((
- 'connection',
- 'keep-alive',
- 'proxy-authenticate',
- 'proxy-authorization',
- 'te',
- 'trailers',
- 'transfer-encoding',
- 'upgrade'
-))
+hop_by_hop = frozenset(
+ (
+ "connection",
+ "keep-alive",
+ "proxy-authenticate",
+ "proxy-authorization",
+ "te",
+ "trailers",
+ "transfer-encoding",
+ "upgrade",
+ )
+)
class ThreadedTaskDispatcher(object):
"""A Task Dispatcher that creates a thread for each task.
"""
+
stop_count = 0 # Number of threads that will stop soon.
active_count = 0 # Number of currently active threads
logger = logger
@@ -59,7 +62,7 @@ class ThreadedTaskDispatcher(object):
self.thread_exit_cv = threading.Condition(self.lock)
def start_new_thread(self, target, args):
- t = threading.Thread(target=target, name='waitress', args=args)
+ t = threading.Thread(target=target, name="waitress", args=args)
t.daemon = True
t.start()
@@ -84,8 +87,7 @@ class ThreadedTaskDispatcher(object):
try:
task.service()
except BaseException:
- self.logger.exception(
- 'Exception when servicing %r', task)
+ self.logger.exception("Exception when servicing %r", task)
def set_thread_count(self, count):
with self.lock:
@@ -111,11 +113,11 @@ class ThreadedTaskDispatcher(object):
self.queue.append(task)
self.queue_cv.notify()
queue_size = len(self.queue)
- idle_threads = (
- len(self.threads) - self.stop_count - self.active_count)
+ idle_threads = len(self.threads) - self.stop_count - self.active_count
if queue_size > idle_threads:
self.queue_logger.warning(
- "Task queue depth is %d", queue_size - idle_threads)
+ "Task queue depth is %d", queue_size - idle_threads
+ )
def shutdown(self, cancel_pending=True, timeout=5):
self.set_thread_count(0)
@@ -125,16 +127,14 @@ class ThreadedTaskDispatcher(object):
with self.lock:
while threads:
if time.time() >= expiration:
- self.logger.warning(
- "%d thread(s) still running", len(threads))
+ self.logger.warning("%d thread(s) still running", len(threads))
break
self.thread_exit_cv.wait(0.1)
if cancel_pending:
# Cancel remaining tasks.
queue = self.queue
if len(queue) > 0:
- self.logger.warning(
- "Canceling %d pending task(s)", len(queue))
+ self.logger.warning("Canceling %d pending task(s)", len(queue))
while queue:
task = queue.popleft()
task.cancel()
@@ -142,9 +142,10 @@ class ThreadedTaskDispatcher(object):
return True
return False
+
class Task(object):
close_on_finish = False
- status = '200 OK'
+ status = "200 OK"
wrote_header = False
start_time = 0
content_length = None
@@ -160,9 +161,9 @@ class Task(object):
self.request = request
self.response_headers = []
version = request.version
- if version not in ('1.0', '1.1'):
+ if version not in ("1.0", "1.1"):
# fall back to a version we support.
- version = '1.0'
+ version = "1.0"
self.version = version
def service(self):
@@ -180,15 +181,16 @@ class Task(object):
@property
def has_body(self):
- return not (self.status.startswith('1') or
- self.status.startswith('204') or
- self.status.startswith('304')
- )
+ return not (
+ self.status.startswith("1")
+ or self.status.startswith("204")
+ or self.status.startswith("304")
+ )
def build_response_header(self):
version = self.version
# Figure out whether the connection should be closed.
- connection = self.request.headers.get('CONNECTION', '').lower()
+ connection = self.request.headers.get("CONNECTION", "").lower()
response_headers = []
content_length_header = None
date_header = None
@@ -196,53 +198,49 @@ class Task(object):
connection_close_header = None
for (headername, headerval) in self.response_headers:
- headername = '-'.join(
- [x.capitalize() for x in headername.split('-')]
- )
+ headername = "-".join([x.capitalize() for x in headername.split("-")])
- if headername == 'Content-Length':
+ if headername == "Content-Length":
if self.has_body:
content_length_header = headerval
else:
continue # pragma: no cover
- if headername == 'Date':
+ if headername == "Date":
date_header = headerval
- if headername == 'Server':
+ if headername == "Server":
server_header = headerval
- if headername == 'Connection':
+ if headername == "Connection":
connection_close_header = headerval.lower()
# replace with properly capitalized version
response_headers.append((headername, headerval))
if (
- content_length_header is None and
- self.content_length is not None and
- self.has_body
+ content_length_header is None
+ and self.content_length is not None
+ and self.has_body
):
content_length_header = str(self.content_length)
- response_headers.append(
- ('Content-Length', content_length_header)
- )
+ response_headers.append(("Content-Length", content_length_header))
def close_on_finish():
if connection_close_header is None:
- response_headers.append(('Connection', 'close'))
+ response_headers.append(("Connection", "close"))
self.close_on_finish = True
- if version == '1.0':
- if connection == 'keep-alive':
+ if version == "1.0":
+ if connection == "keep-alive":
if not content_length_header:
close_on_finish()
else:
- response_headers.append(('Connection', 'Keep-Alive'))
+ response_headers.append(("Connection", "Keep-Alive"))
else:
close_on_finish()
- elif version == '1.1':
- if connection == 'close':
+ elif version == "1.1":
+ if connection == "close":
close_on_finish()
if not content_length_header:
@@ -250,7 +248,7 @@ class Task(object):
# for any response with a status code of 1xx, 204 or 304.
if self.has_body:
- response_headers.append(('Transfer-Encoding', 'chunked'))
+ response_headers.append(("Transfer-Encoding", "chunked"))
self.chunked_response = True
if not self.close_on_finish:
@@ -258,7 +256,7 @@ class Task(object):
# under HTTP 1.1 keep-alive is default, no need to set the header
else:
- raise AssertionError('neither HTTP/1.0 or HTTP/1.1')
+ raise AssertionError("neither HTTP/1.0 or HTTP/1.1")
# Set the Server and Date field, if not yet specified. This is needed
# if the server is used as a proxy.
@@ -266,23 +264,24 @@ class Task(object):
if not server_header:
if ident:
- response_headers.append(('Server', ident))
+ response_headers.append(("Server", ident))
else:
- response_headers.append(('Via', ident or 'waitress'))
+ response_headers.append(("Via", ident or "waitress"))
if not date_header:
- response_headers.append(('Date', build_http_date(self.start_time)))
+ response_headers.append(("Date", build_http_date(self.start_time)))
self.response_headers = response_headers
- first_line = 'HTTP/%s %s' % (self.version, self.status)
+ first_line = "HTTP/%s %s" % (self.version, self.status)
# NB: sorting headers needs to preserve same-named-header order
# as per RFC 2616 section 4.2; thus the key=lambda x: x[0] here;
# rely on stable sort to keep relative position of same-named headers
- next_lines = ['%s: %s' % hv for hv in sorted(
- self.response_headers, key=lambda x: x[0])]
+ next_lines = [
+ "%s: %s" % hv for hv in sorted(self.response_headers, key=lambda x: x[0])
+ ]
lines = [first_line] + next_lines
- res = '%s\r\n\r\n' % '\r\n'.join(lines)
+ res = "%s\r\n\r\n" % "\r\n".join(lines)
return tobytes(res)
@@ -290,7 +289,7 @@ class Task(object):
response_headers = []
for header_name, header_value in self.response_headers:
- if header_name.lower() == 'content-length':
+ if header_name.lower() == "content-length":
continue # pragma: nocover
response_headers.append((header_name, header_value))
@@ -301,15 +300,14 @@ class Task(object):
def finish(self):
if not self.wrote_header:
- self.write(b'')
+ self.write(b"")
if self.chunked_response:
# not self.write, it will chunk it!
- self.channel.write_soon(b'0\r\n\r\n')
+ self.channel.write_soon(b"0\r\n\r\n")
def write(self, data):
if not self.complete:
- raise RuntimeError('start_response was not called before body '
- 'written')
+ raise RuntimeError("start_response was not called before body " "written")
channel = self.channel
if not self.wrote_header:
rh = self.build_response_header()
@@ -321,15 +319,16 @@ class Task(object):
cl = self.content_length
if self.chunked_response:
# use chunked encoding response
- towrite = tobytes(hex(len(data))[2:].upper()) + b'\r\n'
- towrite += data + b'\r\n'
+ towrite = tobytes(hex(len(data))[2:].upper()) + b"\r\n"
+ towrite += data + b"\r\n"
elif cl is not None:
- towrite = data[:cl - self.content_bytes_written]
+ towrite = data[: cl - self.content_bytes_written]
self.content_bytes_written += len(towrite)
if towrite != data and not self.logged_write_excess:
self.logger.warning(
- 'application-written content exceeded the number of '
- 'bytes specified by Content-Length header (%s)' % cl)
+ "application-written content exceeded the number of "
+ "bytes specified by Content-Length header (%s)" % cl
+ )
self.logged_write_excess = True
if towrite:
channel.write_soon(towrite)
@@ -341,14 +340,16 @@ class Task(object):
if not self.logged_write_no_body:
self.logger.warning(
- 'application-written content was ignored due to HTTP '
- 'response that may not contain a message-body: (%s)' % self.status)
+ "application-written content was ignored due to HTTP "
+ "response that may not contain a message-body: (%s)" % self.status
+ )
self.logged_write_no_body = True
class ErrorTask(Task):
""" An error task produces an error response
"""
+
complete = True
def execute(self):
@@ -356,14 +357,14 @@ class ErrorTask(Task):
status, headers, body = e.to_response()
self.status = status
self.response_headers.extend(headers)
- if self.version == '1.1':
- connection = self.request.headers.get('CONNECTION', '').lower()
- if connection == 'close':
- self.response_headers.append(('Connection', 'close'))
+ if self.version == "1.1":
+ connection = self.request.headers.get("CONNECTION", "").lower()
+ if connection == "close":
+ self.response_headers.append(("Connection", "close"))
# under HTTP 1.1 keep-alive is default, no need to set the header
else:
# HTTP 1.0
- self.response_headers.append(('Connection', 'close'))
+ self.response_headers.append(("Connection", "close"))
self.close_on_finish = True
self.content_length = len(body)
self.write(tobytes(body))
@@ -372,6 +373,7 @@ class ErrorTask(Task):
class WSGITask(Task):
"""A WSGI task produces a response from a WSGI application.
"""
+
environ = None
def execute(self):
@@ -379,8 +381,9 @@ class WSGITask(Task):
def start_response(status, headers, exc_info=None):
if self.complete and not exc_info:
- raise AssertionError("start_response called a second time "
- "without providing exc_info.")
+ raise AssertionError(
+ "start_response called a second time " "without providing exc_info."
+ )
if exc_info:
try:
if self.wrote_header:
@@ -398,10 +401,11 @@ class WSGITask(Task):
self.complete = True
if not status.__class__ is str:
- raise AssertionError('status %s is not a string' % status)
- if '\n' in status or '\r' in status:
- raise ValueError("carriage return/line "
- "feed character present in status")
+ raise AssertionError("status %s is not a string" % status)
+ if "\n" in status or "\r" in status:
+ raise ValueError(
+ "carriage return/line " "feed character present in status"
+ )
self.status = status
@@ -409,27 +413,30 @@ class WSGITask(Task):
for k, v in headers:
if not k.__class__ is str:
raise AssertionError(
- 'Header name %r is not a string in %r' % (k, (k, v))
+ "Header name %r is not a string in %r" % (k, (k, v))
)
if not v.__class__ is str:
raise AssertionError(
- 'Header value %r is not a string in %r' % (v, (k, v))
+ "Header value %r is not a string in %r" % (v, (k, v))
)
- if '\n' in v or '\r' in v:
- raise ValueError("carriage return/line "
- "feed character present in header value")
- if '\n' in k or '\r' in k:
- raise ValueError("carriage return/line "
- "feed character present in header name")
+ if "\n" in v or "\r" in v:
+ raise ValueError(
+ "carriage return/line " "feed character present in header value"
+ )
+ if "\n" in k or "\r" in k:
+ raise ValueError(
+ "carriage return/line " "feed character present in header name"
+ )
kl = k.lower()
- if kl == 'content-length':
+ if kl == "content-length":
self.content_length = int(v)
elif kl in hop_by_hop:
raise AssertionError(
'%s is a "hop-by-hop" header; it cannot be used by '
- 'a WSGI application (see PEP 3333)' % k)
+ "a WSGI application (see PEP 3333)" % k
+ )
self.response_headers.extend(headers)
@@ -449,7 +456,7 @@ class WSGITask(Task):
if cl is not None:
self.remove_content_length_header()
self.content_length = size
- self.write(b'') # generate headers
+ self.write(b"") # generate headers
# if the write_soon below succeeds then the channel will
# take over closing the underlying file via the channel's
# _flush_some or handle_close so we intentionally avoid
@@ -468,7 +475,7 @@ class WSGITask(Task):
# self.content_length here
if self.content_length is None:
app_iter_len = None
- if hasattr(app_iter, '__len__'):
+ if hasattr(app_iter, "__len__"):
app_iter_len = len(app_iter)
if app_iter_len == 1:
self.content_length = first_chunk_len
@@ -484,14 +491,14 @@ class WSGITask(Task):
# waiting for more data when there are too few bytes
# to service content-length
self.close_on_finish = True
- if self.request.command != 'HEAD':
+ if self.request.command != "HEAD":
self.logger.warning(
- 'application returned too few bytes (%s) '
- 'for specified Content-Length (%s) via app_iter' % (
- self.content_bytes_written, cl),
+ "application returned too few bytes (%s) "
+ "for specified Content-Length (%s) via app_iter"
+ % (self.content_bytes_written, cl),
)
finally:
- if can_close_app_iter and hasattr(app_iter, 'close'):
+ if can_close_app_iter and hasattr(app_iter, "close"):
app_iter.close()
def get_environment(self):
@@ -507,10 +514,10 @@ class WSGITask(Task):
server = channel.server
url_prefix = server.adj.url_prefix
- if path.startswith('/'):
+ if path.startswith("/"):
# strip extra slashes at the beginning of a path that starts
# with any number of slashes
- path = '/' + path.lstrip('/')
+ path = "/" + path.lstrip("/")
if url_prefix:
# NB: url_prefix is guaranteed by the configuration machinery to
@@ -519,52 +526,50 @@ class WSGITask(Task):
if path == url_prefix:
# if the path is the same as the url prefix, the SCRIPT_NAME
# should be the url_prefix and PATH_INFO should be empty
- path = ''
+ path = ""
else:
# if the path starts with the url prefix plus a slash,
# the SCRIPT_NAME should be the url_prefix and PATH_INFO should
# the value of path from the slash until its end
- url_prefix_with_trailing_slash = url_prefix + '/'
+ url_prefix_with_trailing_slash = url_prefix + "/"
if path.startswith(url_prefix_with_trailing_slash):
- path = path[len(url_prefix):]
+ path = path[len(url_prefix) :]
environ = {
- 'REMOTE_ADDR': channel.addr[0],
+ "REMOTE_ADDR": channel.addr[0],
# Nah, we aren't actually going to look up the reverse DNS for
# REMOTE_ADDR, but we will happily set this environment variable
# for the WSGI application. Spec says we can just set this to
# REMOTE_ADDR, so we do.
- 'REMOTE_HOST': channel.addr[0],
+ "REMOTE_HOST": channel.addr[0],
# try and set the REMOTE_PORT to something useful, but maybe None
- 'REMOTE_PORT': str(channel.addr[1]),
- 'REQUEST_METHOD': request.command.upper(),
- 'SERVER_PORT': str(server.effective_port),
- 'SERVER_NAME': server.server_name,
- 'SERVER_SOFTWARE': server.adj.ident,
- 'SERVER_PROTOCOL': 'HTTP/%s' % self.version,
- 'SCRIPT_NAME': url_prefix,
- 'PATH_INFO': path,
- 'QUERY_STRING': request.query,
- 'wsgi.url_scheme': request.url_scheme,
-
+ "REMOTE_PORT": str(channel.addr[1]),
+ "REQUEST_METHOD": request.command.upper(),
+ "SERVER_PORT": str(server.effective_port),
+ "SERVER_NAME": server.server_name,
+ "SERVER_SOFTWARE": server.adj.ident,
+ "SERVER_PROTOCOL": "HTTP/%s" % self.version,
+ "SCRIPT_NAME": url_prefix,
+ "PATH_INFO": path,
+ "QUERY_STRING": request.query,
+ "wsgi.url_scheme": request.url_scheme,
# the following environment variables are required by the WSGI spec
- 'wsgi.version': (1, 0),
-
+ "wsgi.version": (1, 0),
# apps should use the logging module
- 'wsgi.errors': sys.stderr,
- 'wsgi.multithread': True,
- 'wsgi.multiprocess': False,
- 'wsgi.run_once': False,
- 'wsgi.input': request.get_body_stream(),
- 'wsgi.file_wrapper': ReadOnlyFileBasedBuffer,
- 'wsgi.input_terminated': True, # wsgi.input is EOF terminated
+ "wsgi.errors": sys.stderr,
+ "wsgi.multithread": True,
+ "wsgi.multiprocess": False,
+ "wsgi.run_once": False,
+ "wsgi.input": request.get_body_stream(),
+ "wsgi.file_wrapper": ReadOnlyFileBasedBuffer,
+ "wsgi.input_terminated": True, # wsgi.input is EOF terminated
}
for key, value in dict(request.headers).items():
value = value.strip()
mykey = rename_headers.get(key, None)
if mykey is None:
- mykey = 'HTTP_' + key
+ mykey = "HTTP_" + key
if mykey not in environ:
environ[mykey] = value
diff --git a/waitress/tests/fixtureapps/badcl.py b/waitress/tests/fixtureapps/badcl.py
index 2289a12..24067de 100644
--- a/waitress/tests/fixtureapps/badcl.py
+++ b/waitress/tests/fixtureapps/badcl.py
@@ -1,12 +1,11 @@
-def app(environ, start_response): # pragma: no cover
- body = b'abcdefghi'
+def app(environ, start_response): # pragma: no cover
+ body = b"abcdefghi"
cl = len(body)
- if environ['PATH_INFO'] == '/short_body':
+ if environ["PATH_INFO"] == "/short_body":
cl = len(body) + 1
- if environ['PATH_INFO'] == '/long_body':
+ if environ["PATH_INFO"] == "/long_body":
cl = len(body) - 1
start_response(
- '200 OK',
- [('Content-Length', str(cl)), ('Content-Type', 'text/plain')]
+ "200 OK", [("Content-Length", str(cl)), ("Content-Type", "text/plain")]
)
return [body]
diff --git a/waitress/tests/fixtureapps/echo.py b/waitress/tests/fixtureapps/echo.py
index 5509703..813bdac 100644
--- a/waitress/tests/fixtureapps/echo.py
+++ b/waitress/tests/fixtureapps/echo.py
@@ -1,55 +1,56 @@
from collections import namedtuple
import json
-def app_body_only(environ, start_response): # pragma: no cover
- cl = environ.get('CONTENT_LENGTH', None)
+
+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)
+ body = environ["wsgi.input"].read(cl)
cl = str(len(body))
- start_response('200 OK', [
- ('Content-Length', cl),
- ('Content-Type', 'text/plain'),
- ])
+ 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)
+
+def app(environ, start_response): # pragma: no cover
+ cl = environ.get("CONTENT_LENGTH", None)
if cl is not None:
cl = int(cl)
- request_body = environ['wsgi.input'].read(cl)
+ request_body = environ["wsgi.input"].read(cl)
cl = str(len(request_body))
meta = {
- 'method': environ['REQUEST_METHOD'],
- 'path_info': environ['PATH_INFO'],
- 'script_name': environ['SCRIPT_NAME'],
- 'query_string': environ['QUERY_STRING'],
- 'content_length': cl,
- 'scheme': environ['wsgi.url_scheme'],
- 'remote_addr': environ['REMOTE_ADDR'],
- 'remote_host': environ['REMOTE_HOST'],
- 'server_port': environ['SERVER_PORT'],
- 'server_name': environ['SERVER_NAME'],
- 'headers': {
- k[len('HTTP_'):]: v
- for k, v in environ.items()
- if k.startswith('HTTP_')
+ "method": environ["REQUEST_METHOD"],
+ "path_info": environ["PATH_INFO"],
+ "script_name": environ["SCRIPT_NAME"],
+ "query_string": environ["QUERY_STRING"],
+ "content_length": cl,
+ "scheme": environ["wsgi.url_scheme"],
+ "remote_addr": environ["REMOTE_ADDR"],
+ "remote_host": environ["REMOTE_HOST"],
+ "server_port": environ["SERVER_PORT"],
+ "server_name": environ["SERVER_NAME"],
+ "headers": {
+ k[len("HTTP_") :]: v for k, v in environ.items() if k.startswith("HTTP_")
},
}
- response = json.dumps(meta).encode('utf8') + b'\r\n\r\n' + request_body
- start_response('200 OK', [
- ('Content-Length', str(len(response))),
- ('Content-Type', 'text/plain'),
- ])
+ response = json.dumps(meta).encode("utf8") + b"\r\n\r\n" + request_body
+ start_response(
+ "200 OK",
+ [("Content-Length", str(len(response))), ("Content-Type", "text/plain"),],
+ )
return [response]
-Echo = namedtuple('Echo', (
- 'method path_info script_name query_string content_length scheme '
- 'remote_addr remote_host server_port server_name headers body'
-))
+Echo = namedtuple(
+ "Echo",
+ (
+ "method path_info script_name query_string content_length scheme "
+ "remote_addr remote_host server_port server_name headers body"
+ ),
+)
+
def parse_response(response):
- meta, body = response.split(b'\r\n\r\n', 1)
- meta = json.loads(meta.decode('utf8'))
+ meta, body = response.split(b"\r\n\r\n", 1)
+ meta = json.loads(meta.decode("utf8"))
return Echo(body=body, **meta)
diff --git a/waitress/tests/fixtureapps/error.py b/waitress/tests/fixtureapps/error.py
index cab8ad6..5afb1c5 100644
--- a/waitress/tests/fixtureapps/error.py
+++ b/waitress/tests/fixtureapps/error.py
@@ -1,20 +1,21 @@
-def app(environ, start_response): # pragma: no cover
- cl = environ.get('CONTENT_LENGTH', None)
+def app(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)
+ body = environ["wsgi.input"].read(cl)
cl = str(len(body))
- if environ['PATH_INFO'] == '/before_start_response':
- raise ValueError('wrong')
+ if environ["PATH_INFO"] == "/before_start_response":
+ raise ValueError("wrong")
write = start_response(
- '200 OK',
- [('Content-Length', cl), ('Content-Type', 'text/plain')]
+ "200 OK", [("Content-Length", cl), ("Content-Type", "text/plain")]
)
- if environ['PATH_INFO'] == '/after_write_cb':
- write('abc')
- if environ['PATH_INFO'] == '/in_generator':
+ if environ["PATH_INFO"] == "/after_write_cb":
+ write("abc")
+ if environ["PATH_INFO"] == "/in_generator":
+
def foo():
- yield 'abc'
+ yield "abc"
raise ValueError
+
return foo()
- raise ValueError('wrong')
+ raise ValueError("wrong")
diff --git a/waitress/tests/fixtureapps/filewrapper.py b/waitress/tests/fixtureapps/filewrapper.py
index f38c606..63df5a6 100644
--- a/waitress/tests/fixtureapps/filewrapper.py
+++ b/waitress/tests/fixtureapps/filewrapper.py
@@ -2,10 +2,10 @@ import io
import os
here = os.path.dirname(os.path.abspath(__file__))
-fn = os.path.join(here, 'groundhog1.jpg')
+fn = os.path.join(here, "groundhog1.jpg")
-class KindaFilelike(object): # pragma: no cover
+class KindaFilelike(object): # pragma: no cover
def __init__(self, bytes):
self.bytes = bytes
@@ -14,8 +14,8 @@ class KindaFilelike(object): # pragma: no cover
self.bytes = self.bytes[n:]
return bytes
-class UnseekableIOBase(io.RawIOBase): # pragma: no cover
+class UnseekableIOBase(io.RawIOBase): # pragma: no cover
def __init__(self, bytes):
self.buf = io.BytesIO(bytes)
@@ -31,65 +31,63 @@ class UnseekableIOBase(io.RawIOBase): # pragma: no cover
def read(self, n):
return self.buf.read(n)
-def app(environ, start_response): # pragma: no cover
- path_info = environ['PATH_INFO']
- if path_info.startswith('/filelike'):
- f = open(fn, 'rb')
+
+def app(environ, start_response): # pragma: no cover
+ path_info = environ["PATH_INFO"]
+ if path_info.startswith("/filelike"):
+ f = open(fn, "rb")
f.seek(0, 2)
cl = f.tell()
f.seek(0)
- if path_info == '/filelike':
+ if path_info == "/filelike":
headers = [
- ('Content-Length', str(cl)),
- ('Content-Type', 'image/jpeg'),
+ ("Content-Length", str(cl)),
+ ("Content-Type", "image/jpeg"),
]
- elif path_info == '/filelike_nocl':
- headers = [('Content-Type', 'image/jpeg')]
- elif path_info == '/filelike_shortcl':
+ elif path_info == "/filelike_nocl":
+ headers = [("Content-Type", "image/jpeg")]
+ elif path_info == "/filelike_shortcl":
# short content length
headers = [
- ('Content-Length', '1'),
- ('Content-Type', 'image/jpeg'),
+ ("Content-Length", "1"),
+ ("Content-Type", "image/jpeg"),
]
else:
# long content length (/filelike_longcl)
headers = [
- ('Content-Length', str(cl + 10)),
- ('Content-Type', 'image/jpeg'),
+ ("Content-Length", str(cl + 10)),
+ ("Content-Type", "image/jpeg"),
]
else:
- with open(fn, 'rb') as fp:
+ with open(fn, "rb") as fp:
data = fp.read()
cl = len(data)
f = KindaFilelike(data)
- if path_info == '/notfilelike':
+ if path_info == "/notfilelike":
headers = [
- ('Content-Length', str(len(data))),
- ('Content-Type', 'image/jpeg'),
+ ("Content-Length", str(len(data))),
+ ("Content-Type", "image/jpeg"),
]
- elif path_info == '/notfilelike_iobase':
+ elif path_info == "/notfilelike_iobase":
headers = [
- ('Content-Length', str(len(data))),
- ('Content-Type', 'image/jpeg'),
+ ("Content-Length", str(len(data))),
+ ("Content-Type", "image/jpeg"),
]
f = UnseekableIOBase(data)
- elif path_info == '/notfilelike_nocl':
- headers = [('Content-Type', 'image/jpeg')]
- elif path_info == '/notfilelike_shortcl':
+ elif path_info == "/notfilelike_nocl":
+ headers = [("Content-Type", "image/jpeg")]
+ elif path_info == "/notfilelike_shortcl":
# short content length
headers = [
- ('Content-Length', '1'),
- ('Content-Type', 'image/jpeg'),
+ ("Content-Length", "1"),
+ ("Content-Type", "image/jpeg"),
]
else:
# long content length (/notfilelike_longcl)
headers = [
- ('Content-Length', str(cl + 10)),
- ('Content-Type', 'image/jpeg'),
+ ("Content-Length", str(cl + 10)),
+ ("Content-Type", "image/jpeg"),
]
- start_response(
- '200 OK',
- headers
- )
- return environ['wsgi.file_wrapper'](f, 8192)
+ start_response("200 OK", headers)
+ return environ["wsgi.file_wrapper"](f, 8192)
diff --git a/waitress/tests/fixtureapps/getline.py b/waitress/tests/fixtureapps/getline.py
index 7d8ae5d..5e0ad3a 100644
--- a/waitress/tests/fixtureapps/getline.py
+++ b/waitress/tests/fixtureapps/getline.py
@@ -1,17 +1,17 @@
import sys
-if __name__ == '__main__':
+if __name__ == "__main__":
try:
from urllib.request import urlopen, URLError
except ImportError:
from urllib2 import urlopen, URLError
url = sys.argv[1]
- headers = {'Content-Type': 'text/plain; charset=utf-8'}
+ headers = {"Content-Type": "text/plain; charset=utf-8"}
try:
resp = urlopen(url)
- line = resp.readline().decode('ascii') # py3
+ line = resp.readline().decode("ascii") # py3
except URLError:
- line = 'failed to read %s' % url
+ line = "failed to read %s" % url
sys.stdout.write(line)
sys.stdout.flush()
diff --git a/waitress/tests/fixtureapps/nocl.py b/waitress/tests/fixtureapps/nocl.py
index 05e1d18..f82bba0 100644
--- a/waitress/tests/fixtureapps/nocl.py
+++ b/waitress/tests/fixtureapps/nocl.py
@@ -1,24 +1,23 @@
-def chunks(l, n): # pragma: no cover
+def chunks(l, n): # pragma: no cover
""" Yield successive n-sized chunks from l.
"""
for i in range(0, len(l), n):
- yield l[i:i + n]
+ yield l[i : i + n]
-def gen(body): # pragma: no cover
+
+def gen(body): # pragma: no cover
for chunk in chunks(body, 10):
yield chunk
-def app(environ, start_response): # pragma: no cover
- cl = environ.get('CONTENT_LENGTH', None)
+
+def app(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)
- start_response(
- '200 OK',
- [('Content-Type', 'text/plain')]
- )
- if environ['PATH_INFO'] == '/list':
+ body = environ["wsgi.input"].read(cl)
+ start_response("200 OK", [("Content-Type", "text/plain")])
+ if environ["PATH_INFO"] == "/list":
return [body]
- if environ['PATH_INFO'] == '/list_lentwo':
+ if environ["PATH_INFO"] == "/list_lentwo":
return [body[0:1], body[1:]]
return gen(body)
diff --git a/waitress/tests/fixtureapps/runner.py b/waitress/tests/fixtureapps/runner.py
index eee0e45..1d66ad1 100644
--- a/waitress/tests/fixtureapps/runner.py
+++ b/waitress/tests/fixtureapps/runner.py
@@ -1,5 +1,6 @@
-def app(): # pragma: no cover
+def app(): # pragma: no cover
return None
-def returns_app(): # pragma: no cover
+
+def returns_app(): # pragma: no cover
return app
diff --git a/waitress/tests/fixtureapps/sleepy.py b/waitress/tests/fixtureapps/sleepy.py
index 03bd0ab..2d171d8 100644
--- a/waitress/tests/fixtureapps/sleepy.py
+++ b/waitress/tests/fixtureapps/sleepy.py
@@ -1,14 +1,12 @@
import time
-def app(environ, start_response): # pragma: no cover
- if environ['PATH_INFO'] == '/sleepy':
+
+def app(environ, start_response): # pragma: no cover
+ if environ["PATH_INFO"] == "/sleepy":
time.sleep(2)
- body = b'sleepy returned'
+ body = b"sleepy returned"
else:
- body = b'notsleepy returned'
+ body = b"notsleepy returned"
cl = str(len(body))
- start_response(
- '200 OK',
- [('Content-Length', cl), ('Content-Type', 'text/plain')]
- )
+ start_response("200 OK", [("Content-Length", cl), ("Content-Type", "text/plain")])
return [body]
diff --git a/waitress/tests/fixtureapps/toolarge.py b/waitress/tests/fixtureapps/toolarge.py
index 150e908..a0f36d2 100644
--- a/waitress/tests/fixtureapps/toolarge.py
+++ b/waitress/tests/fixtureapps/toolarge.py
@@ -1,8 +1,7 @@
-def app(environ, start_response): # pragma: no cover
- body = b'abcdef'
+def app(environ, start_response): # pragma: no cover
+ body = b"abcdef"
cl = len(body)
start_response(
- '200 OK',
- [('Content-Length', str(cl)), ('Content-Type', 'text/plain')]
+ "200 OK", [("Content-Length", str(cl)), ("Content-Type", "text/plain")]
)
return [body]
diff --git a/waitress/tests/fixtureapps/writecb.py b/waitress/tests/fixtureapps/writecb.py
index ac59eb9..e1d2792 100644
--- a/waitress/tests/fixtureapps/writecb.py
+++ b/waitress/tests/fixtureapps/writecb.py
@@ -1,14 +1,14 @@
-def app(environ, start_response): # pragma: no cover
- path_info = environ['PATH_INFO']
- if path_info == '/no_content_length':
+def app(environ, start_response): # pragma: no cover
+ path_info = environ["PATH_INFO"]
+ if path_info == "/no_content_length":
headers = []
else:
- headers = [('Content-Length', '9')]
- write = start_response('200 OK', headers)
- if path_info == '/long_body':
- write(b'abcdefghij')
- elif path_info == '/short_body':
- write(b'abcdefgh')
+ headers = [("Content-Length", "9")]
+ write = start_response("200 OK", headers)
+ if path_info == "/long_body":
+ write(b"abcdefghij")
+ elif path_info == "/short_body":
+ write(b"abcdefgh")
else:
- write(b'abcdefghi')
+ write(b"abcdefghi")
return []
diff --git a/waitress/tests/test_adjustments.py b/waitress/tests/test_adjustments.py
index 00b0353..303c1aa 100644
--- a/waitress/tests/test_adjustments.py
+++ b/waitress/tests/test_adjustments.py
@@ -5,17 +5,18 @@ import warnings
from waitress.compat import (
PY2,
WIN,
- )
+)
-if sys.version_info[:2] == (2, 6): # pragma: no cover
+if sys.version_info[:2] == (2, 6): # pragma: no cover
import unittest2 as unittest
-else: # pragma: no cover
+else: # pragma: no cover
import unittest
-class Test_asbool(unittest.TestCase):
+class Test_asbool(unittest.TestCase):
def _callFUT(self, s):
from waitress.adjustments import asbool
+
return asbool(s)
def test_s_is_None(self):
@@ -31,33 +32,35 @@ class Test_asbool(unittest.TestCase):
self.assertEqual(result, False)
def test_s_is_true(self):
- result = self._callFUT('True')
+ result = self._callFUT("True")
self.assertEqual(result, True)
def test_s_is_false(self):
- result = self._callFUT('False')
+ result = self._callFUT("False")
self.assertEqual(result, False)
def test_s_is_yes(self):
- result = self._callFUT('yes')
+ result = self._callFUT("yes")
self.assertEqual(result, True)
def test_s_is_on(self):
- result = self._callFUT('on')
+ result = self._callFUT("on")
self.assertEqual(result, True)
def test_s_is_1(self):
result = self._callFUT(1)
self.assertEqual(result, True)
-class Test_as_socket_list(unittest.TestCase):
+class Test_as_socket_list(unittest.TestCase):
def test_only_sockets_in_list(self):
from waitress.adjustments import as_socket_list
+
sockets = [
socket.socket(socket.AF_INET, socket.SOCK_STREAM),
- socket.socket(socket.AF_INET6, socket.SOCK_STREAM)]
- if hasattr(socket, 'AF_UNIX'):
+ socket.socket(socket.AF_INET6, socket.SOCK_STREAM),
+ ]
+ if hasattr(socket, "AF_UNIX"):
sockets.append(socket.socket(socket.AF_UNIX, socket.SOCK_STREAM))
new_sockets = as_socket_list(sockets)
self.assertEqual(sockets, new_sockets)
@@ -66,29 +69,31 @@ class Test_as_socket_list(unittest.TestCase):
def test_not_only_sockets_in_list(self):
from waitress.adjustments import as_socket_list
+
sockets = [
socket.socket(socket.AF_INET, socket.SOCK_STREAM),
socket.socket(socket.AF_INET6, socket.SOCK_STREAM),
- {'something': 'else'}]
+ {"something": "else"},
+ ]
new_sockets = as_socket_list(sockets)
self.assertEqual(new_sockets, [sockets[0], sockets[1]])
for sock in [sock for sock in sockets if isinstance(sock, socket.socket)]:
sock.close()
-class TestAdjustments(unittest.TestCase):
- def _hasIPv6(self): # pragma: nocover
+class TestAdjustments(unittest.TestCase):
+ def _hasIPv6(self): # pragma: nocover
if not socket.has_ipv6:
return False
try:
socket.getaddrinfo(
- '::1',
+ "::1",
0,
socket.AF_UNSPEC,
socket.SOCK_STREAM,
socket.IPPROTO_TCP,
- socket.AI_PASSIVE | socket.AI_ADDRCONFIG
+ socket.AI_PASSIVE | socket.AI_ADDRCONFIG,
)
return True
@@ -101,47 +106,48 @@ class TestAdjustments(unittest.TestCase):
def _makeOne(self, **kw):
from waitress.adjustments import Adjustments
+
return Adjustments(**kw)
def test_goodvars(self):
inst = self._makeOne(
- host='localhost',
- port='8080',
- threads='5',
- trusted_proxy='192.168.1.1',
- trusted_proxy_headers={'forwarded'},
+ host="localhost",
+ port="8080",
+ threads="5",
+ trusted_proxy="192.168.1.1",
+ trusted_proxy_headers={"forwarded"},
trusted_proxy_count=2,
log_untrusted_proxy_headers=True,
- url_scheme='https',
- backlog='20',
- recv_bytes='200',
- send_bytes='300',
- outbuf_overflow='400',
- inbuf_overflow='500',
- connection_limit='1000',
- cleanup_interval='1100',
- channel_timeout='1200',
- log_socket_errors='true',
- max_request_header_size='1300',
- max_request_body_size='1400',
- expose_tracebacks='true',
- ident='abc',
- asyncore_loop_timeout='5',
+ url_scheme="https",
+ backlog="20",
+ recv_bytes="200",
+ send_bytes="300",
+ outbuf_overflow="400",
+ inbuf_overflow="500",
+ connection_limit="1000",
+ cleanup_interval="1100",
+ channel_timeout="1200",
+ log_socket_errors="true",
+ max_request_header_size="1300",
+ max_request_body_size="1400",
+ expose_tracebacks="true",
+ ident="abc",
+ asyncore_loop_timeout="5",
asyncore_use_poll=True,
- unix_socket_perms='777',
- url_prefix='///foo/',
+ unix_socket_perms="777",
+ url_prefix="///foo/",
ipv4=True,
ipv6=False,
)
- self.assertEqual(inst.host, 'localhost')
+ self.assertEqual(inst.host, "localhost")
self.assertEqual(inst.port, 8080)
self.assertEqual(inst.threads, 5)
- self.assertEqual(inst.trusted_proxy, '192.168.1.1')
- self.assertEqual(inst.trusted_proxy_headers, {'forwarded'})
+ self.assertEqual(inst.trusted_proxy, "192.168.1.1")
+ self.assertEqual(inst.trusted_proxy_headers, {"forwarded"})
self.assertEqual(inst.trusted_proxy_count, 2)
self.assertEqual(inst.log_untrusted_proxy_headers, True)
- self.assertEqual(inst.url_scheme, 'https')
+ self.assertEqual(inst.url_scheme, "https")
self.assertEqual(inst.backlog, 20)
self.assertEqual(inst.recv_bytes, 200)
self.assertEqual(inst.send_bytes, 300)
@@ -156,9 +162,9 @@ class TestAdjustments(unittest.TestCase):
self.assertEqual(inst.expose_tracebacks, True)
self.assertEqual(inst.asyncore_loop_timeout, 5)
self.assertEqual(inst.asyncore_use_poll, True)
- self.assertEqual(inst.ident, 'abc')
+ self.assertEqual(inst.ident, "abc")
self.assertEqual(inst.unix_socket_perms, 0o777)
- self.assertEqual(inst.url_prefix, '/foo')
+ self.assertEqual(inst.url_prefix, "/foo")
self.assertEqual(inst.ipv4, True)
self.assertEqual(inst.ipv6, False)
@@ -170,80 +176,77 @@ class TestAdjustments(unittest.TestCase):
# On Travis, somehow we start listening to two sockets when resolving
# localhost...
- self.assertEqual(('127.0.0.1', 8080), bind_pairs[0])
+ self.assertEqual(("127.0.0.1", 8080), bind_pairs[0])
def test_goodvar_listen(self):
- inst = self._makeOne(listen='127.0.0.1')
+ inst = self._makeOne(listen="127.0.0.1")
bind_pairs = [(host, port) for (_, _, _, (host, port)) in inst.listen]
- self.assertEqual(bind_pairs, [('127.0.0.1', 8080)])
+ self.assertEqual(bind_pairs, [("127.0.0.1", 8080)])
def test_default_listen(self):
inst = self._makeOne()
bind_pairs = [(host, port) for (_, _, _, (host, port)) in inst.listen]
- self.assertEqual(bind_pairs, [('0.0.0.0', 8080)])
+ self.assertEqual(bind_pairs, [("0.0.0.0", 8080)])
def test_multiple_listen(self):
- inst = self._makeOne(listen='127.0.0.1:9090 127.0.0.1:8080')
+ inst = self._makeOne(listen="127.0.0.1:9090 127.0.0.1:8080")
bind_pairs = [sockaddr[:2] for (_, _, _, sockaddr) in inst.listen]
- self.assertEqual(bind_pairs,
- [('127.0.0.1', 9090),
- ('127.0.0.1', 8080)])
+ self.assertEqual(bind_pairs, [("127.0.0.1", 9090), ("127.0.0.1", 8080)])
def test_wildcard_listen(self):
- inst = self._makeOne(listen='*:8080')
+ inst = self._makeOne(listen="*:8080")
bind_pairs = [sockaddr[:2] for (_, _, _, sockaddr) in inst.listen]
self.assertTrue(len(bind_pairs) >= 1)
- def test_ipv6_no_port(self): # pragma: nocover
+ def test_ipv6_no_port(self): # pragma: nocover
if not self._hasIPv6():
return
- inst = self._makeOne(listen='[::1]')
+ inst = self._makeOne(listen="[::1]")
bind_pairs = [sockaddr[:2] for (_, _, _, sockaddr) in inst.listen]
- self.assertEqual(bind_pairs, [('::1', 8080)])
+ self.assertEqual(bind_pairs, [("::1", 8080)])
def test_bad_port(self):
- self.assertRaises(ValueError, self._makeOne, listen='127.0.0.1:test')
+ self.assertRaises(ValueError, self._makeOne, listen="127.0.0.1:test")
def test_service_port(self):
- if WIN and PY2: # pragma: no cover
+ 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',
+ ValueError, self._makeOne, listen="127.0.0.1:http",
)
return
- inst = self._makeOne(listen='127.0.0.1:http 0.0.0.0:https')
+ inst = self._makeOne(listen="127.0.0.1:http 0.0.0.0:https")
bind_pairs = [sockaddr[:2] for (_, _, _, sockaddr) in inst.listen]
- self.assertEqual(bind_pairs, [('127.0.0.1', 80), ('0.0.0.0', 443)])
+ self.assertEqual(bind_pairs, [("127.0.0.1", 80), ("0.0.0.0", 443)])
def test_dont_mix_host_port_listen(self):
self.assertRaises(
ValueError,
self._makeOne,
- host='localhost',
- port='8080',
- listen='127.0.0.1:8080',
+ host="localhost",
+ port="8080",
+ listen="127.0.0.1:8080",
)
def test_good_sockets(self):
sockets = [
socket.socket(socket.AF_INET6, socket.SOCK_STREAM),
- socket.socket(socket.AF_INET, socket.SOCK_STREAM)]
+ socket.socket(socket.AF_INET, socket.SOCK_STREAM),
+ ]
inst = self._makeOne(sockets=sockets)
self.assertEqual(inst.sockets, sockets)
sockets[0].close()
@@ -252,100 +255,100 @@ class TestAdjustments(unittest.TestCase):
def test_dont_mix_sockets_and_listen(self):
sockets = [socket.socket(socket.AF_INET, socket.SOCK_STREAM)]
self.assertRaises(
- ValueError,
- self._makeOne,
- listen='127.0.0.1:8080',
- sockets=sockets)
+ ValueError, self._makeOne, listen="127.0.0.1:8080", sockets=sockets
+ )
sockets[0].close()
def test_dont_mix_sockets_and_host_port(self):
sockets = [socket.socket(socket.AF_INET, socket.SOCK_STREAM)]
self.assertRaises(
- ValueError,
- self._makeOne,
- host='localhost',
- port='8080',
- sockets=sockets)
+ ValueError, self._makeOne, host="localhost", port="8080", sockets=sockets
+ )
sockets[0].close()
def test_dont_mix_sockets_and_unix_socket(self):
sockets = [socket.socket(socket.AF_INET, socket.SOCK_STREAM)]
self.assertRaises(
- ValueError,
- self._makeOne,
- unix_socket='./tmp/test',
- sockets=sockets)
+ ValueError, self._makeOne, unix_socket="./tmp/test", sockets=sockets
+ )
sockets[0].close()
def test_dont_mix_unix_socket_and_host_port(self):
self.assertRaises(
ValueError,
self._makeOne,
- unix_socket='./tmp/test',
- host='localhost',
- port='8080')
+ unix_socket="./tmp/test",
+ host="localhost",
+ port="8080",
+ )
def test_dont_mix_unix_socket_and_listen(self):
self.assertRaises(
- ValueError,
- self._makeOne,
- unix_socket='./tmp/test',
- listen='127.0.0.1:8080')
+ ValueError, self._makeOne, unix_socket="./tmp/test", listen="127.0.0.1:8080"
+ )
def test_dont_use_unsupported_socket_types(self):
sockets = [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]
- self.assertRaises(
- ValueError,
- self._makeOne,
- sockets=sockets)
+ self.assertRaises(ValueError, self._makeOne, sockets=sockets)
sockets[0].close()
def test_dont_mix_forwarded_with_x_forwarded(self):
with self.assertRaises(ValueError) as cm:
- self._makeOne(trusted_proxy='localhost', trusted_proxy_headers={'forwarded', 'x-forwarded-for'})
+ self._makeOne(
+ trusted_proxy="localhost",
+ trusted_proxy_headers={"forwarded", "x-forwarded-for"},
+ )
- self.assertIn('The Forwarded proxy header', str(cm.exception))
+ self.assertIn("The Forwarded proxy header", str(cm.exception))
def test_unknown_trusted_proxy_header(self):
with self.assertRaises(ValueError) as cm:
- self._makeOne(trusted_proxy='localhost', trusted_proxy_headers={'forwarded', 'x-forwarded-unknown'})
+ self._makeOne(
+ trusted_proxy="localhost",
+ trusted_proxy_headers={"forwarded", "x-forwarded-unknown"},
+ )
self.assertIn(
- 'unknown trusted_proxy_headers value (x-forwarded-unknown)',
- str(cm.exception)
+ "unknown trusted_proxy_headers value (x-forwarded-unknown)",
+ str(cm.exception),
)
def test_trusted_proxy_count_no_trusted_proxy(self):
with self.assertRaises(ValueError) as cm:
self._makeOne(trusted_proxy_count=1)
- self.assertIn(
- 'trusted_proxy_count has no meaning',
- str(cm.exception)
- )
+ self.assertIn("trusted_proxy_count has no meaning", str(cm.exception))
def test_trusted_proxy_headers_no_trusted_proxy(self):
with self.assertRaises(ValueError) as cm:
- self._makeOne(trusted_proxy_headers={'forwarded'})
+ self._makeOne(trusted_proxy_headers={"forwarded"})
- self.assertIn(
- 'trusted_proxy_headers has no meaning',
- str(cm.exception)
- )
+ self.assertIn("trusted_proxy_headers has no meaning", str(cm.exception))
def test_trusted_proxy_headers_string_list(self):
- inst = self._makeOne(trusted_proxy='localhost', trusted_proxy_headers='x-forwarded-for x-forwarded-by')
- self.assertEqual(inst.trusted_proxy_headers, {'x-forwarded-for', 'x-forwarded-by'})
+ inst = self._makeOne(
+ trusted_proxy="localhost",
+ trusted_proxy_headers="x-forwarded-for x-forwarded-by",
+ )
+ self.assertEqual(
+ inst.trusted_proxy_headers, {"x-forwarded-for", "x-forwarded-by"}
+ )
def test_trusted_proxy_headers_string_list_newlines(self):
- inst = self._makeOne(trusted_proxy='localhost', trusted_proxy_headers='x-forwarded-for\nx-forwarded-by\nx-forwarded-host')
- self.assertEqual(inst.trusted_proxy_headers, {'x-forwarded-for', 'x-forwarded-by', 'x-forwarded-host'})
+ inst = self._makeOne(
+ trusted_proxy="localhost",
+ trusted_proxy_headers="x-forwarded-for\nx-forwarded-by\nx-forwarded-host",
+ )
+ self.assertEqual(
+ inst.trusted_proxy_headers,
+ {"x-forwarded-for", "x-forwarded-by", "x-forwarded-host"},
+ )
def test_no_trusted_proxy_headers_trusted_proxy(self):
with warnings.catch_warnings(record=True) as w:
warnings.resetwarnings()
warnings.simplefilter("always")
- self._makeOne(trusted_proxy='localhost')
+ self._makeOne(trusted_proxy="localhost")
self.assertGreaterEqual(len(w), 1)
self.assertTrue(issubclass(w[0].category, DeprecationWarning))
@@ -355,11 +358,15 @@ class TestAdjustments(unittest.TestCase):
with warnings.catch_warnings(record=True) as w:
warnings.resetwarnings()
warnings.simplefilter("always")
- self._makeOne(trusted_proxy='localhost', trusted_proxy_headers={'x-forwarded-for'})
+ self._makeOne(
+ trusted_proxy="localhost", trusted_proxy_headers={"x-forwarded-for"}
+ )
self.assertGreaterEqual(len(w), 1)
self.assertTrue(issubclass(w[0].category, DeprecationWarning))
- self.assertIn("clear_untrusted_proxy_headers will be set to True", str(w[0]))
+ self.assertIn(
+ "clear_untrusted_proxy_headers will be set to True", str(w[0])
+ )
def test_deprecated_send_bytes(self):
with warnings.catch_warnings(record=True) as w:
@@ -375,7 +382,9 @@ class TestAdjustments(unittest.TestCase):
self.assertRaises(ValueError, self._makeOne, nope=True)
def test_ipv4_disabled(self):
- self.assertRaises(ValueError, self._makeOne, ipv4=False, listen="127.0.0.1:8080")
+ self.assertRaises(
+ ValueError, self._makeOne, ipv4=False, listen="127.0.0.1:8080"
+ )
def test_ipv6_disabled(self):
self.assertRaises(ValueError, self._makeOne, ipv6=False, listen="[::]:8080")
@@ -384,101 +393,89 @@ class TestAdjustments(unittest.TestCase):
inst = self._makeOne(ident=None)
self.assertEqual(inst.ident, None)
- inst = self._makeOne(ident='')
+ inst = self._makeOne(ident="")
self.assertEqual(inst.ident, None)
- inst = self._makeOne(ident='specific_header')
- self.assertEqual(inst.ident, 'specific_header')
+ inst = self._makeOne(ident="specific_header")
+ self.assertEqual(inst.ident, "specific_header")
class TestCLI(unittest.TestCase):
-
def parse(self, argv):
from waitress.adjustments import Adjustments
+
return Adjustments.parse_args(argv)
def test_noargs(self):
opts, args = self.parse([])
- self.assertDictEqual(opts, {'call': False, 'help': False})
+ self.assertDictEqual(opts, {"call": False, "help": False})
self.assertSequenceEqual(args, [])
def test_help(self):
- opts, args = self.parse(['--help'])
- self.assertDictEqual(opts, {'call': False, 'help': True})
+ opts, args = self.parse(["--help"])
+ self.assertDictEqual(opts, {"call": False, "help": True})
self.assertSequenceEqual(args, [])
def test_call(self):
- opts, args = self.parse(['--call'])
- self.assertDictEqual(opts, {'call': True, 'help': False})
+ opts, args = self.parse(["--call"])
+ self.assertDictEqual(opts, {"call": True, "help": False})
self.assertSequenceEqual(args, [])
def test_both(self):
- opts, args = self.parse(['--call', '--help'])
- self.assertDictEqual(opts, {'call': True, 'help': True})
+ opts, args = self.parse(["--call", "--help"])
+ self.assertDictEqual(opts, {"call": True, "help": True})
self.assertSequenceEqual(args, [])
def test_positive_boolean(self):
- opts, args = self.parse(['--expose-tracebacks'])
- self.assertDictContainsSubset({'expose_tracebacks': 'true'}, opts)
+ opts, args = self.parse(["--expose-tracebacks"])
+ self.assertDictContainsSubset({"expose_tracebacks": "true"}, opts)
self.assertSequenceEqual(args, [])
def test_negative_boolean(self):
- opts, args = self.parse(['--no-expose-tracebacks'])
- self.assertDictContainsSubset({'expose_tracebacks': 'false'}, opts)
+ opts, args = self.parse(["--no-expose-tracebacks"])
+ self.assertDictContainsSubset({"expose_tracebacks": "false"}, opts)
self.assertSequenceEqual(args, [])
def test_cast_params(self):
- opts, args = self.parse([
- '--host=localhost',
- '--port=80',
- '--unix-socket-perms=777'
- ])
- self.assertDictContainsSubset({
- 'host': 'localhost',
- 'port': '80',
- 'unix_socket_perms': '777',
- }, opts)
+ opts, args = self.parse(
+ ["--host=localhost", "--port=80", "--unix-socket-perms=777"]
+ )
+ self.assertDictContainsSubset(
+ {"host": "localhost", "port": "80", "unix_socket_perms": "777",}, opts
+ )
self.assertSequenceEqual(args, [])
def test_listen_params(self):
- opts, args = self.parse([
- '--listen=test:80',
- ])
+ opts, args = self.parse(["--listen=test:80",])
- self.assertDictContainsSubset({
- 'listen': ' test:80'
- }, opts)
+ self.assertDictContainsSubset({"listen": " test:80"}, opts)
self.assertSequenceEqual(args, [])
def test_multiple_listen_params(self):
- opts, args = self.parse([
- '--listen=test:80',
- '--listen=test:8080',
- ])
-
- self.assertDictContainsSubset({
- 'listen': ' test:80 test:8080'
- }, opts)
+ opts, args = self.parse(["--listen=test:80", "--listen=test:8080",])
+
+ self.assertDictContainsSubset({"listen": " test:80 test:8080"}, opts)
self.assertSequenceEqual(args, [])
def test_bad_param(self):
import getopt
- self.assertRaises(getopt.GetoptError, self.parse, ['--no-host'])
+
+ self.assertRaises(getopt.GetoptError, self.parse, ["--no-host"])
-if hasattr(socket, 'AF_UNIX'):
+if hasattr(socket, "AF_UNIX"):
+
class TestUnixSocket(unittest.TestCase):
def _makeOne(self, **kw):
from waitress.adjustments import Adjustments
+
return Adjustments(**kw)
def test_dont_mix_internet_and_unix_sockets(self):
sockets = [
socket.socket(socket.AF_INET, socket.SOCK_STREAM),
- socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)]
- self.assertRaises(
- ValueError,
- self._makeOne,
- sockets=sockets)
+ socket.socket(socket.AF_UNIX, socket.SOCK_STREAM),
+ ]
+ self.assertRaises(ValueError, self._makeOne, sockets=sockets)
sockets[0].close()
sockets[1].close()
diff --git a/waitress/tests/test_buffers.py b/waitress/tests/test_buffers.py
index a7c3427..a1330ac 100644
--- a/waitress/tests/test_buffers.py
+++ b/waitress/tests/test_buffers.py
@@ -1,10 +1,11 @@
import unittest
import io
-class TestFileBasedBuffer(unittest.TestCase):
+class TestFileBasedBuffer(unittest.TestCase):
def _makeOne(self, file=None, from_buffer=None):
from waitress.buffers import FileBasedBuffer
+
buf = FileBasedBuffer(file, from_buffer=from_buffer)
self.buffers_to_close.append(buf)
return buf
@@ -17,11 +18,11 @@ class TestFileBasedBuffer(unittest.TestCase):
buf.close()
def test_ctor_from_buffer_None(self):
- inst = self._makeOne('file')
- self.assertEqual(inst.file, 'file')
+ inst = self._makeOne("file")
+ self.assertEqual(inst.file, "file")
def test_ctor_from_buffer(self):
- from_buffer = io.BytesIO(b'data')
+ from_buffer = io.BytesIO(b"data")
from_buffer.getfile = lambda *x: from_buffer
f = io.BytesIO()
inst = self._makeOne(f, from_buffer)
@@ -43,42 +44,42 @@ class TestFileBasedBuffer(unittest.TestCase):
self.assertEqual(bool(inst), True)
def test_append(self):
- f = io.BytesIO(b'data')
+ f = io.BytesIO(b"data")
inst = self._makeOne(f)
- inst.append(b'data2')
- self.assertEqual(f.getvalue(), b'datadata2')
+ inst.append(b"data2")
+ self.assertEqual(f.getvalue(), b"datadata2")
self.assertEqual(inst.remain, 5)
def test_get_skip_true(self):
- f = io.BytesIO(b'data')
+ f = io.BytesIO(b"data")
inst = self._makeOne(f)
result = inst.get(100, skip=True)
- self.assertEqual(result, b'data')
+ self.assertEqual(result, b"data")
self.assertEqual(inst.remain, -4)
def test_get_skip_false(self):
- f = io.BytesIO(b'data')
+ f = io.BytesIO(b"data")
inst = self._makeOne(f)
result = inst.get(100, skip=False)
- self.assertEqual(result, b'data')
+ self.assertEqual(result, b"data")
self.assertEqual(inst.remain, 0)
def test_get_skip_bytes_less_than_zero(self):
- f = io.BytesIO(b'data')
+ f = io.BytesIO(b"data")
inst = self._makeOne(f)
result = inst.get(-1, skip=False)
- self.assertEqual(result, b'data')
+ self.assertEqual(result, b"data")
self.assertEqual(inst.remain, 0)
def test_skip_remain_gt_bytes(self):
- f = io.BytesIO(b'd')
+ f = io.BytesIO(b"d")
inst = self._makeOne(f)
inst.remain = 1
inst.skip(1)
self.assertEqual(inst.remain, 0)
def test_skip_remain_lt_bytes(self):
- f = io.BytesIO(b'd')
+ f = io.BytesIO(b"d")
inst = self._makeOne(f)
inst.remain = 1
self.assertRaises(ValueError, inst.skip, 2)
@@ -88,24 +89,24 @@ class TestFileBasedBuffer(unittest.TestCase):
self.assertRaises(NotImplementedError, inst.newfile)
def test_prune_remain_notzero(self):
- f = io.BytesIO(b'd')
+ f = io.BytesIO(b"d")
inst = self._makeOne(f)
inst.remain = 1
nf = io.BytesIO()
inst.newfile = lambda *x: nf
inst.prune()
self.assertTrue(inst.file is not f)
- self.assertEqual(nf.getvalue(), b'd')
+ self.assertEqual(nf.getvalue(), b"d")
def test_prune_remain_zero_tell_notzero(self):
- f = io.BytesIO(b'd')
+ f = io.BytesIO(b"d")
inst = self._makeOne(f)
- nf = io.BytesIO(b'd')
+ nf = io.BytesIO(b"d")
inst.newfile = lambda *x: nf
inst.remain = 0
inst.prune()
self.assertTrue(inst.file is not f)
- self.assertEqual(nf.getvalue(), b'd')
+ self.assertEqual(nf.getvalue(), b"d")
def test_prune_remain_zero_tell_zero(self):
f = io.BytesIO()
@@ -121,10 +122,11 @@ class TestFileBasedBuffer(unittest.TestCase):
self.assertTrue(f.closed)
self.buffers_to_close.remove(inst)
-class TestTempfileBasedBuffer(unittest.TestCase):
+class TestTempfileBasedBuffer(unittest.TestCase):
def _makeOne(self, from_buffer=None):
from waitress.buffers import TempfileBasedBuffer
+
buf = TempfileBasedBuffer(from_buffer=from_buffer)
self.buffers_to_close.append(buf)
return buf
@@ -139,34 +141,36 @@ class TestTempfileBasedBuffer(unittest.TestCase):
def test_newfile(self):
inst = self._makeOne()
r = inst.newfile()
- self.assertTrue(hasattr(r, 'fileno')) # file
+ self.assertTrue(hasattr(r, "fileno")) # file
r.close()
-class TestBytesIOBasedBuffer(unittest.TestCase):
+class TestBytesIOBasedBuffer(unittest.TestCase):
def _makeOne(self, from_buffer=None):
from waitress.buffers import BytesIOBasedBuffer
+
return BytesIOBasedBuffer(from_buffer=from_buffer)
def test_ctor_from_buffer_not_None(self):
f = io.BytesIO()
f.getfile = lambda *x: f
inst = self._makeOne(f)
- self.assertTrue(hasattr(inst.file, 'read'))
+ self.assertTrue(hasattr(inst.file, "read"))
def test_ctor_from_buffer_None(self):
inst = self._makeOne()
- self.assertTrue(hasattr(inst.file, 'read'))
+ self.assertTrue(hasattr(inst.file, "read"))
def test_newfile(self):
inst = self._makeOne()
r = inst.newfile()
- self.assertTrue(hasattr(r, 'read'))
+ self.assertTrue(hasattr(r, "read"))
-class TestReadOnlyFileBasedBuffer(unittest.TestCase):
+class TestReadOnlyFileBasedBuffer(unittest.TestCase):
def _makeOne(self, file, block_size=8192):
from waitress.buffers import ReadOnlyFileBasedBuffer
+
buf = ReadOnlyFileBasedBuffer(file, block_size)
self.buffers_to_close.append(buf)
return buf
@@ -179,91 +183,92 @@ class TestReadOnlyFileBasedBuffer(unittest.TestCase):
buf.close()
def test_prepare_not_seekable(self):
- f = KindaFilelike(b'abc')
+ f = KindaFilelike(b"abc")
inst = self._makeOne(f)
result = inst.prepare()
self.assertEqual(result, False)
self.assertEqual(inst.remain, 0)
def test_prepare_not_seekable_closeable(self):
- f = KindaFilelike(b'abc', close=1)
+ f = KindaFilelike(b"abc", close=1)
inst = self._makeOne(f)
result = inst.prepare()
self.assertEqual(result, False)
self.assertEqual(inst.remain, 0)
- self.assertTrue(hasattr(inst, 'close'))
+ self.assertTrue(hasattr(inst, "close"))
def test_prepare_seekable_closeable(self):
- f = Filelike(b'abc', close=1, tellresults=[0, 10])
+ f = Filelike(b"abc", close=1, tellresults=[0, 10])
inst = self._makeOne(f)
result = inst.prepare()
self.assertEqual(result, 10)
self.assertEqual(inst.remain, 10)
self.assertEqual(inst.file.seeked, 0)
- self.assertTrue(hasattr(inst, 'close'))
+ self.assertTrue(hasattr(inst, "close"))
def test_get_numbytes_neg_one(self):
- f = io.BytesIO(b'abcdef')
+ f = io.BytesIO(b"abcdef")
inst = self._makeOne(f)
inst.remain = 2
result = inst.get(-1)
- self.assertEqual(result, b'ab')
+ self.assertEqual(result, b"ab")
self.assertEqual(inst.remain, 2)
self.assertEqual(f.tell(), 0)
def test_get_numbytes_gt_remain(self):
- f = io.BytesIO(b'abcdef')
+ f = io.BytesIO(b"abcdef")
inst = self._makeOne(f)
inst.remain = 2
result = inst.get(3)
- self.assertEqual(result, b'ab')
+ self.assertEqual(result, b"ab")
self.assertEqual(inst.remain, 2)
self.assertEqual(f.tell(), 0)
def test_get_numbytes_lt_remain(self):
- f = io.BytesIO(b'abcdef')
+ f = io.BytesIO(b"abcdef")
inst = self._makeOne(f)
inst.remain = 2
result = inst.get(1)
- self.assertEqual(result, b'a')
+ self.assertEqual(result, b"a")
self.assertEqual(inst.remain, 2)
self.assertEqual(f.tell(), 0)
def test_get_numbytes_gt_remain_withskip(self):
- f = io.BytesIO(b'abcdef')
+ f = io.BytesIO(b"abcdef")
inst = self._makeOne(f)
inst.remain = 2
result = inst.get(3, skip=True)
- self.assertEqual(result, b'ab')
+ self.assertEqual(result, b"ab")
self.assertEqual(inst.remain, 0)
self.assertEqual(f.tell(), 2)
def test_get_numbytes_lt_remain_withskip(self):
- f = io.BytesIO(b'abcdef')
+ f = io.BytesIO(b"abcdef")
inst = self._makeOne(f)
inst.remain = 2
result = inst.get(1, skip=True)
- self.assertEqual(result, b'a')
+ self.assertEqual(result, b"a")
self.assertEqual(inst.remain, 1)
self.assertEqual(f.tell(), 1)
def test___iter__(self):
- data = b'a' * 10000
+ data = b"a" * 10000
f = io.BytesIO(data)
inst = self._makeOne(f)
- r = b''
+ r = b""
for val in inst:
r += val
self.assertEqual(r, data)
def test_append(self):
inst = self._makeOne(None)
- self.assertRaises(NotImplementedError, inst.append, 'a')
+ self.assertRaises(NotImplementedError, inst.append, "a")
-class TestOverflowableBuffer(unittest.TestCase):
+class TestOverflowableBuffer(unittest.TestCase):
def _makeOne(self, overflow=10):
from waitress.buffers import OverflowableBuffer
+
buf = OverflowableBuffer(overflow)
self.buffers_to_close.append(buf)
return buf
@@ -281,15 +286,15 @@ class TestOverflowableBuffer(unittest.TestCase):
def test___len__buf_is_not_None(self):
inst = self._makeOne()
- inst.buf = b'abc'
+ inst.buf = b"abc"
self.assertEqual(len(inst), 3)
self.buffers_to_close.remove(inst)
def test___nonzero__(self):
inst = self._makeOne()
- inst.buf = b'abc'
+ inst.buf = b"abc"
self.assertEqual(bool(inst), True)
- inst.buf = b''
+ inst.buf = b""
self.assertEqual(bool(inst), False)
self.buffers_to_close.remove(inst)
@@ -299,112 +304,119 @@ class TestOverflowableBuffer(unittest.TestCase):
class int_overflow_buf(bytes):
def __len__(self):
# maxint + 1
- return 0x7fffffffffffffff + 1
+ return 0x7FFFFFFFFFFFFFFF + 1
+
inst.buf = int_overflow_buf()
self.assertEqual(bool(inst), True)
- inst.buf = b''
+ inst.buf = b""
self.assertEqual(bool(inst), False)
self.buffers_to_close.remove(inst)
def test__create_buffer_large(self):
from waitress.buffers import TempfileBasedBuffer
+
inst = self._makeOne()
- inst.strbuf = b'x' * 11
+ inst.strbuf = b"x" * 11
inst._create_buffer()
self.assertEqual(inst.buf.__class__, TempfileBasedBuffer)
- self.assertEqual(inst.buf.get(100), b'x' * 11)
- self.assertEqual(inst.strbuf, b'')
+ self.assertEqual(inst.buf.get(100), b"x" * 11)
+ self.assertEqual(inst.strbuf, b"")
def test__create_buffer_small(self):
from waitress.buffers import BytesIOBasedBuffer
+
inst = self._makeOne()
- inst.strbuf = b'x' * 5
+ inst.strbuf = b"x" * 5
inst._create_buffer()
self.assertEqual(inst.buf.__class__, BytesIOBasedBuffer)
- self.assertEqual(inst.buf.get(100), b'x' * 5)
- self.assertEqual(inst.strbuf, b'')
+ self.assertEqual(inst.buf.get(100), b"x" * 5)
+ self.assertEqual(inst.strbuf, b"")
def test_append_with_len_more_than_max_int(self):
from waitress.compat import MAXINT
+
inst = self._makeOne()
inst.overflowed = True
buf = DummyBuffer(length=MAXINT)
inst.buf = buf
- result = inst.append(b'x')
+ result = inst.append(b"x")
# we don't want this to throw an OverflowError on Python 2 (see
# https://github.com/Pylons/waitress/issues/47)
self.assertEqual(result, None)
self.buffers_to_close.remove(inst)
-
+
def test_append_buf_None_not_longer_than_srtbuf_limit(self):
inst = self._makeOne()
- inst.strbuf = b'x' * 5
- inst.append(b'hello')
- self.assertEqual(inst.strbuf, b'xxxxxhello')
+ inst.strbuf = b"x" * 5
+ inst.append(b"hello")
+ self.assertEqual(inst.strbuf, b"xxxxxhello")
def test_append_buf_None_longer_than_strbuf_limit(self):
inst = self._makeOne(10000)
- inst.strbuf = b'x' * 8192
- inst.append(b'hello')
- self.assertEqual(inst.strbuf, b'')
+ inst.strbuf = b"x" * 8192
+ inst.append(b"hello")
+ self.assertEqual(inst.strbuf, b"")
self.assertEqual(len(inst.buf), 8197)
def test_append_overflow(self):
inst = self._makeOne(10)
- inst.strbuf = b'x' * 8192
- inst.append(b'hello')
- self.assertEqual(inst.strbuf, b'')
+ inst.strbuf = b"x" * 8192
+ inst.append(b"hello")
+ self.assertEqual(inst.strbuf, b"")
self.assertEqual(len(inst.buf), 8197)
def test_append_sz_gt_overflow(self):
from waitress.buffers import BytesIOBasedBuffer
- f = io.BytesIO(b'data')
+
+ f = io.BytesIO(b"data")
inst = self._makeOne(f)
buf = BytesIOBasedBuffer()
inst.buf = buf
inst.overflow = 2
- inst.append(b'data2')
- self.assertEqual(f.getvalue(), b'data')
+ inst.append(b"data2")
+ self.assertEqual(f.getvalue(), b"data")
self.assertTrue(inst.overflowed)
self.assertNotEqual(inst.buf, buf)
def test_get_buf_None_skip_False(self):
inst = self._makeOne()
- inst.strbuf = b'x' * 5
+ inst.strbuf = b"x" * 5
r = inst.get(5)
- self.assertEqual(r, b'xxxxx')
+ self.assertEqual(r, b"xxxxx")
def test_get_buf_None_skip_True(self):
inst = self._makeOne()
- inst.strbuf = b'x' * 5
+ inst.strbuf = b"x" * 5
r = inst.get(5, skip=True)
self.assertFalse(inst.buf is None)
- self.assertEqual(r, b'xxxxx')
+ self.assertEqual(r, b"xxxxx")
def test_skip_buf_None(self):
inst = self._makeOne()
- inst.strbuf = b'data'
+ inst.strbuf = b"data"
inst.skip(4)
- self.assertEqual(inst.strbuf, b'')
+ self.assertEqual(inst.strbuf, b"")
self.assertNotEqual(inst.buf, None)
def test_skip_buf_None_allow_prune_True(self):
inst = self._makeOne()
- inst.strbuf = b'data'
+ inst.strbuf = b"data"
inst.skip(4, True)
- self.assertEqual(inst.strbuf, b'')
+ self.assertEqual(inst.strbuf, b"")
self.assertEqual(inst.buf, None)
def test_prune_buf_None(self):
inst = self._makeOne()
inst.prune()
- self.assertEqual(inst.strbuf, b'')
+ self.assertEqual(inst.strbuf, b"")
def test_prune_with_buf(self):
inst = self._makeOne()
+
class Buf(object):
def prune(self):
self.pruned = True
+
inst.buf = Buf()
inst.prune()
self.assertEqual(inst.buf.pruned, True)
@@ -412,16 +424,21 @@ class TestOverflowableBuffer(unittest.TestCase):
def test_prune_with_buf_overflow(self):
inst = self._makeOne()
+
class DummyBuffer(io.BytesIO):
def getfile(self):
return self
+
def prune(self):
return True
+
def __len__(self):
return 5
+
def close(self):
pass
- buf = DummyBuffer(b'data')
+
+ buf = DummyBuffer(b"data")
inst.buf = buf
inst.overflowed = True
inst.overflow = 10
@@ -430,19 +447,20 @@ class TestOverflowableBuffer(unittest.TestCase):
def test_prune_with_buflen_more_than_max_int(self):
from waitress.compat import MAXINT
+
inst = self._makeOne()
inst.overflowed = True
- buf = DummyBuffer(length=MAXINT+1)
+ buf = DummyBuffer(length=MAXINT + 1)
inst.buf = buf
result = inst.prune()
# we don't want this to throw an OverflowError on Python 2 (see
# https://github.com/Pylons/waitress/issues/47)
self.assertEqual(result, None)
-
+
def test_getfile_buf_None(self):
inst = self._makeOne()
f = inst.getfile()
- self.assertTrue(hasattr(f, 'read'))
+ self.assertTrue(hasattr(f, "read"))
def test_getfile_buf_not_None(self):
inst = self._makeOne()
@@ -455,13 +473,14 @@ class TestOverflowableBuffer(unittest.TestCase):
def test_close_nobuf(self):
inst = self._makeOne()
inst.buf = None
- self.assertEqual(inst.close(), None) # doesnt raise
+ self.assertEqual(inst.close(), None) # doesnt raise
self.buffers_to_close.remove(inst)
def test_close_withbuf(self):
class Buffer(object):
def close(self):
self.closed = True
+
buf = Buffer()
inst = self._makeOne()
inst.buf = buf
@@ -469,16 +488,16 @@ class TestOverflowableBuffer(unittest.TestCase):
self.assertTrue(buf.closed)
self.buffers_to_close.remove(inst)
-class KindaFilelike(object):
+class KindaFilelike(object):
def __init__(self, bytes, close=None, tellresults=None):
self.bytes = bytes
self.tellresults = tellresults
if close is not None:
self.close = lambda: close
-class Filelike(KindaFilelike):
+class Filelike(KindaFilelike):
def seek(self, v, whence=0):
self.seeked = v
@@ -486,6 +505,7 @@ class Filelike(KindaFilelike):
v = self.tellresults.pop(0)
return v
+
class DummyBuffer(object):
def __init__(self, length=0):
self.length = length
diff --git a/waitress/tests/test_channel.py b/waitress/tests/test_channel.py
index 35fccf6..33dfe3f 100644
--- a/waitress/tests/test_channel.py
+++ b/waitress/tests/test_channel.py
@@ -1,10 +1,11 @@
import unittest
import io
-class TestHTTPChannel(unittest.TestCase):
+class TestHTTPChannel(unittest.TestCase):
def _makeOne(self, sock, addr, adj, map=None):
from waitress.channel import HTTPChannel
+
server = DummyServer()
return HTTPChannel(server, sock, addr, adj=adj, map=map)
@@ -13,32 +14,37 @@ class TestHTTPChannel(unittest.TestCase):
adj = DummyAdjustments()
sock = DummySock()
map = {}
- inst = self._makeOne(sock, '127.0.0.1', adj, map=map)
+ inst = self._makeOne(sock, "127.0.0.1", adj, map=map)
inst.outbuf_lock = DummyLock()
return inst, sock, map
def test_ctor(self):
inst, _, map = self._makeOneWithMap()
- self.assertEqual(inst.addr, '127.0.0.1')
+ self.assertEqual(inst.addr, "127.0.0.1")
self.assertEqual(inst.sendbuf_len, 2048)
self.assertEqual(map[100], inst)
def test_total_outbufs_len_an_outbuf_size_gt_sys_maxint(self):
from waitress.compat import MAXINT
+
inst, _, map = self._makeOneWithMap()
+
class DummyBuffer(object):
chunks = []
+
def append(self, data):
self.chunks.append(data)
+
class DummyData(object):
def __len__(self):
return MAXINT
+
inst.total_outbufs_len = 1
inst.outbufs = [DummyBuffer()]
inst.write_soon(DummyData())
# we are testing that this method does not raise an OverflowError
# (see https://github.com/Pylons/waitress/issues/47)
- self.assertEqual(inst.total_outbufs_len, MAXINT+1)
+ self.assertEqual(inst.total_outbufs_len, MAXINT + 1)
def test_writable_something_in_outbuf(self):
inst, sock, map = self._makeOneWithMap()
@@ -70,19 +76,20 @@ class TestHTTPChannel(unittest.TestCase):
def test_handle_write_no_request_with_outbuf(self):
inst, sock, map = self._makeOneWithMap()
inst.requests = []
- inst.outbufs = [DummyBuffer(b'abc')]
+ inst.outbufs = [DummyBuffer(b"abc")]
inst.total_outbufs_len = len(inst.outbufs[0])
inst.last_activity = 0
result = inst.handle_write()
self.assertEqual(result, None)
self.assertNotEqual(inst.last_activity, 0)
- self.assertEqual(sock.sent, b'abc')
+ self.assertEqual(sock.sent, b"abc")
def test_handle_write_outbuf_raises_socketerror(self):
import socket
+
inst, sock, map = self._makeOneWithMap()
inst.requests = []
- outbuf = DummyBuffer(b'abc', socket.error)
+ outbuf = DummyBuffer(b"abc", socket.error)
inst.outbufs = [outbuf]
inst.total_outbufs_len = len(outbuf)
inst.last_activity = 0
@@ -90,14 +97,14 @@ class TestHTTPChannel(unittest.TestCase):
result = inst.handle_write()
self.assertEqual(result, None)
self.assertEqual(inst.last_activity, 0)
- self.assertEqual(sock.sent, b'')
+ self.assertEqual(sock.sent, b"")
self.assertEqual(len(inst.logger.exceptions), 1)
self.assertTrue(outbuf.closed)
def test_handle_write_outbuf_raises_othererror(self):
inst, sock, map = self._makeOneWithMap()
inst.requests = []
- outbuf = DummyBuffer(b'abc', IOError)
+ outbuf = DummyBuffer(b"abc", IOError)
inst.outbufs = [outbuf]
inst.total_outbufs_len = len(outbuf)
inst.last_activity = 0
@@ -105,14 +112,14 @@ class TestHTTPChannel(unittest.TestCase):
result = inst.handle_write()
self.assertEqual(result, None)
self.assertEqual(inst.last_activity, 0)
- self.assertEqual(sock.sent, b'')
+ self.assertEqual(sock.sent, b"")
self.assertEqual(len(inst.logger.exceptions), 1)
self.assertTrue(outbuf.closed)
def test_handle_write_no_requests_no_outbuf_will_close(self):
inst, sock, map = self._makeOneWithMap()
inst.requests = []
- outbuf = DummyBuffer(b'')
+ outbuf = DummyBuffer(b"")
inst.outbufs = [outbuf]
inst.will_close = True
inst.last_activity = 0
@@ -126,7 +133,7 @@ class TestHTTPChannel(unittest.TestCase):
def test_handle_write_no_requests_outbuf_gt_send_bytes(self):
inst, sock, map = self._makeOneWithMap()
inst.requests = [True]
- inst.outbufs = [DummyBuffer(b'abc')]
+ inst.outbufs = [DummyBuffer(b"abc")]
inst.total_outbufs_len = len(inst.outbufs[0])
inst.adj.send_bytes = 2
inst.will_close = False
@@ -135,11 +142,11 @@ class TestHTTPChannel(unittest.TestCase):
self.assertEqual(result, None)
self.assertEqual(inst.will_close, False)
self.assertTrue(inst.outbuf_lock.acquired)
- self.assertEqual(sock.sent, b'abc')
+ self.assertEqual(sock.sent, b"abc")
def test_handle_write_close_when_flushed(self):
inst, sock, map = self._makeOneWithMap()
- outbuf = DummyBuffer(b'abc')
+ outbuf = DummyBuffer(b"abc")
inst.outbufs = [outbuf]
inst.total_outbufs_len = len(outbuf)
inst.will_close = False
@@ -149,7 +156,7 @@ class TestHTTPChannel(unittest.TestCase):
self.assertEqual(result, None)
self.assertEqual(inst.will_close, True)
self.assertEqual(inst.close_when_flushed, False)
- self.assertEqual(sock.sent, b'abc')
+ self.assertEqual(sock.sent, b"abc")
self.assertTrue(outbuf.closed)
def test_readable_no_requests_not_will_close(self):
@@ -172,21 +179,24 @@ class TestHTTPChannel(unittest.TestCase):
def test_handle_read_no_error(self):
inst, sock, map = self._makeOneWithMap()
inst.will_close = False
- inst.recv = lambda *arg: b'abc'
+ inst.recv = lambda *arg: b"abc"
inst.last_activity = 0
L = []
inst.received = lambda x: L.append(x)
result = inst.handle_read()
self.assertEqual(result, None)
self.assertNotEqual(inst.last_activity, 0)
- self.assertEqual(L, [b'abc'])
+ self.assertEqual(L, [b"abc"])
def test_handle_read_error(self):
import socket
+
inst, sock, map = self._makeOneWithMap()
inst.will_close = False
+
def recv(b):
raise socket.error
+
inst.recv = recv
inst.last_activity = 0
inst.logger = DummyLogger()
@@ -197,19 +207,20 @@ class TestHTTPChannel(unittest.TestCase):
def test_write_soon_empty_byte(self):
inst, sock, map = self._makeOneWithMap()
- wrote = inst.write_soon(b'')
+ wrote = inst.write_soon(b"")
self.assertEqual(wrote, 0)
self.assertEqual(len(inst.outbufs[0]), 0)
def test_write_soon_nonempty_byte(self):
inst, sock, map = self._makeOneWithMap()
- wrote = inst.write_soon(b'a')
+ wrote = inst.write_soon(b"a")
self.assertEqual(wrote, 1)
self.assertEqual(len(inst.outbufs[0]), 1)
def test_write_soon_filewrapper(self):
from waitress.buffers import ReadOnlyFileBasedBuffer
- f = io.BytesIO(b'abc')
+
+ f = io.BytesIO(b"abc")
wrapper = ReadOnlyFileBasedBuffer(f, 8192)
wrapper.prepare()
inst, sock, map = self._makeOneWithMap()
@@ -220,53 +231,59 @@ class TestHTTPChannel(unittest.TestCase):
self.assertEqual(len(outbufs), 3)
self.assertEqual(outbufs[0], orig_outbuf)
self.assertEqual(outbufs[1], wrapper)
- self.assertEqual(outbufs[2].__class__.__name__, 'OverflowableBuffer')
+ self.assertEqual(outbufs[2].__class__.__name__, "OverflowableBuffer")
def test_write_soon_disconnected(self):
from waitress.channel import ClientDisconnected
+
inst, sock, map = self._makeOneWithMap()
inst.connected = False
- self.assertRaises(ClientDisconnected, lambda: inst.write_soon(b'stuff'))
+ self.assertRaises(ClientDisconnected, lambda: inst.write_soon(b"stuff"))
def test_write_soon_disconnected_while_over_watermark(self):
from waitress.channel import ClientDisconnected
+
inst, sock, map = self._makeOneWithMap()
+
def dummy_flush():
inst.connected = False
+
inst._flush_outbufs_below_high_watermark = dummy_flush
- self.assertRaises(ClientDisconnected, lambda: inst.write_soon(b'stuff'))
+ self.assertRaises(ClientDisconnected, lambda: inst.write_soon(b"stuff"))
def test_write_soon_rotates_outbuf_on_overflow(self):
inst, sock, map = self._makeOneWithMap()
inst.adj.outbuf_high_watermark = 3
inst.current_outbuf_count = 4
- wrote = inst.write_soon(b'xyz')
+ wrote = inst.write_soon(b"xyz")
self.assertEqual(wrote, 3)
self.assertEqual(len(inst.outbufs), 2)
- self.assertEqual(inst.outbufs[0].get(), b'')
- self.assertEqual(inst.outbufs[1].get(), b'xyz')
+ self.assertEqual(inst.outbufs[0].get(), b"")
+ self.assertEqual(inst.outbufs[1].get(), b"xyz")
def test_write_soon_waits_on_backpressure(self):
inst, sock, map = self._makeOneWithMap()
inst.adj.outbuf_high_watermark = 3
inst.total_outbufs_len = 4
inst.current_outbuf_count = 4
+
class Lock(DummyLock):
def wait(self):
inst.total_outbufs_len = 0
super(Lock, self).wait()
+
inst.outbuf_lock = Lock()
- wrote = inst.write_soon(b'xyz')
+ wrote = inst.write_soon(b"xyz")
self.assertEqual(wrote, 3)
self.assertEqual(len(inst.outbufs), 2)
- self.assertEqual(inst.outbufs[0].get(), b'')
- self.assertEqual(inst.outbufs[1].get(), b'xyz')
+ self.assertEqual(inst.outbufs[0].get(), b"")
+ self.assertEqual(inst.outbufs[1].get(), b"xyz")
self.assertTrue(inst.outbuf_lock.waited)
def test_handle_write_notify_after_flush(self):
inst, sock, map = self._makeOneWithMap()
inst.requests = [True]
- inst.outbufs = [DummyBuffer(b'abc')]
+ inst.outbufs = [DummyBuffer(b"abc")]
inst.total_outbufs_len = len(inst.outbufs[0])
inst.adj.send_bytes = 1
inst.adj.outbuf_high_watermark = 5
@@ -277,12 +294,12 @@ class TestHTTPChannel(unittest.TestCase):
self.assertEqual(inst.will_close, False)
self.assertTrue(inst.outbuf_lock.acquired)
self.assertTrue(inst.outbuf_lock.notified)
- self.assertEqual(sock.sent, b'abc')
+ self.assertEqual(sock.sent, b"abc")
def test_handle_write_no_notify_after_flush(self):
inst, sock, map = self._makeOneWithMap()
inst.requests = [True]
- inst.outbufs = [DummyBuffer(b'abc')]
+ inst.outbufs = [DummyBuffer(b"abc")]
inst.total_outbufs_len = len(inst.outbufs[0])
inst.adj.send_bytes = 1
inst.adj.outbuf_high_watermark = 2
@@ -294,7 +311,7 @@ class TestHTTPChannel(unittest.TestCase):
self.assertEqual(inst.will_close, False)
self.assertTrue(inst.outbuf_lock.acquired)
self.assertFalse(inst.outbuf_lock.notified)
- self.assertEqual(sock.sent, b'')
+ self.assertEqual(sock.sent, b"")
def test__flush_some_empty_outbuf(self):
inst, sock, map = self._makeOneWithMap()
@@ -303,7 +320,7 @@ class TestHTTPChannel(unittest.TestCase):
def test__flush_some_full_outbuf_socket_returns_nonzero(self):
inst, sock, map = self._makeOneWithMap()
- inst.outbufs[0].append(b'abc')
+ inst.outbufs[0].append(b"abc")
inst.total_outbufs_len = sum(len(x) for x in inst.outbufs)
result = inst._flush_some()
self.assertEqual(result, True)
@@ -311,7 +328,7 @@ class TestHTTPChannel(unittest.TestCase):
def test__flush_some_full_outbuf_socket_returns_zero(self):
inst, sock, map = self._makeOneWithMap()
sock.send = lambda x: False
- inst.outbufs[0].append(b'abc')
+ inst.outbufs[0].append(b"abc")
inst.total_outbufs_len = sum(len(x) for x in inst.outbufs)
result = inst._flush_some()
self.assertEqual(result, False)
@@ -319,7 +336,7 @@ class TestHTTPChannel(unittest.TestCase):
def test_flush_some_multiple_buffers_first_empty(self):
inst, sock, map = self._makeOneWithMap()
sock.send = lambda x: len(x)
- buffer = DummyBuffer(b'abc')
+ buffer = DummyBuffer(b"abc")
inst.outbufs.append(buffer)
inst.total_outbufs_len = sum(len(x) for x in inst.outbufs)
result = inst._flush_some()
@@ -330,12 +347,14 @@ class TestHTTPChannel(unittest.TestCase):
def test_flush_some_multiple_buffers_close_raises(self):
inst, sock, map = self._makeOneWithMap()
sock.send = lambda x: len(x)
- buffer = DummyBuffer(b'abc')
+ buffer = DummyBuffer(b"abc")
inst.outbufs.append(buffer)
inst.total_outbufs_len = sum(len(x) for x in inst.outbufs)
inst.logger = DummyLogger()
+
def doraise():
raise NotImplementedError
+
inst.outbufs[0].close = doraise
result = inst._flush_some()
self.assertEqual(result, True)
@@ -345,16 +364,23 @@ class TestHTTPChannel(unittest.TestCase):
def test__flush_some_outbuf_len_gt_sys_maxint(self):
from waitress.compat import MAXINT
+
inst, sock, map = self._makeOneWithMap()
+
class DummyHugeOutbuffer(object):
def __init__(self):
self.length = MAXINT + 1
+
def __len__(self):
return self.length
+
def get(self, numbytes):
self.length = 0
- return b'123'
- def skip(self, *args): pass
+ return b"123"
+
+ def skip(self, *args):
+ pass
+
buf = DummyHugeOutbuffer()
inst.outbufs = [buf]
inst.send = lambda *arg: 0
@@ -362,7 +388,7 @@ class TestHTTPChannel(unittest.TestCase):
# we are testing that _flush_some doesn't raise an OverflowError
# when one of its outbufs has a __len__ that returns gt sys.maxint
self.assertEqual(result, False)
-
+
def test_handle_close(self):
inst, sock, map = self._makeOneWithMap()
inst.handle_close()
@@ -371,8 +397,10 @@ class TestHTTPChannel(unittest.TestCase):
def test_handle_close_outbuf_raises_on_close(self):
inst, sock, map = self._makeOneWithMap()
+
def doraise():
raise NotImplementedError
+
inst.outbufs[0].close = doraise
inst.logger = DummyLogger()
inst.handle_close()
@@ -398,13 +426,13 @@ class TestHTTPChannel(unittest.TestCase):
def test_received(self):
inst, sock, map = self._makeOneWithMap()
inst.server = DummyServer()
- inst.received(b'GET / HTTP/1.1\n\n')
+ inst.received(b"GET / HTTP/1.1\n\n")
self.assertEqual(inst.server.tasks, [inst])
self.assertTrue(inst.requests)
def test_received_no_chunk(self):
inst, sock, map = self._makeOneWithMap()
- self.assertEqual(inst.received(b''), False)
+ self.assertEqual(inst.received(b""), False)
def test_received_preq_not_completed(self):
inst, sock, map = self._makeOneWithMap()
@@ -413,7 +441,7 @@ class TestHTTPChannel(unittest.TestCase):
inst.request = preq
preq.completed = False
preq.empty = True
- inst.received(b'GET / HTTP/1.1\n\n')
+ inst.received(b"GET / HTTP/1.1\n\n")
self.assertEqual(inst.requests, ())
self.assertEqual(inst.server.tasks, [])
@@ -424,7 +452,7 @@ class TestHTTPChannel(unittest.TestCase):
inst.request = preq
preq.completed = True
preq.empty = True
- inst.received(b'GET / HTTP/1.1\n\n')
+ inst.received(b"GET / HTTP/1.1\n\n")
self.assertEqual(inst.request, None)
self.assertEqual(inst.server.tasks, [])
@@ -435,7 +463,7 @@ class TestHTTPChannel(unittest.TestCase):
inst.request = preq
preq.completed = True
preq.error = True
- inst.received(b'GET / HTTP/1.1\n\n')
+ inst.received(b"GET / HTTP/1.1\n\n")
self.assertEqual(inst.request, None)
self.assertEqual(len(inst.server.tasks), 1)
self.assertTrue(inst.requests)
@@ -448,7 +476,7 @@ class TestHTTPChannel(unittest.TestCase):
preq.completed = True
preq.empty = True
preq.connection_close = True
- inst.received(b'GET / HTTP/1.1\n\n' + b'a' * 50000)
+ inst.received(b"GET / HTTP/1.1\n\n" + b"a" * 50000)
self.assertEqual(inst.request, None)
self.assertEqual(inst.server.tasks, [])
@@ -459,7 +487,7 @@ class TestHTTPChannel(unittest.TestCase):
inst.request = preq
preq.completed = True
preq.empty = False
- line = b'GET / HTTP/1.1\n\n'
+ line = b"GET / HTTP/1.1\n\n"
preq.retval = len(line)
inst.received(line + line)
self.assertEqual(inst.request, None)
@@ -476,10 +504,10 @@ class TestHTTPChannel(unittest.TestCase):
preq.completed = False
preq.empty = False
preq.retval = 1
- inst.received(b'GET / HTTP/1.1\n\n')
+ inst.received(b"GET / HTTP/1.1\n\n")
self.assertEqual(inst.request, preq)
self.assertEqual(inst.server.tasks, [])
- self.assertEqual(inst.outbufs[0].get(100), b'')
+ self.assertEqual(inst.outbufs[0].get(100), b"")
def test_received_headers_finished_expect_continue_true(self):
inst, sock, map = self._makeOneWithMap()
@@ -490,10 +518,10 @@ class TestHTTPChannel(unittest.TestCase):
preq.headers_finished = True
preq.completed = False
preq.empty = False
- inst.received(b'GET / HTTP/1.1\n\n')
+ inst.received(b"GET / HTTP/1.1\n\n")
self.assertEqual(inst.request, preq)
self.assertEqual(inst.server.tasks, [])
- self.assertEqual(sock.sent, b'HTTP/1.1 100 Continue\r\n\r\n')
+ self.assertEqual(sock.sent, b"HTTP/1.1 100 Continue\r\n\r\n")
self.assertEqual(inst.sent_continue, True)
self.assertEqual(preq.completed, False)
@@ -507,10 +535,10 @@ class TestHTTPChannel(unittest.TestCase):
preq.completed = False
preq.empty = False
inst.sent_continue = True
- inst.received(b'GET / HTTP/1.1\n\n')
+ inst.received(b"GET / HTTP/1.1\n\n")
self.assertEqual(inst.request, preq)
self.assertEqual(inst.server.tasks, [])
- self.assertEqual(sock.sent, b'')
+ self.assertEqual(sock.sent, b"")
self.assertEqual(inst.sent_continue, True)
self.assertEqual(preq.completed, False)
@@ -693,12 +721,13 @@ class TestHTTPChannel(unittest.TestCase):
inst.cancel()
self.assertEqual(inst.requests, [])
+
class DummySock(object):
blocking = False
closed = False
def __init__(self):
- self.sent = b''
+ self.sent = b""
def setblocking(self, *arg):
self.blocking = True
@@ -707,7 +736,7 @@ class DummySock(object):
return 100
def getpeername(self):
- return '127.0.0.1'
+ return "127.0.0.1"
def getsockopt(self, level, option):
return 2048
@@ -719,6 +748,7 @@ class DummySock(object):
self.sent += data
return len(data)
+
class DummyLock(object):
notified = False
@@ -745,6 +775,7 @@ class DummyLock(object):
def __enter__(self):
pass
+
class DummyBuffer(object):
closed = False
@@ -756,7 +787,7 @@ class DummyBuffer(object):
if self.toraise:
raise self.toraise
data = self.data
- self.data = b''
+ self.data = b""
return data
def skip(self, num, x):
@@ -768,20 +799,22 @@ class DummyBuffer(object):
def close(self):
self.closed = True
+
class DummyAdjustments(object):
outbuf_overflow = 1048576
outbuf_high_watermark = 1048576
inbuf_overflow = 512000
cleanup_interval = 900
- url_scheme = 'http'
+ url_scheme = "http"
channel_timeout = 300
log_socket_errors = True
recv_bytes = 8192
send_bytes = 1
expose_tracebacks = True
- ident = 'waitress'
+ ident = "waitress"
max_request_header_size = 10000
+
class DummyServer(object):
trigger_pulled = False
adj = DummyAdjustments()
@@ -796,6 +829,7 @@ class DummyServer(object):
def pull_trigger(self):
self.trigger_pulled = True
+
class DummyParser(object):
version = 1
data = None
@@ -813,10 +847,11 @@ class DummyParser(object):
return self.retval
return len(data)
+
class DummyRequest(object):
error = None
- path = '/'
- version = '1.0'
+ path = "/"
+ version = "1.0"
closed = False
def __init__(self):
@@ -825,8 +860,8 @@ class DummyRequest(object):
def close(self):
self.closed = True
-class DummyLogger(object):
+class DummyLogger(object):
def __init__(self):
self.exceptions = []
self.infos = []
@@ -838,10 +873,12 @@ class DummyLogger(object):
def exception(self, msg):
self.exceptions.append(msg)
+
class DummyError(object):
- code = '431'
- reason = 'Bleh'
- body = 'My body'
+ code = "431"
+ reason = "Bleh"
+ body = "My body"
+
class DummyTaskClass(object):
wrote_header = True
diff --git a/waitress/tests/test_compat.py b/waitress/tests/test_compat.py
index b5f6625..37c2193 100644
--- a/waitress/tests/test_compat.py
+++ b/waitress/tests/test_compat.py
@@ -2,19 +2,21 @@
import unittest
-class Test_unquote_bytes_to_wsgi(unittest.TestCase):
+class Test_unquote_bytes_to_wsgi(unittest.TestCase):
def _callFUT(self, v):
from waitress.compat import unquote_bytes_to_wsgi
+
return unquote_bytes_to_wsgi(v)
def test_highorder(self):
from waitress.compat import PY3
- val = b'/a%C5%9B'
+
+ val = b"/a%C5%9B"
result = self._callFUT(val)
- if PY3: # pragma: no cover
+ if PY3: # pragma: no cover
# PEP 3333 urlunquoted-latin1-decoded-bytes
- self.assertEqual(result, '/aÃ…\x9b')
- else: # pragma: no cover
+ self.assertEqual(result, "/aÃ…\x9b")
+ else: # pragma: no cover
# sanity
- self.assertEqual(result, b'/a\xc5\x9b')
+ self.assertEqual(result, b"/a\xc5\x9b")
diff --git a/waitress/tests/test_functional.py b/waitress/tests/test_functional.py
index 0a39771..2225d6a 100644
--- a/waitress/tests/test_functional.py
+++ b/waitress/tests/test_functional.py
@@ -10,57 +10,62 @@ import sys
import time
import unittest
from waitress import server
-from waitress.compat import (
- httplib,
- tobytes
-)
+from waitress.compat import httplib, tobytes
from waitress.utilities import cleanup_unix_socket
dn = os.path.dirname
here = dn(__file__)
-class NullHandler(logging.Handler): # pragma: no cover
+
+class NullHandler(logging.Handler): # pragma: no cover
"""A logging handler that swallows all emitted messages.
"""
+
def emit(self, record):
pass
-def start_server(app, svr, queue, **kwargs): # pragma: no cover
+
+def start_server(app, svr, queue, **kwargs): # pragma: no cover
"""Run a fixture application.
"""
- logging.getLogger('waitress').addHandler(NullHandler())
+ 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:
+ if "_COVERAGE_RCFILE" in os.environ:
import coverage
- cov = coverage.Coverage(config_file=os.getenv('_COVERAGE_RCFILE'))
+
+ 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.
"""
- family = socket.AF_INET # Testing
+ family = socket.AF_INET # Testing
- def __init__(self, application, queue, **kw): # pragma: no cover
+ def __init__(self, application, queue, **kw): # pragma: no cover
# Coverage doesn't see this as it's ran in a separate process.
- kw['port'] = 0 # Bind to any available port.
+ kw["port"] = 0 # Bind to any available port.
super(FixtureTcpWSGIServer, self).__init__(application, **kw)
host, port = self.socket.getsockname()
- if os.name == 'nt':
- host = '127.0.0.1'
+ if os.name == "nt":
+ host = "127.0.0.1"
queue.put((host, port))
+
class SubprocessTests(object):
# For nose: all tests may be ran in separate processes.
@@ -74,12 +79,10 @@ class SubprocessTests(object):
# Spawn a server process.
self.queue = multiprocessing.Queue()
self.proc = multiprocessing.Process(
- target=start_server,
- args=(target, self.server, self.queue),
- kwargs=kw,
+ target=start_server, args=(target, self.server, self.queue), kwargs=kw,
)
self.proc.start()
- if self.proc.exitcode is not None: # pragma: no cover
+ if self.proc.exitcode is not None: # pragma: no cover
raise RuntimeError("%s didn't start" % str(target))
# Get the socket the server is listening on.
self.bound_to = self.queue.get(timeout=5)
@@ -106,11 +109,12 @@ class SubprocessTests(object):
self.sock.connect(self.bound_to)
def make_http_connection(self):
- raise NotImplementedError # pragma: no cover
+ raise NotImplementedError # pragma: no cover
def send_check_error(self, to_send):
self.sock.send(to_send)
+
class TcpTests(SubprocessTests):
server = FixtureTcpWSGIServer
@@ -118,21 +122,23 @@ class TcpTests(SubprocessTests):
def make_http_connection(self):
return httplib.HTTPConnection(*self.bound_to)
+
class SleepyThreadTests(TcpTests, unittest.TestCase):
# test that sleepy thread doesnt block other requests
def setUp(self):
from waitress.tests.fixtureapps import sleepy
+
self.start_subprocess(sleepy.app)
def tearDown(self):
self.stop_subprocess()
def test_it(self):
- getline = os.path.join(here, 'fixtureapps', 'getline.py')
+ getline = os.path.join(here, "fixtureapps", "getline.py")
cmds = (
- [self.exe, getline, 'http://%s:%d/sleepy' % self.bound_to],
- [self.exe, getline, 'http://%s:%d/' % self.bound_to]
+ [self.exe, getline, "http://%s:%d/sleepy" % self.bound_to],
+ [self.exe, getline, "http://%s:%d/" % self.bound_to],
)
r, w = os.pipe()
procs = []
@@ -140,7 +146,7 @@ class SleepyThreadTests(TcpTests, unittest.TestCase):
procs.append(subprocess.Popen(cmd, stdout=w))
time.sleep(3)
for proc in procs:
- if proc.returncode is not None: # pragma: no cover
+ if proc.returncode is not None: # pragma: no cover
proc.terminate()
proc.wait()
# the notsleepy response should always be first returned (it sleeps
@@ -149,17 +155,18 @@ class SleepyThreadTests(TcpTests, unittest.TestCase):
result = os.read(r, 10000)
os.close(r)
os.close(w)
- self.assertEqual(result, b'notsleepy returnedsleepy returned')
+ self.assertEqual(result, b"notsleepy returnedsleepy returned")
-class EchoTests(object):
+class EchoTests(object):
def setUp(self):
from waitress.tests.fixtureapps import echo
+
self.start_subprocess(
echo.app,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=1,
- trusted_proxy_headers={'x-forwarded-for', 'x-forwarded-proto'},
+ trusted_proxy_headers={"x-forwarded-for", "x-forwarded-proto"},
clear_untrusted_proxy_headers=True,
)
@@ -168,58 +175,55 @@ class EchoTests(object):
def _read_echo(self, fp):
from waitress.tests.fixtureapps import echo
+
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"
- "Content-Length: 0\n\n")
+ to_send = "GET / HTTP/1.0\n" "Content-Length: 0\n\n"
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ 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.assertline(line, "200", "OK", "HTTP/1.0")
+ self.assertEqual(headers.get("server"), "waitress")
+ self.assertTrue(headers.get("date"))
def test_bad_host_header(self):
# https://corte.si/posts/code/pathod/pythonservers/index.html
- to_send = ("GET / HTTP/1.0\n"
- " Host: 0\n\n")
+ to_send = "GET / HTTP/1.0\n" " Host: 0\n\n"
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '400', 'Bad Request', 'HTTP/1.0')
- self.assertEqual(headers.get('server'), 'waitress')
- self.assertTrue(headers.get('date'))
+ self.assertline(line, "400", "Bad Request", "HTTP/1.0")
+ self.assertEqual(headers.get("server"), "waitress")
+ self.assertTrue(headers.get("date"))
def test_send_with_body(self):
- to_send = ("GET / HTTP/1.0\n"
- "Content-Length: 5\n\n")
- to_send += 'hello'
+ to_send = "GET / HTTP/1.0\n" "Content-Length: 5\n\n"
+ to_send += "hello"
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, echo = self._read_echo(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- self.assertEqual(echo.content_length, '5')
- self.assertEqual(echo.body, b'hello')
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ self.assertEqual(echo.content_length, "5")
+ self.assertEqual(echo.body, b"hello")
def test_send_empty_body(self):
- to_send = ("GET / HTTP/1.0\n"
- "Content-Length: 0\n\n")
+ to_send = "GET / HTTP/1.0\n" "Content-Length: 0\n\n"
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, echo = self._read_echo(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- self.assertEqual(echo.content_length, '0')
- self.assertEqual(echo.body, b'')
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ self.assertEqual(echo.content_length, "0")
+ self.assertEqual(echo.body, b"")
def test_multiple_requests_with_body(self):
orig_sock = self.sock
@@ -248,28 +252,25 @@ class EchoTests(object):
)
self.connect()
self.sock.send(s)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, echo = self._read_echo(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
+ self.assertline(line, "200", "OK", "HTTP/1.0")
self.assertEqual(int(echo.content_length), len(data))
self.assertEqual(len(echo.body), len(data))
self.assertEqual(echo.body, tobytes(data))
def test_large_body(self):
# 1024 characters.
- body = 'This string has 32 characters.\r\n' * 32
+ body = "This string has 32 characters.\r\n" * 32
s = tobytes(
- "GET / HTTP/1.0\n"
- "Content-Length: %d\n"
- "\n"
- "%s" % (len(body), body)
+ "GET / HTTP/1.0\n" "Content-Length: %d\n" "\n" "%s" % (len(body), body)
)
self.connect()
self.sock.send(s)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, echo = self._read_echo(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- self.assertEqual(echo.content_length, '1024')
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ self.assertEqual(echo.content_length, "1024")
self.assertEqual(echo.body, tobytes(body))
def test_many_clients(self):
@@ -289,61 +290,56 @@ class EchoTests(object):
h.close()
def test_chunking_request_without_content(self):
- header = tobytes(
- "GET / HTTP/1.1\n"
- "Transfer-Encoding: chunked\n\n"
- )
+ header = tobytes("GET / HTTP/1.1\n" "Transfer-Encoding: chunked\n\n")
self.connect()
self.sock.send(header)
self.sock.send(b"0\r\n\r\n")
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, echo = self._read_echo(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- self.assertEqual(echo.body, b'')
- self.assertEqual(echo.content_length, '0')
- self.assertFalse('transfer-encoding' in headers)
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ self.assertEqual(echo.body, b"")
+ self.assertEqual(echo.content_length, "0")
+ self.assertFalse("transfer-encoding" in headers)
def test_chunking_request_with_content(self):
- control_line = b"20;\r\n" # 20 hex = 32 dec
- s = b'This string has 32 characters.\r\n'
+ control_line = b"20;\r\n" # 20 hex = 32 dec
+ s = b"This string has 32 characters.\r\n"
expected = s * 12
- header = tobytes(
- "GET / HTTP/1.1\n"
- "Transfer-Encoding: chunked\n\n"
- )
+ header = tobytes("GET / HTTP/1.1\n" "Transfer-Encoding: chunked\n\n")
self.connect()
self.sock.send(header)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
for n in range(12):
self.sock.send(control_line)
self.sock.send(s)
self.sock.send(b"0\r\n\r\n")
line, headers, echo = self._read_echo(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
+ self.assertline(line, "200", "OK", "HTTP/1.1")
self.assertEqual(echo.body, expected)
self.assertEqual(echo.content_length, str(len(expected)))
- self.assertFalse('transfer-encoding' in headers)
+ self.assertFalse("transfer-encoding" in headers)
def test_broken_chunked_encoding(self):
- control_line = "20;\r\n" # 20 hex = 32 dec
- s = 'This string has 32 characters.\r\n'
+ control_line = "20;\r\n" # 20 hex = 32 dec
+ s = "This string has 32 characters.\r\n"
to_send = "GET / HTTP/1.1\nTransfer-Encoding: chunked\n\n"
- to_send += (control_line + s)
+ to_send += control_line + s
# garbage in input
to_send += "GET / HTTP/1.1\nTransfer-Encoding: chunked\n\n"
- to_send += (control_line + s)
+ to_send += control_line + s
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
# receiver caught garbage and turned it into a 400
- self.assertline(line, '400', 'Bad Request', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "400", "Bad Request", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- self.assertEqual(sorted(headers.keys()),
- ['content-length', 'content-type', 'date', 'server'])
- self.assertEqual(headers['content-type'], 'text/plain')
+ self.assertEqual(
+ sorted(headers.keys()), ["content-length", "content-type", "date", "server"]
+ )
+ self.assertEqual(headers["content-type"], "text/plain")
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
@@ -352,20 +348,17 @@ class EchoTests(object):
# Handling of Keep-Alive within HTTP 1.0
data = "Default: Don't keep me alive"
s = tobytes(
- "GET / HTTP/1.0\n"
- "Content-Length: %d\n"
- "\n"
- "%s" % (len(data), data)
+ "GET / HTTP/1.0\n" "Content-Length: %d\n" "\n" "%s" % (len(data), data)
)
self.connect()
self.sock.send(s)
response = httplib.HTTPResponse(self.sock)
response.begin()
self.assertEqual(int(response.status), 200)
- connection = response.getheader('Connection', '')
+ connection = response.getheader("Connection", "")
# We sent no Connection: Keep-Alive header
# Connection: close (or no header) is default.
- self.assertTrue(connection != 'Keep-Alive')
+ self.assertTrue(connection != "Keep-Alive")
def test_keepalive_http10_explicit(self):
# If header Connection: Keep-Alive is explicitly sent,
@@ -384,8 +377,8 @@ class EchoTests(object):
response = httplib.HTTPResponse(self.sock)
response.begin()
self.assertEqual(int(response.status), 200)
- connection = response.getheader('Connection', '')
- self.assertEqual(connection, 'Keep-Alive')
+ connection = response.getheader("Connection", "")
+ self.assertEqual(connection, "Keep-Alive")
def test_keepalive_http_11(self):
# Handling of Keep-Alive within HTTP 1.1
@@ -393,16 +386,14 @@ class EchoTests(object):
# All connections are kept alive, unless stated otherwise
data = "Default: Keep me alive"
s = tobytes(
- "GET / HTTP/1.1\n"
- "Content-Length: %d\n"
- "\n"
- "%s" % (len(data), data))
+ "GET / HTTP/1.1\n" "Content-Length: %d\n" "\n" "%s" % (len(data), data)
+ )
self.connect()
self.sock.send(s)
response = httplib.HTTPResponse(self.sock)
response.begin()
self.assertEqual(int(response.status), 200)
- self.assertTrue(response.getheader('connection') != 'close')
+ self.assertTrue(response.getheader("connection") != "close")
def test_keepalive_http11_explicit(self):
# Explicitly set keep-alive
@@ -419,7 +410,7 @@ class EchoTests(object):
response = httplib.HTTPResponse(self.sock)
response.begin()
self.assertEqual(int(response.status), 200)
- self.assertTrue(response.getheader('connection') != 'close')
+ self.assertTrue(response.getheader("connection") != "close")
def test_keepalive_http11_connclose(self):
# specifying Connection: close explicitly
@@ -436,7 +427,7 @@ class EchoTests(object):
response = httplib.HTTPResponse(self.sock)
response.begin()
self.assertEqual(int(response.status), 200)
- self.assertEqual(response.getheader('connection'), 'close')
+ self.assertEqual(response.getheader("connection"), "close")
def test_proxy_headers(self):
to_send = (
@@ -450,61 +441,64 @@ class EchoTests(object):
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ 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')
+ 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_body_only)
def tearDown(self):
self.stop_subprocess()
def test_pipelining(self):
- s = ("GET / HTTP/1.0\r\n"
- "Connection: %s\r\n"
- "Content-Length: %d\r\n"
- "\r\n"
- "%s")
- to_send = b''
+ s = (
+ "GET / HTTP/1.0\r\n"
+ "Connection: %s\r\n"
+ "Content-Length: %d\r\n"
+ "\r\n"
+ "%s"
+ )
+ to_send = b""
count = 25
for n in range(count):
body = "Response #%d\r\n" % (n + 1)
if n + 1 < count:
- conn = 'keep-alive'
+ conn = "keep-alive"
else:
- conn = 'close'
+ conn = "close"
to_send += tobytes(s % (conn, len(body), body))
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
for n in range(count):
expect_body = tobytes("Response #%d\r\n" % (n + 1))
- line = fp.readline() # status line
+ line = fp.readline() # status line
version, status, reason = (x.strip() for x in line.split(None, 2))
headers = parse_headers(fp)
- length = int(headers.get('content-length')) or None
+ length = int(headers.get("content-length")) or None
response_body = fp.read(length)
self.assertEqual(int(status), 200)
self.assertEqual(length, len(response_body))
self.assertEqual(response_body, expect_body)
-class ExpectContinueTests(object):
+class ExpectContinueTests(object):
def setUp(self):
from waitress.tests.fixtureapps import echo
+
self.start_subprocess(echo.app_body_only)
def tearDown(self):
@@ -523,26 +517,27 @@ class ExpectContinueTests(object):
)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
- line = fp.readline() # continue status line
+ fp = self.sock.makefile("rb", 0)
+ line = fp.readline() # continue status line
version, status, reason = (x.strip() for x in line.split(None, 2))
self.assertEqual(int(status), 100)
- self.assertEqual(reason, b'Continue')
- self.assertEqual(version, b'HTTP/1.1')
- fp.readline() # blank line
- line = fp.readline() # next status line
+ self.assertEqual(reason, b"Continue")
+ self.assertEqual(version, b"HTTP/1.1")
+ fp.readline() # blank line
+ line = fp.readline() # next status line
version, status, reason = (x.strip() for x in line.split(None, 2))
headers = parse_headers(fp)
- length = int(headers.get('content-length')) or None
+ length = int(headers.get("content-length")) or None
response_body = fp.read(length)
self.assertEqual(int(status), 200)
self.assertEqual(length, len(response_body))
self.assertEqual(response_body, tobytes(data))
-class BadContentLengthTests(object):
+class BadContentLengthTests(object):
def setUp(self):
from waitress.tests.fixtureapps import badcl
+
self.start_subprocess(badcl.app)
def tearDown(self):
@@ -559,16 +554,16 @@ class BadContentLengthTests(object):
)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
- line = fp.readline() # status line
+ fp = self.sock.makefile("rb", 0)
+ line = fp.readline() # status line
version, status, reason = (x.strip() for x in line.split(None, 2))
headers = parse_headers(fp)
- content_length = int(headers.get('content-length'))
+ content_length = int(headers.get("content-length"))
response_body = fp.read(content_length)
self.assertEqual(int(status), 200)
self.assertNotEqual(content_length, len(response_body))
self.assertEqual(len(response_body), content_length - 1)
- self.assertEqual(response_body, tobytes('abcdefghi'))
+ self.assertEqual(response_body, tobytes("abcdefghi"))
# remote closed connection (despite keepalive header); not sure why
# first send succeeds
self.send_check_error(to_send)
@@ -585,29 +580,30 @@ class BadContentLengthTests(object):
)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
- line = fp.readline() # status line
+ fp = self.sock.makefile("rb", 0)
+ line = fp.readline() # status line
version, status, reason = (x.strip() for x in line.split(None, 2))
headers = parse_headers(fp)
- content_length = int(headers.get('content-length')) or None
+ content_length = int(headers.get("content-length")) or None
response_body = fp.read(content_length)
self.assertEqual(int(status), 200)
self.assertEqual(content_length, len(response_body))
- self.assertEqual(response_body, tobytes('abcdefgh'))
+ self.assertEqual(response_body, tobytes("abcdefgh"))
# remote does not close connection (keepalive header)
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
- line = fp.readline() # status line
+ fp = self.sock.makefile("rb", 0)
+ line = fp.readline() # status line
version, status, reason = (x.strip() for x in line.split(None, 2))
headers = parse_headers(fp)
- content_length = int(headers.get('content-length')) or None
+ content_length = int(headers.get("content-length")) or None
response_body = fp.read(content_length)
self.assertEqual(int(status), 200)
-class NoContentLengthTests(object):
+class NoContentLengthTests(object):
def setUp(self):
from waitress.tests.fixtureapps import nocl
+
self.start_subprocess(nocl.app)
def tearDown(self):
@@ -615,18 +611,20 @@ class NoContentLengthTests(object):
def test_http10_generator(self):
body = string.ascii_letters
- to_send = ("GET / HTTP/1.0\n"
- "Connection: Keep-Alive\n"
- "Content-Length: %d\n\n" % len(body))
+ to_send = (
+ "GET / HTTP/1.0\n"
+ "Connection: Keep-Alive\n"
+ "Content-Length: %d\n\n" % len(body)
+ )
to_send += body
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- self.assertEqual(headers.get('content-length'), None)
- self.assertEqual(headers.get('connection'), 'close')
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ self.assertEqual(headers.get("content-length"), None)
+ self.assertEqual(headers.get("connection"), "close")
self.assertEqual(response_body, tobytes(body))
# remote closed connection (despite keepalive header), because
# generators cannot have a content-length divined
@@ -635,39 +633,43 @@ class NoContentLengthTests(object):
def test_http10_list(self):
body = string.ascii_letters
- to_send = ("GET /list HTTP/1.0\n"
- "Connection: Keep-Alive\n"
- "Content-Length: %d\n\n" % len(body))
+ to_send = (
+ "GET /list HTTP/1.0\n"
+ "Connection: Keep-Alive\n"
+ "Content-Length: %d\n\n" % len(body)
+ )
to_send += body
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- self.assertEqual(headers['content-length'], str(len(body)))
- self.assertEqual(headers.get('connection'), 'Keep-Alive')
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ self.assertEqual(headers["content-length"], str(len(body)))
+ self.assertEqual(headers.get("connection"), "Keep-Alive")
self.assertEqual(response_body, tobytes(body))
# remote keeps connection open because it divined the content length
# from a length-1 list
self.sock.send(to_send)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
+ self.assertline(line, "200", "OK", "HTTP/1.0")
def test_http10_listlentwo(self):
body = string.ascii_letters
- to_send = ("GET /list_lentwo HTTP/1.0\n"
- "Connection: Keep-Alive\n"
- "Content-Length: %d\n\n" % len(body))
+ to_send = (
+ "GET /list_lentwo HTTP/1.0\n"
+ "Connection: Keep-Alive\n"
+ "Content-Length: %d\n\n" % len(body)
+ )
to_send += body
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- self.assertEqual(headers.get('content-length'), None)
- self.assertEqual(headers.get('connection'), 'close')
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ self.assertEqual(headers.get("content-length"), None)
+ self.assertEqual(headers.get("connection"), "close")
self.assertEqual(response_body, tobytes(body))
# remote closed connection (despite keepalive header), because
# lists of length > 1 cannot have their content length divined
@@ -676,21 +678,20 @@ class NoContentLengthTests(object):
def test_http11_generator(self):
body = string.ascii_letters
- to_send = ("GET / HTTP/1.1\n"
- "Content-Length: %s\n\n" % len(body))
+ to_send = "GET / HTTP/1.1\n" "Content-Length: %s\n\n" % len(body)
to_send += body
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb')
+ fp = self.sock.makefile("rb")
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- expected = b''
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ expected = b""
for chunk in chunks(body, 10):
expected += tobytes(
- '%s\r\n%s\r\n' % (str(hex(len(chunk))[2:].upper()), chunk)
+ "%s\r\n%s\r\n" % (str(hex(len(chunk))[2:].upper()), chunk)
)
- expected += b'0\r\n\r\n'
+ expected += b"0\r\n\r\n"
self.assertEqual(response_body, expected)
# connection is always closed at the end of a chunked response
self.send_check_error(to_send)
@@ -698,49 +699,48 @@ class NoContentLengthTests(object):
def test_http11_list(self):
body = string.ascii_letters
- to_send = ("GET /list HTTP/1.1\n"
- "Content-Length: %d\n\n" % len(body))
+ to_send = "GET /list HTTP/1.1\n" "Content-Length: %d\n\n" % len(body)
to_send += body
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- self.assertEqual(headers['content-length'], str(len(body)))
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ self.assertEqual(headers["content-length"], str(len(body)))
self.assertEqual(response_body, tobytes(body))
# remote keeps connection open because it divined the content length
# from a length-1 list
self.sock.send(to_send)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
+ self.assertline(line, "200", "OK", "HTTP/1.1")
def test_http11_listlentwo(self):
body = string.ascii_letters
- to_send = ("GET /list_lentwo HTTP/1.1\n"
- "Content-Length: %s\n\n" % len(body))
+ to_send = "GET /list_lentwo HTTP/1.1\n" "Content-Length: %s\n\n" % len(body)
to_send += body
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb')
+ fp = self.sock.makefile("rb")
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- expected = b''
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ expected = b""
for chunk in (body[0], body[1:]):
expected += tobytes(
- '%s\r\n%s\r\n' % (str(hex(len(chunk))[2:].upper()), chunk)
+ "%s\r\n%s\r\n" % (str(hex(len(chunk))[2:].upper()), chunk)
)
- expected += b'0\r\n\r\n'
+ expected += b"0\r\n\r\n"
self.assertEqual(response_body, expected)
# connection is always closed at the end of a chunked response
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
-class WriteCallbackTests(object):
+class WriteCallbackTests(object):
def setUp(self):
from waitress.tests.fixtureapps import writecb
+
self.start_subprocess(writecb.app)
def tearDown(self):
@@ -757,15 +757,15 @@ class WriteCallbackTests(object):
)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
# server trusts the content-length header (5)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ cl = int(headers["content-length"])
self.assertEqual(cl, 9)
self.assertNotEqual(cl, len(response_body))
self.assertEqual(len(response_body), cl - 1)
- self.assertEqual(response_body, tobytes('abcdefgh'))
+ self.assertEqual(response_body, tobytes("abcdefgh"))
# remote closed connection (despite keepalive header)
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
@@ -781,17 +781,17 @@ class WriteCallbackTests(object):
)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- content_length = int(headers.get('content-length')) or None
+ content_length = int(headers.get("content-length")) or None
self.assertEqual(content_length, 9)
self.assertEqual(content_length, len(response_body))
- self.assertEqual(response_body, tobytes('abcdefghi'))
+ self.assertEqual(response_body, tobytes("abcdefghi"))
# remote does not close connection (keepalive header)
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
+ self.assertline(line, "200", "OK", "HTTP/1.0")
def test_equal_body(self):
# check server doesnt close connection when body is equal to
@@ -804,18 +804,18 @@ class WriteCallbackTests(object):
)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- content_length = int(headers.get('content-length')) or None
+ content_length = int(headers.get("content-length")) or None
self.assertEqual(content_length, 9)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
+ self.assertline(line, "200", "OK", "HTTP/1.0")
self.assertEqual(content_length, len(response_body))
- self.assertEqual(response_body, tobytes('abcdefghi'))
+ self.assertEqual(response_body, tobytes("abcdefghi"))
# remote does not close connection (keepalive header)
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
+ self.assertline(line, "200", "OK", "HTTP/1.0")
def test_no_content_length(self):
# wtf happens when there's no content-length
@@ -827,42 +827,43 @@ class WriteCallbackTests(object):
)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
- line = fp.readline() # status line
+ fp = self.sock.makefile("rb", 0)
+ line = fp.readline() # status line
line, headers, response_body = read_http(fp)
- content_length = headers.get('content-length')
+ content_length = headers.get("content-length")
self.assertEqual(content_length, None)
- self.assertEqual(response_body, tobytes('abcdefghi'))
+ self.assertEqual(response_body, tobytes("abcdefghi"))
# remote closed connection (despite keepalive header)
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
+
class TooLargeTests(object):
toobig = 1050
def setUp(self):
from waitress.tests.fixtureapps import toolarge
- self.start_subprocess(toolarge.app,
- max_request_header_size=1000,
- max_request_body_size=1000)
+
+ self.start_subprocess(
+ toolarge.app, max_request_header_size=1000, max_request_body_size=1000
+ )
def tearDown(self):
self.stop_subprocess()
def test_request_body_too_large_with_wrong_cl_http10(self):
- body = 'a' * self.toobig
- to_send = ("GET / HTTP/1.0\n"
- "Content-Length: 5\n\n")
+ body = "a" * self.toobig
+ to_send = "GET / HTTP/1.0\n" "Content-Length: 5\n\n"
to_send += body
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb')
+ fp = self.sock.makefile("rb")
# first request succeeds (content-length 5)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
# server trusts the content-length header; no pipelining,
# so request fulfilled, extra bytes are thrown away
@@ -871,177 +872,171 @@ class TooLargeTests(object):
self.assertRaises(ConnectionClosed, read_http, fp)
def test_request_body_too_large_with_wrong_cl_http10_keepalive(self):
- body = 'a' * self.toobig
- to_send = ("GET / HTTP/1.0\n"
- "Content-Length: 5\n"
- "Connection: Keep-Alive\n\n")
+ body = "a" * self.toobig
+ to_send = "GET / HTTP/1.0\n" "Content-Length: 5\n" "Connection: Keep-Alive\n\n"
to_send += body
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb')
+ fp = self.sock.makefile("rb")
# first request succeeds (content-length 5)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
line, headers, response_body = read_http(fp)
- self.assertline(line, '431', 'Request Header Fields Too Large',
- 'HTTP/1.0')
- cl = int(headers['content-length'])
+ self.assertline(line, "431", "Request Header Fields Too Large", "HTTP/1.0")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
def test_request_body_too_large_with_no_cl_http10(self):
- body = 'a' * self.toobig
+ body = "a" * self.toobig
to_send = "GET / HTTP/1.0\n\n"
to_send += body
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
# extra bytes are thrown away (no pipelining), connection closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
def test_request_body_too_large_with_no_cl_http10_keepalive(self):
- body = 'a' * self.toobig
+ body = "a" * self.toobig
to_send = "GET / HTTP/1.0\nConnection: Keep-Alive\n\n"
to_send += body
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
# server trusts the content-length header (assumed zero)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
line, headers, response_body = read_http(fp)
# next response overruns because the extra data appears to be
# header data
- self.assertline(line, '431', 'Request Header Fields Too Large',
- 'HTTP/1.0')
- cl = int(headers['content-length'])
+ self.assertline(line, "431", "Request Header Fields Too Large", "HTTP/1.0")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
def test_request_body_too_large_with_wrong_cl_http11(self):
- body = 'a' * self.toobig
- to_send = ("GET / HTTP/1.1\n"
- "Content-Length: 5\n\n")
+ body = "a" * self.toobig
+ to_send = "GET / HTTP/1.1\n" "Content-Length: 5\n\n"
to_send += body
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb')
+ fp = self.sock.makefile("rb")
# first request succeeds (content-length 5)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
# second response is an error response
line, headers, response_body = read_http(fp)
- self.assertline(line, '431', 'Request Header Fields Too Large',
- 'HTTP/1.0')
- cl = int(headers['content-length'])
+ self.assertline(line, "431", "Request Header Fields Too Large", "HTTP/1.0")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
def test_request_body_too_large_with_wrong_cl_http11_connclose(self):
- body = 'a' * self.toobig
+ body = "a" * self.toobig
to_send = "GET / HTTP/1.1\nContent-Length: 5\nConnection: close\n\n"
to_send += body
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
# server trusts the content-length header (5)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
def test_request_body_too_large_with_no_cl_http11(self):
- body = 'a' * self.toobig
+ body = "a" * self.toobig
to_send = "GET / HTTP/1.1\n\n"
to_send += body
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb')
+ fp = self.sock.makefile("rb")
# server trusts the content-length header (assumed 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
# server assumes pipelined requests due to http/1.1, and the first
# request was assumed c-l 0 because it had no content-length header,
# so entire body looks like the header of the subsequent request
# second response is an error response
line, headers, response_body = read_http(fp)
- self.assertline(line, '431', 'Request Header Fields Too Large',
- 'HTTP/1.0')
- cl = int(headers['content-length'])
+ self.assertline(line, "431", "Request Header Fields Too Large", "HTTP/1.0")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
def test_request_body_too_large_with_no_cl_http11_connclose(self):
- body = 'a' * self.toobig
+ body = "a" * self.toobig
to_send = "GET / HTTP/1.1\nConnection: close\n\n"
to_send += body
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
# server trusts the content-length header (assumed 0)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
def test_request_body_too_large_chunked_encoding(self):
- control_line = "20;\r\n" # 20 hex = 32 dec
- s = 'This string has 32 characters.\r\n'
+ control_line = "20;\r\n" # 20 hex = 32 dec
+ s = "This string has 32 characters.\r\n"
to_send = "GET / HTTP/1.1\nTransfer-Encoding: chunked\n\n"
repeat = control_line + s
to_send += repeat * ((self.toobig // len(repeat)) + 1)
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
# body bytes counter caught a max_request_body_size overrun
- self.assertline(line, '413', 'Request Entity Too Large', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "413", "Request Entity Too Large", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- self.assertEqual(headers['content-type'], 'text/plain')
+ self.assertEqual(headers["content-type"], "text/plain")
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
-class InternalServerErrorTests(object):
+class InternalServerErrorTests(object):
def setUp(self):
from waitress.tests.fixtureapps import error
+
self.start_subprocess(error.app, expose_tracebacks=True)
def tearDown(self):
@@ -1052,13 +1047,13 @@ class InternalServerErrorTests(object):
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '500', 'Internal Server Error', 'HTTP/1.0')
- cl = int(headers['content-length'])
+ self.assertline(line, "500", "Internal Server Error", "HTTP/1.0")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- self.assertTrue(response_body.startswith(b'Internal Server Error'))
- self.assertEqual(headers['connection'], 'close')
+ self.assertTrue(response_body.startswith(b"Internal Server Error"))
+ self.assertEqual(headers["connection"], "close")
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
@@ -1068,34 +1063,36 @@ class InternalServerErrorTests(object):
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '500', 'Internal Server Error', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "500", "Internal Server Error", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- self.assertTrue(response_body.startswith(b'Internal Server Error'))
- self.assertEqual(sorted(headers.keys()),
- ['content-length', 'content-type', 'date', 'server'])
+ self.assertTrue(response_body.startswith(b"Internal Server Error"))
+ self.assertEqual(
+ sorted(headers.keys()), ["content-length", "content-type", "date", "server"]
+ )
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
def test_before_start_response_http_11_close(self):
to_send = tobytes(
- "GET /before_start_response HTTP/1.1\n"
- "Connection: close\n\n")
+ "GET /before_start_response HTTP/1.1\n" "Connection: close\n\n"
+ )
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '500', 'Internal Server Error', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "500", "Internal Server Error", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- self.assertTrue(response_body.startswith(b'Internal Server Error'))
- self.assertEqual(sorted(headers.keys()),
- ['connection', 'content-length', 'content-type', 'date',
- 'server'])
- self.assertEqual(headers['connection'], 'close')
+ self.assertTrue(response_body.startswith(b"Internal Server Error"))
+ self.assertEqual(
+ sorted(headers.keys()),
+ ["connection", "content-length", "content-type", "date", "server"],
+ )
+ self.assertEqual(headers["connection"], "close")
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
@@ -1105,16 +1102,17 @@ class InternalServerErrorTests(object):
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '500', 'Internal Server Error', 'HTTP/1.0')
- cl = int(headers['content-length'])
+ self.assertline(line, "500", "Internal Server Error", "HTTP/1.0")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- self.assertTrue(response_body.startswith(b'Internal Server Error'))
- self.assertEqual(sorted(headers.keys()),
- ['connection', 'content-length', 'content-type', 'date',
- 'server'])
- self.assertEqual(headers['connection'], 'close')
+ self.assertTrue(response_body.startswith(b"Internal Server Error"))
+ self.assertEqual(
+ sorted(headers.keys()),
+ ["connection", "content-length", "content-type", "date", "server"],
+ )
+ self.assertEqual(headers["connection"], "close")
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
@@ -1124,34 +1122,36 @@ class InternalServerErrorTests(object):
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '500', 'Internal Server Error', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "500", "Internal Server Error", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- self.assertTrue(response_body.startswith(b'Internal Server Error'))
- self.assertEqual(sorted(headers.keys()),
- ['content-length', 'content-type', 'date', 'server'])
+ self.assertTrue(response_body.startswith(b"Internal Server Error"))
+ self.assertEqual(
+ sorted(headers.keys()), ["content-length", "content-type", "date", "server"]
+ )
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
def test_after_start_response_http11_close(self):
to_send = tobytes(
- "GET /after_start_response HTTP/1.1\n"
- "Connection: close\n\n")
+ "GET /after_start_response HTTP/1.1\n" "Connection: close\n\n"
+ )
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '500', 'Internal Server Error', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "500", "Internal Server Error", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- self.assertTrue(response_body.startswith(b'Internal Server Error'))
- self.assertEqual(sorted(headers.keys()),
- ['connection', 'content-length', 'content-type', 'date',
- 'server'])
- self.assertEqual(headers['connection'], 'close')
+ self.assertTrue(response_body.startswith(b"Internal Server Error"))
+ self.assertEqual(
+ sorted(headers.keys()),
+ ["connection", "content-length", "content-type", "date", "server"],
+ )
+ self.assertEqual(headers["connection"], "close")
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
@@ -1161,10 +1161,10 @@ class InternalServerErrorTests(object):
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- self.assertEqual(response_body, b'')
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ self.assertEqual(response_body, b"")
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
@@ -1174,18 +1174,19 @@ class InternalServerErrorTests(object):
to_send = tobytes(to_send)
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- self.assertEqual(response_body, b'')
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ self.assertEqual(response_body, b"")
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
-class FileWrapperTests(object):
+class FileWrapperTests(object):
def setUp(self):
from waitress.tests.fixtureapps import filewrapper
+
self.start_subprocess(filewrapper.app)
def tearDown(self):
@@ -1199,14 +1200,14 @@ class FileWrapperTests(object):
for t in range(0, 2):
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- ct = headers['content-type']
- self.assertEqual(ct, 'image/jpeg')
- self.assertTrue(b'\377\330\377' in response_body)
+ ct = headers["content-type"]
+ self.assertEqual(ct, "image/jpeg")
+ self.assertTrue(b"\377\330\377" in response_body)
# connection has not been closed
def test_filelike_nocl_http11(self):
@@ -1217,14 +1218,14 @@ class FileWrapperTests(object):
for t in range(0, 2):
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- ct = headers['content-type']
- self.assertEqual(ct, 'image/jpeg')
- self.assertTrue(b'\377\330\377' in response_body)
+ ct = headers["content-type"]
+ self.assertEqual(ct, "image/jpeg")
+ self.assertTrue(b"\377\330\377" in response_body)
# connection has not been closed
def test_filelike_shortcl_http11(self):
@@ -1235,15 +1236,15 @@ class FileWrapperTests(object):
for t in range(0, 2):
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, 1)
self.assertEqual(cl, len(response_body))
- ct = headers['content-type']
- self.assertEqual(ct, 'image/jpeg')
- self.assertTrue(b'\377' in response_body)
+ ct = headers["content-type"]
+ self.assertEqual(ct, "image/jpeg")
+ self.assertTrue(b"\377" in response_body)
# connection has not been closed
def test_filelike_longcl_http11(self):
@@ -1254,14 +1255,14 @@ class FileWrapperTests(object):
for t in range(0, 2):
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- ct = headers['content-type']
- self.assertEqual(ct, 'image/jpeg')
- self.assertTrue(b'\377\330\377' in response_body)
+ ct = headers["content-type"]
+ self.assertEqual(ct, "image/jpeg")
+ self.assertTrue(b"\377\330\377" in response_body)
# connection has not been closed
def test_notfilelike_http11(self):
@@ -1272,14 +1273,14 @@ class FileWrapperTests(object):
for t in range(0, 2):
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- ct = headers['content-type']
- self.assertEqual(ct, 'image/jpeg')
- self.assertTrue(b'\377\330\377' in response_body)
+ ct = headers["content-type"]
+ self.assertEqual(ct, "image/jpeg")
+ self.assertTrue(b"\377\330\377" in response_body)
# connection has not been closed
def test_notfilelike_iobase_http11(self):
@@ -1290,14 +1291,14 @@ class FileWrapperTests(object):
for t in range(0, 2):
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- ct = headers['content-type']
- self.assertEqual(ct, 'image/jpeg')
- self.assertTrue(b'\377\330\377' in response_body)
+ ct = headers["content-type"]
+ self.assertEqual(ct, "image/jpeg")
+ self.assertTrue(b"\377\330\377" in response_body)
# connection has not been closed
def test_notfilelike_nocl_http11(self):
@@ -1307,12 +1308,12 @@ class FileWrapperTests(object):
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- ct = headers['content-type']
- self.assertEqual(ct, 'image/jpeg')
- self.assertTrue(b'\377\330\377' in response_body)
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ ct = headers["content-type"]
+ self.assertEqual(ct, "image/jpeg")
+ self.assertTrue(b"\377\330\377" in response_body)
# connection has been closed (no content-length)
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
@@ -1325,15 +1326,15 @@ class FileWrapperTests(object):
for t in range(0, 2):
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, 1)
self.assertEqual(cl, len(response_body))
- ct = headers['content-type']
- self.assertEqual(ct, 'image/jpeg')
- self.assertTrue(b'\377' in response_body)
+ ct = headers["content-type"]
+ self.assertEqual(ct, "image/jpeg")
+ self.assertTrue(b"\377" in response_body)
# connection has not been closed
def test_notfilelike_longcl_http11(self):
@@ -1343,14 +1344,14 @@ class FileWrapperTests(object):
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.1')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.1")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body) + 10)
- ct = headers['content-type']
- self.assertEqual(ct, 'image/jpeg')
- self.assertTrue(b'\377\330\377' in response_body)
+ ct = headers["content-type"]
+ self.assertEqual(ct, "image/jpeg")
+ self.assertTrue(b"\377\330\377" in response_body)
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
@@ -1362,14 +1363,14 @@ class FileWrapperTests(object):
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- ct = headers['content-type']
- self.assertEqual(ct, 'image/jpeg')
- self.assertTrue(b'\377\330\377' in response_body)
+ ct = headers["content-type"]
+ self.assertEqual(ct, "image/jpeg")
+ self.assertTrue(b"\377\330\377" in response_body)
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
@@ -1381,14 +1382,14 @@ class FileWrapperTests(object):
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- ct = headers['content-type']
- self.assertEqual(ct, 'image/jpeg')
- self.assertTrue(b'\377\330\377' in response_body)
+ ct = headers["content-type"]
+ self.assertEqual(ct, "image/jpeg")
+ self.assertTrue(b"\377\330\377" in response_body)
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
@@ -1400,14 +1401,14 @@ class FileWrapperTests(object):
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- cl = int(headers['content-length'])
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
- ct = headers['content-type']
- self.assertEqual(ct, 'image/jpeg')
- self.assertTrue(b'\377\330\377' in response_body)
+ ct = headers["content-type"]
+ self.assertEqual(ct, "image/jpeg")
+ self.assertTrue(b"\377\330\377" in response_body)
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
@@ -1419,58 +1420,67 @@ class FileWrapperTests(object):
self.connect()
self.sock.send(to_send)
- fp = self.sock.makefile('rb', 0)
+ fp = self.sock.makefile("rb", 0)
line, headers, response_body = read_http(fp)
- self.assertline(line, '200', 'OK', 'HTTP/1.0')
- ct = headers['content-type']
- self.assertEqual(ct, 'image/jpeg')
- self.assertTrue(b'\377\330\377' in response_body)
+ self.assertline(line, "200", "OK", "HTTP/1.0")
+ ct = headers["content-type"]
+ self.assertEqual(ct, "image/jpeg")
+ self.assertTrue(b"\377\330\377" in response_body)
# connection has been closed (no content-length)
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
+
class TcpEchoTests(EchoTests, TcpTests, unittest.TestCase):
pass
+
class TcpPipeliningTests(PipeliningTests, TcpTests, unittest.TestCase):
pass
+
class TcpExpectContinueTests(ExpectContinueTests, TcpTests, unittest.TestCase):
pass
-class TcpBadContentLengthTests(
- BadContentLengthTests, TcpTests, unittest.TestCase):
+
+class TcpBadContentLengthTests(BadContentLengthTests, TcpTests, unittest.TestCase):
pass
-class TcpNoContentLengthTests(
- NoContentLengthTests, TcpTests, unittest.TestCase):
+
+class TcpNoContentLengthTests(NoContentLengthTests, TcpTests, unittest.TestCase):
pass
+
class TcpWriteCallbackTests(WriteCallbackTests, TcpTests, unittest.TestCase):
pass
+
class TcpTooLargeTests(TooLargeTests, TcpTests, unittest.TestCase):
pass
+
class TcpInternalServerErrorTests(
- InternalServerErrorTests, TcpTests, unittest.TestCase):
+ InternalServerErrorTests, TcpTests, unittest.TestCase
+):
pass
+
class TcpFileWrapperTests(FileWrapperTests, TcpTests, unittest.TestCase):
pass
-if hasattr(socket, 'AF_UNIX'):
+
+if hasattr(socket, "AF_UNIX"):
class FixtureUnixWSGIServer(server.UnixWSGIServer):
"""A version of UnixWSGIServer that relays back what it's bound to.
"""
- family = socket.AF_UNIX # Testing
+ family = socket.AF_UNIX # Testing
- def __init__(self, application, queue, **kw): # pragma: no cover
+ def __init__(self, application, queue, **kw): # pragma: no cover
# Coverage doesn't see this as it's ran in a separate process.
# To permit parallel testing, use a PID-dependent socket.
- kw['unix_socket'] = '/tmp/waitress.test-%d.sock' % os.getpid()
+ kw["unix_socket"] = "/tmp/waitress.test-%d.sock" % os.getpid()
super(FixtureUnixWSGIServer, self).__init__(application, **kw)
queue.put(self.socket.getsockname())
@@ -1499,51 +1509,52 @@ if hasattr(socket, 'AF_UNIX'):
class UnixPipeliningTests(PipeliningTests, UnixTests, unittest.TestCase):
pass
- class UnixExpectContinueTests(
- ExpectContinueTests, UnixTests, unittest.TestCase):
+ class UnixExpectContinueTests(ExpectContinueTests, UnixTests, unittest.TestCase):
pass
class UnixBadContentLengthTests(
- BadContentLengthTests, UnixTests, unittest.TestCase):
+ BadContentLengthTests, UnixTests, unittest.TestCase
+ ):
pass
- class UnixNoContentLengthTests(
- NoContentLengthTests, UnixTests, unittest.TestCase):
+ class UnixNoContentLengthTests(NoContentLengthTests, UnixTests, unittest.TestCase):
pass
- class UnixWriteCallbackTests(
- WriteCallbackTests, UnixTests, unittest.TestCase):
+ class UnixWriteCallbackTests(WriteCallbackTests, UnixTests, unittest.TestCase):
pass
class UnixTooLargeTests(TooLargeTests, UnixTests, unittest.TestCase):
pass
class UnixInternalServerErrorTests(
- InternalServerErrorTests, UnixTests, unittest.TestCase):
+ InternalServerErrorTests, UnixTests, unittest.TestCase
+ ):
pass
class UnixFileWrapperTests(FileWrapperTests, UnixTests, unittest.TestCase):
pass
+
def parse_headers(fp):
"""Parses only RFC2822 headers from a file pointer.
"""
headers = {}
while True:
line = fp.readline()
- if line in (b'\r\n', b'\n', b''):
+ if line in (b"\r\n", b"\n", b""):
break
- line = line.decode('iso-8859-1')
- name, value = line.strip().split(':', 1)
+ line = line.decode("iso-8859-1")
+ name, value = line.strip().split(":", 1)
headers[name.lower().strip()] = value.lower().strip()
return headers
+
class UnixHTTPConnection(httplib.HTTPConnection):
"""Patched version of HTTPConnection that uses Unix domain sockets.
"""
def __init__(self, path):
- httplib.HTTPConnection.__init__(self, 'localhost')
+ httplib.HTTPConnection.__init__(self, "localhost")
self.path = path
def connect(self):
@@ -1551,11 +1562,13 @@ class UnixHTTPConnection(httplib.HTTPConnection):
sock.connect(self.path)
self.sock = sock
+
class ConnectionClosed(Exception):
pass
+
# stolen from gevent
-def read_http(fp): # pragma: no cover
+def read_http(fp): # pragma: no cover
try:
response_line = fp.readline()
except socket.error as exc:
@@ -1570,7 +1583,7 @@ def read_http(fp): # pragma: no cover
header_lines = []
while True:
line = fp.readline()
- if line in (b'\r\n', b'\n', b''):
+ if line in (b"\r\n", b"\n", b""):
break
else:
header_lines.append(line)
@@ -1579,15 +1592,15 @@ def read_http(fp): # pragma: no cover
x = x.strip()
if not x:
continue
- key, value = x.split(b': ', 1)
- key = key.decode('iso-8859-1').lower()
- value = value.decode('iso-8859-1')
+ key, value = x.split(b": ", 1)
+ key = key.decode("iso-8859-1").lower()
+ value = value.decode("iso-8859-1")
assert key not in headers, "%s header duplicated" % key
headers[key] = value
- if 'content-length' in headers:
- num = int(headers['content-length'])
- body = b''
+ if "content-length" in headers:
+ num = int(headers["content-length"])
+ body = b""
left = num
while left > 0:
data = fp.read(left)
@@ -1601,8 +1614,9 @@ def read_http(fp): # pragma: no cover
return response_line, headers, body
+
# stolen from gevent
-def get_errno(exc): # pragma: no cover
+def get_errno(exc): # pragma: no cover
""" Get the error code out of socket.error objects.
socket.error in <2.5 does not have errno attribute
socket.error in 3.x does not allow indexing access
@@ -1621,8 +1635,9 @@ def get_errno(exc): # pragma: no cover
except IndexError:
return None
+
def chunks(l, n):
""" Yield successive n-sized chunks from l.
"""
for i in range(0, len(l), n):
- yield l[i:i + n]
+ yield l[i : i + n]
diff --git a/waitress/tests/test_init.py b/waitress/tests/test_init.py
index 9fbd31a..f9b91d7 100644
--- a/waitress/tests/test_init.py
+++ b/waitress/tests/test_init.py
@@ -1,9 +1,10 @@
import unittest
-class Test_serve(unittest.TestCase):
+class Test_serve(unittest.TestCase):
def _callFUT(self, app, **kw):
from waitress import serve
+
return serve(app, **kw)
def test_it(self):
@@ -16,9 +17,9 @@ class Test_serve(unittest.TestCase):
class Test_serve_paste(unittest.TestCase):
-
def _callFUT(self, app, **kw):
from waitress import serve_paste
+
return serve_paste(app, None, **kw)
def test_it(self):
@@ -29,6 +30,7 @@ class Test_serve_paste(unittest.TestCase):
self.assertEqual(result, 0)
self.assertEqual(server.ran, True)
+
class DummyServerFactory(object):
ran = False
@@ -41,6 +43,7 @@ class DummyServerFactory(object):
def run(self):
self.ran = True
+
class DummyAdj(object):
verbose = False
diff --git a/waitress/tests/test_parser.py b/waitress/tests/test_parser.py
index 920de96..2e81981 100644
--- a/waitress/tests/test_parser.py
+++ b/waitress/tests/test_parser.py
@@ -20,18 +20,19 @@ from waitress.compat import (
tobytes,
)
-class TestHTTPRequestParser(unittest.TestCase):
+class TestHTTPRequestParser(unittest.TestCase):
def setUp(self):
from waitress.parser import HTTPRequestParser
from waitress.adjustments import Adjustments
+
my_adj = Adjustments()
self.parser = HTTPRequestParser(my_adj)
def test_get_body_stream_None(self):
self.parser.body_recv = None
result = self.parser.get_body_stream()
- self.assertEqual(result.getvalue(), b'')
+ self.assertEqual(result.getvalue(), b"")
def test_get_body_stream_nonNone(self):
body_rcv = DummyBodyStream()
@@ -52,6 +53,7 @@ HTTP/1.0 GET /foobar
def test_received_bad_host_header(self):
from waitress.utilities import BadRequest
+
data = b"""\
HTTP/1.0 GET /foobar
Host: foo
@@ -84,11 +86,12 @@ GET /foobar HTTP/8.4
def test_received_already_completed(self):
self.parser.completed = True
- result = self.parser.received(b'a')
+ result = self.parser.received(b"a")
self.assertEqual(result, 0)
def test_received_cl_too_large(self):
from waitress.utilities import RequestEntityTooLarge
+
self.parser.adj.max_request_body_size = 2
data = b"""\
GET /foobar HTTP/8.4
@@ -102,6 +105,7 @@ Content-Length: 10
def test_received_headers_too_large(self):
from waitress.utilities import RequestHeaderFieldsTooLarge
+
self.parser.adj.max_request_header_size = 2
data = b"""\
GET /foobar HTTP/8.4
@@ -110,11 +114,11 @@ X-Foo: 1
result = self.parser.received(data)
self.assertEqual(result, 30)
self.assertTrue(self.parser.completed)
- self.assertTrue(isinstance(self.parser.error,
- RequestHeaderFieldsTooLarge))
+ self.assertTrue(isinstance(self.parser.error, RequestHeaderFieldsTooLarge))
def test_received_body_too_large(self):
from waitress.utilities import RequestEntityTooLarge
+
self.parser.adj.max_request_body_size = 2
data = b"""\
GET /foobar HTTP/1.1
@@ -128,11 +132,11 @@ This string has 32 characters\r\n
self.assertEqual(result, 58)
self.parser.received(data[result:])
self.assertTrue(self.parser.completed)
- self.assertTrue(isinstance(self.parser.error,
- RequestEntityTooLarge))
+ self.assertTrue(isinstance(self.parser.error, RequestEntityTooLarge))
def test_received_error_from_parser(self):
from waitress.utilities import BadRequest
+
data = b"""\
GET /foobar HTTP/1.1
Transfer-Encoding: chunked
@@ -146,8 +150,7 @@ garbage
result = self.parser.received(data[result:])
self.assertEqual(result, 8)
self.assertTrue(self.parser.completed)
- self.assertTrue(isinstance(self.parser.error,
- BadRequest))
+ self.assertTrue(isinstance(self.parser.error, BadRequest))
def test_received_chunked_completed_sets_content_length(self):
data = b"""\
@@ -164,15 +167,15 @@ This string has 32 characters\r\n
result = self.parser.received(data)
self.assertTrue(self.parser.completed)
self.assertTrue(self.parser.error is None)
- self.assertEqual(self.parser.headers['CONTENT_LENGTH'], '32')
-
+ self.assertEqual(self.parser.headers["CONTENT_LENGTH"], "32")
+
def test_parse_header_gardenpath(self):
data = b"""\
GET /foobar HTTP/8.4
foo: bar"""
self.parser.parse_header(data)
- self.assertEqual(self.parser.first_line, b'GET /foobar HTTP/8.4')
- self.assertEqual(self.parser.headers['FOO'], 'bar')
+ self.assertEqual(self.parser.first_line, b"GET /foobar HTTP/8.4")
+ self.assertEqual(self.parser.headers["FOO"], "bar")
def test_parse_header_no_cr_in_headerplus(self):
data = b"GET /foobar HTTP/8.4"
@@ -188,8 +191,7 @@ foo: bar"""
# NB: test that capitalization of header value is unimportant
data = b"GET /foobar HTTP/1.1\ntransfer-encoding: ChUnKed"
self.parser.parse_header(data)
- self.assertEqual(self.parser.body_rcv.__class__.__name__,
- 'ChunkedReceiver')
+ self.assertEqual(self.parser.body_rcv.__class__.__name__, "ChunkedReceiver")
def test_parse_header_11_expect_continue(self):
data = b"GET /foobar HTTP/1.1\nexpect: 100-continue"
@@ -209,89 +211,95 @@ foo: bar"""
def test_close_with_no_body_rcv(self):
self.parser.body_rcv = None
- self.parser.close() # doesn't raise
+ self.parser.close() # doesn't raise
-class Test_split_uri(unittest.TestCase):
+class Test_split_uri(unittest.TestCase):
def _callFUT(self, uri):
from waitress.parser import split_uri
- (self.proxy_scheme,
- self.proxy_netloc,
- self.path,
- self.query, self.fragment) = split_uri(uri)
+
+ (
+ self.proxy_scheme,
+ self.proxy_netloc,
+ self.path,
+ self.query,
+ self.fragment,
+ ) = split_uri(uri)
def test_split_uri_unquoting_unneeded(self):
- self._callFUT(b'http://localhost:8080/abc def')
- self.assertEqual(self.path, '/abc def')
+ self._callFUT(b"http://localhost:8080/abc def")
+ self.assertEqual(self.path, "/abc def")
def test_split_uri_unquoting_needed(self):
- self._callFUT(b'http://localhost:8080/abc%20def')
- self.assertEqual(self.path, '/abc def')
+ self._callFUT(b"http://localhost:8080/abc%20def")
+ self.assertEqual(self.path, "/abc def")
def test_split_url_with_query(self):
- self._callFUT(b'http://localhost:8080/abc?a=1&b=2')
- self.assertEqual(self.path, '/abc')
- self.assertEqual(self.query, 'a=1&b=2')
+ self._callFUT(b"http://localhost:8080/abc?a=1&b=2")
+ self.assertEqual(self.path, "/abc")
+ self.assertEqual(self.query, "a=1&b=2")
def test_split_url_with_query_empty(self):
- self._callFUT(b'http://localhost:8080/abc?')
- self.assertEqual(self.path, '/abc')
- self.assertEqual(self.query, '')
+ self._callFUT(b"http://localhost:8080/abc?")
+ self.assertEqual(self.path, "/abc")
+ self.assertEqual(self.query, "")
def test_split_url_with_fragment(self):
- self._callFUT(b'http://localhost:8080/#foo')
- self.assertEqual(self.path, '/')
- self.assertEqual(self.fragment, 'foo')
+ self._callFUT(b"http://localhost:8080/#foo")
+ self.assertEqual(self.path, "/")
+ self.assertEqual(self.fragment, "foo")
def test_split_url_https(self):
- self._callFUT(b'https://localhost:8080/')
- self.assertEqual(self.path, '/')
- self.assertEqual(self.proxy_scheme, 'https')
- self.assertEqual(self.proxy_netloc, 'localhost:8080')
+ self._callFUT(b"https://localhost:8080/")
+ self.assertEqual(self.path, "/")
+ self.assertEqual(self.proxy_scheme, "https")
+ self.assertEqual(self.proxy_netloc, "localhost:8080")
def test_split_uri_unicode_error_raises_parsing_error(self):
# See https://github.com/Pylons/waitress/issues/64
from waitress.parser import ParsingError
+
# Either pass or throw a ParsingError, just don't throw another type of
# exception as that will cause the connection to close badly:
try:
- self._callFUT(b'/\xd0')
+ self._callFUT(b"/\xd0")
except ParsingError:
pass
def test_split_uri_path(self):
- self._callFUT(b'//testing/whatever')
- self.assertEqual(self.path, '//testing/whatever')
- self.assertEqual(self.proxy_scheme, '')
- self.assertEqual(self.proxy_netloc, '')
- self.assertEqual(self.query, '')
- self.assertEqual(self.fragment, '')
+ self._callFUT(b"//testing/whatever")
+ self.assertEqual(self.path, "//testing/whatever")
+ self.assertEqual(self.proxy_scheme, "")
+ self.assertEqual(self.proxy_netloc, "")
+ self.assertEqual(self.query, "")
+ self.assertEqual(self.fragment, "")
def test_split_uri_path_query(self):
- self._callFUT(b'//testing/whatever?a=1&b=2')
- self.assertEqual(self.path, '//testing/whatever')
- self.assertEqual(self.proxy_scheme, '')
- self.assertEqual(self.proxy_netloc, '')
- self.assertEqual(self.query, 'a=1&b=2')
- self.assertEqual(self.fragment, '')
+ self._callFUT(b"//testing/whatever?a=1&b=2")
+ self.assertEqual(self.path, "//testing/whatever")
+ self.assertEqual(self.proxy_scheme, "")
+ self.assertEqual(self.proxy_netloc, "")
+ self.assertEqual(self.query, "a=1&b=2")
+ self.assertEqual(self.fragment, "")
def test_split_uri_path_query_fragment(self):
- self._callFUT(b'//testing/whatever?a=1&b=2#fragment')
- self.assertEqual(self.path, '//testing/whatever')
- self.assertEqual(self.proxy_scheme, '')
- self.assertEqual(self.proxy_netloc, '')
- self.assertEqual(self.query, 'a=1&b=2')
- self.assertEqual(self.fragment, 'fragment')
+ self._callFUT(b"//testing/whatever?a=1&b=2#fragment")
+ self.assertEqual(self.path, "//testing/whatever")
+ self.assertEqual(self.proxy_scheme, "")
+ self.assertEqual(self.proxy_netloc, "")
+ self.assertEqual(self.query, "a=1&b=2")
+ self.assertEqual(self.fragment, "fragment")
-class Test_get_header_lines(unittest.TestCase):
+class Test_get_header_lines(unittest.TestCase):
def _callFUT(self, data):
from waitress.parser import get_header_lines
+
return get_header_lines(data)
def test_get_header_lines(self):
- result = self._callFUT(b'slam\nslim')
- self.assertEqual(result, [b'slam', b'slim'])
+ result = self._callFUT(b"slam\nslim")
+ self.assertEqual(result, [b"slam", b"slim"])
def test_get_header_lines_folded(self):
# From RFC2616:
@@ -302,60 +310,63 @@ class Test_get_header_lines(unittest.TestCase):
# interpreting the field value or forwarding the message downstream.
# We are just preserving the whitespace that indicates folding.
- result = self._callFUT(b'slim\n slam')
- self.assertEqual(result, [b'slim slam'])
+ result = self._callFUT(b"slim\n slam")
+ self.assertEqual(result, [b"slim slam"])
def test_get_header_lines_tabbed(self):
- result = self._callFUT(b'slam\n\tslim')
- self.assertEqual(result, [b'slam\tslim'])
+ result = self._callFUT(b"slam\n\tslim")
+ self.assertEqual(result, [b"slam\tslim"])
def test_get_header_lines_malformed(self):
# https://corte.si/posts/code/pathod/pythonservers/index.html
from waitress.parser import ParsingError
- self.assertRaises(ParsingError,
- self._callFUT, b' Host: localhost\r\n\r\n')
-class Test_crack_first_line(unittest.TestCase):
+ self.assertRaises(ParsingError, self._callFUT, b" Host: localhost\r\n\r\n")
+
+class Test_crack_first_line(unittest.TestCase):
def _callFUT(self, line):
from waitress.parser import crack_first_line
+
return crack_first_line(line)
def test_crack_first_line_matchok(self):
- result = self._callFUT(b'GET / HTTP/1.0')
- self.assertEqual(result, (b'GET', b'/', b'1.0'))
+ result = self._callFUT(b"GET / HTTP/1.0")
+ self.assertEqual(result, (b"GET", b"/", b"1.0"))
def test_crack_first_line_lowercase_method(self):
from waitress.parser import ParsingError
- self.assertRaises(ParsingError, self._callFUT, b'get / HTTP/1.0')
+
+ self.assertRaises(ParsingError, self._callFUT, b"get / HTTP/1.0")
def test_crack_first_line_nomatch(self):
- result = self._callFUT(b'GET / bleh')
- self.assertEqual(result, (b'', b'', b''))
+ result = self._callFUT(b"GET / bleh")
+ self.assertEqual(result, (b"", b"", b""))
- result = self._callFUT(b'GET /info?txtAirPlay&txtRAOP RTSP/1.0')
- self.assertEqual(result, (b'', b'', b''))
+ result = self._callFUT(b"GET /info?txtAirPlay&txtRAOP RTSP/1.0")
+ self.assertEqual(result, (b"", b"", b""))
def test_crack_first_line_missing_version(self):
- result = self._callFUT(b'GET /')
- self.assertEqual(result, (b'GET', b'/', b''))
+ result = self._callFUT(b"GET /")
+ self.assertEqual(result, (b"GET", b"/", b""))
-class TestHTTPRequestParserIntegration(unittest.TestCase):
+class TestHTTPRequestParserIntegration(unittest.TestCase):
def setUp(self):
from waitress.parser import HTTPRequestParser
from waitress.adjustments import Adjustments
+
my_adj = Adjustments()
self.parser = HTTPRequestParser(my_adj)
def feed(self, data):
parser = self.parser
- for n in range(100): # make sure we never loop forever
+ for n in range(100): # make sure we never loop forever
consumed = parser.received(data)
data = data[consumed:]
if parser.completed:
return
- raise ValueError('Looping') # pragma: no cover
+ raise ValueError("Looping") # pragma: no cover
def testSimpleGET(self):
data = b"""\
@@ -369,19 +380,18 @@ Hello.
parser = self.parser
self.feed(data)
self.assertTrue(parser.completed)
- self.assertEqual(parser.version, '8.4')
+ self.assertEqual(parser.version, "8.4")
self.assertFalse(parser.empty)
- self.assertEqual(parser.headers,
- {'FIRSTNAME': 'mickey',
- 'LASTNAME': 'Mouse',
- 'CONTENT_LENGTH': '7',
- })
- self.assertEqual(parser.path, '/foobar')
- self.assertEqual(parser.command, 'GET')
- self.assertEqual(parser.query, '')
- self.assertEqual(parser.proxy_scheme, '')
- self.assertEqual(parser.proxy_netloc, '')
- self.assertEqual(parser.get_body_stream().getvalue(), b'Hello.\n')
+ self.assertEqual(
+ parser.headers,
+ {"FIRSTNAME": "mickey", "LASTNAME": "Mouse", "CONTENT_LENGTH": "7",},
+ )
+ self.assertEqual(parser.path, "/foobar")
+ self.assertEqual(parser.command, "GET")
+ self.assertEqual(parser.query, "")
+ self.assertEqual(parser.proxy_scheme, "")
+ self.assertEqual(parser.proxy_netloc, "")
+ self.assertEqual(parser.get_body_stream().getvalue(), b"Hello.\n")
def testComplexGET(self):
data = b"""\
@@ -394,20 +404,22 @@ Hello mickey.
"""
parser = self.parser
self.feed(data)
- self.assertEqual(parser.command, 'GET')
- self.assertEqual(parser.version, '8.4')
+ self.assertEqual(parser.command, "GET")
+ self.assertEqual(parser.version, "8.4")
self.assertFalse(parser.empty)
- self.assertEqual(parser.headers,
- {'FIRSTNAME': 'mickey',
- 'LASTNAME': 'Mouse',
- 'CONTENT_LENGTH': '10',
- })
+ self.assertEqual(
+ parser.headers,
+ {"FIRSTNAME": "mickey", "LASTNAME": "Mouse", "CONTENT_LENGTH": "10",},
+ )
# path should be utf-8 encoded
- self.assertEqual(tobytes(parser.path).decode('utf-8'),
- text_(b'/foo/a++/\xc3\xa4=&a:int', 'utf-8'))
- self.assertEqual(parser.query,
- 'd=b+%2B%2F%3D%26b%3Aint&c+%2B%2F%3D%26c%3Aint=6')
- self.assertEqual(parser.get_body_stream().getvalue(), b'Hello mick')
+ self.assertEqual(
+ tobytes(parser.path).decode("utf-8"),
+ text_(b"/foo/a++/\xc3\xa4=&a:int", "utf-8"),
+ )
+ self.assertEqual(
+ parser.query, "d=b+%2B%2F%3D%26b%3Aint&c+%2B%2F%3D%26c%3Aint=6"
+ )
+ self.assertEqual(parser.get_body_stream().getvalue(), b"Hello mick")
def testProxyGET(self):
data = b"""\
@@ -419,18 +431,16 @@ Hello.
parser = self.parser
self.feed(data)
self.assertTrue(parser.completed)
- self.assertEqual(parser.version, '8.4')
+ self.assertEqual(parser.version, "8.4")
self.assertFalse(parser.empty)
- self.assertEqual(parser.headers,
- {'CONTENT_LENGTH': '7',
- })
- self.assertEqual(parser.path, '/foobar')
- self.assertEqual(parser.command, 'GET')
- self.assertEqual(parser.proxy_scheme, 'https')
- self.assertEqual(parser.proxy_netloc, 'example.com:8080')
- self.assertEqual(parser.command, 'GET')
- self.assertEqual(parser.query, '')
- self.assertEqual(parser.get_body_stream().getvalue(), b'Hello.\n')
+ self.assertEqual(parser.headers, {"CONTENT_LENGTH": "7",})
+ self.assertEqual(parser.path, "/foobar")
+ self.assertEqual(parser.command, "GET")
+ self.assertEqual(parser.proxy_scheme, "https")
+ self.assertEqual(parser.proxy_netloc, "example.com:8080")
+ self.assertEqual(parser.command, "GET")
+ self.assertEqual(parser.query, "")
+ self.assertEqual(parser.get_body_stream().getvalue(), b"Hello.\n")
def testDuplicateHeaders(self):
# Ensure that headers with the same key get concatenated as per
@@ -446,11 +456,13 @@ Hello.
"""
self.feed(data)
self.assertTrue(self.parser.completed)
- self.assertEqual(self.parser.headers, {
- 'CONTENT_LENGTH': '7',
- 'X_FORWARDED_FOR':
- '10.11.12.13, unknown,127.0.0.1',
- })
+ self.assertEqual(
+ self.parser.headers,
+ {
+ "CONTENT_LENGTH": "7",
+ "X_FORWARDED_FOR": "10.11.12.13, unknown,127.0.0.1",
+ },
+ )
def testSpoofedHeadersDropped(self):
data = b"""\
@@ -462,13 +474,10 @@ Hello.
"""
self.feed(data)
self.assertTrue(self.parser.completed)
- self.assertEqual(self.parser.headers, {
- 'CONTENT_LENGTH': '7',
- })
+ self.assertEqual(self.parser.headers, {"CONTENT_LENGTH": "7",})
class DummyBodyStream(object):
-
def getfile(self):
return self
diff --git a/waitress/tests/test_proxy_headers.py b/waitress/tests/test_proxy_headers.py
index f3af58a..15b4a08 100644
--- a/waitress/tests/test_proxy_headers.py
+++ b/waitress/tests/test_proxy_headers.py
@@ -2,71 +2,70 @@ import unittest
from waitress.compat import tobytes
-class TestProxyHeadersMiddleware(unittest.TestCase):
+class TestProxyHeadersMiddleware(unittest.TestCase):
def _makeOne(self, app, **kw):
from waitress.proxy_headers import proxy_headers_middleware
+
return proxy_headers_middleware(app, **kw)
def _callFUT(self, app, **kw):
response = DummyResponse()
environ = DummyEnviron(**kw)
+
def start_response(status, response_headers):
response.status = status
response.headers = response_headers
+
response.steps = list(app(environ, start_response))
- response.body = b''.join(tobytes(s) for s in response.steps)
+ response.body = b"".join(tobytes(s) for s in response.steps)
return response
def test_get_environment_values_w_scheme_override_untrusted(self):
inner = DummyApp()
app = self._makeOne(inner)
- response = self._callFUT(app, headers={
- 'X_FOO': 'BAR',
- 'X_FORWARDED_PROTO': 'https',
- })
- self.assertEqual(response.status, '200 OK')
- self.assertEqual(inner.environ['wsgi.url_scheme'], 'http')
+ response = self._callFUT(
+ app, headers={"X_FOO": "BAR", "X_FORWARDED_PROTO": "https",}
+ )
+ self.assertEqual(response.status, "200 OK")
+ self.assertEqual(inner.environ["wsgi.url_scheme"], "http")
def test_get_environment_values_w_scheme_override_trusted(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='192.168.1.1',
- trusted_proxy_headers={'x-forwarded-proto'},
+ trusted_proxy="192.168.1.1",
+ trusted_proxy_headers={"x-forwarded-proto"},
)
response = self._callFUT(
app,
- addr=['192.168.1.1', 8080],
- headers={
- 'X_FOO': 'BAR',
- 'X_FORWARDED_PROTO': 'https',
- },
+ addr=["192.168.1.1", 8080],
+ headers={"X_FOO": "BAR", "X_FORWARDED_PROTO": "https",},
)
environ = inner.environ
- self.assertEqual(response.status, '200 OK')
- self.assertEqual(environ['SERVER_PORT'], '443')
- self.assertEqual(environ['SERVER_NAME'], 'localhost')
- self.assertEqual(environ['REMOTE_ADDR'], '192.168.1.1')
- self.assertEqual(environ['HTTP_X_FOO'], 'BAR')
+ self.assertEqual(response.status, "200 OK")
+ self.assertEqual(environ["SERVER_PORT"], "443")
+ self.assertEqual(environ["SERVER_NAME"], "localhost")
+ self.assertEqual(environ["REMOTE_ADDR"], "192.168.1.1")
+ self.assertEqual(environ["HTTP_X_FOO"], "BAR")
def test_get_environment_values_w_bogus_scheme_override(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='192.168.1.1',
- trusted_proxy_headers={'x-forwarded-proto'},
+ trusted_proxy="192.168.1.1",
+ trusted_proxy_headers={"x-forwarded-proto"},
)
response = self._callFUT(
app,
- addr=['192.168.1.1', 80],
+ addr=["192.168.1.1", 80],
headers={
- 'X_FOO': 'BAR',
- 'X_FORWARDED_PROTO': 'http://p02n3e.com?url=http',
+ "X_FOO": "BAR",
+ "X_FORWARDED_PROTO": "http://p02n3e.com?url=http",
},
)
- self.assertEqual(response.status, '400 Bad Request')
+ self.assertEqual(response.status, "400 Bad Request")
self.assertIn(b'Header "X-Forwarded-Proto" malformed', response.body)
def test_get_environment_warning_other_proxy_headers(self):
@@ -74,426 +73,453 @@ class TestProxyHeadersMiddleware(unittest.TestCase):
logger = DummyLogger()
app = self._makeOne(
inner,
- trusted_proxy='192.168.1.1',
+ trusted_proxy="192.168.1.1",
trusted_proxy_count=1,
- trusted_proxy_headers={'forwarded'},
+ trusted_proxy_headers={"forwarded"},
log_untrusted=True,
logger=logger,
)
response = self._callFUT(
app,
- addr=['192.168.1.1', 80],
+ addr=["192.168.1.1", 80],
headers={
- 'X_FORWARDED_FOR': '[2001:db8::1]',
- 'FORWARDED': 'For=198.51.100.2;host=example.com:8080;proto=https'
+ "X_FORWARDED_FOR": "[2001:db8::1]",
+ "FORWARDED": "For=198.51.100.2;host=example.com:8080;proto=https",
},
)
- self.assertEqual(response.status, '200 OK')
+ self.assertEqual(response.status, "200 OK")
self.assertEqual(len(logger.logged), 1)
environ = inner.environ
- self.assertNotIn('HTTP_X_FORWARDED_FOR', environ)
- self.assertEqual(environ['REMOTE_ADDR'], '198.51.100.2')
- self.assertEqual(environ['SERVER_NAME'], 'example.com')
- self.assertEqual(environ['HTTP_HOST'], 'example.com:8080')
- self.assertEqual(environ['SERVER_PORT'], '8080')
- self.assertEqual(environ['wsgi.url_scheme'], 'https')
+ self.assertNotIn("HTTP_X_FORWARDED_FOR", environ)
+ self.assertEqual(environ["REMOTE_ADDR"], "198.51.100.2")
+ self.assertEqual(environ["SERVER_NAME"], "example.com")
+ self.assertEqual(environ["HTTP_HOST"], "example.com:8080")
+ self.assertEqual(environ["SERVER_PORT"], "8080")
+ self.assertEqual(environ["wsgi.url_scheme"], "https")
def test_get_environment_contains_all_headers_including_untrusted(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='192.168.1.1',
+ trusted_proxy="192.168.1.1",
trusted_proxy_count=1,
- trusted_proxy_headers={'x-forwarded-by'},
+ trusted_proxy_headers={"x-forwarded-by"},
clear_untrusted=False,
)
headers_orig = {
- 'X_FORWARDED_FOR': '198.51.100.2',
- 'X_FORWARDED_BY': 'Waitress',
- 'X_FORWARDED_PROTO': 'https',
- 'X_FORWARDED_HOST': 'example.org',
+ "X_FORWARDED_FOR": "198.51.100.2",
+ "X_FORWARDED_BY": "Waitress",
+ "X_FORWARDED_PROTO": "https",
+ "X_FORWARDED_HOST": "example.org",
}
response = self._callFUT(
- app,
- addr=['192.168.1.1', 80],
- headers=headers_orig.copy(),
+ app, addr=["192.168.1.1", 80], headers=headers_orig.copy(),
)
- self.assertEqual(response.status, '200 OK')
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
for k, expected in headers_orig.items():
- result = environ['HTTP_%s' % k]
+ result = environ["HTTP_%s" % k]
self.assertEqual(result, expected)
def test_get_environment_contains_only_trusted_headers(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='192.168.1.1',
+ trusted_proxy="192.168.1.1",
trusted_proxy_count=1,
- trusted_proxy_headers={'x-forwarded-by'},
+ trusted_proxy_headers={"x-forwarded-by"},
clear_untrusted=True,
)
response = self._callFUT(
app,
- addr=['192.168.1.1', 80],
+ addr=["192.168.1.1", 80],
headers={
- 'X_FORWARDED_FOR': '198.51.100.2',
- 'X_FORWARDED_BY': 'Waitress',
- 'X_FORWARDED_PROTO': 'https',
- 'X_FORWARDED_HOST': 'example.org',
+ "X_FORWARDED_FOR": "198.51.100.2",
+ "X_FORWARDED_BY": "Waitress",
+ "X_FORWARDED_PROTO": "https",
+ "X_FORWARDED_HOST": "example.org",
},
)
- self.assertEqual(response.status, '200 OK')
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['HTTP_X_FORWARDED_BY'], 'Waitress')
- self.assertNotIn('HTTP_X_FORWARDED_FOR', environ)
- self.assertNotIn('HTTP_X_FORWARDED_PROTO', environ)
- self.assertNotIn('HTTP_X_FORWARDED_HOST', environ)
+ self.assertEqual(environ["HTTP_X_FORWARDED_BY"], "Waitress")
+ self.assertNotIn("HTTP_X_FORWARDED_FOR", environ)
+ self.assertNotIn("HTTP_X_FORWARDED_PROTO", environ)
+ self.assertNotIn("HTTP_X_FORWARDED_HOST", environ)
def test_get_environment_clears_headers_if_untrusted_proxy(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='192.168.1.1',
+ trusted_proxy="192.168.1.1",
trusted_proxy_count=1,
- trusted_proxy_headers={'x-forwarded-by'},
+ trusted_proxy_headers={"x-forwarded-by"},
clear_untrusted=True,
)
response = self._callFUT(
app,
- addr=['192.168.1.255', 80],
+ addr=["192.168.1.255", 80],
headers={
- 'X_FORWARDED_FOR': '198.51.100.2',
- 'X_FORWARDED_BY': 'Waitress',
- 'X_FORWARDED_PROTO': 'https',
- 'X_FORWARDED_HOST': 'example.org',
+ "X_FORWARDED_FOR": "198.51.100.2",
+ "X_FORWARDED_BY": "Waitress",
+ "X_FORWARDED_PROTO": "https",
+ "X_FORWARDED_HOST": "example.org",
},
)
- self.assertEqual(response.status, '200 OK')
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertNotIn('HTTP_X_FORWARDED_BY', environ)
- self.assertNotIn('HTTP_X_FORWARDED_FOR', environ)
- self.assertNotIn('HTTP_X_FORWARDED_PROTO', environ)
- self.assertNotIn('HTTP_X_FORWARDED_HOST', environ)
+ self.assertNotIn("HTTP_X_FORWARDED_BY", environ)
+ self.assertNotIn("HTTP_X_FORWARDED_FOR", environ)
+ self.assertNotIn("HTTP_X_FORWARDED_PROTO", environ)
+ self.assertNotIn("HTTP_X_FORWARDED_HOST", environ)
def test_parse_proxy_headers_forwarded_for(self):
inner = DummyApp()
app = self._makeOne(
- inner,
- trusted_proxy='*',
- trusted_proxy_headers={'x-forwarded-for'},
+ inner, trusted_proxy="*", trusted_proxy_headers={"x-forwarded-for"},
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_FOR': '192.0.2.1'
- })
- self.assertEqual(response.status, '200 OK')
+ response = self._callFUT(app, headers={"X_FORWARDED_FOR": "192.0.2.1"})
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '192.0.2.1')
+ self.assertEqual(environ["REMOTE_ADDR"], "192.0.2.1")
def test_parse_proxy_headers_forwarded_for_v6_missing_brackets(self):
inner = DummyApp()
app = self._makeOne(
- inner,
- trusted_proxy='*',
- trusted_proxy_headers={'x-forwarded-for'},
+ inner, trusted_proxy="*", trusted_proxy_headers={"x-forwarded-for"},
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_FOR': '2001:db8::0'
- })
- self.assertEqual(response.status, '200 OK')
+ response = self._callFUT(app, headers={"X_FORWARDED_FOR": "2001:db8::0"})
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '2001:db8::0')
+ self.assertEqual(environ["REMOTE_ADDR"], "2001:db8::0")
def test_parse_proxy_headers_forwared_for_multiple(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=2,
- trusted_proxy_headers={'x-forwarded-for'},
+ trusted_proxy_headers={"x-forwarded-for"},
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_FOR': '192.0.2.1, 198.51.100.2, 203.0.113.1'
- })
- self.assertEqual(response.status, '200 OK')
+ response = self._callFUT(
+ app, headers={"X_FORWARDED_FOR": "192.0.2.1, 198.51.100.2, 203.0.113.1"}
+ )
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '198.51.100.2')
+ self.assertEqual(environ["REMOTE_ADDR"], "198.51.100.2")
def test_parse_forwarded_multiple_proxies_trust_only_two(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=2,
- trusted_proxy_headers={'forwarded'},
+ trusted_proxy_headers={"forwarded"},
)
- response = self._callFUT(app, headers={
- 'FORWARDED': (
- 'For=192.0.2.1;host=fake.com, '
- 'For=198.51.100.2;host=example.com:8080, '
- 'For=203.0.113.1'
- ),
- })
- self.assertEqual(response.status, '200 OK')
+ response = self._callFUT(
+ app,
+ headers={
+ "FORWARDED": (
+ "For=192.0.2.1;host=fake.com, "
+ "For=198.51.100.2;host=example.com:8080, "
+ "For=203.0.113.1"
+ ),
+ },
+ )
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '198.51.100.2')
- self.assertEqual(environ['SERVER_NAME'], 'example.com')
- self.assertEqual(environ['HTTP_HOST'], 'example.com:8080')
- self.assertEqual(environ['SERVER_PORT'], '8080')
+ self.assertEqual(environ["REMOTE_ADDR"], "198.51.100.2")
+ self.assertEqual(environ["SERVER_NAME"], "example.com")
+ self.assertEqual(environ["HTTP_HOST"], "example.com:8080")
+ self.assertEqual(environ["SERVER_PORT"], "8080")
def test_parse_forwarded_multiple_proxies(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=2,
- trusted_proxy_headers={'forwarded'},
+ trusted_proxy_headers={"forwarded"},
)
- response = self._callFUT(app, headers={
- 'FORWARDED': (
- 'for="[2001:db8::1]:3821";host="example.com:8443";proto="https", '
- 'for=192.0.2.1;host="example.internal:8080"'
- ),
- })
- self.assertEqual(response.status, '200 OK')
+ response = self._callFUT(
+ app,
+ headers={
+ "FORWARDED": (
+ 'for="[2001:db8::1]:3821";host="example.com:8443";proto="https", '
+ 'for=192.0.2.1;host="example.internal:8080"'
+ ),
+ },
+ )
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '2001:db8::1')
- self.assertEqual(environ['REMOTE_PORT'], '3821')
- self.assertEqual(environ['SERVER_NAME'], 'example.com')
- self.assertEqual(environ['HTTP_HOST'], 'example.com:8443')
- self.assertEqual(environ['SERVER_PORT'], '8443')
- self.assertEqual(environ['wsgi.url_scheme'], 'https')
+ self.assertEqual(environ["REMOTE_ADDR"], "2001:db8::1")
+ self.assertEqual(environ["REMOTE_PORT"], "3821")
+ self.assertEqual(environ["SERVER_NAME"], "example.com")
+ self.assertEqual(environ["HTTP_HOST"], "example.com:8443")
+ self.assertEqual(environ["SERVER_PORT"], "8443")
+ self.assertEqual(environ["wsgi.url_scheme"], "https")
def test_parse_forwarded_multiple_proxies_minimal(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=2,
- trusted_proxy_headers={'forwarded'},
+ trusted_proxy_headers={"forwarded"},
+ )
+ response = self._callFUT(
+ app,
+ headers={
+ "FORWARDED": (
+ 'for="[2001:db8::1]";proto="https", '
+ 'for=192.0.2.1;host="example.org"'
+ ),
+ },
)
- response = self._callFUT(app, headers={
- 'FORWARDED': (
- 'for="[2001:db8::1]";proto="https", '
- 'for=192.0.2.1;host="example.org"'
- ),
- })
- self.assertEqual(response.status, '200 OK')
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '2001:db8::1')
- self.assertEqual(environ['SERVER_NAME'], 'example.org')
- self.assertEqual(environ['HTTP_HOST'], 'example.org')
- self.assertEqual(environ['SERVER_PORT'], '443')
- self.assertEqual(environ['wsgi.url_scheme'], 'https')
+ self.assertEqual(environ["REMOTE_ADDR"], "2001:db8::1")
+ self.assertEqual(environ["SERVER_NAME"], "example.org")
+ self.assertEqual(environ["HTTP_HOST"], "example.org")
+ self.assertEqual(environ["SERVER_PORT"], "443")
+ self.assertEqual(environ["wsgi.url_scheme"], "https")
def test_parse_proxy_headers_forwarded_host_with_port(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=2,
trusted_proxy_headers={
- 'x-forwarded-for', 'x-forwarded-proto', 'x-forwarded-host',
+ "x-forwarded-for",
+ "x-forwarded-proto",
+ "x-forwarded-host",
+ },
+ )
+ response = self._callFUT(
+ app,
+ headers={
+ "X_FORWARDED_FOR": "192.0.2.1, 198.51.100.2, 203.0.113.1",
+ "X_FORWARDED_PROTO": "http",
+ "X_FORWARDED_HOST": "example.com:8080",
},
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_FOR': '192.0.2.1, 198.51.100.2, 203.0.113.1',
- 'X_FORWARDED_PROTO': 'http',
- 'X_FORWARDED_HOST': 'example.com:8080',
- })
- self.assertEqual(response.status, '200 OK')
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '198.51.100.2')
- self.assertEqual(environ['SERVER_NAME'], 'example.com')
- self.assertEqual(environ['HTTP_HOST'], 'example.com:8080')
- self.assertEqual(environ['SERVER_PORT'], '8080')
+ self.assertEqual(environ["REMOTE_ADDR"], "198.51.100.2")
+ self.assertEqual(environ["SERVER_NAME"], "example.com")
+ self.assertEqual(environ["HTTP_HOST"], "example.com:8080")
+ self.assertEqual(environ["SERVER_PORT"], "8080")
def test_parse_proxy_headers_forwarded_host_without_port(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=2,
trusted_proxy_headers={
- 'x-forwarded-for', 'x-forwarded-proto', 'x-forwarded-host',
+ "x-forwarded-for",
+ "x-forwarded-proto",
+ "x-forwarded-host",
},
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_FOR': '192.0.2.1, 198.51.100.2, 203.0.113.1',
- 'X_FORWARDED_PROTO': 'http',
- 'X_FORWARDED_HOST': 'example.com',
- })
- self.assertEqual(response.status, '200 OK')
+ response = self._callFUT(
+ app,
+ headers={
+ "X_FORWARDED_FOR": "192.0.2.1, 198.51.100.2, 203.0.113.1",
+ "X_FORWARDED_PROTO": "http",
+ "X_FORWARDED_HOST": "example.com",
+ },
+ )
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '198.51.100.2')
- self.assertEqual(environ['SERVER_NAME'], 'example.com')
- self.assertEqual(environ['HTTP_HOST'], 'example.com')
- self.assertEqual(environ['SERVER_PORT'], '80')
+ self.assertEqual(environ["REMOTE_ADDR"], "198.51.100.2")
+ self.assertEqual(environ["SERVER_NAME"], "example.com")
+ self.assertEqual(environ["HTTP_HOST"], "example.com")
+ self.assertEqual(environ["SERVER_PORT"], "80")
def test_parse_proxy_headers_forwarded_host_with_forwarded_port(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=2,
trusted_proxy_headers={
- 'x-forwarded-for', 'x-forwarded-proto', 'x-forwarded-host',
- 'x-forwarded-port',
+ "x-forwarded-for",
+ "x-forwarded-proto",
+ "x-forwarded-host",
+ "x-forwarded-port",
},
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_FOR': '192.0.2.1, 198.51.100.2, 203.0.113.1',
- 'X_FORWARDED_PROTO': 'http',
- 'X_FORWARDED_HOST': 'example.com',
- 'X_FORWARDED_PORT': '8080'
- })
- self.assertEqual(response.status, '200 OK')
+ response = self._callFUT(
+ app,
+ headers={
+ "X_FORWARDED_FOR": "192.0.2.1, 198.51.100.2, 203.0.113.1",
+ "X_FORWARDED_PROTO": "http",
+ "X_FORWARDED_HOST": "example.com",
+ "X_FORWARDED_PORT": "8080",
+ },
+ )
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '198.51.100.2')
- self.assertEqual(environ['SERVER_NAME'], 'example.com')
- self.assertEqual(environ['HTTP_HOST'], 'example.com:8080')
- self.assertEqual(environ['SERVER_PORT'], '8080')
+ self.assertEqual(environ["REMOTE_ADDR"], "198.51.100.2")
+ self.assertEqual(environ["SERVER_NAME"], "example.com")
+ self.assertEqual(environ["HTTP_HOST"], "example.com:8080")
+ self.assertEqual(environ["SERVER_PORT"], "8080")
def test_parse_proxy_headers_forwarded_host_multiple_with_forwarded_port(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=2,
trusted_proxy_headers={
- 'x-forwarded-for', 'x-forwarded-proto', 'x-forwarded-host',
- 'x-forwarded-port',
+ "x-forwarded-for",
+ "x-forwarded-proto",
+ "x-forwarded-host",
+ "x-forwarded-port",
},
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_FOR': '192.0.2.1, 198.51.100.2, 203.0.113.1',
- 'X_FORWARDED_PROTO': 'http',
- 'X_FORWARDED_HOST': 'example.com, example.org',
- 'X_FORWARDED_PORT': '8080'
- })
- self.assertEqual(response.status, '200 OK')
+ response = self._callFUT(
+ app,
+ headers={
+ "X_FORWARDED_FOR": "192.0.2.1, 198.51.100.2, 203.0.113.1",
+ "X_FORWARDED_PROTO": "http",
+ "X_FORWARDED_HOST": "example.com, example.org",
+ "X_FORWARDED_PORT": "8080",
+ },
+ )
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '198.51.100.2')
- self.assertEqual(environ['SERVER_NAME'], 'example.com')
- self.assertEqual(environ['HTTP_HOST'], 'example.com:8080')
- self.assertEqual(environ['SERVER_PORT'], '8080')
-
- def test_parse_proxy_headers_forwarded_host_multiple_with_forwarded_port_limit_one_trusted(self):
+ self.assertEqual(environ["REMOTE_ADDR"], "198.51.100.2")
+ self.assertEqual(environ["SERVER_NAME"], "example.com")
+ self.assertEqual(environ["HTTP_HOST"], "example.com:8080")
+ self.assertEqual(environ["SERVER_PORT"], "8080")
+
+ def test_parse_proxy_headers_forwarded_host_multiple_with_forwarded_port_limit_one_trusted(
+ self,
+ ):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=1,
trusted_proxy_headers={
- 'x-forwarded-for', 'x-forwarded-proto', 'x-forwarded-host',
- 'x-forwarded-port',
+ "x-forwarded-for",
+ "x-forwarded-proto",
+ "x-forwarded-host",
+ "x-forwarded-port",
},
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_FOR': '192.0.2.1, 198.51.100.2, 203.0.113.1',
- 'X_FORWARDED_PROTO': 'http',
- 'X_FORWARDED_HOST': 'example.com, example.org',
- 'X_FORWARDED_PORT': '8080'
- })
- self.assertEqual(response.status, '200 OK')
+ response = self._callFUT(
+ app,
+ headers={
+ "X_FORWARDED_FOR": "192.0.2.1, 198.51.100.2, 203.0.113.1",
+ "X_FORWARDED_PROTO": "http",
+ "X_FORWARDED_HOST": "example.com, example.org",
+ "X_FORWARDED_PORT": "8080",
+ },
+ )
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '203.0.113.1')
- self.assertEqual(environ['SERVER_NAME'], 'example.org')
- self.assertEqual(environ['HTTP_HOST'], 'example.org:8080')
- self.assertEqual(environ['SERVER_PORT'], '8080')
+ self.assertEqual(environ["REMOTE_ADDR"], "203.0.113.1")
+ self.assertEqual(environ["SERVER_NAME"], "example.org")
+ self.assertEqual(environ["HTTP_HOST"], "example.org:8080")
+ self.assertEqual(environ["SERVER_PORT"], "8080")
def test_parse_forwarded(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=1,
- trusted_proxy_headers={'forwarded'},
+ trusted_proxy_headers={"forwarded"},
+ )
+ response = self._callFUT(
+ app,
+ headers={
+ "FORWARDED": "For=198.51.100.2:5858;host=example.com:8080;proto=https",
+ },
)
- response = self._callFUT(app, headers={
- 'FORWARDED': 'For=198.51.100.2:5858;host=example.com:8080;proto=https',
- })
- self.assertEqual(response.status, '200 OK')
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '198.51.100.2')
- self.assertEqual(environ['REMOTE_PORT'], '5858')
- self.assertEqual(environ['SERVER_NAME'], 'example.com')
- self.assertEqual(environ['HTTP_HOST'], 'example.com:8080')
- self.assertEqual(environ['SERVER_PORT'], '8080')
- self.assertEqual(environ['wsgi.url_scheme'], 'https')
+ self.assertEqual(environ["REMOTE_ADDR"], "198.51.100.2")
+ self.assertEqual(environ["REMOTE_PORT"], "5858")
+ self.assertEqual(environ["SERVER_NAME"], "example.com")
+ self.assertEqual(environ["HTTP_HOST"], "example.com:8080")
+ self.assertEqual(environ["SERVER_PORT"], "8080")
+ self.assertEqual(environ["wsgi.url_scheme"], "https")
def test_parse_forwarded_empty_pair(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=1,
- trusted_proxy_headers={'forwarded'},
+ trusted_proxy_headers={"forwarded"},
+ )
+ response = self._callFUT(
+ app, headers={"FORWARDED": "For=198.51.100.2;;proto=https;by=_unused",}
)
- response = self._callFUT(app, headers={
- 'FORWARDED': 'For=198.51.100.2;;proto=https;by=_unused',
- })
- self.assertEqual(response.status, '200 OK')
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '198.51.100.2')
+ self.assertEqual(environ["REMOTE_ADDR"], "198.51.100.2")
def test_parse_forwarded_pair_token_whitespace(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=1,
- trusted_proxy_headers={'forwarded'},
+ trusted_proxy_headers={"forwarded"},
)
- response = self._callFUT(app, headers={
- 'FORWARDED': 'For=198.51.100.2; proto =https',
- })
- self.assertEqual(response.status, '400 Bad Request')
+ response = self._callFUT(
+ app, headers={"FORWARDED": "For=198.51.100.2; proto =https",}
+ )
+ self.assertEqual(response.status, "400 Bad Request")
self.assertIn(b'Header "Forwarded" malformed', response.body)
def test_parse_forwarded_pair_value_whitespace(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=1,
- trusted_proxy_headers={'forwarded'},
+ trusted_proxy_headers={"forwarded"},
)
- response = self._callFUT(app, headers={
- 'FORWARDED': 'For= "198.51.100.2"; proto =https',
- })
- self.assertEqual(response.status, '400 Bad Request')
+ response = self._callFUT(
+ app, headers={"FORWARDED": 'For= "198.51.100.2"; proto =https',}
+ )
+ self.assertEqual(response.status, "400 Bad Request")
self.assertIn(b'Header "Forwarded" malformed', response.body)
def test_parse_forwarded_pair_no_equals(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=1,
- trusted_proxy_headers={'forwarded'},
+ trusted_proxy_headers={"forwarded"},
)
- response = self._callFUT(app, headers={
- 'FORWARDED': 'For'
- })
- self.assertEqual(response.status, '400 Bad Request')
+ response = self._callFUT(app, headers={"FORWARDED": "For"})
+ self.assertEqual(response.status, "400 Bad Request")
self.assertIn(b'Header "Forwarded" malformed', response.body)
def test_parse_forwarded_warning_unknown_token(self):
@@ -501,63 +527,63 @@ class TestProxyHeadersMiddleware(unittest.TestCase):
logger = DummyLogger()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=1,
- trusted_proxy_headers={'forwarded'},
+ trusted_proxy_headers={"forwarded"},
logger=logger,
)
- response = self._callFUT(app, headers={
- 'FORWARDED': (
- 'For=198.51.100.2;host=example.com:8080;proto=https;'
- 'unknown="yolo"'
- ),
- })
- self.assertEqual(response.status, '200 OK')
+ response = self._callFUT(
+ app,
+ headers={
+ "FORWARDED": (
+ "For=198.51.100.2;host=example.com:8080;proto=https;"
+ 'unknown="yolo"'
+ ),
+ },
+ )
+ self.assertEqual(response.status, "200 OK")
self.assertEqual(len(logger.logged), 1)
- self.assertIn('Unknown Forwarded token', logger.logged[0])
+ self.assertIn("Unknown Forwarded token", logger.logged[0])
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '198.51.100.2')
- self.assertEqual(environ['SERVER_NAME'], 'example.com')
- self.assertEqual(environ['HTTP_HOST'], 'example.com:8080')
- self.assertEqual(environ['SERVER_PORT'], '8080')
- self.assertEqual(environ['wsgi.url_scheme'], 'https')
+ self.assertEqual(environ["REMOTE_ADDR"], "198.51.100.2")
+ self.assertEqual(environ["SERVER_NAME"], "example.com")
+ self.assertEqual(environ["HTTP_HOST"], "example.com:8080")
+ self.assertEqual(environ["SERVER_PORT"], "8080")
+ self.assertEqual(environ["wsgi.url_scheme"], "https")
def test_parse_no_valid_proxy_headers(self):
inner = DummyApp()
- app = self._makeOne(
- inner,
- trusted_proxy='*',
- trusted_proxy_count=1,
+ app = self._makeOne(inner, trusted_proxy="*", trusted_proxy_count=1,)
+ response = self._callFUT(
+ app,
+ headers={
+ "X_FORWARDED_FOR": "198.51.100.2",
+ "FORWARDED": "For=198.51.100.2;host=example.com:8080;proto=https",
+ },
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_FOR': '198.51.100.2',
- 'FORWARDED': 'For=198.51.100.2;host=example.com:8080;proto=https'
- })
- self.assertEqual(response.status, '200 OK')
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['REMOTE_ADDR'], '127.0.0.1')
- self.assertEqual(environ['SERVER_NAME'], 'localhost')
- self.assertEqual(environ['HTTP_HOST'], '192.168.1.1:80')
- self.assertEqual(environ['SERVER_PORT'], '8080')
- self.assertEqual(environ['wsgi.url_scheme'], 'http')
+ self.assertEqual(environ["REMOTE_ADDR"], "127.0.0.1")
+ self.assertEqual(environ["SERVER_NAME"], "localhost")
+ self.assertEqual(environ["HTTP_HOST"], "192.168.1.1:80")
+ self.assertEqual(environ["SERVER_PORT"], "8080")
+ self.assertEqual(environ["wsgi.url_scheme"], "http")
def test_parse_multiple_x_forwarded_proto(self):
inner = DummyApp()
logger = DummyLogger()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=1,
- trusted_proxy_headers={'x-forwarded-proto'},
+ trusted_proxy_headers={"x-forwarded-proto"},
logger=logger,
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_PROTO': 'http, https',
- })
- self.assertEqual(response.status, '400 Bad Request')
+ response = self._callFUT(app, headers={"X_FORWARDED_PROTO": "http, https",})
+ self.assertEqual(response.status, "400 Bad Request")
self.assertIn(b'Header "X-Forwarded-Proto" malformed', response.body)
def test_parse_multiple_x_forwarded_port(self):
@@ -565,89 +591,93 @@ class TestProxyHeadersMiddleware(unittest.TestCase):
logger = DummyLogger()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=1,
- trusted_proxy_headers={'x-forwarded-port'},
+ trusted_proxy_headers={"x-forwarded-port"},
logger=logger,
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_PORT': '443, 80',
- })
- self.assertEqual(response.status, '400 Bad Request')
+ response = self._callFUT(app, headers={"X_FORWARDED_PORT": "443, 80",})
+ self.assertEqual(response.status, "400 Bad Request")
self.assertIn(b'Header "X-Forwarded-Port" malformed', response.body)
def test_parse_forwarded_port_wrong_proto_port_80(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=1,
trusted_proxy_headers={
- 'x-forwarded-port', 'x-forwarded-host', 'x-forwarded-proto',
+ "x-forwarded-port",
+ "x-forwarded-host",
+ "x-forwarded-proto",
},
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_PORT': '80',
- 'X_FORWARDED_PROTO': 'https',
- 'X_FORWARDED_HOST': 'example.com',
- })
- self.assertEqual(response.status, '200 OK')
+ response = self._callFUT(
+ app,
+ headers={
+ "X_FORWARDED_PORT": "80",
+ "X_FORWARDED_PROTO": "https",
+ "X_FORWARDED_HOST": "example.com",
+ },
+ )
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['SERVER_NAME'], 'example.com')
- self.assertEqual(environ['HTTP_HOST'], 'example.com:80')
- self.assertEqual(environ['SERVER_PORT'], '80')
- self.assertEqual(environ['wsgi.url_scheme'], 'https')
+ self.assertEqual(environ["SERVER_NAME"], "example.com")
+ self.assertEqual(environ["HTTP_HOST"], "example.com:80")
+ self.assertEqual(environ["SERVER_PORT"], "80")
+ self.assertEqual(environ["wsgi.url_scheme"], "https")
def test_parse_forwarded_port_wrong_proto_port_443(self):
inner = DummyApp()
app = self._makeOne(
inner,
- trusted_proxy='*',
+ trusted_proxy="*",
trusted_proxy_count=1,
trusted_proxy_headers={
- 'x-forwarded-port', 'x-forwarded-host', 'x-forwarded-proto',
+ "x-forwarded-port",
+ "x-forwarded-host",
+ "x-forwarded-proto",
},
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_PORT': '443',
- 'X_FORWARDED_PROTO': 'http',
- 'X_FORWARDED_HOST': 'example.com',
- })
- self.assertEqual(response.status, '200 OK')
+ response = self._callFUT(
+ app,
+ headers={
+ "X_FORWARDED_PORT": "443",
+ "X_FORWARDED_PROTO": "http",
+ "X_FORWARDED_HOST": "example.com",
+ },
+ )
+ self.assertEqual(response.status, "200 OK")
environ = inner.environ
- self.assertEqual(environ['SERVER_NAME'], 'example.com')
- self.assertEqual(environ['HTTP_HOST'], 'example.com:443')
- self.assertEqual(environ['SERVER_PORT'], '443')
- self.assertEqual(environ['wsgi.url_scheme'], 'http')
+ self.assertEqual(environ["SERVER_NAME"], "example.com")
+ self.assertEqual(environ["HTTP_HOST"], "example.com:443")
+ 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="*",
trusted_proxy_count=1,
- trusted_proxy_headers={'x-forwarded-for'},
+ trusted_proxy_headers={"x-forwarded-for"},
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_FOR': '"foo'
- })
- self.assertEqual(response.status, '400 Bad Request')
+ 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="*",
trusted_proxy_count=1,
- trusted_proxy_headers={'x-forwarded-host'},
+ trusted_proxy_headers={"x-forwarded-host"},
)
- response = self._callFUT(app, headers={
- 'X_FORWARDED_HOST': '"foo'
- })
- self.assertEqual(response.status, '400 Bad Request')
+ 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)
@@ -662,8 +692,8 @@ class DummyLogger(object):
class DummyApp(object):
def __call__(self, environ, start_response):
self.environ = environ
- start_response('200 OK', [('Content-Type', 'text/plain')])
- yield 'hello'
+ start_response("200 OK", [("Content-Type", "text/plain")])
+ yield "hello"
class DummyResponse(object):
@@ -673,23 +703,22 @@ class DummyResponse(object):
def DummyEnviron(
- addr=('127.0.0.1', 8080),
- scheme='http',
- server='localhost',
- headers=None,
+ addr=("127.0.0.1", 8080), scheme="http", server="localhost", headers=None,
):
environ = {
- 'REMOTE_ADDR': addr[0],
- 'REMOTE_HOST': addr[0],
- 'REMOTE_PORT': addr[1],
- 'SERVER_PORT': str(addr[1]),
- 'SERVER_NAME': server,
- 'wsgi.url_scheme': scheme,
- 'HTTP_HOST': '192.168.1.1:80',
+ "REMOTE_ADDR": addr[0],
+ "REMOTE_HOST": addr[0],
+ "REMOTE_PORT": addr[1],
+ "SERVER_PORT": str(addr[1]),
+ "SERVER_NAME": server,
+ "wsgi.url_scheme": scheme,
+ "HTTP_HOST": "192.168.1.1:80",
}
if headers:
- environ.update({
- 'HTTP_' + key.upper().replace('-', '_'): value
- for key, value in headers.items()
- })
+ environ.update(
+ {
+ "HTTP_" + key.upper().replace("-", "_"): value
+ for key, value in headers.items()
+ }
+ )
return environ
diff --git a/waitress/tests/test_receiver.py b/waitress/tests/test_receiver.py
index 707f328..fcd0305 100644
--- a/waitress/tests/test_receiver.py
+++ b/waitress/tests/test_receiver.py
@@ -1,36 +1,37 @@
import unittest
-class TestFixedStreamReceiver(unittest.TestCase):
+class TestFixedStreamReceiver(unittest.TestCase):
def _makeOne(self, cl, buf):
from waitress.receiver import FixedStreamReceiver
+
return FixedStreamReceiver(cl, buf)
def test_received_remain_lt_1(self):
buf = DummyBuffer()
inst = self._makeOne(0, buf)
- result = inst.received('a')
+ result = inst.received("a")
self.assertEqual(result, 0)
self.assertEqual(inst.completed, True)
def test_received_remain_lte_datalen(self):
buf = DummyBuffer()
inst = self._makeOne(1, buf)
- result = inst.received('aa')
+ result = inst.received("aa")
self.assertEqual(result, 1)
self.assertEqual(inst.completed, True)
self.assertEqual(inst.completed, 1)
self.assertEqual(inst.remain, 0)
- self.assertEqual(buf.data, ['a'])
+ self.assertEqual(buf.data, ["a"])
def test_received_remain_gt_datalen(self):
buf = DummyBuffer()
inst = self._makeOne(10, buf)
- result = inst.received('aa')
+ result = inst.received("aa")
self.assertEqual(result, 2)
self.assertEqual(inst.completed, False)
self.assertEqual(inst.remain, 8)
- self.assertEqual(buf.data, ['aa'])
+ self.assertEqual(buf.data, ["aa"])
def test_getfile(self):
buf = DummyBuffer()
@@ -43,21 +44,22 @@ class TestFixedStreamReceiver(unittest.TestCase):
self.assertEqual(inst.getbuf(), buf)
def test___len__(self):
- buf = DummyBuffer(['1', '2'])
+ buf = DummyBuffer(["1", "2"])
inst = self._makeOne(10, buf)
self.assertEqual(inst.__len__(), 2)
-class TestChunkedReceiver(unittest.TestCase):
+class TestChunkedReceiver(unittest.TestCase):
def _makeOne(self, buf):
from waitress.receiver import ChunkedReceiver
+
return ChunkedReceiver(buf)
def test_alreadycompleted(self):
buf = DummyBuffer()
inst = self._makeOne(buf)
inst.completed = True
- result = inst.received(b'a')
+ result = inst.received(b"a")
self.assertEqual(result, 0)
self.assertEqual(inst.completed, True)
@@ -65,7 +67,7 @@ class TestChunkedReceiver(unittest.TestCase):
buf = DummyBuffer()
inst = self._makeOne(buf)
inst.chunk_remainder = 100
- result = inst.received(b'a')
+ result = inst.received(b"a")
self.assertEqual(inst.chunk_remainder, 99)
self.assertEqual(result, 1)
self.assertEqual(inst.completed, False)
@@ -73,23 +75,23 @@ class TestChunkedReceiver(unittest.TestCase):
def test_received_control_line_notfinished(self):
buf = DummyBuffer()
inst = self._makeOne(buf)
- result = inst.received(b'a')
- self.assertEqual(inst.control_line, b'a')
+ result = inst.received(b"a")
+ self.assertEqual(inst.control_line, b"a")
self.assertEqual(result, 1)
self.assertEqual(inst.completed, False)
def test_received_control_line_finished_garbage_in_input(self):
buf = DummyBuffer()
inst = self._makeOne(buf)
- result = inst.received(b'garbage\n')
+ result = inst.received(b"garbage\n")
self.assertEqual(result, 8)
self.assertTrue(inst.error)
def test_received_control_line_finished_all_chunks_not_received(self):
buf = DummyBuffer()
inst = self._makeOne(buf)
- result = inst.received(b'a;discard\n')
- self.assertEqual(inst.control_line, b'')
+ result = inst.received(b"a;discard\n")
+ self.assertEqual(inst.control_line, b"")
self.assertEqual(inst.chunk_remainder, 10)
self.assertEqual(inst.all_chunks_received, False)
self.assertEqual(result, 10)
@@ -98,8 +100,8 @@ class TestChunkedReceiver(unittest.TestCase):
def test_received_control_line_finished_all_chunks_received(self):
buf = DummyBuffer()
inst = self._makeOne(buf)
- result = inst.received(b'0;discard\n')
- self.assertEqual(inst.control_line, b'')
+ result = inst.received(b"0;discard\n")
+ self.assertEqual(inst.control_line, b"")
self.assertEqual(inst.all_chunks_received, True)
self.assertEqual(result, 10)
self.assertEqual(inst.completed, False)
@@ -108,7 +110,7 @@ class TestChunkedReceiver(unittest.TestCase):
buf = DummyBuffer()
inst = self._makeOne(buf)
inst.all_chunks_received = True
- result = inst.received(b'\r\n')
+ result = inst.received(b"\r\n")
self.assertEqual(result, 2)
self.assertEqual(inst.completed, True)
@@ -116,7 +118,7 @@ class TestChunkedReceiver(unittest.TestCase):
buf = DummyBuffer()
inst = self._makeOne(buf)
inst.all_chunks_received = True
- result = inst.received(b'\n')
+ result = inst.received(b"\n")
self.assertEqual(result, 1)
self.assertEqual(inst.completed, True)
@@ -124,7 +126,7 @@ class TestChunkedReceiver(unittest.TestCase):
buf = DummyBuffer()
inst = self._makeOne(buf)
inst.all_chunks_received = True
- result = inst.received(b'a')
+ result = inst.received(b"a")
self.assertEqual(result, 1)
self.assertEqual(inst.completed, False)
@@ -132,8 +134,8 @@ class TestChunkedReceiver(unittest.TestCase):
buf = DummyBuffer()
inst = self._makeOne(buf)
inst.all_chunks_received = True
- result = inst.received(b'abc\r\n\r\n')
- self.assertEqual(inst.trailer, b'abc\r\n\r\n')
+ result = inst.received(b"abc\r\n\r\n")
+ self.assertEqual(inst.trailer, b"abc\r\n\r\n")
self.assertEqual(result, 7)
self.assertEqual(inst.completed, True)
@@ -148,12 +150,12 @@ class TestChunkedReceiver(unittest.TestCase):
self.assertEqual(inst.getbuf(), buf)
def test___len__(self):
- buf = DummyBuffer(['1', '2'])
+ buf = DummyBuffer(["1", "2"])
inst = self._makeOne(buf)
self.assertEqual(inst.__len__(), 2)
-
-class DummyBuffer(object):
+
+class DummyBuffer(object):
def __init__(self, data=None):
if data is None:
data = []
diff --git a/waitress/tests/test_regression.py b/waitress/tests/test_regression.py
index f43895e..3c4c6c2 100644
--- a/waitress/tests/test_regression.py
+++ b/waitress/tests/test_regression.py
@@ -15,8 +15,9 @@
"""
import doctest
-class FakeSocket: # pragma: no cover
- data = ''
+
+class FakeSocket: # pragma: no cover
+ data = ""
setblocking = lambda *_: None
close = lambda *_: None
@@ -27,14 +28,15 @@ class FakeSocket: # pragma: no cover
return self.no
def getpeername(self):
- return ('localhost', self.no)
+ return ("localhost", self.no)
def send(self, data):
self.data += data
return len(data)
def recv(self, data):
- return 'data'
+ return "data"
+
def zombies_test():
"""Regression test for HTTPChannel.maintenance method
@@ -140,5 +142,6 @@ def zombies_test():
"""
+
def test_suite():
return doctest.DocTestSuite()
diff --git a/waitress/tests/test_runner.py b/waitress/tests/test_runner.py
index fa927f0..127757e 100644
--- a/waitress/tests/test_runner.py
+++ b/waitress/tests/test_runner.py
@@ -2,124 +2,111 @@ import contextlib
import os
import sys
-if sys.version_info[:2] == (2, 6): # pragma: no cover
+if sys.version_info[:2] == (2, 6): # pragma: no cover
import unittest2 as unittest
-else: # pragma: no cover
+else: # pragma: no cover
import unittest
from waitress import runner
-class Test_match(unittest.TestCase):
+class Test_match(unittest.TestCase):
def test_empty(self):
self.assertRaisesRegexp(
- ValueError, "^Malformed application ''$",
- runner.match, '')
+ ValueError, "^Malformed application ''$", runner.match, ""
+ )
def test_module_only(self):
self.assertRaisesRegexp(
- ValueError, r"^Malformed application 'foo\.bar'$",
- runner.match, 'foo.bar')
+ ValueError, r"^Malformed application 'foo\.bar'$", runner.match, "foo.bar"
+ )
def test_bad_module(self):
self.assertRaisesRegexp(
ValueError,
r"^Malformed application 'foo#bar:barney'$",
- runner.match, 'foo#bar:barney')
+ runner.match,
+ "foo#bar:barney",
+ )
def test_module_obj(self):
self.assertTupleEqual(
- runner.match('foo.bar:fred.barney'),
- ('foo.bar', 'fred.barney'))
+ runner.match("foo.bar:fred.barney"), ("foo.bar", "fred.barney")
+ )
-class Test_resolve(unittest.TestCase):
+class Test_resolve(unittest.TestCase):
def test_bad_module(self):
self.assertRaises(
- ImportError,
- runner.resolve, 'nonexistent', 'nonexistent_function')
+ ImportError, runner.resolve, "nonexistent", "nonexistent_function"
+ )
def test_nonexistent_function(self):
self.assertRaisesRegexp(
AttributeError,
r"has no attribute 'nonexistent_function'",
- runner.resolve, 'os.path', 'nonexistent_function')
+ runner.resolve,
+ "os.path",
+ "nonexistent_function",
+ )
def test_simple_happy_path(self):
from os.path import exists
- self.assertIs(runner.resolve('os.path', 'exists'), exists)
+
+ self.assertIs(runner.resolve("os.path", "exists"), exists)
def test_complex_happy_path(self):
# Ensure we can recursively resolve object attributes if necessary.
- self.assertEquals(
- runner.resolve('os.path', 'exists.__name__'),
- 'exists')
+ self.assertEquals(runner.resolve("os.path", "exists.__name__"), "exists")
-class Test_run(unittest.TestCase):
+class Test_run(unittest.TestCase):
def match_output(self, argv, code, regex):
- argv = ['waitress-serve'] + argv
+ argv = ["waitress-serve"] + argv
with capture() as captured:
self.assertEqual(runner.run(argv=argv), code)
self.assertRegexpMatches(captured.getvalue(), regex)
captured.close()
def test_bad(self):
- self.match_output(
- ['--bad-opt'],
- 1,
- '^Error: option --bad-opt not recognized')
+ self.match_output(["--bad-opt"], 1, "^Error: option --bad-opt not recognized")
def test_help(self):
- self.match_output(
- ['--help'],
- 0,
- "^Usage:\n\n waitress-serve")
+ self.match_output(["--help"], 0, "^Usage:\n\n waitress-serve")
def test_no_app(self):
- self.match_output(
- [],
- 1,
- "^Error: Specify one application only")
+ self.match_output([], 1, "^Error: Specify one application only")
def test_multiple_apps_app(self):
- self.match_output(
- ['a:a', 'b:b'],
- 1,
- "^Error: Specify one application only")
+ self.match_output(["a:a", "b:b"], 1, "^Error: Specify one application only")
def test_bad_apps_app(self):
- self.match_output(
- ['a'],
- 1,
- "^Error: Malformed application 'a'")
+ self.match_output(["a"], 1, "^Error: Malformed application 'a'")
def test_bad_app_module(self):
- self.match_output(
- ['nonexistent:a'],
- 1,
- "^Error: Bad module 'nonexistent'")
+ self.match_output(["nonexistent:a"], 1, "^Error: Bad module 'nonexistent'")
self.match_output(
- ['nonexistent:a'],
+ ["nonexistent:a"],
1,
(
r"There was an exception \((ImportError|ModuleNotFoundError)\) "
"importing your module.\n\nIt had these arguments: \n"
"1. No module named '?nonexistent'?"
- )
+ ),
)
def test_cwd_added_to_path(self):
def null_serve(app, **kw):
pass
+
sys_path = sys.path
current_dir = os.getcwd()
try:
os.chdir(os.path.dirname(__file__))
argv = [
- 'waitress-serve',
- 'fixtureapps.runner:app',
+ "waitress-serve",
+ "fixtureapps.runner:app",
]
self.assertEqual(runner.run(argv=argv, _serve=null_serve), 0)
finally:
@@ -128,37 +115,40 @@ class Test_run(unittest.TestCase):
def test_bad_app_object(self):
self.match_output(
- ['waitress.tests.fixtureapps.runner:a'],
- 1,
- "^Error: Bad object name 'a'")
+ ["waitress.tests.fixtureapps.runner:a"], 1, "^Error: Bad object name 'a'"
+ )
def test_simple_call(self):
import waitress.tests.fixtureapps.runner as _apps
+
def check_server(app, **kw):
self.assertIs(app, _apps.app)
- self.assertDictEqual(kw, {'port': '80'})
+ self.assertDictEqual(kw, {"port": "80"})
+
argv = [
- 'waitress-serve',
- '--port=80',
- 'waitress.tests.fixtureapps.runner:app',
+ "waitress-serve",
+ "--port=80",
+ "waitress.tests.fixtureapps.runner:app",
]
self.assertEqual(runner.run(argv=argv, _serve=check_server), 0)
def test_returned_app(self):
import waitress.tests.fixtureapps.runner as _apps
+
def check_server(app, **kw):
self.assertIs(app, _apps.app)
- self.assertDictEqual(kw, {'port': '80'})
+ self.assertDictEqual(kw, {"port": "80"})
+
argv = [
- 'waitress-serve',
- '--port=80',
- '--call',
- 'waitress.tests.fixtureapps.runner:returns_app',
+ "waitress-serve",
+ "--port=80",
+ "--call",
+ "waitress.tests.fixtureapps.runner:returns_app",
]
self.assertEqual(runner.run(argv=argv, _serve=check_server), 0)
-class Test_helper(unittest.TestCase):
+class Test_helper(unittest.TestCase):
def test_exception_logging(self):
from waitress.runner import show_exception
@@ -172,10 +162,7 @@ class Test_helper(unittest.TestCase):
raise ImportError("My reason")
except ImportError:
self.assertEqual(show_exception(sys.stderr), None)
- self.assertRegexpMatches(
- captured.getvalue(),
- regex
- )
+ self.assertRegexpMatches(captured.getvalue(), regex)
captured.close()
regex = (
@@ -188,15 +175,14 @@ class Test_helper(unittest.TestCase):
raise ImportError
except ImportError:
self.assertEqual(show_exception(sys.stderr), None)
- self.assertRegexpMatches(
- captured.getvalue(),
- regex
- )
+ self.assertRegexpMatches(captured.getvalue(), regex)
captured.close()
+
@contextlib.contextmanager
def capture():
from waitress.compat import NativeIO
+
fd = NativeIO()
sys.stdout = fd
sys.stderr = fd
diff --git a/waitress/tests/test_server.py b/waitress/tests/test_server.py
index 7cd6345..9134fb8 100644
--- a/waitress/tests/test_server.py
+++ b/waitress/tests/test_server.py
@@ -4,12 +4,22 @@ import unittest
dummy_app = object()
-class TestWSGIServer(unittest.TestCase):
- def _makeOne(self, application=dummy_app, host='127.0.0.1', port=0,
- _dispatcher=None, adj=None, map=None, _start=True,
- _sock=None, _server=None):
+class TestWSGIServer(unittest.TestCase):
+ def _makeOne(
+ self,
+ application=dummy_app,
+ host="127.0.0.1",
+ port=0,
+ _dispatcher=None,
+ adj=None,
+ map=None,
+ _start=True,
+ _sock=None,
+ _server=None,
+ ):
from waitress.server import create_server
+
self.inst = create_server(
application,
host=host,
@@ -17,11 +27,13 @@ class TestWSGIServer(unittest.TestCase):
map=map,
_dispatcher=_dispatcher,
_start=_start,
- _sock=_sock)
+ _sock=_sock,
+ )
return self.inst
- def _makeOneWithMap(self, adj=None, _start=True, host='127.0.0.1',
- port=0, app=dummy_app):
+ def _makeOneWithMap(
+ self, adj=None, _start=True, host="127.0.0.1", port=0, app=dummy_app
+ ):
sock = DummySock()
task_dispatcher = DummyTaskDispatcher()
map = {}
@@ -35,24 +47,36 @@ class TestWSGIServer(unittest.TestCase):
_start=_start,
)
- def _makeOneWithMulti(self, adj=None, _start=True,
- app=dummy_app, listen="127.0.0.1:0 127.0.0.1:0"):
+ def _makeOneWithMulti(
+ self, adj=None, _start=True, app=dummy_app, listen="127.0.0.1:0 127.0.0.1:0"
+ ):
sock = DummySock()
task_dispatcher = DummyTaskDispatcher()
map = {}
from waitress.server import create_server
+
self.inst = create_server(
app,
listen=listen,
map=map,
_dispatcher=task_dispatcher,
_start=_start,
- _sock=sock)
+ _sock=sock,
+ )
return self.inst
- def _makeWithSockets(self, application=dummy_app, _dispatcher=None, map=None,
- _start=True, _sock=None, _server=None, sockets=None):
+ def _makeWithSockets(
+ self,
+ application=dummy_app,
+ _dispatcher=None,
+ map=None,
+ _start=True,
+ _sock=None,
+ _server=None,
+ sockets=None,
+ ):
from waitress.server import create_server
+
_sockets = []
if sockets is not None:
_sockets = sockets
@@ -62,7 +86,8 @@ class TestWSGIServer(unittest.TestCase):
_dispatcher=_dispatcher,
_start=_start,
_sock=_sock,
- sockets=_sockets)
+ sockets=_sockets,
+ )
return self.inst
def tearDown(self):
@@ -80,8 +105,9 @@ class TestWSGIServer(unittest.TestCase):
def test_ctor_makes_dispatcher(self):
inst = self._makeOne(_start=False, map={})
- self.assertEqual(inst.task_dispatcher.__class__.__name__,
- 'ThreadedTaskDispatcher')
+ self.assertEqual(
+ inst.task_dispatcher.__class__.__name__, "ThreadedTaskDispatcher"
+ )
def test_ctor_start_false(self):
inst = self._makeOneWithMap(_start=False)
@@ -89,36 +115,36 @@ class TestWSGIServer(unittest.TestCase):
def test_get_server_name_empty(self):
inst = self._makeOneWithMap(_start=False)
- self.assertRaises(ValueError, inst.get_server_name, '')
+ self.assertRaises(ValueError, inst.get_server_name, "")
def test_get_server_name_with_ip(self):
inst = self._makeOneWithMap(_start=False)
- result = inst.get_server_name('127.0.0.1')
+ result = inst.get_server_name("127.0.0.1")
self.assertTrue(result)
def test_get_server_name_with_hostname(self):
inst = self._makeOneWithMap(_start=False)
- result = inst.get_server_name('fred.flintstone.com')
- self.assertEqual(result, 'fred.flintstone.com')
+ result = inst.get_server_name("fred.flintstone.com")
+ self.assertEqual(result, "fred.flintstone.com")
def test_get_server_name_0000(self):
inst = self._makeOneWithMap(_start=False)
- result = inst.get_server_name('0.0.0.0')
+ result = inst.get_server_name("0.0.0.0")
self.assertTrue(len(result) != 0)
def test_get_server_name_double_colon(self):
inst = self._makeOneWithMap(_start=False)
- result = inst.get_server_name('::')
+ result = inst.get_server_name("::")
self.assertTrue(len(result) != 0)
def test_get_server_name_ipv6(self):
inst = self._makeOneWithMap(_start=False)
- result = inst.get_server_name('2001:DB8::ffff')
- self.assertEqual('[2001:DB8::ffff]', result)
+ result = inst.get_server_name("2001:DB8::ffff")
+ self.assertEqual("[2001:DB8::ffff]", result)
def test_get_server_multi(self):
inst = self._makeOneWithMulti()
- self.assertEqual(inst.__class__.__name__, 'MultiSocketServer')
+ self.assertEqual(inst.__class__.__name__, "MultiSocketServer")
def test_run(self):
inst = self._makeOneWithMap(_start=False)
@@ -157,7 +183,7 @@ class TestWSGIServer(unittest.TestCase):
inst = self._makeOneWithMap()
inst.accepting = True
inst.adj = DummyAdj
- inst._map = {'a': 1, 'b': 2}
+ inst._map = {"a": 1, "b": 2}
self.assertFalse(inst.readable())
def test_readable_maplen_lt_connection_limit(self):
@@ -169,6 +195,7 @@ class TestWSGIServer(unittest.TestCase):
def test_readable_maintenance_false(self):
import time
+
inst = self._makeOneWithMap()
then = time.time() + 1000
inst.next_channel_cleanup = then
@@ -211,8 +238,10 @@ class TestWSGIServer(unittest.TestCase):
eaborted = socket.error(errno.ECONNABORTED)
inst.socket = DummySock(toraise=eaborted)
inst.adj = DummyAdj
+
def foo():
raise socket.error
+
inst.accept = foo
inst.logger = DummyLogger()
inst.handle_accept()
@@ -228,7 +257,7 @@ class TestWSGIServer(unittest.TestCase):
inst.channel_class = lambda *arg, **kw: L.append(arg)
inst.handle_accept()
self.assertEqual(inst.socket.accepted, True)
- self.assertEqual(innersock.opts, [('level', 'optname', 'value')])
+ self.assertEqual(innersock.opts, [("level", "optname", "value")])
self.assertEqual(L, [(inst, innersock, None, inst.adj)])
def test_maintenance(self):
@@ -236,6 +265,7 @@ class TestWSGIServer(unittest.TestCase):
class DummyChannel(object):
requests = []
+
zombie = DummyChannel()
zombie.last_activity = 0
zombie.running_tasks = False
@@ -246,6 +276,7 @@ class TestWSGIServer(unittest.TestCase):
def test_backward_compatibility(self):
from waitress.server import WSGIServer, TcpWSGIServer
from waitress.adjustments import Adjustments
+
self.assertTrue(WSGIServer is TcpWSGIServer)
self.inst = WSGIServer(None, _start=False, port=1234)
# Ensure the adjustment was actually applied.
@@ -254,18 +285,21 @@ class TestWSGIServer(unittest.TestCase):
def test_create_with_one_tcp_socket(self):
from waitress.server import TcpWSGIServer
+
sockets = [socket.socket(socket.AF_INET, socket.SOCK_STREAM)]
- sockets[0].bind(('127.0.0.1', 0))
+ sockets[0].bind(("127.0.0.1", 0))
inst = self._makeWithSockets(_start=False, sockets=sockets)
self.assertTrue(isinstance(inst, TcpWSGIServer))
def test_create_with_multiple_tcp_sockets(self):
from waitress.server import MultiSocketServer
+
sockets = [
socket.socket(socket.AF_INET, socket.SOCK_STREAM),
- socket.socket(socket.AF_INET, socket.SOCK_STREAM)]
- sockets[0].bind(('127.0.0.1', 0))
- sockets[1].bind(('127.0.0.1', 0))
+ socket.socket(socket.AF_INET, socket.SOCK_STREAM),
+ ]
+ sockets[0].bind(("127.0.0.1", 0))
+ sockets[1].bind(("127.0.0.1", 0))
inst = self._makeWithSockets(_start=False, sockets=sockets)
self.assertTrue(isinstance(inst, MultiSocketServer))
self.assertEqual(len(inst.effective_listen), 2)
@@ -273,33 +307,34 @@ class TestWSGIServer(unittest.TestCase):
def test_create_with_one_socket_should_not_bind_socket(self):
innersock = DummySock()
sockets = [DummySock(acceptresult=(innersock, None))]
- sockets[0].bind(('127.0.0.1', 80))
+ sockets[0].bind(("127.0.0.1", 80))
sockets[0].bind_called = False
inst = self._makeWithSockets(_start=False, sockets=sockets)
- self.assertEqual(inst.socket.bound, ('127.0.0.1', 80))
+ self.assertEqual(inst.socket.bound, ("127.0.0.1", 80))
self.assertFalse(inst.socket.bind_called)
def test_create_with_one_socket_handle_accept_noerror(self):
innersock = DummySock()
sockets = [DummySock(acceptresult=(innersock, None))]
- sockets[0].bind(('127.0.0.1', 80))
+ sockets[0].bind(("127.0.0.1", 80))
inst = self._makeWithSockets(sockets=sockets)
L = []
inst.channel_class = lambda *arg, **kw: L.append(arg)
inst.adj = DummyAdj
inst.handle_accept()
self.assertEqual(sockets[0].accepted, True)
- self.assertEqual(innersock.opts, [('level', 'optname', 'value')])
+ self.assertEqual(innersock.opts, [("level", "optname", "value")])
self.assertEqual(L, [(inst, innersock, None, inst.adj)])
-if hasattr(socket, 'AF_UNIX'):
+if hasattr(socket, "AF_UNIX"):
class TestUnixWSGIServer(unittest.TestCase):
- unix_socket = '/tmp/waitress.test.sock'
+ unix_socket = "/tmp/waitress.test.sock"
def _makeOne(self, _start=True, _sock=None):
from waitress.server import create_server
+
self.inst = create_server(
dummy_app,
map={},
@@ -307,13 +342,22 @@ if hasattr(socket, 'AF_UNIX'):
_sock=_sock,
_dispatcher=DummyTaskDispatcher(),
unix_socket=self.unix_socket,
- unix_socket_perms='600'
+ unix_socket_perms="600",
)
return self.inst
- def _makeWithSockets(self, application=dummy_app, _dispatcher=None, map=None,
- _start=True, _sock=None, _server=None, sockets=None):
+ def _makeWithSockets(
+ self,
+ application=dummy_app,
+ _dispatcher=None,
+ map=None,
+ _start=True,
+ _sock=None,
+ _server=None,
+ sockets=None,
+ ):
from waitress.server import create_server
+
_sockets = []
if sockets is not None:
_sockets = sockets
@@ -323,7 +367,8 @@ if hasattr(socket, 'AF_UNIX'):
_dispatcher=_dispatcher,
_start=_start,
_sock=_sock,
- sockets=_sockets)
+ sockets=_sockets,
+ )
return self.inst
def tearDown(self):
@@ -353,30 +398,34 @@ if hasattr(socket, 'AF_UNIX'):
inst.handle_accept()
self.assertEqual(inst.socket.accepted, True)
self.assertEqual(client.opts, [])
- self.assertEqual(
- L,
- [(inst, client, ('localhost', None), inst.adj)]
- )
+ self.assertEqual(L, [(inst, client, ("localhost", None), inst.adj)])
def test_creates_new_sockinfo(self):
from waitress.server import UnixWSGIServer
+
self.inst = UnixWSGIServer(
- dummy_app,
- unix_socket=self.unix_socket,
- unix_socket_perms='600'
+ dummy_app, unix_socket=self.unix_socket, unix_socket_perms="600"
)
self.assertEqual(self.inst.sockinfo[0], socket.AF_UNIX)
def test_create_with_unix_socket(self):
- from waitress.server import MultiSocketServer, BaseWSGIServer, \
- TcpWSGIServer, UnixWSGIServer
+ from waitress.server import (
+ MultiSocketServer,
+ BaseWSGIServer,
+ TcpWSGIServer,
+ UnixWSGIServer,
+ )
+
sockets = [
socket.socket(socket.AF_UNIX, socket.SOCK_STREAM),
- socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)]
+ socket.socket(socket.AF_UNIX, socket.SOCK_STREAM),
+ ]
inst = self._makeWithSockets(sockets=sockets, _start=False)
self.assertTrue(isinstance(inst, MultiSocketServer))
- server = list(filter(lambda s: isinstance(s, BaseWSGIServer), inst.map.values()))
+ server = list(
+ filter(lambda s: isinstance(s, BaseWSGIServer), inst.map.values())
+ )
self.assertTrue(isinstance(server[0], UnixWSGIServer))
self.assertTrue(isinstance(server[1], UnixWSGIServer))
@@ -412,7 +461,7 @@ class DummySock(socket.socket):
return 10
def getpeername(self):
- return '127.0.0.1'
+ return "127.0.0.1"
def setsockopt(self, *arg):
self.opts.append(arg)
@@ -429,8 +478,8 @@ class DummySock(socket.socket):
def close(self):
pass
-class DummyTaskDispatcher(object):
+class DummyTaskDispatcher(object):
def __init__(self):
self.tasks = []
@@ -440,41 +489,43 @@ class DummyTaskDispatcher(object):
def shutdown(self):
self.was_shutdown = True
+
class DummyTask(object):
serviced = False
start_response_called = False
wrote_header = False
- status = '200 OK'
+ status = "200 OK"
def __init__(self):
self.response_headers = {}
- self.written = ''
+ self.written = ""
- def service(self): # pragma: no cover
+ def service(self): # pragma: no cover
self.serviced = True
+
class DummyAdj:
connection_limit = 1
log_socket_errors = True
- socket_options = [('level', 'optname', 'value')]
+ socket_options = [("level", "optname", "value")]
cleanup_interval = 900
channel_timeout = 300
-class DummyAsyncore(object):
+class DummyAsyncore(object):
def loop(self, timeout=30.0, use_poll=False, map=None, count=None):
raise SystemExit
-class DummyTrigger(object):
+class DummyTrigger(object):
def pull_trigger(self):
self.pulled = True
def close(self):
pass
-class DummyLogger(object):
+class DummyLogger(object):
def __init__(self):
self.logged = []
diff --git a/waitress/tests/test_task.py b/waitress/tests/test_task.py
index a75b87f..584add1 100644
--- a/waitress/tests/test_task.py
+++ b/waitress/tests/test_task.py
@@ -1,21 +1,24 @@
import unittest
import io
-class TestThreadedTaskDispatcher(unittest.TestCase):
+class TestThreadedTaskDispatcher(unittest.TestCase):
def _makeOne(self):
from waitress.task import ThreadedTaskDispatcher
+
return ThreadedTaskDispatcher()
def test_handler_thread_task_raises(self):
inst = self._makeOne()
inst.threads.add(0)
inst.logger = DummyLogger()
+
class BadDummyTask(DummyTask):
def service(self):
super(BadDummyTask, self).service()
inst.stop_count += 1
raise Exception
+
task = BadDummyTask()
inst.logger = DummyLogger()
inst.queue.append(task)
@@ -79,225 +82,228 @@ class TestThreadedTaskDispatcher(unittest.TestCase):
inst.logger = DummyLogger()
task = DummyTask()
inst.queue.append(task)
- self.assertEqual(inst.shutdown(timeout=.01), True)
- self.assertEqual(inst.logger.logged, [
- '1 thread(s) still running',
- 'Canceling 1 pending task(s)',
- ])
+ self.assertEqual(inst.shutdown(timeout=0.01), True)
+ self.assertEqual(
+ inst.logger.logged,
+ ["1 thread(s) still running", "Canceling 1 pending task(s)",],
+ )
self.assertEqual(task.cancelled, True)
def test_shutdown_no_threads(self):
inst = self._makeOne()
- self.assertEqual(inst.shutdown(timeout=.01), True)
+ self.assertEqual(inst.shutdown(timeout=0.01), True)
def test_shutdown_no_cancel_pending(self):
inst = self._makeOne()
- self.assertEqual(inst.shutdown(cancel_pending=False, timeout=.01),
- False)
+ self.assertEqual(inst.shutdown(cancel_pending=False, timeout=0.01), False)
-class TestTask(unittest.TestCase):
+class TestTask(unittest.TestCase):
def _makeOne(self, channel=None, request=None):
if channel is None:
channel = DummyChannel()
if request is None:
request = DummyParser()
from waitress.task import Task
+
return Task(channel, request)
def test_ctor_version_not_in_known(self):
request = DummyParser()
- request.version = '8.4'
+ request.version = "8.4"
inst = self._makeOne(request=request)
- self.assertEqual(inst.version, '1.0')
+ self.assertEqual(inst.version, "1.0")
def test_build_response_header_bad_http_version(self):
inst = self._makeOne()
inst.request = DummyParser()
- inst.version = '8.4'
+ inst.version = "8.4"
self.assertRaises(AssertionError, inst.build_response_header)
def test_build_response_header_v10_keepalive_no_content_length(self):
inst = self._makeOne()
inst.request = DummyParser()
- inst.request.headers['CONNECTION'] = 'keep-alive'
- inst.version = '1.0'
+ inst.request.headers["CONNECTION"] = "keep-alive"
+ inst.version = "1.0"
result = inst.build_response_header()
lines = filter_lines(result)
self.assertEqual(len(lines), 4)
- self.assertEqual(lines[0], b'HTTP/1.0 200 OK')
- self.assertEqual(lines[1], b'Connection: close')
- self.assertTrue(lines[2].startswith(b'Date:'))
- self.assertEqual(lines[3], b'Server: waitress')
+ self.assertEqual(lines[0], b"HTTP/1.0 200 OK")
+ self.assertEqual(lines[1], b"Connection: close")
+ self.assertTrue(lines[2].startswith(b"Date:"))
+ self.assertEqual(lines[3], b"Server: waitress")
self.assertEqual(inst.close_on_finish, True)
- self.assertTrue(('Connection', 'close') in inst.response_headers)
+ self.assertTrue(("Connection", "close") in inst.response_headers)
def test_build_response_header_v10_keepalive_with_content_length(self):
inst = self._makeOne()
inst.request = DummyParser()
- inst.request.headers['CONNECTION'] = 'keep-alive'
- inst.response_headers = [('Content-Length', '10')]
- inst.version = '1.0'
+ inst.request.headers["CONNECTION"] = "keep-alive"
+ inst.response_headers = [("Content-Length", "10")]
+ inst.version = "1.0"
inst.content_length = 0
result = inst.build_response_header()
lines = filter_lines(result)
self.assertEqual(len(lines), 5)
- self.assertEqual(lines[0], b'HTTP/1.0 200 OK')
- self.assertEqual(lines[1], b'Connection: Keep-Alive')
- self.assertEqual(lines[2], b'Content-Length: 10')
- self.assertTrue(lines[3].startswith(b'Date:'))
- self.assertEqual(lines[4], b'Server: waitress')
+ self.assertEqual(lines[0], b"HTTP/1.0 200 OK")
+ self.assertEqual(lines[1], b"Connection: Keep-Alive")
+ self.assertEqual(lines[2], b"Content-Length: 10")
+ self.assertTrue(lines[3].startswith(b"Date:"))
+ self.assertEqual(lines[4], b"Server: waitress")
self.assertEqual(inst.close_on_finish, False)
def test_build_response_header_v11_connection_closed_by_client(self):
inst = self._makeOne()
inst.request = DummyParser()
- inst.version = '1.1'
- inst.request.headers['CONNECTION'] = 'close'
+ inst.version = "1.1"
+ inst.request.headers["CONNECTION"] = "close"
result = inst.build_response_header()
lines = filter_lines(result)
self.assertEqual(len(lines), 5)
- self.assertEqual(lines[0], b'HTTP/1.1 200 OK')
- self.assertEqual(lines[1], b'Connection: close')
- self.assertTrue(lines[2].startswith(b'Date:'))
- self.assertEqual(lines[3], b'Server: waitress')
- self.assertEqual(lines[4], b'Transfer-Encoding: chunked')
- self.assertTrue(('Connection', 'close') in inst.response_headers)
+ self.assertEqual(lines[0], b"HTTP/1.1 200 OK")
+ self.assertEqual(lines[1], b"Connection: close")
+ self.assertTrue(lines[2].startswith(b"Date:"))
+ self.assertEqual(lines[3], b"Server: waitress")
+ self.assertEqual(lines[4], b"Transfer-Encoding: chunked")
+ self.assertTrue(("Connection", "close") in inst.response_headers)
self.assertEqual(inst.close_on_finish, True)
def test_build_response_header_v11_connection_keepalive_by_client(self):
inst = self._makeOne()
inst.request = DummyParser()
- inst.request.headers['CONNECTION'] = 'keep-alive'
- inst.version = '1.1'
+ inst.request.headers["CONNECTION"] = "keep-alive"
+ inst.version = "1.1"
result = inst.build_response_header()
lines = filter_lines(result)
self.assertEqual(len(lines), 5)
- self.assertEqual(lines[0], b'HTTP/1.1 200 OK')
- self.assertEqual(lines[1], b'Connection: close')
- self.assertTrue(lines[2].startswith(b'Date:'))
- self.assertEqual(lines[3], b'Server: waitress')
- self.assertEqual(lines[4], b'Transfer-Encoding: chunked')
- self.assertTrue(('Connection', 'close') in inst.response_headers)
+ self.assertEqual(lines[0], b"HTTP/1.1 200 OK")
+ self.assertEqual(lines[1], b"Connection: close")
+ self.assertTrue(lines[2].startswith(b"Date:"))
+ self.assertEqual(lines[3], b"Server: waitress")
+ self.assertEqual(lines[4], b"Transfer-Encoding: chunked")
+ self.assertTrue(("Connection", "close") in inst.response_headers)
self.assertEqual(inst.close_on_finish, True)
def test_build_response_header_v11_200_no_content_length(self):
inst = self._makeOne()
inst.request = DummyParser()
- inst.version = '1.1'
+ inst.version = "1.1"
result = inst.build_response_header()
lines = filter_lines(result)
self.assertEqual(len(lines), 5)
- self.assertEqual(lines[0], b'HTTP/1.1 200 OK')
- self.assertEqual(lines[1], b'Connection: close')
- self.assertTrue(lines[2].startswith(b'Date:'))
- self.assertEqual(lines[3], b'Server: waitress')
- self.assertEqual(lines[4], b'Transfer-Encoding: chunked')
+ self.assertEqual(lines[0], b"HTTP/1.1 200 OK")
+ self.assertEqual(lines[1], b"Connection: close")
+ self.assertTrue(lines[2].startswith(b"Date:"))
+ self.assertEqual(lines[3], b"Server: waitress")
+ self.assertEqual(lines[4], b"Transfer-Encoding: chunked")
self.assertEqual(inst.close_on_finish, True)
- self.assertTrue(('Connection', 'close') in inst.response_headers)
+ self.assertTrue(("Connection", "close") in inst.response_headers)
def test_build_response_header_v11_204_no_content_length_or_transfer_encoding(self):
# RFC 7230: MUST NOT send Transfer-Encoding or Content-Length
# for any response with a status code of 1xx or 204.
inst = self._makeOne()
inst.request = DummyParser()
- inst.version = '1.1'
- inst.status = '204 No Content'
+ inst.version = "1.1"
+ inst.status = "204 No Content"
result = inst.build_response_header()
lines = filter_lines(result)
self.assertEqual(len(lines), 4)
- self.assertEqual(lines[0], b'HTTP/1.1 204 No Content')
- self.assertEqual(lines[1], b'Connection: close')
- self.assertTrue(lines[2].startswith(b'Date:'))
- self.assertEqual(lines[3], b'Server: waitress')
+ self.assertEqual(lines[0], b"HTTP/1.1 204 No Content")
+ self.assertEqual(lines[1], b"Connection: close")
+ self.assertTrue(lines[2].startswith(b"Date:"))
+ self.assertEqual(lines[3], b"Server: waitress")
self.assertEqual(inst.close_on_finish, True)
- self.assertTrue(('Connection', 'close') in inst.response_headers)
+ self.assertTrue(("Connection", "close") in inst.response_headers)
def test_build_response_header_v11_1xx_no_content_length_or_transfer_encoding(self):
# RFC 7230: MUST NOT send Transfer-Encoding or Content-Length
# for any response with a status code of 1xx or 204.
inst = self._makeOne()
inst.request = DummyParser()
- inst.version = '1.1'
- inst.status = '100 Continue'
+ inst.version = "1.1"
+ inst.status = "100 Continue"
result = inst.build_response_header()
lines = filter_lines(result)
self.assertEqual(len(lines), 4)
- self.assertEqual(lines[0], b'HTTP/1.1 100 Continue')
- self.assertEqual(lines[1], b'Connection: close')
- self.assertTrue(lines[2].startswith(b'Date:'))
- self.assertEqual(lines[3], b'Server: waitress')
+ self.assertEqual(lines[0], b"HTTP/1.1 100 Continue")
+ self.assertEqual(lines[1], b"Connection: close")
+ self.assertTrue(lines[2].startswith(b"Date:"))
+ self.assertEqual(lines[3], b"Server: waitress")
self.assertEqual(inst.close_on_finish, True)
- self.assertTrue(('Connection', 'close') in inst.response_headers)
+ self.assertTrue(("Connection", "close") in inst.response_headers)
def test_build_response_header_v11_304_no_content_length_or_transfer_encoding(self):
# RFC 7230: MUST NOT send Transfer-Encoding or Content-Length
# for any response with a status code of 1xx, 204 or 304.
inst = self._makeOne()
inst.request = DummyParser()
- inst.version = '1.1'
- inst.status = '304 Not Modified'
+ inst.version = "1.1"
+ inst.status = "304 Not Modified"
result = inst.build_response_header()
lines = filter_lines(result)
self.assertEqual(len(lines), 4)
- self.assertEqual(lines[0], b'HTTP/1.1 304 Not Modified')
- self.assertEqual(lines[1], b'Connection: close')
- self.assertTrue(lines[2].startswith(b'Date:'))
- self.assertEqual(lines[3], b'Server: waitress')
+ self.assertEqual(lines[0], b"HTTP/1.1 304 Not Modified")
+ self.assertEqual(lines[1], b"Connection: close")
+ self.assertTrue(lines[2].startswith(b"Date:"))
+ self.assertEqual(lines[3], b"Server: waitress")
self.assertEqual(inst.close_on_finish, True)
- self.assertTrue(('Connection', 'close') in inst.response_headers)
+ self.assertTrue(("Connection", "close") in inst.response_headers)
def test_build_response_header_via_added(self):
inst = self._makeOne()
inst.request = DummyParser()
- inst.version = '1.0'
- inst.response_headers = [('Server', 'abc')]
+ inst.version = "1.0"
+ inst.response_headers = [("Server", "abc")]
result = inst.build_response_header()
lines = filter_lines(result)
self.assertEqual(len(lines), 5)
- self.assertEqual(lines[0], b'HTTP/1.0 200 OK')
- self.assertEqual(lines[1], b'Connection: close')
- self.assertTrue(lines[2].startswith(b'Date:'))
- self.assertEqual(lines[3], b'Server: abc')
- self.assertEqual(lines[4], b'Via: waitress')
+ self.assertEqual(lines[0], b"HTTP/1.0 200 OK")
+ self.assertEqual(lines[1], b"Connection: close")
+ self.assertTrue(lines[2].startswith(b"Date:"))
+ self.assertEqual(lines[3], b"Server: abc")
+ self.assertEqual(lines[4], b"Via: waitress")
def test_build_response_header_date_exists(self):
inst = self._makeOne()
inst.request = DummyParser()
- inst.version = '1.0'
- inst.response_headers = [('Date', 'date')]
+ inst.version = "1.0"
+ inst.response_headers = [("Date", "date")]
result = inst.build_response_header()
lines = filter_lines(result)
self.assertEqual(len(lines), 4)
- self.assertEqual(lines[0], b'HTTP/1.0 200 OK')
- self.assertEqual(lines[1], b'Connection: close')
- self.assertTrue(lines[2].startswith(b'Date:'))
- self.assertEqual(lines[3], b'Server: waitress')
+ self.assertEqual(lines[0], b"HTTP/1.0 200 OK")
+ self.assertEqual(lines[1], b"Connection: close")
+ self.assertTrue(lines[2].startswith(b"Date:"))
+ self.assertEqual(lines[3], b"Server: waitress")
def test_build_response_header_preexisting_content_length(self):
inst = self._makeOne()
inst.request = DummyParser()
- inst.version = '1.1'
+ inst.version = "1.1"
inst.content_length = 100
result = inst.build_response_header()
lines = filter_lines(result)
self.assertEqual(len(lines), 4)
- self.assertEqual(lines[0], b'HTTP/1.1 200 OK')
- self.assertEqual(lines[1], b'Content-Length: 100')
- self.assertTrue(lines[2].startswith(b'Date:'))
- self.assertEqual(lines[3], b'Server: waitress')
+ self.assertEqual(lines[0], b"HTTP/1.1 200 OK")
+ self.assertEqual(lines[1], b"Content-Length: 100")
+ self.assertTrue(lines[2].startswith(b"Date:"))
+ self.assertEqual(lines[3], b"Server: waitress")
def test_remove_content_length_header(self):
inst = self._makeOne()
- inst.response_headers = [('Content-Length', '70')]
+ inst.response_headers = [("Content-Length", "70")]
inst.remove_content_length_header()
self.assertEqual(inst.response_headers, [])
def test_remove_content_length_header_with_other(self):
inst = self._makeOne()
- inst.response_headers = [('Content-Length', '70'), ('Content-Type', 'text/html')]
+ inst.response_headers = [
+ ("Content-Length", "70"),
+ ("Content-Type", "text/html"),
+ ]
inst.remove_content_length_header()
- self.assertEqual(inst.response_headers, [('Content-Type', 'text/html')])
+ self.assertEqual(inst.response_headers, [("Content-Type", "text/html")])
def test_start(self):
inst = self._makeOne()
@@ -322,35 +328,35 @@ class TestTask(unittest.TestCase):
inst.wrote_header = True
inst.chunked_response = True
inst.finish()
- self.assertEqual(inst.channel.written, b'0\r\n\r\n')
+ self.assertEqual(inst.channel.written, b"0\r\n\r\n")
def test_write_wrote_header(self):
inst = self._makeOne()
inst.wrote_header = True
inst.complete = True
inst.content_length = 3
- inst.write(b'abc')
- self.assertEqual(inst.channel.written, b'abc')
+ inst.write(b"abc")
+ self.assertEqual(inst.channel.written, b"abc")
def test_write_header_not_written(self):
inst = self._makeOne()
inst.wrote_header = False
inst.complete = True
- inst.write(b'abc')
+ inst.write(b"abc")
self.assertTrue(inst.channel.written)
self.assertEqual(inst.wrote_header, True)
def test_write_start_response_uncalled(self):
inst = self._makeOne()
- self.assertRaises(RuntimeError, inst.write, b'')
+ self.assertRaises(RuntimeError, inst.write, b"")
def test_write_chunked_response(self):
inst = self._makeOne()
inst.wrote_header = True
inst.chunked_response = True
inst.complete = True
- inst.write(b'abc')
- self.assertEqual(inst.channel.written, b'3\r\nabc\r\n')
+ inst.write(b"abc")
+ self.assertEqual(inst.channel.written, b"3\r\nabc\r\n")
def test_write_preexisting_content_length(self):
inst = self._makeOne()
@@ -358,25 +364,28 @@ class TestTask(unittest.TestCase):
inst.complete = True
inst.content_length = 1
inst.logger = DummyLogger()
- inst.write(b'abc')
+ inst.write(b"abc")
self.assertTrue(inst.channel.written)
self.assertEqual(inst.logged_write_excess, True)
self.assertEqual(len(inst.logger.logged), 1)
-class TestWSGITask(unittest.TestCase):
+class TestWSGITask(unittest.TestCase):
def _makeOne(self, channel=None, request=None):
if channel is None:
channel = DummyChannel()
if request is None:
request = DummyParser()
from waitress.task import WSGITask
+
return WSGITask(channel, request)
def test_service(self):
inst = self._makeOne()
+
def execute():
inst.executed = True
+
inst.execute = execute
inst.complete = True
inst.service()
@@ -387,9 +396,12 @@ class TestWSGITask(unittest.TestCase):
def test_service_server_raises_socket_error(self):
import socket
+
inst = self._makeOne()
+
def execute():
raise socket.error
+
inst.execute = execute
self.assertRaises(socket.error, inst.service)
self.assertTrue(inst.start_time)
@@ -398,41 +410,45 @@ class TestWSGITask(unittest.TestCase):
def test_execute_app_calls_start_response_twice_wo_exc_info(self):
def app(environ, start_response):
- start_response('200 OK', [])
- start_response('200 OK', [])
+ start_response("200 OK", [])
+ start_response("200 OK", [])
+
inst = self._makeOne()
inst.channel.server.application = app
self.assertRaises(AssertionError, inst.execute)
def test_execute_app_calls_start_response_w_exc_info_complete(self):
def app(environ, start_response):
- start_response('200 OK', [], [ValueError, ValueError(), None])
- return [b'a']
+ start_response("200 OK", [], [ValueError, ValueError(), None])
+ return [b"a"]
+
inst = self._makeOne()
inst.complete = True
inst.channel.server.application = app
inst.execute()
self.assertTrue(inst.complete)
- self.assertEqual(inst.status, '200 OK')
+ self.assertEqual(inst.status, "200 OK")
self.assertTrue(inst.channel.written)
def test_execute_app_calls_start_response_w_excinf_headers_unwritten(self):
def app(environ, start_response):
- start_response('200 OK', [], [ValueError, None, None])
- return [b'a']
+ start_response("200 OK", [], [ValueError, None, None])
+ return [b"a"]
+
inst = self._makeOne()
inst.wrote_header = False
inst.channel.server.application = app
- inst.response_headers = [('a', 'b')]
+ inst.response_headers = [("a", "b")]
inst.execute()
self.assertTrue(inst.complete)
- self.assertEqual(inst.status, '200 OK')
+ self.assertEqual(inst.status, "200 OK")
self.assertTrue(inst.channel.written)
- self.assertFalse(('a','b') in inst.response_headers)
+ self.assertFalse(("a", "b") in inst.response_headers)
def test_execute_app_calls_start_response_w_excinf_headers_written(self):
def app(environ, start_response):
- start_response('200 OK', [], [ValueError, ValueError(), None])
+ start_response("200 OK", [], [ValueError, ValueError(), None])
+
inst = self._makeOne()
inst.complete = True
inst.wrote_header = True
@@ -441,67 +457,76 @@ class TestWSGITask(unittest.TestCase):
def test_execute_bad_header_key(self):
def app(environ, start_response):
- start_response('200 OK', [(None, 'a')])
+ start_response("200 OK", [(None, "a")])
+
inst = self._makeOne()
inst.channel.server.application = app
self.assertRaises(AssertionError, inst.execute)
def test_execute_bad_header_value(self):
def app(environ, start_response):
- start_response('200 OK', [('a', None)])
+ start_response("200 OK", [("a", None)])
+
inst = self._makeOne()
inst.channel.server.application = app
self.assertRaises(AssertionError, inst.execute)
def test_execute_hopbyhop_header(self):
def app(environ, start_response):
- start_response('200 OK', [('Connection', 'close')])
+ start_response("200 OK", [("Connection", "close")])
+
inst = self._makeOne()
inst.channel.server.application = app
self.assertRaises(AssertionError, inst.execute)
def test_execute_bad_header_value_control_characters(self):
def app(environ, start_response):
- start_response('200 OK', [('a', '\n')])
+ start_response("200 OK", [("a", "\n")])
+
inst = self._makeOne()
inst.channel.server.application = app
self.assertRaises(ValueError, inst.execute)
def test_execute_bad_header_name_control_characters(self):
def app(environ, start_response):
- start_response('200 OK', [('a\r', 'value')])
+ start_response("200 OK", [("a\r", "value")])
+
inst = self._makeOne()
inst.channel.server.application = app
self.assertRaises(ValueError, inst.execute)
def test_execute_bad_status_control_characters(self):
def app(environ, start_response):
- start_response('200 OK\r', [])
+ start_response("200 OK\r", [])
+
inst = self._makeOne()
inst.channel.server.application = app
self.assertRaises(ValueError, inst.execute)
def test_preserve_header_value_order(self):
def app(environ, start_response):
- write = start_response('200 OK', [('C', 'b'), ('A', 'b'), ('A', 'a')])
- write(b'abc')
+ write = start_response("200 OK", [("C", "b"), ("A", "b"), ("A", "a")])
+ write(b"abc")
return []
+
inst = self._makeOne()
inst.channel.server.application = app
inst.execute()
- self.assertTrue(b'A: b\r\nA: a\r\nC: b\r\n' in inst.channel.written)
+ self.assertTrue(b"A: b\r\nA: a\r\nC: b\r\n" in inst.channel.written)
def test_execute_bad_status_value(self):
def app(environ, start_response):
start_response(None, [])
+
inst = self._makeOne()
inst.channel.server.application = app
self.assertRaises(AssertionError, inst.execute)
def test_execute_with_content_length_header(self):
def app(environ, start_response):
- start_response('200 OK', [('Content-Length', '1')])
- return [b'a']
+ start_response("200 OK", [("Content-Length", "1")])
+ return [b"a"]
+
inst = self._makeOne()
inst.channel.server.application = app
inst.execute()
@@ -509,18 +534,20 @@ class TestWSGITask(unittest.TestCase):
def test_execute_app_calls_write(self):
def app(environ, start_response):
- write = start_response('200 OK', [('Content-Length', '3')])
- write(b'abc')
+ write = start_response("200 OK", [("Content-Length", "3")])
+ write(b"abc")
return []
+
inst = self._makeOne()
inst.channel.server.application = app
inst.execute()
- self.assertEqual(inst.channel.written[-3:], b'abc')
+ self.assertEqual(inst.channel.written[-3:], b"abc")
def test_execute_app_returns_len1_chunk_without_cl(self):
def app(environ, start_response):
- start_response('200 OK', [])
- return [b'abc']
+ start_response("200 OK", [])
+ return [b"abc"]
+
inst = self._makeOne()
inst.channel.server.application = app
inst.execute()
@@ -528,8 +555,9 @@ class TestWSGITask(unittest.TestCase):
def test_execute_app_returns_empty_chunk_as_first(self):
def app(environ, start_response):
- start_response('200 OK', [])
- return ['', b'abc']
+ start_response("200 OK", [])
+ return ["", b"abc"]
+
inst = self._makeOne()
inst.channel.server.application = app
inst.execute()
@@ -537,8 +565,9 @@ class TestWSGITask(unittest.TestCase):
def test_execute_app_returns_too_many_bytes(self):
def app(environ, start_response):
- start_response('200 OK', [('Content-Length', '1')])
- return [b'abc']
+ start_response("200 OK", [("Content-Length", "1")])
+ return [b"abc"]
+
inst = self._makeOne()
inst.channel.server.application = app
inst.logger = DummyLogger()
@@ -548,8 +577,9 @@ class TestWSGITask(unittest.TestCase):
def test_execute_app_returns_too_few_bytes(self):
def app(environ, start_response):
- start_response('200 OK', [('Content-Length', '3')])
- return [b'a']
+ start_response("200 OK", [("Content-Length", "3")])
+ return [b"a"]
+
inst = self._makeOne()
inst.channel.server.application = app
inst.logger = DummyLogger()
@@ -559,10 +589,11 @@ class TestWSGITask(unittest.TestCase):
def test_execute_app_do_not_warn_on_head(self):
def app(environ, start_response):
- start_response('200 OK', [('Content-Length', '3')])
- return [b'']
+ start_response("200 OK", [("Content-Length", "3")])
+ return [b""]
+
inst = self._makeOne()
- inst.request.command = 'HEAD'
+ inst.request.command = "HEAD"
inst.channel.server.application = app
inst.logger = DummyLogger()
inst.execute()
@@ -571,40 +602,45 @@ class TestWSGITask(unittest.TestCase):
def test_execute_app_without_body_204_logged(self):
def app(environ, start_response):
- start_response('204 No Content', [('Content-Length', '3')])
- return [b'abc']
+ start_response("204 No Content", [("Content-Length", "3")])
+ return [b"abc"]
+
inst = self._makeOne()
inst.channel.server.application = app
inst.logger = DummyLogger()
inst.execute()
self.assertEqual(inst.close_on_finish, True)
- self.assertNotIn(b'abc', inst.channel.written)
- self.assertNotIn(b'Content-Length', inst.channel.written)
- self.assertNotIn(b'Transfer-Encoding', inst.channel.written)
+ self.assertNotIn(b"abc", inst.channel.written)
+ self.assertNotIn(b"Content-Length", inst.channel.written)
+ self.assertNotIn(b"Transfer-Encoding", inst.channel.written)
self.assertEqual(len(inst.logger.logged), 1)
def test_execute_app_without_body_304_logged(self):
def app(environ, start_response):
- start_response('304 Not Modified', [('Content-Length', '3')])
- return [b'abc']
+ start_response("304 Not Modified", [("Content-Length", "3")])
+ return [b"abc"]
+
inst = self._makeOne()
inst.channel.server.application = app
inst.logger = DummyLogger()
inst.execute()
self.assertEqual(inst.close_on_finish, True)
- self.assertNotIn(b'abc', inst.channel.written)
- self.assertNotIn(b'Content-Length', inst.channel.written)
- self.assertNotIn(b'Transfer-Encoding', inst.channel.written)
+ self.assertNotIn(b"abc", inst.channel.written)
+ self.assertNotIn(b"Content-Length", inst.channel.written)
+ self.assertNotIn(b"Transfer-Encoding", inst.channel.written)
self.assertEqual(len(inst.logger.logged), 1)
def test_execute_app_returns_closeable(self):
class closeable(list):
def close(self):
self.closed = True
- foo = closeable([b'abc'])
+
+ foo = closeable([b"abc"])
+
def app(environ, start_response):
- start_response('200 OK', [('Content-Length', '3')])
+ start_response("200 OK", [("Content-Length", "3")])
return foo
+
inst = self._makeOne()
inst.channel.server.application = app
inst.execute()
@@ -612,47 +648,56 @@ class TestWSGITask(unittest.TestCase):
def test_execute_app_returns_filewrapper_prepare_returns_True(self):
from waitress.buffers import ReadOnlyFileBasedBuffer
- f = io.BytesIO(b'abc')
+
+ f = io.BytesIO(b"abc")
app_iter = ReadOnlyFileBasedBuffer(f, 8192)
+
def app(environ, start_response):
- start_response('200 OK', [('Content-Length', '3')])
+ start_response("200 OK", [("Content-Length", "3")])
return app_iter
+
inst = self._makeOne()
inst.channel.server.application = app
inst.execute()
- self.assertTrue(inst.channel.written) # header
+ self.assertTrue(inst.channel.written) # header
self.assertEqual(inst.channel.otherdata, [app_iter])
def test_execute_app_returns_filewrapper_prepare_returns_True_nocl(self):
from waitress.buffers import ReadOnlyFileBasedBuffer
- f = io.BytesIO(b'abc')
+
+ f = io.BytesIO(b"abc")
app_iter = ReadOnlyFileBasedBuffer(f, 8192)
+
def app(environ, start_response):
- start_response('200 OK', [])
+ start_response("200 OK", [])
return app_iter
+
inst = self._makeOne()
inst.channel.server.application = app
inst.execute()
- self.assertTrue(inst.channel.written) # header
+ self.assertTrue(inst.channel.written) # header
self.assertEqual(inst.channel.otherdata, [app_iter])
self.assertEqual(inst.content_length, 3)
def test_execute_app_returns_filewrapper_prepare_returns_True_badcl(self):
from waitress.buffers import ReadOnlyFileBasedBuffer
- f = io.BytesIO(b'abc')
+
+ f = io.BytesIO(b"abc")
app_iter = ReadOnlyFileBasedBuffer(f, 8192)
+
def app(environ, start_response):
- start_response('200 OK', [])
+ start_response("200 OK", [])
return app_iter
+
inst = self._makeOne()
inst.channel.server.application = app
inst.content_length = 10
- inst.response_headers = [('Content-Length', '10')]
+ inst.response_headers = [("Content-Length", "10")]
inst.execute()
- self.assertTrue(inst.channel.written) # header
+ self.assertTrue(inst.channel.written) # header
self.assertEqual(inst.channel.otherdata, [app_iter])
self.assertEqual(inst.content_length, 3)
- self.assertEqual(dict(inst.response_headers)['Content-Length'], '3')
+ self.assertEqual(dict(inst.response_headers)["Content-Length"], "3")
def test_get_environment_already_cached(self):
inst = self._makeOne()
@@ -662,117 +707,137 @@ class TestWSGITask(unittest.TestCase):
def test_get_environment_path_startswith_more_than_one_slash(self):
inst = self._makeOne()
request = DummyParser()
- request.path = '///abc'
+ request.path = "///abc"
inst.request = request
environ = inst.get_environment()
- self.assertEqual(environ['PATH_INFO'], '/abc')
+ self.assertEqual(environ["PATH_INFO"], "/abc")
def test_get_environment_path_empty(self):
inst = self._makeOne()
request = DummyParser()
- request.path = ''
+ request.path = ""
inst.request = request
environ = inst.get_environment()
- self.assertEqual(environ['PATH_INFO'], '')
+ self.assertEqual(environ["PATH_INFO"], "")
def test_get_environment_no_query(self):
inst = self._makeOne()
request = DummyParser()
inst.request = request
environ = inst.get_environment()
- self.assertEqual(environ['QUERY_STRING'], '')
+ self.assertEqual(environ["QUERY_STRING"], "")
def test_get_environment_with_query(self):
inst = self._makeOne()
request = DummyParser()
- request.query = 'abc'
+ request.query = "abc"
inst.request = request
environ = inst.get_environment()
- self.assertEqual(environ['QUERY_STRING'], 'abc')
+ self.assertEqual(environ["QUERY_STRING"], "abc")
def test_get_environ_with_url_prefix_miss(self):
inst = self._makeOne()
- inst.channel.server.adj.url_prefix = '/foo'
+ inst.channel.server.adj.url_prefix = "/foo"
request = DummyParser()
- request.path = '/bar'
+ request.path = "/bar"
inst.request = request
environ = inst.get_environment()
- self.assertEqual(environ['PATH_INFO'], '/bar')
- self.assertEqual(environ['SCRIPT_NAME'], '/foo')
+ self.assertEqual(environ["PATH_INFO"], "/bar")
+ self.assertEqual(environ["SCRIPT_NAME"], "/foo")
def test_get_environ_with_url_prefix_hit(self):
inst = self._makeOne()
- inst.channel.server.adj.url_prefix = '/foo'
+ inst.channel.server.adj.url_prefix = "/foo"
request = DummyParser()
- request.path = '/foo/fuz'
+ request.path = "/foo/fuz"
inst.request = request
environ = inst.get_environment()
- self.assertEqual(environ['PATH_INFO'], '/fuz')
- self.assertEqual(environ['SCRIPT_NAME'], '/foo')
+ self.assertEqual(environ["PATH_INFO"], "/fuz")
+ self.assertEqual(environ["SCRIPT_NAME"], "/foo")
def test_get_environ_with_url_prefix_empty_path(self):
inst = self._makeOne()
- inst.channel.server.adj.url_prefix = '/foo'
+ inst.channel.server.adj.url_prefix = "/foo"
request = DummyParser()
- request.path = '/foo'
+ request.path = "/foo"
inst.request = request
environ = inst.get_environment()
- self.assertEqual(environ['PATH_INFO'], '')
- self.assertEqual(environ['SCRIPT_NAME'], '/foo')
+ self.assertEqual(environ["PATH_INFO"], "")
+ self.assertEqual(environ["SCRIPT_NAME"], "/foo")
def test_get_environment_values(self):
import sys
+
inst = self._makeOne()
request = DummyParser()
request.headers = {
- 'CONTENT_TYPE': 'abc',
- 'CONTENT_LENGTH': '10',
- 'X_FOO': 'BAR',
- 'CONNECTION': 'close',
+ "CONTENT_TYPE": "abc",
+ "CONTENT_LENGTH": "10",
+ "X_FOO": "BAR",
+ "CONNECTION": "close",
}
- request.query = 'abc'
+ request.query = "abc"
inst.request = request
environ = inst.get_environment()
# nail the keys of environ
- self.assertEqual(sorted(environ.keys()), [
- 'CONTENT_LENGTH', 'CONTENT_TYPE', 'HTTP_CONNECTION', 'HTTP_X_FOO',
- 'PATH_INFO', 'QUERY_STRING', 'REMOTE_ADDR', 'REMOTE_HOST',
- 'REMOTE_PORT', 'REQUEST_METHOD', 'SCRIPT_NAME', 'SERVER_NAME',
- 'SERVER_PORT', 'SERVER_PROTOCOL', 'SERVER_SOFTWARE', 'wsgi.errors',
- 'wsgi.file_wrapper', 'wsgi.input', 'wsgi.input_terminated',
- 'wsgi.multiprocess', 'wsgi.multithread', 'wsgi.run_once',
- 'wsgi.url_scheme', 'wsgi.version'
- ])
-
- self.assertEqual(environ['REQUEST_METHOD'], 'GET')
- self.assertEqual(environ['SERVER_PORT'], '80')
- self.assertEqual(environ['SERVER_NAME'], 'localhost')
- self.assertEqual(environ['SERVER_SOFTWARE'], 'waitress')
- self.assertEqual(environ['SERVER_PROTOCOL'], 'HTTP/1.0')
- self.assertEqual(environ['SCRIPT_NAME'], '')
- self.assertEqual(environ['HTTP_CONNECTION'], 'close')
- self.assertEqual(environ['PATH_INFO'], '/')
- self.assertEqual(environ['QUERY_STRING'], 'abc')
- self.assertEqual(environ['REMOTE_ADDR'], '127.0.0.1')
- self.assertEqual(environ['REMOTE_HOST'], '127.0.0.1')
- self.assertEqual(environ['REMOTE_PORT'], '39830')
- self.assertEqual(environ['CONTENT_TYPE'], 'abc')
- self.assertEqual(environ['CONTENT_LENGTH'], '10')
- self.assertEqual(environ['HTTP_X_FOO'], 'BAR')
- self.assertEqual(environ['wsgi.version'], (1, 0))
- self.assertEqual(environ['wsgi.url_scheme'], 'http')
- self.assertEqual(environ['wsgi.errors'], sys.stderr)
- self.assertEqual(environ['wsgi.multithread'], True)
- self.assertEqual(environ['wsgi.multiprocess'], False)
- self.assertEqual(environ['wsgi.run_once'], False)
- self.assertEqual(environ['wsgi.input'], 'stream')
- self.assertEqual(environ['wsgi.input_terminated'], True)
+ self.assertEqual(
+ sorted(environ.keys()),
+ [
+ "CONTENT_LENGTH",
+ "CONTENT_TYPE",
+ "HTTP_CONNECTION",
+ "HTTP_X_FOO",
+ "PATH_INFO",
+ "QUERY_STRING",
+ "REMOTE_ADDR",
+ "REMOTE_HOST",
+ "REMOTE_PORT",
+ "REQUEST_METHOD",
+ "SCRIPT_NAME",
+ "SERVER_NAME",
+ "SERVER_PORT",
+ "SERVER_PROTOCOL",
+ "SERVER_SOFTWARE",
+ "wsgi.errors",
+ "wsgi.file_wrapper",
+ "wsgi.input",
+ "wsgi.input_terminated",
+ "wsgi.multiprocess",
+ "wsgi.multithread",
+ "wsgi.run_once",
+ "wsgi.url_scheme",
+ "wsgi.version",
+ ],
+ )
+
+ self.assertEqual(environ["REQUEST_METHOD"], "GET")
+ self.assertEqual(environ["SERVER_PORT"], "80")
+ self.assertEqual(environ["SERVER_NAME"], "localhost")
+ self.assertEqual(environ["SERVER_SOFTWARE"], "waitress")
+ self.assertEqual(environ["SERVER_PROTOCOL"], "HTTP/1.0")
+ self.assertEqual(environ["SCRIPT_NAME"], "")
+ self.assertEqual(environ["HTTP_CONNECTION"], "close")
+ self.assertEqual(environ["PATH_INFO"], "/")
+ self.assertEqual(environ["QUERY_STRING"], "abc")
+ self.assertEqual(environ["REMOTE_ADDR"], "127.0.0.1")
+ self.assertEqual(environ["REMOTE_HOST"], "127.0.0.1")
+ self.assertEqual(environ["REMOTE_PORT"], "39830")
+ self.assertEqual(environ["CONTENT_TYPE"], "abc")
+ self.assertEqual(environ["CONTENT_LENGTH"], "10")
+ self.assertEqual(environ["HTTP_X_FOO"], "BAR")
+ self.assertEqual(environ["wsgi.version"], (1, 0))
+ self.assertEqual(environ["wsgi.url_scheme"], "http")
+ self.assertEqual(environ["wsgi.errors"], sys.stderr)
+ self.assertEqual(environ["wsgi.multithread"], True)
+ self.assertEqual(environ["wsgi.multiprocess"], False)
+ self.assertEqual(environ["wsgi.run_once"], False)
+ self.assertEqual(environ["wsgi.input"], "stream")
+ self.assertEqual(environ["wsgi.input_terminated"], True)
self.assertEqual(inst.environ, environ)
class TestErrorTask(unittest.TestCase):
-
def _makeOne(self, channel=None, request=None):
if channel is None:
channel = DummyChannel()
@@ -780,13 +845,15 @@ class TestErrorTask(unittest.TestCase):
request = DummyParser()
request.error = self._makeDummyError()
from waitress.task import ErrorTask
+
return ErrorTask(channel, request)
def _makeDummyError(self):
from waitress.utilities import Error
- e = Error('body')
+
+ e = Error("body")
e.code = 432
- e.reason = 'Too Ugly'
+ e.reason = "Too Ugly"
return e
def test_execute_http_10(self):
@@ -794,63 +861,64 @@ class TestErrorTask(unittest.TestCase):
inst.execute()
lines = filter_lines(inst.channel.written)
self.assertEqual(len(lines), 9)
- self.assertEqual(lines[0], b'HTTP/1.0 432 Too Ugly')
- self.assertEqual(lines[1], b'Connection: close')
- self.assertEqual(lines[2], b'Content-Length: 43')
- self.assertEqual(lines[3], b'Content-Type: text/plain')
+ self.assertEqual(lines[0], b"HTTP/1.0 432 Too Ugly")
+ self.assertEqual(lines[1], b"Connection: close")
+ self.assertEqual(lines[2], b"Content-Length: 43")
+ self.assertEqual(lines[3], b"Content-Type: text/plain")
self.assertTrue(lines[4])
- self.assertEqual(lines[5], b'Server: waitress')
- self.assertEqual(lines[6], b'Too Ugly')
- self.assertEqual(lines[7], b'body')
- self.assertEqual(lines[8], b'(generated by waitress)')
+ self.assertEqual(lines[5], b"Server: waitress")
+ self.assertEqual(lines[6], b"Too Ugly")
+ self.assertEqual(lines[7], b"body")
+ self.assertEqual(lines[8], b"(generated by waitress)")
def test_execute_http_11(self):
inst = self._makeOne()
- inst.version = '1.1'
+ inst.version = "1.1"
inst.execute()
lines = filter_lines(inst.channel.written)
self.assertEqual(len(lines), 8)
- self.assertEqual(lines[0], b'HTTP/1.1 432 Too Ugly')
- self.assertEqual(lines[1], b'Content-Length: 43')
- self.assertEqual(lines[2], b'Content-Type: text/plain')
+ self.assertEqual(lines[0], b"HTTP/1.1 432 Too Ugly")
+ self.assertEqual(lines[1], b"Content-Length: 43")
+ self.assertEqual(lines[2], b"Content-Type: text/plain")
self.assertTrue(lines[3])
- self.assertEqual(lines[4], b'Server: waitress')
- self.assertEqual(lines[5], b'Too Ugly')
- self.assertEqual(lines[6], b'body')
- self.assertEqual(lines[7], b'(generated by waitress)')
+ self.assertEqual(lines[4], b"Server: waitress")
+ self.assertEqual(lines[5], b"Too Ugly")
+ self.assertEqual(lines[6], b"body")
+ self.assertEqual(lines[7], b"(generated by waitress)")
def test_execute_http_11_close(self):
inst = self._makeOne()
- inst.version = '1.1'
- inst.request.headers['CONNECTION'] = 'close'
+ inst.version = "1.1"
+ inst.request.headers["CONNECTION"] = "close"
inst.execute()
lines = filter_lines(inst.channel.written)
self.assertEqual(len(lines), 9)
- self.assertEqual(lines[0], b'HTTP/1.1 432 Too Ugly')
- self.assertEqual(lines[1], b'Connection: close')
- self.assertEqual(lines[2], b'Content-Length: 43')
- self.assertEqual(lines[3], b'Content-Type: text/plain')
+ self.assertEqual(lines[0], b"HTTP/1.1 432 Too Ugly")
+ self.assertEqual(lines[1], b"Connection: close")
+ self.assertEqual(lines[2], b"Content-Length: 43")
+ self.assertEqual(lines[3], b"Content-Type: text/plain")
self.assertTrue(lines[4])
- self.assertEqual(lines[5], b'Server: waitress')
- self.assertEqual(lines[6], b'Too Ugly')
- self.assertEqual(lines[7], b'body')
- self.assertEqual(lines[8], b'(generated by waitress)')
+ self.assertEqual(lines[5], b"Server: waitress")
+ self.assertEqual(lines[6], b"Too Ugly")
+ self.assertEqual(lines[7], b"body")
+ self.assertEqual(lines[8], b"(generated by waitress)")
def test_execute_http_11_keep(self):
inst = self._makeOne()
- inst.version = '1.1'
- inst.request.headers['CONNECTION'] = 'keep-alive'
+ inst.version = "1.1"
+ inst.request.headers["CONNECTION"] = "keep-alive"
inst.execute()
lines = filter_lines(inst.channel.written)
self.assertEqual(len(lines), 8)
- self.assertEqual(lines[0], b'HTTP/1.1 432 Too Ugly')
- self.assertEqual(lines[1], b'Content-Length: 43')
- self.assertEqual(lines[2], b'Content-Type: text/plain')
+ self.assertEqual(lines[0], b"HTTP/1.1 432 Too Ugly")
+ self.assertEqual(lines[1], b"Content-Length: 43")
+ self.assertEqual(lines[2], b"Content-Type: text/plain")
self.assertTrue(lines[3])
- self.assertEqual(lines[4], b'Server: waitress')
- self.assertEqual(lines[5], b'Too Ugly')
- self.assertEqual(lines[6], b'body')
- self.assertEqual(lines[7], b'(generated by waitress)')
+ self.assertEqual(lines[4], b"Server: waitress")
+ self.assertEqual(lines[5], b"Too Ugly")
+ self.assertEqual(lines[6], b"body")
+ self.assertEqual(lines[7], b"(generated by waitress)")
+
class DummyTask(object):
serviced = False
@@ -862,31 +930,34 @@ class DummyTask(object):
def cancel(self):
self.cancelled = True
+
class DummyAdj(object):
log_socket_errors = True
- ident = 'waitress'
- host = '127.0.0.1'
+ ident = "waitress"
+ host = "127.0.0.1"
port = 80
- url_prefix = ''
+ url_prefix = ""
+
class DummyServer(object):
- server_name = 'localhost'
+ server_name = "localhost"
effective_port = 80
def __init__(self):
self.adj = DummyAdj()
+
class DummyChannel(object):
closed_when_done = False
adj = DummyAdj()
creation_time = 0
- addr = ('127.0.0.1', 39830)
+ addr = ("127.0.0.1", 39830)
def __init__(self, server=None):
if server is None:
server = DummyServer()
self.server = server
- self.written = b''
+ self.written = b""
self.otherdata = []
def write_soon(self, data):
@@ -896,12 +967,13 @@ class DummyChannel(object):
self.otherdata.append(data)
return len(data)
+
class DummyParser(object):
- version = '1.0'
- command = 'GET'
- path = '/'
- query = ''
- url_scheme = 'http'
+ version = "1.0"
+ command = "GET"
+ path = "/"
+ query = ""
+ url_scheme = "http"
expect_continue = False
headers_finished = False
@@ -909,13 +981,14 @@ class DummyParser(object):
self.headers = {}
def get_body_stream(self):
- return 'stream'
+ return "stream"
+
def filter_lines(s):
- return list(filter(None, s.split(b'\r\n')))
+ return list(filter(None, s.split(b"\r\n")))
-class DummyLogger(object):
+class DummyLogger(object):
def __init__(self):
self.logged = []
diff --git a/waitress/tests/test_trigger.py b/waitress/tests/test_trigger.py
index 6bd4824..af740f6 100644
--- a/waitress/tests/test_trigger.py
+++ b/waitress/tests/test_trigger.py
@@ -5,14 +5,14 @@ import sys
if not sys.platform.startswith("win"):
class Test_trigger(unittest.TestCase):
-
def _makeOne(self, map):
from waitress.trigger import trigger
+
self.inst = trigger(map)
return self.inst
def tearDown(self):
- self.inst.close() # prevent __del__ warning from file_dispatcher
+ self.inst.close() # prevent __del__ warning from file_dispatcher
def test__close(self):
map = {}
@@ -27,7 +27,7 @@ if not sys.platform.startswith("win"):
inst = self._makeOne(map)
inst._physical_pull()
r = os.read(inst._fds[0], 1)
- self.assertEqual(r, b'x')
+ self.assertEqual(r, b"x")
def test_readable(self):
map = {}
@@ -61,7 +61,7 @@ if not sys.platform.startswith("win"):
inst = self._makeOne(map)
self.assertEqual(inst.pull_trigger(), None)
r = os.read(inst._fds[0], 1)
- self.assertEqual(r, b'x')
+ self.assertEqual(r, b"x")
def test_pull_trigger_thunk(self):
map = {}
@@ -69,7 +69,7 @@ if not sys.platform.startswith("win"):
self.assertEqual(inst.pull_trigger(True), None)
self.assertEqual(len(inst.thunks), 1)
r = os.read(inst._fds[0], 1)
- self.assertEqual(r, b'x')
+ self.assertEqual(r, b"x")
def test_handle_read_socket_error(self):
map = {}
@@ -98,8 +98,10 @@ if not sys.platform.startswith("win"):
def test_handle_read_thunk_error(self):
map = {}
inst = self._makeOne(map)
+
def errorthunk():
raise ValueError
+
inst.pull_trigger(errorthunk)
L = []
inst.log_info = lambda *arg: L.append(arg)
diff --git a/waitress/tests/test_utilities.py b/waitress/tests/test_utilities.py
index f7691f0..c5c01d9 100644
--- a/waitress/tests/test_utilities.py
+++ b/waitress/tests/test_utilities.py
@@ -14,114 +14,121 @@
import unittest
-class Test_parse_http_date(unittest.TestCase):
+class Test_parse_http_date(unittest.TestCase):
def _callFUT(self, v):
from waitress.utilities import parse_http_date
+
return parse_http_date(v)
def test_rfc850(self):
- val = 'Tuesday, 08-Feb-94 14:15:29 GMT'
+ val = "Tuesday, 08-Feb-94 14:15:29 GMT"
result = self._callFUT(val)
self.assertEqual(result, 760716929)
def test_rfc822(self):
- val = 'Sun, 08 Feb 1994 14:15:29 GMT'
+ val = "Sun, 08 Feb 1994 14:15:29 GMT"
result = self._callFUT(val)
self.assertEqual(result, 760716929)
def test_neither(self):
- val = ''
+ val = ""
result = self._callFUT(val)
self.assertEqual(result, 0)
-class Test_build_http_date(unittest.TestCase):
+class Test_build_http_date(unittest.TestCase):
def test_rountdrip(self):
from waitress.utilities import build_http_date, parse_http_date
from time import time
+
t = int(time())
self.assertEqual(t, parse_http_date(build_http_date(t)))
-class Test_unpack_rfc850(unittest.TestCase):
+class Test_unpack_rfc850(unittest.TestCase):
def _callFUT(self, val):
from waitress.utilities import unpack_rfc850, rfc850_reg
+
return unpack_rfc850(rfc850_reg.match(val.lower()))
def test_it(self):
- val = 'Tuesday, 08-Feb-94 14:15:29 GMT'
+ val = "Tuesday, 08-Feb-94 14:15:29 GMT"
result = self._callFUT(val)
self.assertEqual(result, (1994, 2, 8, 14, 15, 29, 0, 0, 0))
-class Test_unpack_rfc_822(unittest.TestCase):
+class Test_unpack_rfc_822(unittest.TestCase):
def _callFUT(self, val):
from waitress.utilities import unpack_rfc822, rfc822_reg
+
return unpack_rfc822(rfc822_reg.match(val.lower()))
def test_it(self):
- val = 'Sun, 08 Feb 1994 14:15:29 GMT'
+ val = "Sun, 08 Feb 1994 14:15:29 GMT"
result = self._callFUT(val)
self.assertEqual(result, (1994, 2, 8, 14, 15, 29, 0, 0, 0))
-class Test_find_double_newline(unittest.TestCase):
+class Test_find_double_newline(unittest.TestCase):
def _callFUT(self, val):
from waitress.utilities import find_double_newline
+
return find_double_newline(val)
def test_empty(self):
- self.assertEqual(self._callFUT(b''), -1)
+ self.assertEqual(self._callFUT(b""), -1)
def test_one_linefeed(self):
- self.assertEqual(self._callFUT(b'\n'), -1)
+ self.assertEqual(self._callFUT(b"\n"), -1)
def test_double_linefeed(self):
- self.assertEqual(self._callFUT(b'\n\n'), 2)
+ self.assertEqual(self._callFUT(b"\n\n"), 2)
def test_one_crlf(self):
- self.assertEqual(self._callFUT(b'\r\n'), -1)
+ self.assertEqual(self._callFUT(b"\r\n"), -1)
def test_double_crfl(self):
- self.assertEqual(self._callFUT(b'\r\n\r\n'), 4)
+ self.assertEqual(self._callFUT(b"\r\n\r\n"), 4)
def test_mixed(self):
- self.assertEqual(self._callFUT(b'\n\n00\r\n\r\n'), 2)
+ self.assertEqual(self._callFUT(b"\n\n00\r\n\r\n"), 2)
-class TestBadRequest(unittest.TestCase):
+class TestBadRequest(unittest.TestCase):
def _makeOne(self):
from waitress.utilities import BadRequest
+
return BadRequest(1)
def test_it(self):
inst = self._makeOne()
self.assertEqual(inst.body, 1)
-class Test_undquote(unittest.TestCase):
+class Test_undquote(unittest.TestCase):
def _callFUT(self, value):
from waitress.utilities import undquote
+
return undquote(value)
def test_empty(self):
- self.assertEqual(self._callFUT(''), '')
+ self.assertEqual(self._callFUT(""), "")
def test_quoted(self):
- self.assertEqual(self._callFUT('"test"'), 'test')
+ self.assertEqual(self._callFUT('"test"'), "test")
def test_unquoted(self):
- self.assertEqual(self._callFUT('test'), 'test')
+ self.assertEqual(self._callFUT("test"), "test")
def test_quoted_backslash_quote(self):
self.assertEqual(self._callFUT('"\\""'), '"')
def test_quoted_htab(self):
- self.assertEqual(self._callFUT("\"\t\""), "\t")
+ self.assertEqual(self._callFUT('"\t"'), "\t")
def test_quoted_backslash_htab(self):
- self.assertEqual(self._callFUT("\"\\\t\""), "\t")
+ self.assertEqual(self._callFUT('"\\\t"'), "\t")
def test_quoted_backslash_invalid(self):
self.assertRaises(ValueError, self._callFUT, '"\\"')
@@ -131,4 +138,3 @@ class Test_undquote(unittest.TestCase):
def test_invalid_quoting_single_quote(self):
self.assertRaises(ValueError, self._callFUT, '"')
-
diff --git a/waitress/tests/test_wasyncore.py b/waitress/tests/test_wasyncore.py
index a5758bb..9c23509 100644
--- a/waitress/tests/test_wasyncore.py
+++ b/waitress/tests/test_wasyncore.py
@@ -18,44 +18,47 @@ import warnings
from io import BytesIO
TIMEOUT = 3
-HAS_UNIX_SOCKETS = hasattr(socket, 'AF_UNIX')
-HOST = 'localhost'
+HAS_UNIX_SOCKETS = hasattr(socket, "AF_UNIX")
+HOST = "localhost"
HOSTv4 = "127.0.0.1"
HOSTv6 = "::1"
# Filename used for testing
-if os.name == 'java': # pragma: no cover
+if os.name == "java": # pragma: no cover
# Jython disallows @ in module names
- TESTFN = '$test'
+ TESTFN = "$test"
else:
- TESTFN = '@test'
+ TESTFN = "@test"
TESTFN = "{}_{}_tmp".format(TESTFN, os.getpid())
-class DummyLogger(object): # pragma: no cover
+
+class DummyLogger(object): # pragma: no cover
def __init__(self):
self.messages = []
def log(self, severity, message):
self.messages.append((severity, message))
-class WarningsRecorder(object): # pragma: no cover
+
+class WarningsRecorder(object): # pragma: no cover
"""Convenience wrapper for the warnings list returned on
entry to the warnings.catch_warnings() context manager.
"""
+
def __init__(self, warnings_list):
self._warnings = warnings_list
self._last = 0
@property
def warnings(self):
- return self._warnings[self._last:]
+ return self._warnings[self._last :]
def reset(self):
self._last = len(self._warnings)
-def _filterwarnings(filters, quiet=False): # pragma: no cover
+def _filterwarnings(filters, quiet=False): # pragma: no cover
"""Catch the warnings, then check if all the expected
warnings have been raised and re-raise unexpected warnings.
If 'quiet' is True, only re-raise the unexpected warnings.
@@ -63,14 +66,14 @@ def _filterwarnings(filters, quiet=False): # pragma: no cover
# Clear the warning registry of the calling module
# in order to re-raise the warnings.
frame = sys._getframe(2)
- registry = frame.f_globals.get('__warningregistry__')
+ registry = frame.f_globals.get("__warningregistry__")
if registry:
registry.clear()
with warnings.catch_warnings(record=True) as w:
# Set filter "always" to record all warnings. Because
# test_warnings swap the module, we need to look up in
# the sys.modules dictionary.
- sys.modules['warnings'].simplefilter("always")
+ sys.modules["warnings"].simplefilter("always")
yield WarningsRecorder(w)
# Filter the recorded warnings
reraise = list(w)
@@ -80,8 +83,7 @@ def _filterwarnings(filters, quiet=False): # pragma: no cover
for w in reraise[:]:
warning = w.message
# Filter out the matching messages
- if (re.match(msg, str(warning), re.I) and
- issubclass(warning.__class__, cat)):
+ if re.match(msg, str(warning), re.I) and issubclass(warning.__class__, cat):
seen = True
reraise.remove(w)
if not seen and not quiet:
@@ -90,12 +92,11 @@ def _filterwarnings(filters, quiet=False): # pragma: no cover
if reraise:
raise AssertionError("unhandled warning %s" % reraise[0])
if missing:
- raise AssertionError("filter (%r, %s) did not catch any warning" %
- missing[0])
+ raise AssertionError("filter (%r, %s) did not catch any warning" % missing[0])
@contextlib.contextmanager
-def check_warnings(*filters, **kwargs): # pragma: no cover
+def check_warnings(*filters, **kwargs): # pragma: no cover
"""Context manager to silence warnings.
Accept 2-tuples as positional arguments:
@@ -109,7 +110,7 @@ def check_warnings(*filters, **kwargs): # pragma: no cover
Without argument, it defaults to:
check_warnings(("", Warning), quiet=True)
"""
- quiet = kwargs.get('quiet')
+ quiet = kwargs.get("quiet")
if not filters:
filters = (("", Warning),)
# Preserve backward compatibility
@@ -117,7 +118,8 @@ def check_warnings(*filters, **kwargs): # pragma: no cover
quiet = True
return _filterwarnings(filters, quiet)
-def gc_collect(): # pragma: no cover
+
+def gc_collect(): # pragma: no cover
"""Force as many objects as possible to be collected.
In non-CPython implementations of Python, this is needed because timely
@@ -128,15 +130,17 @@ def gc_collect(): # pragma: no cover
objects to disappear.
"""
gc.collect()
- if sys.platform.startswith('java'):
+ if sys.platform.startswith("java"):
time.sleep(0.1)
gc.collect()
gc.collect()
-def threading_setup(): # pragma: no cover
+
+def threading_setup(): # pragma: no cover
return (compat.thread._count(), None)
-def threading_cleanup(*original_values): # pragma: no cover
+
+def threading_cleanup(*original_values): # pragma: no cover
global environment_altered
_MAX_COUNT = 100
@@ -152,7 +156,7 @@ def threading_cleanup(*original_values): # pragma: no cover
sys.stderr.write(
"Warning -- threading_cleanup() failed to cleanup "
"%s threads" % (values[0] - original_values[0])
- )
+ )
sys.stderr.flush()
values = None
@@ -161,10 +165,11 @@ def threading_cleanup(*original_values): # pragma: no cover
gc_collect()
-def reap_threads(func): # pragma: no cover
+def reap_threads(func): # pragma: no cover
"""Use this function when threads are being used. This will
ensure that the threads are cleaned up even when the test fails.
"""
+
@functools.wraps(func)
def decorator(*args):
key = threading_setup()
@@ -172,9 +177,11 @@ def reap_threads(func): # pragma: no cover
return func(*args)
finally:
threading_cleanup(*key)
+
return decorator
-def join_thread(thread, timeout=30.0): # pragma: no cover
+
+def join_thread(thread, timeout=30.0): # pragma: no cover
"""Join a thread. Raise an AssertionError if the thread is still alive
after timeout seconds.
"""
@@ -183,7 +190,8 @@ def join_thread(thread, timeout=30.0): # pragma: no cover
msg = "failed to join the thread in %.1f seconds" % timeout
raise AssertionError(msg)
-def bind_port(sock, host=HOST): # pragma: no cover
+
+def bind_port(sock, host=HOST): # pragma: no cover
"""Bind the socket to a free port and return the port number. Relies on
ephemeral ports in order to ensure we are using an unbound port. This is
important as many tests may be running simultaneously, especially in a
@@ -199,36 +207,41 @@ def bind_port(sock, host=HOST): # pragma: no cover
"""
if sock.family == socket.AF_INET and sock.type == socket.SOCK_STREAM:
- if hasattr(socket, 'SO_REUSEADDR'):
+ if hasattr(socket, "SO_REUSEADDR"):
if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) == 1:
- raise RuntimeError("tests should never set the SO_REUSEADDR " \
- "socket option on TCP/IP sockets!")
- if hasattr(socket, 'SO_REUSEPORT'):
+ raise RuntimeError(
+ "tests should never set the SO_REUSEADDR "
+ "socket option on TCP/IP sockets!"
+ )
+ if hasattr(socket, "SO_REUSEPORT"):
try:
if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT) == 1:
raise RuntimeError(
- "tests should never set the SO_REUSEPORT " \
- "socket option on TCP/IP sockets!")
+ "tests should never set the SO_REUSEPORT "
+ "socket option on TCP/IP sockets!"
+ )
except OSError:
# Python's socket module was compiled using modern headers
# thus defining SO_REUSEPORT but this process is running
# under an older kernel that does not support SO_REUSEPORT.
pass
- if hasattr(socket, 'SO_EXCLUSIVEADDRUSE'):
+ if hasattr(socket, "SO_EXCLUSIVEADDRUSE"):
sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
sock.bind((host, 0))
port = sock.getsockname()[1]
return port
+
@contextlib.contextmanager
-def closewrapper(sock): # pragma: no cover
+def closewrapper(sock): # pragma: no cover
try:
yield sock
finally:
sock.close()
-class dummysocket: # pragma: no cover
+
+class dummysocket: # pragma: no cover
def __init__(self):
self.closed = False
@@ -242,16 +255,18 @@ class dummysocket: # pragma: no cover
self.isblocking = yesno
def getpeername(self):
- return 'peername'
+ return "peername"
-class dummychannel: # pragma: no cover
+
+class dummychannel: # pragma: no cover
def __init__(self):
self.socket = dummysocket()
def close(self):
self.socket.close()
-class exitingdummy: # pragma: no cover
+
+class exitingdummy: # pragma: no cover
def __init__(self):
pass
@@ -262,6 +277,7 @@ class exitingdummy: # pragma: no cover
handle_close = handle_read_event
handle_expt_event = handle_read_event
+
class crashingdummy:
def __init__(self):
self.error_handled = False
@@ -276,8 +292,9 @@ class crashingdummy:
def handle_error(self):
self.error_handled = True
+
# used when testing senders; just collects what it gets until newline is sent
-def capture_server(evt, buf, serv): # pragma no cover
+def capture_server(evt, buf, serv): # pragma no cover
try:
serv.listen(0)
conn, addr = serv.accept()
@@ -292,8 +309,8 @@ def capture_server(evt, buf, serv): # pragma no cover
n -= 1
data = conn.recv(10)
# keep everything except for the newline terminator
- buf.write(data.replace(b'\n', b''))
- if b'\n' in data:
+ buf.write(data.replace(b"\n", b""))
+ if b"\n" in data:
break
time.sleep(0.01)
@@ -302,14 +319,16 @@ def capture_server(evt, buf, serv): # pragma no cover
serv.close()
evt.set()
-def bind_unix_socket(sock, addr): # pragma: no cover
+
+def bind_unix_socket(sock, addr): # pragma: no cover
"""Bind a unix socket, raising SkipTest if PermissionError is raised."""
assert sock.family == socket.AF_UNIX
try:
sock.bind(addr)
except PermissionError:
sock.close()
- raise unittest.SkipTest('cannot bind AF_UNIX sockets')
+ raise unittest.SkipTest("cannot bind AF_UNIX sockets")
+
def bind_af_aware(sock, addr):
"""Helper function to bind a socket according to its family."""
@@ -320,7 +339,9 @@ def bind_af_aware(sock, addr):
else:
sock.bind(addr)
-if sys.platform.startswith("win"): # pragma: no cover
+
+if sys.platform.startswith("win"): # pragma: no cover
+
def _waitfor(func, pathname, waitall=False):
# Perform the operation
func(pathname)
@@ -329,7 +350,7 @@ if sys.platform.startswith("win"): # pragma: no cover
dirname = pathname
else:
dirname, name = os.path.split(pathname)
- dirname = dirname or '.'
+ dirname = dirname or "."
# Check for `pathname` to be removed from the filesystem.
# The exponential backoff of the timeout amounts to a total
# of ~1 second after which the deletion is probably an error
@@ -351,11 +372,16 @@ if sys.platform.startswith("win"): # pragma: no cover
# Increase the timeout and try again
time.sleep(timeout)
timeout *= 2
- warnings.warn('tests may fail, delete still pending for ' + pathname,
- RuntimeWarning, stacklevel=4)
+ warnings.warn(
+ "tests may fail, delete still pending for " + pathname,
+ RuntimeWarning,
+ stacklevel=4,
+ )
def _unlink(filename):
_waitfor(os.unlink, filename)
+
+
else:
_unlink = os.unlink
@@ -366,13 +392,14 @@ def unlink(filename):
except OSError:
pass
-def _is_ipv6_enabled(): # pragma: no cover
+
+def _is_ipv6_enabled(): # pragma: no cover
"""Check whether IPv6 is enabled on this host."""
if compat.HAS_IPV6:
sock = None
try:
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
- sock.bind(('::1', 0))
+ sock.bind(("::1", 0))
return True
except socket.error:
pass
@@ -381,8 +408,10 @@ def _is_ipv6_enabled(): # pragma: no cover
sock.close()
return False
+
IPV6_ENABLED = _is_ipv6_enabled()
+
class HelperFunctionTests(unittest.TestCase):
def test_readwriteexc(self):
# Check exception handling behavior of read, write and _exception
@@ -413,20 +442,20 @@ class HelperFunctionTests(unittest.TestCase):
# http://mail.python.org/pipermail/python-list/2001-October/109973.html)
# These constants should be present as long as poll is available
- @unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required')
+ @unittest.skipUnless(hasattr(select, "poll"), "select.poll required")
def test_readwrite(self):
# Check that correct methods are called by readwrite()
- attributes = ('read', 'expt', 'write', 'closed', 'error_handled')
+ attributes = ("read", "expt", "write", "closed", "error_handled")
expected = (
- (select.POLLIN, 'read'),
- (select.POLLPRI, 'expt'),
- (select.POLLOUT, 'write'),
- (select.POLLERR, 'closed'),
- (select.POLLHUP, 'closed'),
- (select.POLLNVAL, 'closed'),
- )
+ (select.POLLIN, "read"),
+ (select.POLLPRI, "expt"),
+ (select.POLLOUT, "write"),
+ (select.POLLERR, "closed"),
+ (select.POLLHUP, "closed"),
+ (select.POLLNVAL, "closed"),
+ )
class testobj:
def __init__(self):
@@ -459,7 +488,7 @@ class HelperFunctionTests(unittest.TestCase):
# Only the attribute modified by the routine we expect to be
# called should be True.
for attr in attributes:
- self.assertEqual(getattr(tobj, attr), attr==expectedattr)
+ self.assertEqual(getattr(tobj, attr), attr == expectedattr)
# check that ExitNow exceptions in the object handler method
# bubbles all the way up through asyncore readwrite call
@@ -513,11 +542,11 @@ class HelperFunctionTests(unittest.TestCase):
r = asyncore.compact_traceback()
(f, function, line), t, v, info = r
- self.assertEqual(os.path.split(f)[-1], 'test_wasyncore.py')
- self.assertEqual(function, 'test_compact_traceback')
+ self.assertEqual(os.path.split(f)[-1], "test_wasyncore.py")
+ self.assertEqual(function, "test_compact_traceback")
self.assertEqual(t, real_t)
self.assertEqual(v, real_v)
- self.assertEqual(info, '[%s|%s|%s]' % (f, function, line))
+ self.assertEqual(info, "[%s|%s|%s]" % (f, function, line))
class DispatcherTests(unittest.TestCase):
@@ -534,54 +563,56 @@ class DispatcherTests(unittest.TestCase):
def test_repr(self):
d = asyncore.dispatcher()
- self.assertEqual(
- repr(d),
- '<waitress.wasyncore.dispatcher at %#x>' % id(d)
- )
+ self.assertEqual(repr(d), "<waitress.wasyncore.dispatcher at %#x>" % id(d))
def test_log_info(self):
import logging
+
inst = asyncore.dispatcher(map={})
logger = DummyLogger()
inst.logger = logger
- inst.log_info('message', 'warning')
- self.assertEqual(logger.messages, [(logging.WARN, 'message')])
+ inst.log_info("message", "warning")
+ self.assertEqual(logger.messages, [(logging.WARN, "message")])
def test_log(self):
import logging
+
inst = asyncore.dispatcher()
logger = DummyLogger()
inst.logger = logger
- inst.log('message')
- self.assertEqual(logger.messages, [(logging.DEBUG, 'message')])
+ inst.log("message")
+ self.assertEqual(logger.messages, [(logging.DEBUG, "message")])
def test_unhandled(self):
import logging
+
inst = asyncore.dispatcher()
logger = DummyLogger()
inst.logger = logger
-
+
inst.handle_expt()
inst.handle_read()
inst.handle_write()
inst.handle_connect()
- expected = [(logging.WARN, 'unhandled incoming priority event'),
- (logging.WARN, 'unhandled read event'),
- (logging.WARN, 'unhandled write event'),
- (logging.WARN, 'unhandled connect event')]
+ expected = [
+ (logging.WARN, "unhandled incoming priority event"),
+ (logging.WARN, "unhandled read event"),
+ (logging.WARN, "unhandled write event"),
+ (logging.WARN, "unhandled connect event"),
+ ]
self.assertEqual(logger.messages, expected)
def test_strerror(self):
# refers to bug #8573
err = asyncore._strerror(errno.EPERM)
- if hasattr(os, 'strerror'):
+ if hasattr(os, "strerror"):
self.assertEqual(err, os.strerror(errno.EPERM))
err = asyncore._strerror(-1)
self.assertTrue(err != "")
-class dispatcherwithsend_noread(asyncore.dispatcher_with_send): # pragma: no cover
+class dispatcherwithsend_noread(asyncore.dispatcher_with_send): # pragma: no cover
def readable(self):
return False
@@ -622,26 +653,27 @@ class DispatcherWithSendTests(unittest.TestCase):
d.send(data)
d.send(data)
- d.send(b'\n')
+ d.send(b"\n")
n = 1000
- while d.out_buffer and n > 0: # pragma: no cover
+ while d.out_buffer and n > 0: # pragma: no cover
asyncore.poll()
n -= 1
evt.wait()
- self.assertEqual(cap.getvalue(), data*2)
+ self.assertEqual(cap.getvalue(), data * 2)
finally:
join_thread(t, timeout=TIMEOUT)
-@unittest.skipUnless(hasattr(asyncore, 'file_wrapper'),
- 'asyncore.file_wrapper required')
+@unittest.skipUnless(
+ hasattr(asyncore, "file_wrapper"), "asyncore.file_wrapper required"
+)
class FileWrapperTest(unittest.TestCase):
def setUp(self):
self.d = b"It's not dead, it's sleeping!"
- with open(TESTFN, 'wb') as file:
+ with open(TESTFN, "wb") as file:
file.write(self.d)
def tearDown(self):
@@ -669,17 +701,20 @@ class FileWrapperTest(unittest.TestCase):
w.write(d1)
w.send(d2)
w.close()
- with open(TESTFN, 'rb') as file:
+ with open(TESTFN, "rb") as file:
self.assertEqual(file.read(), self.d + d1 + d2)
- @unittest.skipUnless(hasattr(asyncore, 'file_dispatcher'),
- 'asyncore.file_dispatcher required')
+ @unittest.skipUnless(
+ hasattr(asyncore, "file_dispatcher"), "asyncore.file_dispatcher required"
+ )
def test_dispatcher(self):
fd = os.open(TESTFN, os.O_RDONLY)
data = []
+
class FileDispatcher(asyncore.file_dispatcher):
def handle_read(self):
data.append(self.recv(29))
+
FileDispatcher(fd)
os.close(fd)
asyncore.loop(timeout=0.01, use_poll=True, count=2)
@@ -697,10 +732,10 @@ class FileWrapperTest(unittest.TestCase):
os.close(fd)
try:
- with check_warnings(('', compat.ResourceWarning)):
+ with check_warnings(("", compat.ResourceWarning)):
f = None
gc_collect()
- except AssertionError: # pragma: no cover
+ except AssertionError: # pragma: no cover
pass
else:
got_warning = True
@@ -719,8 +754,7 @@ class FileWrapperTest(unittest.TestCase):
f.close()
-class BaseTestHandler(asyncore.dispatcher): # pragma: no cover
-
+class BaseTestHandler(asyncore.dispatcher): # pragma: no cover
def __init__(self, sock=None):
asyncore.dispatcher.__init__(self, sock)
self.flag = False
@@ -764,12 +798,11 @@ class BaseServer(asyncore.dispatcher):
def handle_accepted(self, sock, addr):
self.handler(sock)
- def handle_error(self): # pragma: no cover
+ def handle_error(self): # pragma: no cover
raise
class BaseClient(BaseTestHandler):
-
def __init__(self, family, address):
BaseTestHandler.__init__(self)
self.create_socket(family)
@@ -780,11 +813,10 @@ class BaseClient(BaseTestHandler):
class BaseTestAPI:
-
def tearDown(self):
asyncore.close_all(ignore_all=True)
- def loop_waiting_for_flag(self, instance, timeout=5): # pragma: no cover
+ def loop_waiting_for_flag(self, instance, timeout=5): # pragma: no cover
timeout = float(timeout) / 100
count = 100
while asyncore.socket_map and count > 0:
@@ -810,7 +842,6 @@ class BaseTestAPI:
# make sure handle_accept() is called when a client connects
class TestListener(BaseTestHandler):
-
def __init__(self, family, addr):
BaseTestHandler.__init__(self)
self.create_socket(family)
@@ -829,7 +860,6 @@ class BaseTestAPI:
# make sure handle_accepted() is called when a client connects
class TestListener(BaseTestHandler):
-
def __init__(self, family, addr):
BaseTestHandler.__init__(self)
self.create_socket(family)
@@ -848,7 +878,6 @@ class BaseTestAPI:
client = BaseClient(self.family, server.address)
self.loop_waiting_for_flag(server)
-
def test_handle_read(self):
# make sure handle_read is called on data received
@@ -859,7 +888,7 @@ class BaseTestAPI:
class TestHandler(BaseTestHandler):
def __init__(self, conn):
BaseTestHandler.__init__(self, conn)
- self.send(b'x' * 1024)
+ self.send(b"x" * 1024)
server = BaseServer(self.family, self.addr, TestHandler)
client = TestClient(self.family, server.address)
@@ -881,7 +910,6 @@ class BaseTestAPI:
# the connection
class TestClient(BaseClient):
-
def handle_read(self):
# in order to make handle_close be called we are supposed
# to make at least one recv() call
@@ -904,10 +932,9 @@ class BaseTestAPI:
# Check that ECONNRESET/EPIPE is correctly handled (issues #5661 and
# #11265).
- data = b'\0' * 128
+ data = b"\0" * 128
class TestClient(BaseClient):
-
def handle_write(self):
self.send(data)
@@ -916,12 +943,11 @@ class BaseTestAPI:
self.close()
def handle_expt(self): # pragma: no cover
- # needs to exist for MacOS testing
+ # needs to exist for MacOS testing
self.flag = True
self.close()
class TestHandler(BaseTestHandler):
-
def handle_read(self):
self.recv(len(data))
self.close()
@@ -933,8 +959,9 @@ class BaseTestAPI:
client = TestClient(self.family, server.address)
self.loop_waiting_for_flag(client)
- @unittest.skipIf(sys.platform.startswith("sunos"),
- "OOB support is broken on Solaris")
+ @unittest.skipIf(
+ sys.platform.startswith("sunos"), "OOB support is broken on Solaris"
+ )
def test_handle_expt(self):
# Make sure handle_expt is called on OOB data received.
# Note: this might fail on some platforms as OOB data is
@@ -942,7 +969,7 @@ class BaseTestAPI:
if HAS_UNIX_SOCKETS and self.family == socket.AF_UNIX:
self.skipTest("Not applicable to AF_UNIX sockets.")
- if sys.platform == "darwin" and self.use_poll: # pragma: no cover
+ if sys.platform == "darwin" and self.use_poll: # pragma: no cover
self.skipTest("poll may fail on macOS; see issue #28087")
class TestClient(BaseClient):
@@ -953,26 +980,24 @@ class BaseTestAPI:
class TestHandler(BaseTestHandler):
def __init__(self, conn):
BaseTestHandler.__init__(self, conn)
- self.socket.send(
- compat.tobytes(chr(244)), socket.MSG_OOB
- )
+ self.socket.send(compat.tobytes(chr(244)), socket.MSG_OOB)
server = BaseServer(self.family, self.addr, TestHandler)
client = TestClient(self.family, server.address)
self.loop_waiting_for_flag(client)
def test_handle_error(self):
-
class TestClient(BaseClient):
def handle_write(self):
1.0 / 0
+
def handle_error(self):
self.flag = True
try:
raise
except ZeroDivisionError:
pass
- else: # pragma: no cover
+ else: # pragma: no cover
raise Exception("exception not raised")
server = BaseServer(self.family, self.addr)
@@ -987,7 +1012,7 @@ class BaseTestAPI:
self.assertFalse(server.connected)
self.assertTrue(server.accepting)
# this can't be taken for granted across all platforms
- #self.assertFalse(client.connected)
+ # self.assertFalse(client.connected)
self.assertFalse(client.accepting)
# execute some loops so that client connects to server
@@ -1012,10 +1037,10 @@ class BaseTestAPI:
def test_create_socket(self):
s = asyncore.dispatcher()
s.create_socket(self.family)
- #self.assertEqual(s.socket.type, socket.SOCK_STREAM)
+ # self.assertEqual(s.socket.type, socket.SOCK_STREAM)
self.assertEqual(s.socket.family, self.family)
self.assertEqual(s.socket.gettimeout(), 0)
- #self.assertFalse(s.socket.get_inheritable())
+ # self.assertFalse(s.socket.get_inheritable())
def test_bind(self):
if HAS_UNIX_SOCKETS and self.family == socket.AF_UNIX:
@@ -1031,7 +1056,7 @@ class BaseTestAPI:
# EADDRINUSE indicates the socket was correctly bound
self.assertRaises(socket.error, s2.bind, (self.addr[0], port))
- def test_set_reuse_addr(self): # pragma: no cover
+ def test_set_reuse_addr(self): # pragma: no cover
if HAS_UNIX_SOCKETS and self.family == socket.AF_UNIX:
self.skipTest("Not applicable to AF_UNIX sockets.")
@@ -1044,50 +1069,54 @@ class BaseTestAPI:
# if SO_REUSEADDR succeeded for sock we expect asyncore
# to do the same
s = asyncore.dispatcher(socket.socket(self.family))
- self.assertFalse(s.socket.getsockopt(socket.SOL_SOCKET,
- socket.SO_REUSEADDR))
+ self.assertFalse(
+ s.socket.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)
+ )
s.socket.close()
s.create_socket(self.family)
s.set_reuse_addr()
- self.assertTrue(s.socket.getsockopt(socket.SOL_SOCKET,
- socket.SO_REUSEADDR))
+ self.assertTrue(
+ s.socket.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)
+ )
@reap_threads
- def test_quick_connect(self): # pragma: no cover
+ def test_quick_connect(self): # pragma: no cover
# see: http://bugs.python.org/issue10340
- if self.family not in (socket.AF_INET,
- getattr(socket, "AF_INET6", object())):
+ if self.family not in (socket.AF_INET, getattr(socket, "AF_INET6", object())):
self.skipTest("test specific to AF_INET and AF_INET6")
server = BaseServer(self.family, self.addr)
# run the thread 500 ms: the socket should be connected in 200 ms
- t = threading.Thread(target=lambda: asyncore.loop(timeout=0.1,
- count=5))
+ t = threading.Thread(target=lambda: asyncore.loop(timeout=0.1, count=5))
t.start()
try:
sock = socket.socket(self.family, socket.SOCK_STREAM)
with closewrapper(sock) as s:
- s.settimeout(.2)
- s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
- struct.pack('ii', 1, 0))
+ s.settimeout(0.2)
+ s.setsockopt(
+ socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", 1, 0)
+ )
try:
s.connect(server.address)
- except OSError:
+ except OSError:
pass
finally:
join_thread(t, timeout=TIMEOUT)
+
class TestAPI_UseIPv4Sockets(BaseTestAPI):
family = socket.AF_INET
addr = (HOST, 0)
-@unittest.skipUnless(IPV6_ENABLED, 'IPv6 support required')
+
+@unittest.skipUnless(IPV6_ENABLED, "IPv6 support required")
class TestAPI_UseIPv6Sockets(BaseTestAPI):
family = socket.AF_INET6
addr = (HOSTv6, 0)
-@unittest.skipUnless(HAS_UNIX_SOCKETS, 'Unix sockets required')
+
+@unittest.skipUnless(HAS_UNIX_SOCKETS, "Unix sockets required")
class TestAPI_UseUnixSockets(BaseTestAPI):
if HAS_UNIX_SOCKETS:
family = socket.AF_UNIX
@@ -1097,41 +1126,51 @@ class TestAPI_UseUnixSockets(BaseTestAPI):
unlink(self.addr)
BaseTestAPI.tearDown(self)
+
class TestAPI_UseIPv4Select(TestAPI_UseIPv4Sockets, unittest.TestCase):
use_poll = False
-@unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required')
+
+@unittest.skipUnless(hasattr(select, "poll"), "select.poll required")
class TestAPI_UseIPv4Poll(TestAPI_UseIPv4Sockets, unittest.TestCase):
use_poll = True
+
class TestAPI_UseIPv6Select(TestAPI_UseIPv6Sockets, unittest.TestCase):
use_poll = False
-@unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required')
+
+@unittest.skipUnless(hasattr(select, "poll"), "select.poll required")
class TestAPI_UseIPv6Poll(TestAPI_UseIPv6Sockets, unittest.TestCase):
use_poll = True
+
class TestAPI_UseUnixSocketsSelect(TestAPI_UseUnixSockets, unittest.TestCase):
use_poll = False
-@unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required')
+
+@unittest.skipUnless(hasattr(select, "poll"), "select.poll required")
class TestAPI_UseUnixSocketsPoll(TestAPI_UseUnixSockets, unittest.TestCase):
use_poll = True
+
class Test__strerror(unittest.TestCase):
def _callFUT(self, err):
from waitress.wasyncore import _strerror
+
return _strerror(err)
def test_gardenpath(self):
- self.assertEqual(self._callFUT(1), 'Operation not permitted')
+ self.assertEqual(self._callFUT(1), "Operation not permitted")
def test_unknown(self):
- self.assertEqual(self._callFUT('wut'), 'Unknown error wut')
-
+ self.assertEqual(self._callFUT("wut"), "Unknown error wut")
+
+
class Test_read(unittest.TestCase):
def _callFUT(self, dispatcher):
from waitress.wasyncore import read
+
return read(dispatcher)
def test_gardenpath(self):
@@ -1142,8 +1181,9 @@ class Test_read(unittest.TestCase):
def test_reraised(self):
from waitress.wasyncore import ExitNow
+
inst = DummyDispatcher(ExitNow)
- self.assertRaises(ExitNow,self._callFUT, inst)
+ self.assertRaises(ExitNow, self._callFUT, inst)
self.assertTrue(inst.read_event_handled)
self.assertFalse(inst.error_handled)
@@ -1153,9 +1193,11 @@ class Test_read(unittest.TestCase):
self.assertTrue(inst.read_event_handled)
self.assertTrue(inst.error_handled)
+
class Test_write(unittest.TestCase):
def _callFUT(self, dispatcher):
from waitress.wasyncore import write
+
return write(dispatcher)
def test_gardenpath(self):
@@ -1166,8 +1208,9 @@ class Test_write(unittest.TestCase):
def test_reraised(self):
from waitress.wasyncore import ExitNow
+
inst = DummyDispatcher(ExitNow)
- self.assertRaises(ExitNow,self._callFUT, inst)
+ self.assertRaises(ExitNow, self._callFUT, inst)
self.assertTrue(inst.write_event_handled)
self.assertFalse(inst.error_handled)
@@ -1177,9 +1220,11 @@ class Test_write(unittest.TestCase):
self.assertTrue(inst.write_event_handled)
self.assertTrue(inst.error_handled)
+
class Test__exception(unittest.TestCase):
def _callFUT(self, dispatcher):
from waitress.wasyncore import _exception
+
return _exception(dispatcher)
def test_gardenpath(self):
@@ -1190,8 +1235,9 @@ class Test__exception(unittest.TestCase):
def test_reraised(self):
from waitress.wasyncore import ExitNow
+
inst = DummyDispatcher(ExitNow)
- self.assertRaises(ExitNow,self._callFUT, inst)
+ self.assertRaises(ExitNow, self._callFUT, inst)
self.assertTrue(inst.expt_event_handled)
self.assertFalse(inst.error_handled)
@@ -1201,10 +1247,12 @@ class Test__exception(unittest.TestCase):
self.assertTrue(inst.expt_event_handled)
self.assertTrue(inst.error_handled)
-@unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required')
+
+@unittest.skipUnless(hasattr(select, "poll"), "select.poll required")
class Test_readwrite(unittest.TestCase):
def _callFUT(self, obj, flags):
from waitress.wasyncore import readwrite
+
return readwrite(obj, flags)
def test_handle_read_event(self):
@@ -1213,7 +1261,7 @@ class Test_readwrite(unittest.TestCase):
inst = DummyDispatcher()
self._callFUT(inst, flags)
self.assertTrue(inst.read_event_handled)
-
+
def test_handle_write_event(self):
flags = 0
flags |= select.POLLOUT
@@ -1238,21 +1286,22 @@ class Test_readwrite(unittest.TestCase):
def test_socketerror_not_in_disconnected(self):
flags = 0
flags |= select.POLLIN
- inst = DummyDispatcher(socket.error(errno.EALREADY, 'EALREADY'))
+ inst = DummyDispatcher(socket.error(errno.EALREADY, "EALREADY"))
self._callFUT(inst, flags)
self.assertTrue(inst.read_event_handled)
self.assertTrue(inst.error_handled)
-
+
def test_socketerror_in_disconnected(self):
flags = 0
flags |= select.POLLIN
- inst = DummyDispatcher(socket.error(errno.ECONNRESET, 'ECONNRESET'))
+ inst = DummyDispatcher(socket.error(errno.ECONNRESET, "ECONNRESET"))
self._callFUT(inst, flags)
self.assertTrue(inst.read_event_handled)
self.assertTrue(inst.close_handled)
def test_exception_in_reraised(self):
from waitress import wasyncore
+
flags = 0
flags |= select.POLLIN
inst = DummyDispatcher(wasyncore.ExitNow)
@@ -1266,17 +1315,20 @@ class Test_readwrite(unittest.TestCase):
self._callFUT(inst, flags)
self.assertTrue(inst.error_handled)
+
class Test_poll(unittest.TestCase):
def _callFUT(self, timeout=0.0, map=None):
from waitress.wasyncore import poll
+
return poll(timeout, map)
def test_nothing_writable_nothing_readable_but_map_not_empty(self):
# i read the mock.patch docs. nerp.
dummy_time = DummyTime()
- map = {0:DummyDispatcher()}
+ map = {0: DummyDispatcher()}
try:
from waitress import wasyncore
+
old_time = wasyncore.time
wasyncore.time = dummy_time
result = self._callFUT(map=map)
@@ -1284,15 +1336,16 @@ class Test_poll(unittest.TestCase):
wasyncore.time = old_time
self.assertEqual(result, None)
self.assertEqual(dummy_time.sleepvals, [0.0])
-
+
def test_select_raises_EINTR(self):
# i read the mock.patch docs. nerp.
dummy_select = DummySelect(select.error(errno.EINTR))
disp = DummyDispatcher()
disp.readable = lambda: True
- map = {0:disp}
+ map = {0: disp}
try:
from waitress import wasyncore
+
old_select = wasyncore.select
wasyncore.select = dummy_select
result = self._callFUT(map=map)
@@ -1306,9 +1359,10 @@ class Test_poll(unittest.TestCase):
dummy_select = DummySelect(select.error(errno.EBADF))
disp = DummyDispatcher()
disp.readable = lambda: True
- map = {0:disp}
+ map = {0: disp}
try:
from waitress import wasyncore
+
old_select = wasyncore.select
wasyncore.select = dummy_select
self.assertRaises(select.error, self._callFUT, map=map)
@@ -1316,9 +1370,11 @@ class Test_poll(unittest.TestCase):
wasyncore.select = old_select
self.assertEqual(dummy_select.selected, [([0], [], [0], 0.0)])
+
class Test_poll2(unittest.TestCase):
def _callFUT(self, timeout=0.0, map=None):
from waitress.wasyncore import poll2
+
return poll2(timeout, map)
def test_select_raises_EINTR(self):
@@ -1326,9 +1382,10 @@ class Test_poll2(unittest.TestCase):
pollster = DummyPollster(exc=select.error(errno.EINTR))
dummy_select = DummySelect(pollster=pollster)
disp = DummyDispatcher()
- map = {0:disp}
+ map = {0: disp}
try:
from waitress import wasyncore
+
old_select = wasyncore.select
wasyncore.select = dummy_select
self._callFUT(map=map)
@@ -1341,9 +1398,10 @@ class Test_poll2(unittest.TestCase):
pollster = DummyPollster(exc=select.error(errno.EBADF))
dummy_select = DummySelect(pollster=pollster)
disp = DummyDispatcher()
- map = {0:disp}
+ map = {0: disp}
try:
from waitress import wasyncore
+
old_select = wasyncore.select
wasyncore.select = dummy_select
self.assertRaises(select.error, self._callFUT, map=map)
@@ -1351,15 +1409,19 @@ class Test_poll2(unittest.TestCase):
wasyncore.select = old_select
self.assertEqual(pollster.polled, [0.0])
+
class Test_dispatcher(unittest.TestCase):
def _makeOne(self, sock=None, map=None):
from waitress.wasyncore import dispatcher
+
return dispatcher(sock=sock, map=map)
def test_unexpected_getpeername_exc(self):
sock = dummysocket()
+
def getpeername():
raise socket.error(errno.EBADF)
+
map = {}
sock.getpeername = getpeername
self.assertRaises(socket.error, self._makeOne, sock=sock, map=map)
@@ -1370,28 +1432,30 @@ class Test_dispatcher(unittest.TestCase):
map = {}
inst = self._makeOne(sock=sock, map=map)
inst.accepting = True
- inst.addr = ('localhost', 8080)
+ inst.addr = ("localhost", 8080)
result = repr(inst)
- expected = '<waitress.wasyncore.dispatcher listening localhost:8080 at'
- self.assertEqual(result[:len(expected)], expected)
-
+ expected = "<waitress.wasyncore.dispatcher listening localhost:8080 at"
+ self.assertEqual(result[: len(expected)], expected)
+
def test___repr__connected(self):
sock = dummysocket()
map = {}
inst = self._makeOne(sock=sock, map=map)
inst.accepting = False
inst.connected = True
- inst.addr = ('localhost', 8080)
+ inst.addr = ("localhost", 8080)
result = repr(inst)
- expected = '<waitress.wasyncore.dispatcher connected localhost:8080 at'
- self.assertEqual(result[:len(expected)], expected)
+ expected = "<waitress.wasyncore.dispatcher connected localhost:8080 at"
+ self.assertEqual(result[: len(expected)], expected)
def test_set_reuse_addr_with_socketerror(self):
sock = dummysocket()
map = {}
+
def setsockopt(*arg, **kw):
sock.errored = True
raise socket.error
+
sock.setsockopt = setsockopt
sock.getsockopt = lambda *arg: 0
inst = self._makeOne(sock=sock, map=map)
@@ -1408,8 +1472,10 @@ class Test_dispatcher(unittest.TestCase):
def test_accept_raise_TypeError(self):
sock = dummysocket()
map = {}
+
def accept(*arg, **kw):
raise TypeError
+
sock.accept = accept
inst = self._makeOne(sock=sock, map=map)
result = inst.accept()
@@ -1418,8 +1484,10 @@ class Test_dispatcher(unittest.TestCase):
def test_accept_raise_unexpected_socketerror(self):
sock = dummysocket()
map = {}
+
def accept(*arg, **kw):
raise socket.error(122)
+
sock.accept = accept
inst = self._makeOne(sock=sock, map=map)
self.assertRaises(socket.error, inst.accept)
@@ -1427,41 +1495,50 @@ class Test_dispatcher(unittest.TestCase):
def test_send_raise_EWOULDBLOCK(self):
sock = dummysocket()
map = {}
+
def send(*arg, **kw):
raise socket.error(errno.EWOULDBLOCK)
+
sock.send = send
inst = self._makeOne(sock=sock, map=map)
- result = inst.send('a')
+ result = inst.send("a")
self.assertEqual(result, 0)
def test_send_raise_unexpected_socketerror(self):
sock = dummysocket()
map = {}
+
def send(*arg, **kw):
raise socket.error(122)
+
sock.send = send
inst = self._makeOne(sock=sock, map=map)
- self.assertRaises(socket.error, inst.send, 'a')
+ self.assertRaises(socket.error, inst.send, "a")
def test_recv_raises_disconnect(self):
sock = dummysocket()
map = {}
+
def recv(*arg, **kw):
raise socket.error(errno.ECONNRESET)
+
def handle_close():
inst.close_handled = True
+
sock.recv = recv
inst = self._makeOne(sock=sock, map=map)
inst.handle_close = handle_close
result = inst.recv(1)
- self.assertEqual(result, b'')
+ self.assertEqual(result, b"")
self.assertTrue(inst.close_handled)
def test_close_raises_unknown_socket_error(self):
sock = dummysocket()
map = {}
+
def close():
raise socket.error(122)
+
sock.close = close
inst = self._makeOne(sock=sock, map=map)
inst.del_channel = lambda: None
@@ -1471,10 +1548,13 @@ class Test_dispatcher(unittest.TestCase):
sock = dummysocket()
map = {}
inst = self._makeOne(sock=sock, map=map)
+
def handle_connect_event():
inst.connect_event_handled = True
+
def handle_read():
inst.read_handled = True
+
inst.handle_connect_event = handle_connect_event
inst.handle_read = handle_read
inst.accepting = False
@@ -1496,8 +1576,10 @@ class Test_dispatcher(unittest.TestCase):
sock.getsockopt = lambda *arg: 122
map = {}
inst = self._makeOne(sock=sock, map=map)
+
def handle_close():
inst.close_handled = True
+
inst.handle_close = handle_close
inst.handle_expt_event()
self.assertTrue(inst.close_handled)
@@ -1514,78 +1596,90 @@ class Test_dispatcher(unittest.TestCase):
sock = dummysocket()
map = {}
inst = self._makeOne(sock=sock, map=map)
+
def handle_close():
inst.close_handled = True
+
def compact_traceback(*arg, **kw):
return None, None, None, None
+
def log_info(self, *arg):
inst.logged_info = arg
+
inst.handle_close = handle_close
inst.compact_traceback = compact_traceback
inst.log_info = log_info
inst.handle_error()
self.assertTrue(inst.close_handled)
- self.assertEqual(inst.logged_info, ('error',))
+ self.assertEqual(inst.logged_info, ("error",))
def test_handle_close(self):
sock = dummysocket()
map = {}
inst = self._makeOne(sock=sock, map=map)
+
def log_info(self, *arg):
inst.logged_info = arg
+
def close():
inst._closed = True
+
inst.log_info = log_info
inst.close = close
inst.handle_close()
self.assertTrue(inst._closed)
-
+
def test_handle_accepted(self):
sock = dummysocket()
map = {}
inst = self._makeOne(sock=sock, map=map)
- inst.handle_accepted(sock, '1')
+ inst.handle_accepted(sock, "1")
self.assertTrue(sock.closed)
+
class Test_dispatcher_with_send(unittest.TestCase):
def _makeOne(self, sock=None, map=None):
from waitress.wasyncore import dispatcher_with_send
+
return dispatcher_with_send(sock=sock, map=map)
def test_writable(self):
sock = dummysocket()
map = {}
inst = self._makeOne(sock=sock, map=map)
- inst.out_buffer = b'123'
+ inst.out_buffer = b"123"
inst.connected = True
self.assertTrue(inst.writable())
+
class Test_close_all(unittest.TestCase):
def _callFUT(self, map=None, ignore_all=False):
from waitress.wasyncore import close_all
+
return close_all(map, ignore_all)
def test_socketerror_on_close_ebadf(self):
disp = DummyDispatcher(exc=socket.error(errno.EBADF))
- map = {0:disp}
+ map = {0: disp}
self._callFUT(map)
self.assertEqual(map, {})
def test_socketerror_on_close_non_ebadf(self):
disp = DummyDispatcher(exc=socket.error(errno.EAGAIN))
- map = {0:disp}
+ map = {0: disp}
self.assertRaises(socket.error, self._callFUT, map)
def test_reraised_exc_on_close(self):
disp = DummyDispatcher(exc=KeyboardInterrupt)
- map = {0:disp}
+ map = {0: disp}
self.assertRaises(KeyboardInterrupt, self._callFUT, map)
def test_unknown_exc_on_close(self):
disp = DummyDispatcher(exc=RuntimeError)
- map = {0:disp}
+ map = {0: disp}
self.assertRaises(RuntimeError, self._callFUT, map)
+
class DummyDispatcher(object):
read_event_handled = False
write_event_handled = False
@@ -1593,6 +1687,7 @@ class DummyDispatcher(object):
error_handled = False
close_handled = False
accepting = False
+
def __init__(self, exc=None):
self.exc = exc
@@ -1610,7 +1705,7 @@ class DummyDispatcher(object):
self.expt_event_handled = True
if self.exc is not None:
raise self.exc
-
+
def handle_error(self):
self.error_handled = True
@@ -1626,15 +1721,19 @@ class DummyDispatcher(object):
def close(self):
if self.exc is not None:
raise self.exc
-
+
+
class DummyTime(object):
def __init__(self):
self.sleepvals = []
+
def sleep(self, val):
self.sleepvals.append(val)
+
class DummySelect(object):
error = select.error
+
def __init__(self, exc=None, pollster=None):
self.selected = []
self.pollster = pollster
@@ -1648,14 +1747,15 @@ class DummySelect(object):
def poll(self):
return self.pollster
+
class DummyPollster(object):
def __init__(self, exc=None):
self.polled = []
self.exc = exc
-
+
def poll(self, timeout):
self.polled.append(timeout)
if self.exc is not None:
raise self.exc
- else: # pragma: no cover
+ else: # pragma: no cover
return []
diff --git a/waitress/trigger.py b/waitress/trigger.py
index 673e5d8..6a57c12 100644
--- a/waitress/trigger.py
+++ b/waitress/trigger.py
@@ -49,10 +49,11 @@ from . import wasyncore
# new data onto a channel's outgoing data queue at the same time that
# the main thread is trying to remove some]
+
class _triggerbase(object):
"""OS-independent base class for OS-dependent trigger class."""
- kind = None # subclass must set to "pipe" or "loopback"; used by repr
+ kind = None # subclass must set to "pipe" or "loopback"; used by repr
def __init__(self):
self._closed = False
@@ -86,7 +87,7 @@ class _triggerbase(object):
if not self._closed:
self._closed = True
self.del_channel()
- self._close() # subclass does OS-specific stuff
+ self._close() # subclass does OS-specific stuff
def pull_trigger(self, thunk=None):
if thunk:
@@ -106,11 +107,12 @@ class _triggerbase(object):
except:
nil, t, v, tbinfo = wasyncore.compact_traceback()
self.log_info(
- 'exception in trigger thunk: (%s:%s %s)' %
- (t, v, tbinfo))
+ "exception in trigger thunk: (%s:%s %s)" % (t, v, tbinfo)
+ )
self.thunks = []
-if os.name == 'posix':
+
+if os.name == "posix":
class trigger(_triggerbase, wasyncore.file_dispatcher):
kind = "pipe"
@@ -127,9 +129,10 @@ if os.name == 'posix':
wasyncore.file_dispatcher.close(self)
def _physical_pull(self):
- os.write(self.trigger, b'x')
+ os.write(self.trigger, b"x")
+
-else: # pragma: no cover
+else: # pragma: no cover
# Windows version; uses just sockets, because a pipe isn't select'able
# on Windows.
@@ -165,11 +168,11 @@ else: # pragma: no cover
# for hideous details.
a = socket.socket()
a.bind(("127.0.0.1", 0))
- connect_address = a.getsockname() # assigned (host, port) pair
+ connect_address = a.getsockname() # assigned (host, port) pair
a.listen(1)
try:
w.connect(connect_address)
- break # success
+ break # success
except socket.error as detail:
if detail[0] != errno.WSAEADDRINUSE:
# "Address already in use" is the only error
@@ -178,7 +181,7 @@ else: # pragma: no cover
raise
# (10048, 'Address already in use')
# assert count <= 2 # never triggered in Tim's tests
- if count >= 10: # I've never seen it go above 2
+ if count >= 10: # I've never seen it go above 2
a.close()
w.close()
raise RuntimeError("Cannot bind trigger!")
@@ -186,7 +189,7 @@ else: # pragma: no cover
# sleep() here, but it didn't appear to help or hurt.
a.close()
- r, addr = a.accept() # r becomes wasyncore's (self.)socket
+ r, addr = a.accept() # r becomes wasyncore's (self.)socket
a.close()
self.trigger = w
wasyncore.dispatcher.__init__(self, r, map=map)
@@ -197,4 +200,4 @@ else: # pragma: no cover
self.trigger.close()
def _physical_pull(self):
- self.trigger.send(b'x')
+ self.trigger.send(b"x")
diff --git a/waitress/utilities.py b/waitress/utilities.py
index f06bdc9..c77af49 100644
--- a/waitress/utilities.py
+++ b/waitress/utilities.py
@@ -22,15 +22,16 @@ import re
import stat
import time
-logger = logging.getLogger('waitress')
-queue_logger = logging.getLogger('waitress.queue')
+logger = logging.getLogger("waitress")
+queue_logger = logging.getLogger("waitress.queue")
+
def find_double_newline(s):
"""Returns the position just after a double newline in the given string."""
- pos1 = s.find(b'\n\r\n') # One kind of double newline
+ pos1 = s.find(b"\n\r\n") # One kind of double newline
if pos1 >= 0:
pos1 += 3
- pos2 = s.find(b'\n\n') # Another kind of double newline
+ pos2 = s.find(b"\n\n") # Another kind of double newline
if pos2 >= 0:
pos2 += 2
@@ -42,37 +43,60 @@ def find_double_newline(s):
else:
return pos2
+
def concat(*args):
- return ''.join(args)
+ return "".join(args)
+
-def join(seq, field=' '):
+def join(seq, field=" "):
return field.join(seq)
+
def group(s):
- return '(' + s + ')'
+ return "(" + s + ")"
+
-short_days = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
-long_days = ['sunday', 'monday', 'tuesday', 'wednesday',
- 'thursday', 'friday', 'saturday']
+short_days = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"]
+long_days = [
+ "sunday",
+ "monday",
+ "tuesday",
+ "wednesday",
+ "thursday",
+ "friday",
+ "saturday",
+]
-short_day_reg = group(join(short_days, '|'))
-long_day_reg = group(join(long_days, '|'))
+short_day_reg = group(join(short_days, "|"))
+long_day_reg = group(join(long_days, "|"))
daymap = {}
for i in range(7):
daymap[short_days[i]] = i
daymap[long_days[i]] = i
-hms_reg = join(3 * [group('[0-9][0-9]')], ':')
-
-months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul',
- 'aug', 'sep', 'oct', 'nov', 'dec']
+hms_reg = join(3 * [group("[0-9][0-9]")], ":")
+
+months = [
+ "jan",
+ "feb",
+ "mar",
+ "apr",
+ "may",
+ "jun",
+ "jul",
+ "aug",
+ "sep",
+ "oct",
+ "nov",
+ "dec",
+]
monmap = {}
for i in range(12):
monmap[months[i]] = i + 1
-months_reg = group(join(months, '|'))
+months_reg = group(join(months, "|"))
# From draft-ietf-http-v11-spec-07.txt/3.3.1
# Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
@@ -81,48 +105,44 @@ months_reg = group(join(months, '|'))
# rfc822 format
rfc822_date = join(
- [concat(short_day_reg, ','), # day
- group('[0-9][0-9]?'), # date
- months_reg, # month
- group('[0-9]+'), # year
- hms_reg, # hour minute second
- 'gmt'
- ],
- ' '
+ [
+ concat(short_day_reg, ","), # day
+ group("[0-9][0-9]?"), # date
+ months_reg, # month
+ group("[0-9]+"), # year
+ hms_reg, # hour minute second
+ "gmt",
+ ],
+ " ",
)
rfc822_reg = re.compile(rfc822_date)
+
def unpack_rfc822(m):
g = m.group
return (
- int(g(4)), # year
- monmap[g(3)], # month
- int(g(2)), # day
- int(g(5)), # hour
- int(g(6)), # minute
- int(g(7)), # second
+ int(g(4)), # year
+ monmap[g(3)], # month
+ int(g(2)), # day
+ int(g(5)), # hour
+ int(g(6)), # minute
+ int(g(7)), # second
0,
0,
0,
)
+
# rfc850 format
rfc850_date = join(
[
- concat(long_day_reg, ','),
- join(
- [
- group('[0-9][0-9]?'),
- months_reg,
- group('[0-9]+')
- ],
- '-'
- ),
+ concat(long_day_reg, ","),
+ join([group("[0-9][0-9]?"), months_reg, group("[0-9]+")], "-"),
hms_reg,
- 'gmt'
+ "gmt",
],
- ' '
+ " ",
)
rfc850_reg = re.compile(rfc850_date)
@@ -131,32 +151,53 @@ def unpack_rfc850(m):
g = m.group
yr = g(4)
if len(yr) == 2:
- yr = '19' + yr
+ yr = "19" + yr
return (
- int(yr), # year
- monmap[g(3)], # month
- int(g(2)), # day
- int(g(5)), # hour
- int(g(6)), # minute
- int(g(7)), # second
+ int(yr), # year
+ monmap[g(3)], # month
+ int(g(2)), # day
+ int(g(5)), # hour
+ int(g(6)), # minute
+ int(g(7)), # second
+ 0,
0,
0,
- 0
)
+
# parsdate.parsedate - ~700/sec.
# parse_http_date - ~1333/sec.
-weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
-monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
- 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+weekdayname = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
+monthname = [
+ None,
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec",
+]
+
def build_http_date(when):
year, month, day, hh, mm, ss, wd, y, z = time.gmtime(when)
return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
weekdayname[wd],
- day, monthname[month], year,
- hh, mm, ss)
+ day,
+ monthname[month],
+ year,
+ hh,
+ mm,
+ ss,
+ )
+
def parse_http_date(d):
d = d.lower()
@@ -175,21 +216,20 @@ def parse_http_date(d):
# RFC 5234 Appendix B.1 "Core Rules":
# VCHAR = %x21-7E
# ; visible (printing) characters
-vchar_re = '\x21-\x7e'
+vchar_re = "\x21-\x7e"
# RFC 7230 Section 3.2.6 "Field Value Components":
# quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
# qdtext = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text
# obs-text = %x80-FF
# quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
-obs_text_re = '\x80-\xff'
+obs_text_re = "\x80-\xff"
# The '\\' between \x5b and \x5d is needed to escape \x5d (']')
-qdtext_re = '[\t \x21\x23-\x5b\\\x5d-\x7e' + obs_text_re + ']'
+qdtext_re = "[\t \x21\x23-\x5b\\\x5d-\x7e" + obs_text_re + "]"
-quoted_pair_re = r'\\' + '([\t ' + vchar_re + obs_text_re + '])'
-quoted_string_re = \
- '"(?:(?:' + qdtext_re + ')|(?:' + quoted_pair_re + '))*"'
+quoted_pair_re = r"\\" + "([\t " + vchar_re + obs_text_re + "])"
+quoted_string_re = '"(?:(?:' + qdtext_re + ")|(?:" + quoted_pair_re + '))*"'
quoted_string = re.compile(quoted_string_re)
quoted_pair = re.compile(quoted_pair_re)
@@ -206,13 +246,13 @@ def undquote(value):
# Remove all backslashes that are followed by a valid vchar or
# obs-text
- value = quoted_pair.sub(r'\1', value)
+ value = quoted_pair.sub(r"\1", value)
return value
elif not value.startswith('"') and not value.endswith('"'):
return value
- raise ValueError('Invalid quoting in value')
+ raise ValueError("Invalid quoting in value")
def cleanup_unix_socket(path):
@@ -220,26 +260,26 @@ def cleanup_unix_socket(path):
st = os.stat(path)
except OSError as exc:
if exc.errno != errno.ENOENT:
- raise # pragma: no cover
+ raise # pragma: no cover
else:
if stat.S_ISSOCK(st.st_mode):
try:
os.remove(path)
- except OSError: # pragma: no cover
+ except OSError: # pragma: no cover
# avoid race condition error during tests
pass
-class Error(object):
+class Error(object):
def __init__(self, body):
self.body = body
def to_response(self):
- status = '%s %s' % (self.code, self.reason)
- body = '%s\r\n\r\n%s' % (self.reason, self.body)
- tag = '\r\n\r\n(generated by waitress)'
+ status = "%s %s" % (self.code, self.reason)
+ body = "%s\r\n\r\n%s" % (self.reason, self.body)
+ tag = "\r\n\r\n(generated by waitress)"
body = body + tag
- headers = [('Content-Type', 'text/plain')]
+ headers = [("Content-Type", "text/plain")]
return status, headers, body
def wsgi_response(self, environ, start_response):
@@ -247,18 +287,22 @@ class Error(object):
start_response(status, headers)
yield body
+
class BadRequest(Error):
code = 400
- reason = 'Bad Request'
+ reason = "Bad Request"
+
class RequestHeaderFieldsTooLarge(BadRequest):
code = 431
- reason = 'Request Header Fields Too Large'
+ reason = "Request Header Fields Too Large"
+
class RequestEntityTooLarge(BadRequest):
code = 413
- reason = 'Request Entity Too Large'
+ reason = "Request Entity Too Large"
+
class InternalServerError(Error):
code = 500
- reason = 'Internal Server Error'
+ reason = "Internal Server Error"
diff --git a/waitress/wasyncore.py b/waitress/wasyncore.py
index d0648bf..09bcafa 100644
--- a/waitress/wasyncore.py
+++ b/waitress/wasyncore.py
@@ -62,29 +62,45 @@ import time
import warnings
import os
-from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \
- ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, EINTR, \
- errorcode
-
-_DISCONNECTED = frozenset({ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE,
- EBADF})
+from errno import (
+ EALREADY,
+ EINPROGRESS,
+ EWOULDBLOCK,
+ ECONNRESET,
+ EINVAL,
+ ENOTCONN,
+ ESHUTDOWN,
+ EISCONN,
+ EBADF,
+ ECONNABORTED,
+ EPIPE,
+ EAGAIN,
+ EINTR,
+ errorcode,
+)
+
+_DISCONNECTED = frozenset({ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, EBADF})
try:
socket_map
except NameError:
socket_map = {}
+
def _strerror(err):
try:
return os.strerror(err)
except (TypeError, ValueError, OverflowError, NameError):
return "Unknown error %s" % err
+
class ExitNow(Exception):
pass
+
_reraised_exceptions = (ExitNow, KeyboardInterrupt, SystemExit)
+
def read(obj):
try:
obj.handle_read_event()
@@ -93,6 +109,7 @@ def read(obj):
except:
obj.handle_error()
+
def write(obj):
try:
obj.handle_write_event()
@@ -101,6 +118,7 @@ def write(obj):
except:
obj.handle_error()
+
def _exception(obj):
try:
obj.handle_expt_event()
@@ -109,6 +127,7 @@ def _exception(obj):
except:
obj.handle_error()
+
def readwrite(obj, flags):
try:
if flags & select.POLLIN:
@@ -129,12 +148,15 @@ def readwrite(obj, flags):
except:
obj.handle_error()
+
def poll(timeout=0.0, map=None):
- if map is None: # pragma: no cover
+ if map is None: # pragma: no cover
map = socket_map
if map:
- r = []; w = []; e = []
- for fd, obj in list(map.items()): # list() call FBO py3
+ r = []
+ w = []
+ e = []
+ for fd, obj in list(map.items()): # list() call FBO py3
is_r = obj.readable()
is_w = obj.writable()
if is_r:
@@ -158,29 +180,30 @@ def poll(timeout=0.0, map=None):
for fd in r:
obj = map.get(fd)
- if obj is None: # pragma: no cover
+ if obj is None: # pragma: no cover
continue
read(obj)
for fd in w:
obj = map.get(fd)
- if obj is None: # pragma: no cover
+ if obj is None: # pragma: no cover
continue
write(obj)
for fd in e:
obj = map.get(fd)
- if obj is None: # pragma: no cover
+ if obj is None: # pragma: no cover
continue
_exception(obj)
+
def poll2(timeout=0.0, map=None):
# Use the poll() support added to the select module in Python 2.0
- if map is None: # pragma: no cover
+ if map is None: # pragma: no cover
map = socket_map
if timeout is not None:
# timeout is in milliseconds
- timeout = int(timeout*1000)
+ timeout = int(timeout * 1000)
pollster = select.poll()
if map:
for fd, obj in list(map.items()):
@@ -202,22 +225,24 @@ def poll2(timeout=0.0, map=None):
for fd, flags in r:
obj = map.get(fd)
- if obj is None: # pragma: no cover
+ if obj is None: # pragma: no cover
continue
readwrite(obj, flags)
-poll3 = poll2 # Alias for backward compatibility
+
+poll3 = poll2 # Alias for backward compatibility
+
def loop(timeout=30.0, use_poll=False, map=None, count=None):
- if map is None: # pragma: no cover
+ if map is None: # pragma: no cover
map = socket_map
- if use_poll and hasattr(select, 'poll'):
+ if use_poll and hasattr(select, "poll"):
poll_fun = poll2
else:
poll_fun = poll
- if count is None: # pragma: no cover
+ if count is None: # pragma: no cover
while map:
poll_fun(timeout, map)
@@ -226,26 +251,30 @@ def loop(timeout=30.0, use_poll=False, map=None, count=None):
poll_fun(timeout, map)
count = count - 1
+
def compact_traceback():
t, v, tb = sys.exc_info()
tbinfo = []
- if not tb: # pragma: no cover
+ if not tb: # pragma: no cover
raise AssertionError("traceback does not exist")
while tb:
- tbinfo.append((
- tb.tb_frame.f_code.co_filename,
- tb.tb_frame.f_code.co_name,
- str(tb.tb_lineno)
- ))
+ tbinfo.append(
+ (
+ tb.tb_frame.f_code.co_filename,
+ tb.tb_frame.f_code.co_name,
+ str(tb.tb_lineno),
+ )
+ )
tb = tb.tb_next
# just to be safe
del tb
file, function, line = tbinfo[-1]
- info = ' '.join(['[%s|%s|%s]' % x for x in tbinfo])
+ info = " ".join(["[%s|%s|%s]" % x for x in tbinfo])
return (file, function, line), t, v, info
+
class dispatcher:
debug = False
@@ -254,12 +283,12 @@ class dispatcher:
connecting = False
closing = False
addr = None
- ignore_log_types = frozenset({'warning'})
+ ignore_log_types = frozenset({"warning"})
logger = utilities.logger
- compact_traceback = staticmethod(compact_traceback) # for testing
+ compact_traceback = staticmethod(compact_traceback) # for testing
def __init__(self, sock=None, map=None):
- if map is None: # pragma: no cover
+ if map is None: # pragma: no cover
self._map = socket_map
else:
self._map = map
@@ -291,22 +320,22 @@ class dispatcher:
self.socket = None
def __repr__(self):
- status = [self.__class__.__module__+"."+compat.qualname(self.__class__)]
+ status = [self.__class__.__module__ + "." + compat.qualname(self.__class__)]
if self.accepting and self.addr:
- status.append('listening')
+ status.append("listening")
elif self.connected:
- status.append('connected')
+ status.append("connected")
if self.addr is not None:
try:
- status.append('%s:%d' % self.addr)
- except TypeError: # pragma: no cover
+ status.append("%s:%d" % self.addr)
+ except TypeError: # pragma: no cover
status.append(repr(self.addr))
- return '<%s at %#x>' % (' '.join(status), id(self))
+ return "<%s at %#x>" % (" ".join(status), id(self))
__str__ = __repr__
def add_channel(self, map=None):
- #self.log_info('adding channel %s' % self)
+ # self.log_info('adding channel %s' % self)
if map is None:
map = self._map
map[self._fileno] = self
@@ -316,7 +345,7 @@ class dispatcher:
if map is None:
map = self._map
if fd in map:
- #self.log_info('closing channel %d:%s' % (fd, self))
+ # self.log_info('closing channel %d:%s' % (fd, self))
del map[fd]
self._fileno = None
@@ -335,10 +364,10 @@ class dispatcher:
# try to re-use a server port if possible
try:
self.socket.setsockopt(
- socket.SOL_SOCKET, socket.SO_REUSEADDR,
- self.socket.getsockopt(socket.SOL_SOCKET,
- socket.SO_REUSEADDR) | 1
- )
+ socket.SOL_SOCKET,
+ socket.SO_REUSEADDR,
+ self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) | 1,
+ )
except socket.error:
pass
@@ -360,7 +389,7 @@ class dispatcher:
def listen(self, num):
self.accepting = True
- if os.name == 'nt' and num > 5: # pragma: no cover
+ if os.name == "nt" and num > 5: # pragma: no cover
num = 5
return self.socket.listen(num)
@@ -372,8 +401,11 @@ class dispatcher:
self.connected = False
self.connecting = True
err = self.socket.connect_ex(address)
- if err in (EINPROGRESS, EALREADY, EWOULDBLOCK) \
- or err == EINVAL and os.name == 'nt': # pragma: no cover
+ if (
+ err in (EINPROGRESS, EALREADY, EWOULDBLOCK)
+ or err == EINVAL
+ and os.name == "nt"
+ ): # pragma: no cover
self.addr = address
return
if err in (0, EISCONN):
@@ -416,14 +448,14 @@ class dispatcher:
# a closed connection is indicated by signaling
# a read condition, and having recv() return 0.
self.handle_close()
- return b''
+ return b""
else:
return data
except socket.error as why:
# winsock sometimes raises ENOTCONN
if why.args[0] in _DISCONNECTED:
self.handle_close()
- return b''
+ return b""
else:
raise
@@ -446,11 +478,11 @@ class dispatcher:
def log(self, message):
self.logger.log(logging.DEBUG, message)
- def log_info(self, message, type='info'):
+ def log_info(self, message, type="info"):
severity = {
- 'info': logging.INFO,
- 'warning': logging.WARN,
- 'error': logging.ERROR,
+ "info": logging.INFO,
+ "warning": logging.WARN,
+ "error": logging.ERROR,
}
self.logger.log(severity.get(type, logging.INFO), message)
@@ -506,31 +538,27 @@ class dispatcher:
# sometimes a user repr method will crash.
try:
self_repr = repr(self)
- except: # pragma: no cover
- self_repr = '<__repr__(self) failed for object at %0x>' % id(self)
+ except: # pragma: no cover
+ self_repr = "<__repr__(self) failed for object at %0x>" % id(self)
self.log_info(
- 'uncaptured python exception, closing channel %s (%s:%s %s)' % (
- self_repr,
- t,
- v,
- tbinfo
- ),
- 'error'
- )
+ "uncaptured python exception, closing channel %s (%s:%s %s)"
+ % (self_repr, t, v, tbinfo),
+ "error",
+ )
self.handle_close()
def handle_expt(self):
- self.log_info('unhandled incoming priority event', 'warning')
+ self.log_info("unhandled incoming priority event", "warning")
def handle_read(self):
- self.log_info('unhandled read event', 'warning')
+ self.log_info("unhandled read event", "warning")
def handle_write(self):
- self.log_info('unhandled write event', 'warning')
+ self.log_info("unhandled write event", "warning")
def handle_connect(self):
- self.log_info('unhandled connect event', 'warning')
+ self.log_info("unhandled connect event", "warning")
def handle_accept(self):
pair = self.accept()
@@ -539,22 +567,23 @@ class dispatcher:
def handle_accepted(self, sock, addr):
sock.close()
- self.log_info('unhandled accepted event', 'warning')
+ self.log_info("unhandled accepted event", "warning")
def handle_close(self):
- self.log_info('unhandled close event', 'warning')
+ self.log_info("unhandled close event", "warning")
self.close()
+
# ---------------------------------------------------------------------------
# adds simple buffered output capability, useful for simple clients.
# [for more sophisticated usage use asynchat.async_chat]
# ---------------------------------------------------------------------------
-class dispatcher_with_send(dispatcher):
+class dispatcher_with_send(dispatcher):
def __init__(self, sock=None, map=None):
dispatcher.__init__(self, sock, map)
- self.out_buffer = b''
+ self.out_buffer = b""
def initiate_send(self):
num_sent = 0
@@ -567,15 +596,16 @@ class dispatcher_with_send(dispatcher):
return (not self.connected) or len(self.out_buffer)
def send(self, data):
- if self.debug: # pragma: no cover
- self.log_info('sending %s' % repr(data))
+ if self.debug: # pragma: no cover
+ self.log_info("sending %s" % repr(data))
self.out_buffer = self.out_buffer + data
self.initiate_send()
+
def close_all(map=None, ignore_all=False):
- if map is None: # pragma: no cover
+ if map is None: # pragma: no cover
map = socket_map
- for x in list(map.values()): # list() FBO py3
+ for x in list(map.values()): # list() FBO py3
try:
x.close()
except socket.error as x:
@@ -590,6 +620,7 @@ def close_all(map=None, ignore_all=False):
raise
map.clear()
+
# Asynchronous File I/O:
#
# After a little research (reading man pages on various unixen, and
@@ -603,7 +634,8 @@ def close_all(map=None, ignore_all=False):
#
# Regardless, this is useful for pipes, and stdin/stdout...
-if os.name == 'posix':
+if os.name == "posix":
+
class file_wrapper:
# Here we override just enough to make a file
# look like a socket for the purposes of asyncore.
@@ -623,13 +655,12 @@ if os.name == 'posix':
def send(self, *args):
return os.write(self.fd, *args)
- def getsockopt(self, level, optname, buflen=None): # pragma: no cover
- if (level == socket.SOL_SOCKET and
- optname == socket.SO_ERROR and
- not buflen):
+ def getsockopt(self, level, optname, buflen=None): # pragma: no cover
+ if level == socket.SOL_SOCKET and optname == socket.SO_ERROR and not buflen:
return 0
- raise NotImplementedError("Only asyncore specific behaviour "
- "implemented.")
+ raise NotImplementedError(
+ "Only asyncore specific behaviour " "implemented."
+ )
read = recv
write = send
@@ -645,7 +676,6 @@ if os.name == 'posix':
return self.fd
class file_dispatcher(dispatcher):
-
def __init__(self, fd, map=None):
dispatcher.__init__(self, None, map)
self.connected = True
@@ -661,4 +691,3 @@ if os.name == 'posix':
self.socket = file_wrapper(fd)
self._fileno = self.socket.fileno()
self.add_channel()
-