summaryrefslogtreecommitdiff
path: root/src/waitress
diff options
context:
space:
mode:
authorBert JW Regeer <bertjw@regeer.org>2020-08-15 22:29:01 -0700
committerBert JW Regeer <bertjw@regeer.org>2020-08-16 16:49:06 -0700
commitd347823b8eb5bdc9ccecc440168032658bd752fa (patch)
tree4a7bd6794c82a2e5b59c024e16020432fefca2d4 /src/waitress
parent1b4bcce97cceaae588b5508d42308f13be926ce2 (diff)
downloadwaitress-d347823b8eb5bdc9ccecc440168032658bd752fa.tar.gz
Reduce compat.py to minimum size
Diffstat (limited to 'src/waitress')
-rw-r--r--src/waitress/adjustments.py4
-rw-r--r--src/waitress/compat.py36
-rw-r--r--src/waitress/parser.py52
-rw-r--r--src/waitress/rfc7230.py6
-rw-r--r--src/waitress/task.py7
5 files changed, 45 insertions, 60 deletions
diff --git a/src/waitress/adjustments.py b/src/waitress/adjustments.py
index f121b6e..145ac86 100644
--- a/src/waitress/adjustments.py
+++ b/src/waitress/adjustments.py
@@ -17,7 +17,7 @@ import getopt
import socket
import warnings
-from .compat import HAS_IPV6, WIN, string_types
+from .compat import HAS_IPV6, WIN
from .proxy_headers import PROXY_HEADERS
truthy = frozenset(("t", "true", "y", "yes", "on", "1"))
@@ -47,7 +47,7 @@ def asoctal(s):
def aslist_cronly(value):
- if isinstance(value, string_types):
+ if isinstance(value, str):
value = filter(None, [x.strip() for x in value.splitlines()])
return list(value)
diff --git a/src/waitress/compat.py b/src/waitress/compat.py
index 7c2630c..67543b9 100644
--- a/src/waitress/compat.py
+++ b/src/waitress/compat.py
@@ -1,50 +1,14 @@
-import _thread as thread
-from http import client as httplib
-from io import StringIO as NativeIO
-import os
import platform
# Fix for issue reported in https://github.com/Pylons/waitress/issues/138,
# Python on Windows may not define IPPROTO_IPV6 in socket.
import socket
import sys
-from urllib import parse as urlparse
-from urllib.parse import unquote_to_bytes
import warnings
# True if we are running on Windows
WIN = platform.system() == "Windows"
-string_types = (str,)
-integer_types = (int,)
-class_types = (type,)
-text_type = str
-binary_type = bytes
-long = int
-
-
-def unquote_bytes_to_wsgi(bytestring):
- return unquote_to_bytes(bytestring).decode("latin-1")
-
-
-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
-
-
-def tostr(s):
- return str(s, "latin-1", "strict")
-
-
-def tobytes(s):
- return bytes(s, "latin-1")
-
-
MAXINT = sys.maxsize
HAS_IPV6 = socket.has_ipv6
diff --git a/src/waitress/parser.py b/src/waitress/parser.py
index 4530b23..3b99921 100644
--- a/src/waitress/parser.py
+++ b/src/waitress/parser.py
@@ -18,9 +18,10 @@ processing but threads to do work.
"""
from io import BytesIO
import re
+from urllib import parse
+from urllib.parse import unquote_to_bytes
from waitress.buffers import OverflowableBuffer
-from waitress.compat import tostr, unquote_bytes_to_wsgi, urlparse
from waitress.receiver import ChunkedReceiver, FixedStreamReceiver
from waitress.utilities import (
BadRequest,
@@ -33,6 +34,10 @@ from waitress.utilities import (
from .rfc7230 import HEADER_FIELD
+def unquote_bytes_to_wsgi(bytestring):
+ return unquote_to_bytes(bytestring).decode("latin-1")
+
+
class ParsingError(Exception):
pass
@@ -80,11 +85,13 @@ class HTTPRequestParser:
bytes consumed. Sets the completed flag once both the header and the
body have been received.
"""
+
if self.completed:
return 0 # Can't consume any more.
datalen = len(data)
br = self.body_rcv
+
if br is None:
# In header.
max_header = self.adj.max_request_header_size
@@ -106,12 +113,14 @@ class HTTPRequestParser:
# If the first line + headers is over the max length, we return a
# RequestHeaderFieldsTooLarge error rather than continuing to
# attempt to parse the headers.
+
if self.header_bytes_received >= max_header:
self.parse_header(b"GET / HTTP/1.0\r\n")
self.error = RequestHeaderFieldsTooLarge(
"exceeds max_header of %s" % max_header
)
self.completed = True
+
return consumed
if index >= 0:
@@ -195,6 +204,7 @@ class HTTPRequestParser:
first line of the request).
"""
index = header_plus.find(b"\r\n")
+
if index >= 0:
first_line = header_plus[:index].rstrip()
header = header_plus[index + 2 :]
@@ -209,6 +219,7 @@ class HTTPRequestParser:
lines = get_header_lines(header)
headers = self.headers
+
for line in lines:
header = HEADER_FIELD.match(line)
@@ -219,25 +230,26 @@ class HTTPRequestParser:
if b"_" in key:
# TODO(xistence): Should we drop this request instead?
+
continue
# Only strip off whitespace that is considered valid whitespace by
# RFC7230, don't strip the rest
value = value.strip(b" \t")
- key1 = tostr(key.upper().replace(b"-", b"_"))
+ key1 = key.upper().replace(b"-", b"_").decode("latin-1")
# If a header already exists, we append subsequent values
# separated by a comma. Applications already need to handle
# the comma separated values, as HTTP front ends might do
# the concatenation for you (behavior specified in RFC2616).
try:
- headers[key1] += tostr(b", " + value)
+ headers[key1] += (b", " + value).decode("latin-1")
except KeyError:
- headers[key1] = tostr(value)
+ headers[key1] = value.decode("latin-1")
# command, uri, version will be bytes
command, uri, version = crack_first_line(first_line)
- version = tostr(version)
- command = tostr(command)
+ version = version.decode("latin-1")
+ command = command.decode("latin-1")
self.command = command
self.version = version
(
@@ -280,6 +292,7 @@ class HTTPRequestParser:
# Note: the identity transfer-coding was removed in RFC7230:
# https://tools.ietf.org/html/rfc7230#appendix-A.2 and is thus
# not supported
+
if encoding not in {"chunked"}:
raise TransferEncodingNotImplemented(
"Transfer-Encoding requested is not supported."
@@ -296,6 +309,7 @@ class HTTPRequestParser:
expect = headers.get("EXPECT", "").lower()
self.expect_continue = expect == "100-continue"
+
if connection.lower() == "close":
self.connection_close = True
@@ -306,12 +320,14 @@ class HTTPRequestParser:
raise ParsingError("Content-Length is invalid")
self.content_length = cl
+
if cl > 0:
buf = OverflowableBuffer(self.adj.inbuf_overflow)
self.body_rcv = FixedStreamReceiver(cl, buf)
def get_body_stream(self):
body_rcv = self.body_rcv
+
if body_rcv is not None:
return body_rcv.getfile()
else:
@@ -319,6 +335,7 @@ class HTTPRequestParser:
def close(self):
body_rcv = self.body_rcv
+
if body_rcv is not None:
body_rcv.getbuf().close()
@@ -346,16 +363,16 @@ def split_uri(uri):
path, query = path.split(b"?", 1)
else:
try:
- scheme, netloc, path, query, fragment = urlparse.urlsplit(uri)
+ scheme, netloc, path, query, fragment = parse.urlsplit(uri)
except UnicodeError:
raise ParsingError("Bad URI")
return (
- tostr(scheme),
- tostr(netloc),
+ scheme.decode("latin-1"),
+ netloc.decode("latin-1"),
unquote_bytes_to_wsgi(path),
- tostr(query),
- tostr(fragment),
+ query.decode("latin-1"),
+ fragment.decode("latin-1"),
)
@@ -365,20 +382,24 @@ def get_header_lines(header):
"""
r = []
lines = header.split(b"\r\n")
+
for line in lines:
if not line:
continue
if b"\r" in line or b"\n" in line:
- raise ParsingError('Bare CR or LF found in header line "%s"' % tostr(line))
+ raise ParsingError(
+ 'Bare CR or LF found in header line "%s"' % str(line, "latin-1")
+ )
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))
+ raise ParsingError('Malformed header line "%s"' % str(line, "latin-1"))
r[-1] += line
else:
r.append(line)
+
return r
@@ -391,6 +412,7 @@ first_line_re = re.compile(
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)
@@ -407,9 +429,11 @@ def crack_first_line(line):
# unsuspecting souls from sending lowercase HTTP methods to waitress
# and having the request complete, while servers like nginx drop the
# request onto the floor.
+
if method != method.upper():
- raise ParsingError('Malformed HTTP method "%s"' % tostr(method))
+ raise ParsingError('Malformed HTTP method "%s"' % str(method, "latin-1"))
uri = m.group(2)
+
return method, uri, version
else:
return b"", b"", b""
diff --git a/src/waitress/rfc7230.py b/src/waitress/rfc7230.py
index cd33c90..9b25fbd 100644
--- a/src/waitress/rfc7230.py
+++ b/src/waitress/rfc7230.py
@@ -5,8 +5,6 @@ needed to properly parse HTTP messages.
import re
-from .compat import tobytes
-
WS = "[ \t]"
OWS = WS + "{0,}?"
RWS = WS + "{1,}?"
@@ -46,7 +44,7 @@ FIELD_CONTENT = FIELD_VCHAR + "+(?:[ \t]+" + FIELD_VCHAR + "+)*"
FIELD_VALUE = "(?:" + FIELD_CONTENT + ")?"
HEADER_FIELD = re.compile(
- tobytes(
+ (
"^(?P<name>" + TOKEN + "):" + OWS + "(?P<value>" + FIELD_VALUE + ")" + OWS + "$"
- )
+ ).encode("latin-1")
)
diff --git a/src/waitress/task.py b/src/waitress/task.py
index b82109f..779f01e 100644
--- a/src/waitress/task.py
+++ b/src/waitress/task.py
@@ -19,7 +19,6 @@ import threading
import time
from .buffers import ReadOnlyFileBasedBuffer
-from .compat import tobytes
from .utilities import build_http_date, logger, queue_logger
rename_headers = { # or keep them without the HTTP_ prefix added
@@ -281,7 +280,7 @@ class Task:
lines = [first_line] + next_lines
res = "%s\r\n\r\n" % "\r\n".join(lines)
- return tobytes(res)
+ return res.encode("latin-1")
def remove_content_length_header(self):
response_headers = []
@@ -317,7 +316,7 @@ class Task:
cl = self.content_length
if self.chunked_response:
# use chunked encoding response
- towrite = tobytes(hex(len(data))[2:].upper()) + b"\r\n"
+ towrite = hex(len(data))[2:].upper().encode("latin-1") + b"\r\n"
towrite += data + b"\r\n"
elif cl is not None:
towrite = data[: cl - self.content_bytes_written]
@@ -361,7 +360,7 @@ class ErrorTask(Task):
self.response_headers.append(("Connection", "close"))
self.close_on_finish = True
self.content_length = len(body)
- self.write(tobytes(body))
+ self.write(body.encode("latin-1"))
class WSGITask(Task):