summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/h11
diff options
context:
space:
mode:
authorcyfraeviolae <cyfraeviolae>2024-04-03 03:17:55 -0400
committercyfraeviolae <cyfraeviolae>2024-04-03 03:17:55 -0400
commit12cf076118570eebbff08c6b3090e0d4798447a1 (patch)
tree3ba25e17e3c3a5e82316558ba3864b955919ff72 /venv/lib/python3.11/site-packages/h11
parentc45662ff3923b34614ddcc8feb9195541166dcc5 (diff)
no venv
Diffstat (limited to 'venv/lib/python3.11/site-packages/h11')
-rw-r--r--venv/lib/python3.11/site-packages/h11/__init__.py62
-rw-r--r--venv/lib/python3.11/site-packages/h11/__pycache__/__init__.cpython-311.pycbin1320 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/__pycache__/_abnf.cpython-311.pycbin1825 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/__pycache__/_connection.cpython-311.pycbin24311 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/__pycache__/_events.cpython-311.pycbin15199 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/__pycache__/_headers.cpython-311.pycbin9116 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/__pycache__/_readers.cpython-311.pycbin10793 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/__pycache__/_receivebuffer.cpython-311.pycbin5152 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/__pycache__/_state.cpython-311.pycbin9788 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/__pycache__/_util.cpython-311.pycbin5373 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/__pycache__/_version.cpython-311.pycbin210 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/__pycache__/_writers.cpython-311.pycbin7310 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/_abnf.py132
-rw-r--r--venv/lib/python3.11/site-packages/h11/_connection.py633
-rw-r--r--venv/lib/python3.11/site-packages/h11/_events.py369
-rw-r--r--venv/lib/python3.11/site-packages/h11/_headers.py278
-rw-r--r--venv/lib/python3.11/site-packages/h11/_readers.py247
-rw-r--r--venv/lib/python3.11/site-packages/h11/_receivebuffer.py153
-rw-r--r--venv/lib/python3.11/site-packages/h11/_state.py367
-rw-r--r--venv/lib/python3.11/site-packages/h11/_util.py135
-rw-r--r--venv/lib/python3.11/site-packages/h11/_version.py16
-rw-r--r--venv/lib/python3.11/site-packages/h11/_writers.py145
-rw-r--r--venv/lib/python3.11/site-packages/h11/py.typed1
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/__init__.py0
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/__pycache__/__init__.cpython-311.pycbin193 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/__pycache__/helpers.cpython-311.pycbin5113 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_against_stdlib_http.cpython-311.pycbin8277 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_connection.cpython-311.pycbin68029 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_events.cpython-311.pycbin6829 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_headers.cpython-311.pycbin9606 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_helpers.cpython-311.pycbin1529 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_io.cpython-311.pycbin25542 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_receivebuffer.cpython-311.pycbin4608 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_state.cpython-311.pycbin15210 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_util.cpython-311.pycbin7816 -> 0 bytes
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/data/test-file1
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/helpers.py101
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/test_against_stdlib_http.py115
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/test_connection.py1122
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/test_events.py150
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/test_headers.py157
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/test_helpers.py32
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/test_io.py572
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/test_receivebuffer.py135
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/test_state.py271
-rw-r--r--venv/lib/python3.11/site-packages/h11/tests/test_util.py112
46 files changed, 0 insertions, 5306 deletions
diff --git a/venv/lib/python3.11/site-packages/h11/__init__.py b/venv/lib/python3.11/site-packages/h11/__init__.py
deleted file mode 100644
index 989e92c..0000000
--- a/venv/lib/python3.11/site-packages/h11/__init__.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# A highish-level implementation of the HTTP/1.1 wire protocol (RFC 7230),
-# containing no networking code at all, loosely modelled on hyper-h2's generic
-# implementation of HTTP/2 (and in particular the h2.connection.H2Connection
-# class). There's still a bunch of subtle details you need to get right if you
-# want to make this actually useful, because it doesn't implement all the
-# semantics to check that what you're asking to write to the wire is sensible,
-# but at least it gets you out of dealing with the wire itself.
-
-from h11._connection import Connection, NEED_DATA, PAUSED
-from h11._events import (
- ConnectionClosed,
- Data,
- EndOfMessage,
- Event,
- InformationalResponse,
- Request,
- Response,
-)
-from h11._state import (
- CLIENT,
- CLOSED,
- DONE,
- ERROR,
- IDLE,
- MIGHT_SWITCH_PROTOCOL,
- MUST_CLOSE,
- SEND_BODY,
- SEND_RESPONSE,
- SERVER,
- SWITCHED_PROTOCOL,
-)
-from h11._util import LocalProtocolError, ProtocolError, RemoteProtocolError
-from h11._version import __version__
-
-PRODUCT_ID = "python-h11/" + __version__
-
-
-__all__ = (
- "Connection",
- "NEED_DATA",
- "PAUSED",
- "ConnectionClosed",
- "Data",
- "EndOfMessage",
- "Event",
- "InformationalResponse",
- "Request",
- "Response",
- "CLIENT",
- "CLOSED",
- "DONE",
- "ERROR",
- "IDLE",
- "MUST_CLOSE",
- "SEND_BODY",
- "SEND_RESPONSE",
- "SERVER",
- "SWITCHED_PROTOCOL",
- "ProtocolError",
- "LocalProtocolError",
- "RemoteProtocolError",
-)
diff --git a/venv/lib/python3.11/site-packages/h11/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/__pycache__/__init__.cpython-311.pyc
deleted file mode 100644
index 76942e2..0000000
--- a/venv/lib/python3.11/site-packages/h11/__pycache__/__init__.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/__pycache__/_abnf.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/__pycache__/_abnf.cpython-311.pyc
deleted file mode 100644
index 53e94a7..0000000
--- a/venv/lib/python3.11/site-packages/h11/__pycache__/_abnf.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/__pycache__/_connection.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/__pycache__/_connection.cpython-311.pyc
deleted file mode 100644
index 7cca1ce..0000000
--- a/venv/lib/python3.11/site-packages/h11/__pycache__/_connection.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/__pycache__/_events.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/__pycache__/_events.cpython-311.pyc
deleted file mode 100644
index 9ae2694..0000000
--- a/venv/lib/python3.11/site-packages/h11/__pycache__/_events.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/__pycache__/_headers.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/__pycache__/_headers.cpython-311.pyc
deleted file mode 100644
index 67a3984..0000000
--- a/venv/lib/python3.11/site-packages/h11/__pycache__/_headers.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/__pycache__/_readers.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/__pycache__/_readers.cpython-311.pyc
deleted file mode 100644
index 387c362..0000000
--- a/venv/lib/python3.11/site-packages/h11/__pycache__/_readers.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/__pycache__/_receivebuffer.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/__pycache__/_receivebuffer.cpython-311.pyc
deleted file mode 100644
index 83b85cb..0000000
--- a/venv/lib/python3.11/site-packages/h11/__pycache__/_receivebuffer.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/__pycache__/_state.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/__pycache__/_state.cpython-311.pyc
deleted file mode 100644
index eaa2960..0000000
--- a/venv/lib/python3.11/site-packages/h11/__pycache__/_state.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/__pycache__/_util.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/__pycache__/_util.cpython-311.pyc
deleted file mode 100644
index 73e2a9c..0000000
--- a/venv/lib/python3.11/site-packages/h11/__pycache__/_util.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/__pycache__/_version.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/__pycache__/_version.cpython-311.pyc
deleted file mode 100644
index 1ae2072..0000000
--- a/venv/lib/python3.11/site-packages/h11/__pycache__/_version.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/__pycache__/_writers.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/__pycache__/_writers.cpython-311.pyc
deleted file mode 100644
index ea23e1a..0000000
--- a/venv/lib/python3.11/site-packages/h11/__pycache__/_writers.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/_abnf.py b/venv/lib/python3.11/site-packages/h11/_abnf.py
deleted file mode 100644
index 933587f..0000000
--- a/venv/lib/python3.11/site-packages/h11/_abnf.py
+++ /dev/null
@@ -1,132 +0,0 @@
-# We use native strings for all the re patterns, to take advantage of string
-# formatting, and then convert to bytestrings when compiling the final re
-# objects.
-
-# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#whitespace
-# OWS = *( SP / HTAB )
-# ; optional whitespace
-OWS = r"[ \t]*"
-
-# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.token.separators
-# token = 1*tchar
-#
-# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
-# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
-# / DIGIT / ALPHA
-# ; any VCHAR, except delimiters
-token = r"[-!#$%&'*+.^_`|~0-9a-zA-Z]+"
-
-# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#header.fields
-# field-name = token
-field_name = token
-
-# The standard says:
-#
-# field-value = *( field-content / obs-fold )
-# field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
-# field-vchar = VCHAR / obs-text
-# obs-fold = CRLF 1*( SP / HTAB )
-# ; obsolete line folding
-# ; see Section 3.2.4
-#
-# https://tools.ietf.org/html/rfc5234#appendix-B.1
-#
-# VCHAR = %x21-7E
-# ; visible (printing) characters
-#
-# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.quoted-string
-# obs-text = %x80-FF
-#
-# However, the standard definition of field-content is WRONG! It disallows
-# fields containing a single visible character surrounded by whitespace,
-# e.g. "foo a bar".
-#
-# See: https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4189
-#
-# So our definition of field_content attempts to fix it up...
-#
-# Also, we allow lots of control characters, because apparently people assume
-# that they're legal in practice (e.g., google analytics makes cookies with
-# \x01 in them!):
-# https://github.com/python-hyper/h11/issues/57
-# We still don't allow NUL or whitespace, because those are often treated as
-# meta-characters and letting them through can lead to nasty issues like SSRF.
-vchar = r"[\x21-\x7e]"
-vchar_or_obs_text = r"[^\x00\s]"
-field_vchar = vchar_or_obs_text
-field_content = r"{field_vchar}+(?:[ \t]+{field_vchar}+)*".format(**globals())
-
-# We handle obs-fold at a different level, and our fixed-up field_content
-# already grows to swallow the whole value, so ? instead of *
-field_value = r"({field_content})?".format(**globals())
-
-# header-field = field-name ":" OWS field-value OWS
-header_field = (
- r"(?P<field_name>{field_name})"
- r":"
- r"{OWS}"
- r"(?P<field_value>{field_value})"
- r"{OWS}".format(**globals())
-)
-
-# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#request.line
-#
-# request-line = method SP request-target SP HTTP-version CRLF
-# method = token
-# HTTP-version = HTTP-name "/" DIGIT "." DIGIT
-# HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive
-#
-# request-target is complicated (see RFC 7230 sec 5.3) -- could be path, full
-# URL, host+port (for connect), or even "*", but in any case we are guaranteed
-# that it contists of the visible printing characters.
-method = token
-request_target = r"{vchar}+".format(**globals())
-http_version = r"HTTP/(?P<http_version>[0-9]\.[0-9])"
-request_line = (
- r"(?P<method>{method})"
- r" "
- r"(?P<target>{request_target})"
- r" "
- r"{http_version}".format(**globals())
-)
-
-# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#status.line
-#
-# status-line = HTTP-version SP status-code SP reason-phrase CRLF
-# status-code = 3DIGIT
-# reason-phrase = *( HTAB / SP / VCHAR / obs-text )
-status_code = r"[0-9]{3}"
-reason_phrase = r"([ \t]|{vchar_or_obs_text})*".format(**globals())
-status_line = (
- r"{http_version}"
- r" "
- r"(?P<status_code>{status_code})"
- # However, there are apparently a few too many servers out there that just
- # leave out the reason phrase:
- # https://github.com/scrapy/scrapy/issues/345#issuecomment-281756036
- # https://github.com/seanmonstar/httparse/issues/29
- # so make it optional. ?: is a non-capturing group.
- r"(?: (?P<reason>{reason_phrase}))?".format(**globals())
-)
-
-HEXDIG = r"[0-9A-Fa-f]"
-# Actually
-#
-# chunk-size = 1*HEXDIG
-#
-# but we impose an upper-limit to avoid ridiculosity. len(str(2**64)) == 20
-chunk_size = r"({HEXDIG}){{1,20}}".format(**globals())
-# Actually
-#
-# chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
-#
-# but we aren't parsing the things so we don't really care.
-chunk_ext = r";.*"
-chunk_header = (
- r"(?P<chunk_size>{chunk_size})"
- r"(?P<chunk_ext>{chunk_ext})?"
- r"{OWS}\r\n".format(
- **globals()
- ) # Even though the specification does not allow for extra whitespaces,
- # we are lenient with trailing whitespaces because some servers on the wild use it.
-)
diff --git a/venv/lib/python3.11/site-packages/h11/_connection.py b/venv/lib/python3.11/site-packages/h11/_connection.py
deleted file mode 100644
index d175270..0000000
--- a/venv/lib/python3.11/site-packages/h11/_connection.py
+++ /dev/null
@@ -1,633 +0,0 @@
-# This contains the main Connection class. Everything in h11 revolves around
-# this.
-from typing import Any, Callable, cast, Dict, List, Optional, Tuple, Type, Union
-
-from ._events import (
- ConnectionClosed,
- Data,
- EndOfMessage,
- Event,
- InformationalResponse,
- Request,
- Response,
-)
-from ._headers import get_comma_header, has_expect_100_continue, set_comma_header
-from ._readers import READERS, ReadersType
-from ._receivebuffer import ReceiveBuffer
-from ._state import (
- _SWITCH_CONNECT,
- _SWITCH_UPGRADE,
- CLIENT,
- ConnectionState,
- DONE,
- ERROR,
- MIGHT_SWITCH_PROTOCOL,
- SEND_BODY,
- SERVER,
- SWITCHED_PROTOCOL,
-)
-from ._util import ( # Import the internal things we need
- LocalProtocolError,
- RemoteProtocolError,
- Sentinel,
-)
-from ._writers import WRITERS, WritersType
-
-# Everything in __all__ gets re-exported as part of the h11 public API.
-__all__ = ["Connection", "NEED_DATA", "PAUSED"]
-
-
-class NEED_DATA(Sentinel, metaclass=Sentinel):
- pass
-
-
-class PAUSED(Sentinel, metaclass=Sentinel):
- pass
-
-
-# If we ever have this much buffered without it making a complete parseable
-# event, we error out. The only time we really buffer is when reading the
-# request/response line + headers together, so this is effectively the limit on
-# the size of that.
-#
-# Some precedents for defaults:
-# - node.js: 80 * 1024
-# - tomcat: 8 * 1024
-# - IIS: 16 * 1024
-# - Apache: <8 KiB per line>
-DEFAULT_MAX_INCOMPLETE_EVENT_SIZE = 16 * 1024
-
-# RFC 7230's rules for connection lifecycles:
-# - If either side says they want to close the connection, then the connection
-# must close.
-# - HTTP/1.1 defaults to keep-alive unless someone says Connection: close
-# - HTTP/1.0 defaults to close unless both sides say Connection: keep-alive
-# (and even this is a mess -- e.g. if you're implementing a proxy then
-# sending Connection: keep-alive is forbidden).
-#
-# We simplify life by simply not supporting keep-alive with HTTP/1.0 peers. So
-# our rule is:
-# - If someone says Connection: close, we will close
-# - If someone uses HTTP/1.0, we will close.
-def _keep_alive(event: Union[Request, Response]) -> bool:
- connection = get_comma_header(event.headers, b"connection")
- if b"close" in connection:
- return False
- if getattr(event, "http_version", b"1.1") < b"1.1":
- return False
- return True
-
-
-def _body_framing(
- request_method: bytes, event: Union[Request, Response]
-) -> Tuple[str, Union[Tuple[()], Tuple[int]]]:
- # Called when we enter SEND_BODY to figure out framing information for
- # this body.
- #
- # These are the only two events that can trigger a SEND_BODY state:
- assert type(event) in (Request, Response)
- # Returns one of:
- #
- # ("content-length", count)
- # ("chunked", ())
- # ("http/1.0", ())
- #
- # which are (lookup key, *args) for constructing body reader/writer
- # objects.
- #
- # Reference: https://tools.ietf.org/html/rfc7230#section-3.3.3
- #
- # Step 1: some responses always have an empty body, regardless of what the
- # headers say.
- if type(event) is Response:
- if (
- event.status_code in (204, 304)
- or request_method == b"HEAD"
- or (request_method == b"CONNECT" and 200 <= event.status_code < 300)
- ):
- return ("content-length", (0,))
- # Section 3.3.3 also lists another case -- responses with status_code
- # < 200. For us these are InformationalResponses, not Responses, so
- # they can't get into this function in the first place.
- assert event.status_code >= 200
-
- # Step 2: check for Transfer-Encoding (T-E beats C-L):
- transfer_encodings = get_comma_header(event.headers, b"transfer-encoding")
- if transfer_encodings:
- assert transfer_encodings == [b"chunked"]
- return ("chunked", ())
-
- # Step 3: check for Content-Length
- content_lengths = get_comma_header(event.headers, b"content-length")
- if content_lengths:
- return ("content-length", (int(content_lengths[0]),))
-
- # Step 4: no applicable headers; fallback/default depends on type
- if type(event) is Request:
- return ("content-length", (0,))
- else:
- return ("http/1.0", ())
-
-
-################################################################
-#
-# The main Connection class
-#
-################################################################
-
-
-class Connection:
- """An object encapsulating the state of an HTTP connection.
-
- Args:
- our_role: If you're implementing a client, pass :data:`h11.CLIENT`. If
- you're implementing a server, pass :data:`h11.SERVER`.
-
- max_incomplete_event_size (int):
- The maximum number of bytes we're willing to buffer of an
- incomplete event. In practice this mostly sets a limit on the
- maximum size of the request/response line + headers. If this is
- exceeded, then :meth:`next_event` will raise
- :exc:`RemoteProtocolError`.
-
- """
-
- def __init__(
- self,
- our_role: Type[Sentinel],
- max_incomplete_event_size: int = DEFAULT_MAX_INCOMPLETE_EVENT_SIZE,
- ) -> None:
- self._max_incomplete_event_size = max_incomplete_event_size
- # State and role tracking
- if our_role not in (CLIENT, SERVER):
- raise ValueError("expected CLIENT or SERVER, not {!r}".format(our_role))
- self.our_role = our_role
- self.their_role: Type[Sentinel]
- if our_role is CLIENT:
- self.their_role = SERVER
- else:
- self.their_role = CLIENT
- self._cstate = ConnectionState()
-
- # Callables for converting data->events or vice-versa given the
- # current state
- self._writer = self._get_io_object(self.our_role, None, WRITERS)
- self._reader = self._get_io_object(self.their_role, None, READERS)
-
- # Holds any unprocessed received data
- self._receive_buffer = ReceiveBuffer()
- # If this is true, then it indicates that the incoming connection was
- # closed *after* the end of whatever's in self._receive_buffer:
- self._receive_buffer_closed = False
-
- # Extra bits of state that don't fit into the state machine.
- #
- # These two are only used to interpret framing headers for figuring
- # out how to read/write response bodies. their_http_version is also
- # made available as a convenient public API.
- self.their_http_version: Optional[bytes] = None
- self._request_method: Optional[bytes] = None
- # This is pure flow-control and doesn't at all affect the set of legal
- # transitions, so no need to bother ConnectionState with it:
- self.client_is_waiting_for_100_continue = False
-
- @property
- def states(self) -> Dict[Type[Sentinel], Type[Sentinel]]:
- """A dictionary like::
-
- {CLIENT: <client state>, SERVER: <server state>}
-
- See :ref:`state-machine` for details.
-
- """
- return dict(self._cstate.states)
-
- @property
- def our_state(self) -> Type[Sentinel]:
- """The current state of whichever role we are playing. See
- :ref:`state-machine` for details.
- """
- return self._cstate.states[self.our_role]
-
- @property
- def their_state(self) -> Type[Sentinel]:
- """The current state of whichever role we are NOT playing. See
- :ref:`state-machine` for details.
- """
- return self._cstate.states[self.their_role]
-
- @property
- def they_are_waiting_for_100_continue(self) -> bool:
- return self.their_role is CLIENT and self.client_is_waiting_for_100_continue
-
- def start_next_cycle(self) -> None:
- """Attempt to reset our connection state for a new request/response
- cycle.
-
- If both client and server are in :data:`DONE` state, then resets them
- both to :data:`IDLE` state in preparation for a new request/response
- cycle on this same connection. Otherwise, raises a
- :exc:`LocalProtocolError`.
-
- See :ref:`keepalive-and-pipelining`.
-
- """
- old_states = dict(self._cstate.states)
- self._cstate.start_next_cycle()
- self._request_method = None
- # self.their_http_version gets left alone, since it presumably lasts
- # beyond a single request/response cycle
- assert not self.client_is_waiting_for_100_continue
- self._respond_to_state_changes(old_states)
-
- def _process_error(self, role: Type[Sentinel]) -> None:
- old_states = dict(self._cstate.states)
- self._cstate.process_error(role)
- self._respond_to_state_changes(old_states)
-
- def _server_switch_event(self, event: Event) -> Optional[Type[Sentinel]]:
- if type(event) is InformationalResponse and event.status_code == 101:
- return _SWITCH_UPGRADE
- if type(event) is Response:
- if (
- _SWITCH_CONNECT in self._cstate.pending_switch_proposals
- and 200 <= event.status_code < 300
- ):
- return _SWITCH_CONNECT
- return None
-
- # All events go through here
- def _process_event(self, role: Type[Sentinel], event: Event) -> None:
- # First, pass the event through the state machine to make sure it
- # succeeds.
- old_states = dict(self._cstate.states)
- if role is CLIENT and type(event) is Request:
- if event.method == b"CONNECT":
- self._cstate.process_client_switch_proposal(_SWITCH_CONNECT)
- if get_comma_header(event.headers, b"upgrade"):
- self._cstate.process_client_switch_proposal(_SWITCH_UPGRADE)
- server_switch_event = None
- if role is SERVER:
- server_switch_event = self._server_switch_event(event)
- self._cstate.process_event(role, type(event), server_switch_event)
-
- # Then perform the updates triggered by it.
-
- if type(event) is Request:
- self._request_method = event.method
-
- if role is self.their_role and type(event) in (
- Request,
- Response,
- InformationalResponse,
- ):
- event = cast(Union[Request, Response, InformationalResponse], event)
- self.their_http_version = event.http_version
-
- # Keep alive handling
- #
- # RFC 7230 doesn't really say what one should do if Connection: close
- # shows up on a 1xx InformationalResponse. I think the idea is that
- # this is not supposed to happen. In any case, if it does happen, we
- # ignore it.
- if type(event) in (Request, Response) and not _keep_alive(
- cast(Union[Request, Response], event)
- ):
- self._cstate.process_keep_alive_disabled()
-
- # 100-continue
- if type(event) is Request and has_expect_100_continue(event):
- self.client_is_waiting_for_100_continue = True
- if type(event) in (InformationalResponse, Response):
- self.client_is_waiting_for_100_continue = False
- if role is CLIENT and type(event) in (Data, EndOfMessage):
- self.client_is_waiting_for_100_continue = False
-
- self._respond_to_state_changes(old_states, event)
-
- def _get_io_object(
- self,
- role: Type[Sentinel],
- event: Optional[Event],
- io_dict: Union[ReadersType, WritersType],
- ) -> Optional[Callable[..., Any]]:
- # event may be None; it's only used when entering SEND_BODY
- state = self._cstate.states[role]
- if state is SEND_BODY:
- # Special case: the io_dict has a dict of reader/writer factories
- # that depend on the request/response framing.
- framing_type, args = _body_framing(
- cast(bytes, self._request_method), cast(Union[Request, Response], event)
- )
- return io_dict[SEND_BODY][framing_type](*args) # type: ignore[index]
- else:
- # General case: the io_dict just has the appropriate reader/writer
- # for this state
- return io_dict.get((role, state)) # type: ignore[return-value]
-
- # This must be called after any action that might have caused
- # self._cstate.states to change.
- def _respond_to_state_changes(
- self,
- old_states: Dict[Type[Sentinel], Type[Sentinel]],
- event: Optional[Event] = None,
- ) -> None:
- # Update reader/writer
- if self.our_state != old_states[self.our_role]:
- self._writer = self._get_io_object(self.our_role, event, WRITERS)
- if self.their_state != old_states[self.their_role]:
- self._reader = self._get_io_object(self.their_role, event, READERS)
-
- @property
- def trailing_data(self) -> Tuple[bytes, bool]:
- """Data that has been received, but not yet processed, represented as
- a tuple with two elements, where the first is a byte-string containing
- the unprocessed data itself, and the second is a bool that is True if
- the receive connection was closed.
-
- See :ref:`switching-protocols` for discussion of why you'd want this.
- """
- return (bytes(self._receive_buffer), self._receive_buffer_closed)
-
- def receive_data(self, data: bytes) -> None:
- """Add data to our internal receive buffer.
-
- This does not actually do any processing on the data, just stores
- it. To trigger processing, you have to call :meth:`next_event`.
-
- Args:
- data (:term:`bytes-like object`):
- The new data that was just received.
-
- Special case: If *data* is an empty byte-string like ``b""``,
- then this indicates that the remote side has closed the
- connection (end of file). Normally this is convenient, because
- standard Python APIs like :meth:`file.read` or
- :meth:`socket.recv` use ``b""`` to indicate end-of-file, while
- other failures to read are indicated using other mechanisms
- like raising :exc:`TimeoutError`. When using such an API you
- can just blindly pass through whatever you get from ``read``
- to :meth:`receive_data`, and everything will work.
-
- But, if you have an API where reading an empty string is a
- valid non-EOF condition, then you need to be aware of this and
- make sure to check for such strings and avoid passing them to
- :meth:`receive_data`.
-
- Returns:
- Nothing, but after calling this you should call :meth:`next_event`
- to parse the newly received data.
-
- Raises:
- RuntimeError:
- Raised if you pass an empty *data*, indicating EOF, and then
- pass a non-empty *data*, indicating more data that somehow
- arrived after the EOF.
-
- (Calling ``receive_data(b"")`` multiple times is fine,
- and equivalent to calling it once.)
-
- """
- if data:
- if self._receive_buffer_closed:
- raise RuntimeError("received close, then received more data?")
- self._receive_buffer += data
- else:
- self._receive_buffer_closed = True
-
- def _extract_next_receive_event(
- self,
- ) -> Union[Event, Type[NEED_DATA], Type[PAUSED]]:
- state = self.their_state
- # We don't pause immediately when they enter DONE, because even in
- # DONE state we can still process a ConnectionClosed() event. But
- # if we have data in our buffer, then we definitely aren't getting
- # a ConnectionClosed() immediately and we need to pause.
- if state is DONE and self._receive_buffer:
- return PAUSED
- if state is MIGHT_SWITCH_PROTOCOL or state is SWITCHED_PROTOCOL:
- return PAUSED
- assert self._reader is not None
- event = self._reader(self._receive_buffer)
- if event is None:
- if not self._receive_buffer and self._receive_buffer_closed:
- # In some unusual cases (basically just HTTP/1.0 bodies), EOF
- # triggers an actual protocol event; in that case, we want to
- # return that event, and then the state will change and we'll
- # get called again to generate the actual ConnectionClosed().
- if hasattr(self._reader, "read_eof"):
- event = self._reader.read_eof() # type: ignore[attr-defined]
- else:
- event = ConnectionClosed()
- if event is None:
- event = NEED_DATA
- return event # type: ignore[no-any-return]
-
- def next_event(self) -> Union[Event, Type[NEED_DATA], Type[PAUSED]]:
- """Parse the next event out of our receive buffer, update our internal
- state, and return it.
-
- This is a mutating operation -- think of it like calling :func:`next`
- on an iterator.
-
- Returns:
- : One of three things:
-
- 1) An event object -- see :ref:`events`.
-
- 2) The special constant :data:`NEED_DATA`, which indicates that
- you need to read more data from your socket and pass it to
- :meth:`receive_data` before this method will be able to return
- any more events.
-
- 3) The special constant :data:`PAUSED`, which indicates that we
- are not in a state where we can process incoming data (usually
- because the peer has finished their part of the current
- request/response cycle, and you have not yet called
- :meth:`start_next_cycle`). See :ref:`flow-control` for details.
-
- Raises:
- RemoteProtocolError:
- The peer has misbehaved. You should close the connection
- (possibly after sending some kind of 4xx response).
-
- Once this method returns :class:`ConnectionClosed` once, then all
- subsequent calls will also return :class:`ConnectionClosed`.
-
- If this method raises any exception besides :exc:`RemoteProtocolError`
- then that's a bug -- if it happens please file a bug report!
-
- If this method raises any exception then it also sets
- :attr:`Connection.their_state` to :data:`ERROR` -- see
- :ref:`error-handling` for discussion.
-
- """
-
- if self.their_state is ERROR:
- raise RemoteProtocolError("Can't receive data when peer state is ERROR")
- try:
- event = self._extract_next_receive_event()
- if event not in [NEED_DATA, PAUSED]:
- self._process_event(self.their_role, cast(Event, event))
- if event is NEED_DATA:
- if len(self._receive_buffer) > self._max_incomplete_event_size:
- # 431 is "Request header fields too large" which is pretty
- # much the only situation where we can get here
- raise RemoteProtocolError(
- "Receive buffer too long", error_status_hint=431
- )
- if self._receive_buffer_closed:
- # We're still trying to complete some event, but that's
- # never going to happen because no more data is coming
- raise RemoteProtocolError("peer unexpectedly closed connection")
- return event
- except BaseException as exc:
- self._process_error(self.their_role)
- if isinstance(exc, LocalProtocolError):
- exc._reraise_as_remote_protocol_error()
- else:
- raise
-
- def send(self, event: Event) -> Optional[bytes]:
- """Convert a high-level event into bytes that can be sent to the peer,
- while updating our internal state machine.
-
- Args:
- event: The :ref:`event <events>` to send.
-
- Returns:
- If ``type(event) is ConnectionClosed``, then returns
- ``None``. Otherwise, returns a :term:`bytes-like object`.
-
- Raises:
- LocalProtocolError:
- Sending this event at this time would violate our
- understanding of the HTTP/1.1 protocol.
-
- If this method raises any exception then it also sets
- :attr:`Connection.our_state` to :data:`ERROR` -- see
- :ref:`error-handling` for discussion.
-
- """
- data_list = self.send_with_data_passthrough(event)
- if data_list is None:
- return None
- else:
- return b"".join(data_list)
-
- def send_with_data_passthrough(self, event: Event) -> Optional[List[bytes]]:
- """Identical to :meth:`send`, except that in situations where
- :meth:`send` returns a single :term:`bytes-like object`, this instead
- returns a list of them -- and when sending a :class:`Data` event, this
- list is guaranteed to contain the exact object you passed in as
- :attr:`Data.data`. See :ref:`sendfile` for discussion.
-
- """
- if self.our_state is ERROR:
- raise LocalProtocolError("Can't send data when our state is ERROR")
- try:
- if type(event) is Response:
- event = self._clean_up_response_headers_for_sending(event)
- # We want to call _process_event before calling the writer,
- # because if someone tries to do something invalid then this will
- # give a sensible error message, while our writers all just assume
- # they will only receive valid events. But, _process_event might
- # change self._writer. So we have to do a little dance:
- writer = self._writer
- self._process_event(self.our_role, event)
- if type(event) is ConnectionClosed:
- return None
- else:
- # In any situation where writer is None, process_event should
- # have raised ProtocolError
- assert writer is not None
- data_list: List[bytes] = []
- writer(event, data_list.append)
- return data_list
- except:
- self._process_error(self.our_role)
- raise
-
- def send_failed(self) -> None:
- """Notify the state machine that we failed to send the data it gave
- us.
-
- This causes :attr:`Connection.our_state` to immediately become
- :data:`ERROR` -- see :ref:`error-handling` for discussion.
-
- """
- self._process_error(self.our_role)
-
- # When sending a Response, we take responsibility for a few things:
- #
- # - Sometimes you MUST set Connection: close. We take care of those
- # times. (You can also set it yourself if you want, and if you do then
- # we'll respect that and close the connection at the right time. But you
- # don't have to worry about that unless you want to.)
- #
- # - The user has to set Content-Length if they want it. Otherwise, for
- # responses that have bodies (e.g. not HEAD), then we will automatically
- # select the right mechanism for streaming a body of unknown length,
- # which depends on depending on the peer's HTTP version.
- #
- # This function's *only* responsibility is making sure headers are set up
- # right -- everything downstream just looks at the headers. There are no
- # side channels.
- def _clean_up_response_headers_for_sending(self, response: Response) -> Response:
- assert type(response) is Response
-
- headers = response.headers
- need_close = False
-
- # HEAD requests need some special handling: they always act like they
- # have Content-Length: 0, and that's how _body_framing treats
- # them. But their headers are supposed to match what we would send if
- # the request was a GET. (Technically there is one deviation allowed:
- # we're allowed to leave out the framing headers -- see
- # https://tools.ietf.org/html/rfc7231#section-4.3.2 . But it's just as
- # easy to get them right.)
- method_for_choosing_headers = cast(bytes, self._request_method)
- if method_for_choosing_headers == b"HEAD":
- method_for_choosing_headers = b"GET"
- framing_type, _ = _body_framing(method_for_choosing_headers, response)
- if framing_type in ("chunked", "http/1.0"):
- # This response has a body of unknown length.
- # If our peer is HTTP/1.1, we use Transfer-Encoding: chunked
- # If our peer is HTTP/1.0, we use no framing headers, and close the
- # connection afterwards.
- #
- # Make sure to clear Content-Length (in principle user could have
- # set both and then we ignored Content-Length b/c
- # Transfer-Encoding overwrote it -- this would be naughty of them,
- # but the HTTP spec says that if our peer does this then we have
- # to fix it instead of erroring out, so we'll accord the user the
- # same respect).
- headers = set_comma_header(headers, b"content-length", [])
- if self.their_http_version is None or self.their_http_version < b"1.1":
- # Either we never got a valid request and are sending back an
- # error (their_http_version is None), so we assume the worst;
- # or else we did get a valid HTTP/1.0 request, so we know that
- # they don't understand chunked encoding.
- headers = set_comma_header(headers, b"transfer-encoding", [])
- # This is actually redundant ATM, since currently we
- # unconditionally disable keep-alive when talking to HTTP/1.0
- # peers. But let's be defensive just in case we add
- # Connection: keep-alive support later:
- if self._request_method != b"HEAD":
- need_close = True
- else:
- headers = set_comma_header(headers, b"transfer-encoding", [b"chunked"])
-
- if not self._cstate.keep_alive or need_close:
- # Make sure Connection: close is set
- connection = set(get_comma_header(headers, b"connection"))
- connection.discard(b"keep-alive")
- connection.add(b"close")
- headers = set_comma_header(headers, b"connection", sorted(connection))
-
- return Response(
- headers=headers,
- status_code=response.status_code,
- http_version=response.http_version,
- reason=response.reason,
- )
diff --git a/venv/lib/python3.11/site-packages/h11/_events.py b/venv/lib/python3.11/site-packages/h11/_events.py
deleted file mode 100644
index 075bf8a..0000000
--- a/venv/lib/python3.11/site-packages/h11/_events.py
+++ /dev/null
@@ -1,369 +0,0 @@
-# High level events that make up HTTP/1.1 conversations. Loosely inspired by
-# the corresponding events in hyper-h2:
-#
-# http://python-hyper.org/h2/en/stable/api.html#events
-#
-# Don't subclass these. Stuff will break.
-
-import re
-from abc import ABC
-from dataclasses import dataclass, field
-from typing import Any, cast, Dict, List, Tuple, Union
-
-from ._abnf import method, request_target
-from ._headers import Headers, normalize_and_validate
-from ._util import bytesify, LocalProtocolError, validate
-
-# Everything in __all__ gets re-exported as part of the h11 public API.
-__all__ = [
- "Event",
- "Request",
- "InformationalResponse",
- "Response",
- "Data",
- "EndOfMessage",
- "ConnectionClosed",
-]
-
-method_re = re.compile(method.encode("ascii"))
-request_target_re = re.compile(request_target.encode("ascii"))
-
-
-class Event(ABC):
- """
- Base class for h11 events.
- """
-
- __slots__ = ()
-
-
-@dataclass(init=False, frozen=True)
-class Request(Event):
- """The beginning of an HTTP request.
-
- Fields:
-
- .. attribute:: method
-
- An HTTP method, e.g. ``b"GET"`` or ``b"POST"``. Always a byte
- string. :term:`Bytes-like objects <bytes-like object>` and native
- strings containing only ascii characters will be automatically
- converted to byte strings.
-
- .. attribute:: target
-
- The target of an HTTP request, e.g. ``b"/index.html"``, or one of the
- more exotic formats described in `RFC 7320, section 5.3
- <https://tools.ietf.org/html/rfc7230#section-5.3>`_. Always a byte
- string. :term:`Bytes-like objects <bytes-like object>` and native
- strings containing only ascii characters will be automatically
- converted to byte strings.
-
- .. attribute:: headers
-
- Request headers, represented as a list of (name, value) pairs. See
- :ref:`the header normalization rules <headers-format>` for details.
-
- .. attribute:: http_version
-
- The HTTP protocol version, represented as a byte string like
- ``b"1.1"``. See :ref:`the HTTP version normalization rules
- <http_version-format>` for details.
-
- """
-
- __slots__ = ("method", "headers", "target", "http_version")
-
- method: bytes
- headers: Headers
- target: bytes
- http_version: bytes
-
- def __init__(
- self,
- *,
- method: Union[bytes, str],
- headers: Union[Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]]],
- target: Union[bytes, str],
- http_version: Union[bytes, str] = b"1.1",
- _parsed: bool = False,
- ) -> None:
- super().__init__()
- if isinstance(headers, Headers):
- object.__setattr__(self, "headers", headers)
- else:
- object.__setattr__(
- self, "headers", normalize_and_validate(headers, _parsed=_parsed)
- )
- if not _parsed:
- object.__setattr__(self, "method", bytesify(method))
- object.__setattr__(self, "target", bytesify(target))
- object.__setattr__(self, "http_version", bytesify(http_version))
- else:
- object.__setattr__(self, "method", method)
- object.__setattr__(self, "target", target)
- object.__setattr__(self, "http_version", http_version)
-
- # "A server MUST respond with a 400 (Bad Request) status code to any
- # HTTP/1.1 request message that lacks a Host header field and to any
- # request message that contains more than one Host header field or a
- # Host header field with an invalid field-value."
- # -- https://tools.ietf.org/html/rfc7230#section-5.4
- host_count = 0
- for name, value in self.headers:
- if name == b"host":
- host_count += 1
- if self.http_version == b"1.1" and host_count == 0:
- raise LocalProtocolError("Missing mandatory Host: header")
- if host_count > 1:
- raise LocalProtocolError("Found multiple Host: headers")
-
- validate(method_re, self.method, "Illegal method characters")
- validate(request_target_re, self.target, "Illegal target characters")
-
- # This is an unhashable type.
- __hash__ = None # type: ignore
-
-
-@dataclass(init=False, frozen=True)
-class _ResponseBase(Event):
- __slots__ = ("headers", "http_version", "reason", "status_code")
-
- headers: Headers
- http_version: bytes
- reason: bytes
- status_code: int
-
- def __init__(
- self,
- *,
- headers: Union[Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]]],
- status_code: int,
- http_version: Union[bytes, str] = b"1.1",
- reason: Union[bytes, str] = b"",
- _parsed: bool = False,
- ) -> None:
- super().__init__()
- if isinstance(headers, Headers):
- object.__setattr__(self, "headers", headers)
- else:
- object.__setattr__(
- self, "headers", normalize_and_validate(headers, _parsed=_parsed)
- )
- if not _parsed:
- object.__setattr__(self, "reason", bytesify(reason))
- object.__setattr__(self, "http_version", bytesify(http_version))
- if not isinstance(status_code, int):
- raise LocalProtocolError("status code must be integer")
- # Because IntEnum objects are instances of int, but aren't
- # duck-compatible (sigh), see gh-72.
- object.__setattr__(self, "status_code", int(status_code))
- else:
- object.__setattr__(self, "reason", reason)
- object.__setattr__(self, "http_version", http_version)
- object.__setattr__(self, "status_code", status_code)
-
- self.__post_init__()
-
- def __post_init__(self) -> None:
- pass
-
- # This is an unhashable type.
- __hash__ = None # type: ignore
-
-
-@dataclass(init=False, frozen=True)
-class InformationalResponse(_ResponseBase):
- """An HTTP informational response.
-
- Fields:
-
- .. attribute:: status_code
-
- The status code of this response, as an integer. For an
- :class:`InformationalResponse`, this is always in the range [100,
- 200).
-
- .. attribute:: headers
-
- Request headers, represented as a list of (name, value) pairs. See
- :ref:`the header normalization rules <headers-format>` for
- details.
-
- .. attribute:: http_version
-
- The HTTP protocol version, represented as a byte string like
- ``b"1.1"``. See :ref:`the HTTP version normalization rules
- <http_version-format>` for details.
-
- .. attribute:: reason
-
- The reason phrase of this response, as a byte string. For example:
- ``b"OK"``, or ``b"Not Found"``.
-
- """
-
- def __post_init__(self) -> None:
- if not (100 <= self.status_code < 200):
- raise LocalProtocolError(
- "InformationalResponse status_code should be in range "
- "[100, 200), not {}".format(self.status_code)
- )
-
- # This is an unhashable type.
- __hash__ = None # type: ignore
-
-
-@dataclass(init=False, frozen=True)
-class Response(_ResponseBase):
- """The beginning of an HTTP response.
-
- Fields:
-
- .. attribute:: status_code
-
- The status code of this response, as an integer. For an
- :class:`Response`, this is always in the range [200,
- 1000).
-
- .. attribute:: headers
-
- Request headers, represented as a list of (name, value) pairs. See
- :ref:`the header normalization rules <headers-format>` for details.
-
- .. attribute:: http_version
-
- The HTTP protocol version, represented as a byte string like
- ``b"1.1"``. See :ref:`the HTTP version normalization rules
- <http_version-format>` for details.
-
- .. attribute:: reason
-
- The reason phrase of this response, as a byte string. For example:
- ``b"OK"``, or ``b"Not Found"``.
-
- """
-
- def __post_init__(self) -> None:
- if not (200 <= self.status_code < 1000):
- raise LocalProtocolError(
- "Response status_code should be in range [200, 1000), not {}".format(
- self.status_code
- )
- )
-
- # This is an unhashable type.
- __hash__ = None # type: ignore
-
-
-@dataclass(init=False, frozen=True)
-class Data(Event):
- """Part of an HTTP message body.
-
- Fields:
-
- .. attribute:: data
-
- A :term:`bytes-like object` containing part of a message body. Or, if
- using the ``combine=False`` argument to :meth:`Connection.send`, then
- any object that your socket writing code knows what to do with, and for
- which calling :func:`len` returns the number of bytes that will be
- written -- see :ref:`sendfile` for details.
-
- .. attribute:: chunk_start
-
- A marker that indicates whether this data object is from the start of a
- chunked transfer encoding chunk. This field is ignored when when a Data
- event is provided to :meth:`Connection.send`: it is only valid on
- events emitted from :meth:`Connection.next_event`. You probably
- shouldn't use this attribute at all; see
- :ref:`chunk-delimiters-are-bad` for details.
-
- .. attribute:: chunk_end
-
- A marker that indicates whether this data object is the last for a
- given chunked transfer encoding chunk. This field is ignored when when
- a Data event is provided to :meth:`Connection.send`: it is only valid
- on events emitted from :meth:`Connection.next_event`. You probably
- shouldn't use this attribute at all; see
- :ref:`chunk-delimiters-are-bad` for details.
-
- """
-
- __slots__ = ("data", "chunk_start", "chunk_end")
-
- data: bytes
- chunk_start: bool
- chunk_end: bool
-
- def __init__(
- self, data: bytes, chunk_start: bool = False, chunk_end: bool = False
- ) -> None:
- object.__setattr__(self, "data", data)
- object.__setattr__(self, "chunk_start", chunk_start)
- object.__setattr__(self, "chunk_end", chunk_end)
-
- # This is an unhashable type.
- __hash__ = None # type: ignore
-
-
-# XX FIXME: "A recipient MUST ignore (or consider as an error) any fields that
-# are forbidden to be sent in a trailer, since processing them as if they were
-# present in the header section might bypass external security filters."
-# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#chunked.trailer.part
-# Unfortunately, the list of forbidden fields is long and vague :-/
-@dataclass(init=False, frozen=True)
-class EndOfMessage(Event):
- """The end of an HTTP message.
-
- Fields:
-
- .. attribute:: headers
-
- Default value: ``[]``
-
- Any trailing headers attached to this message, represented as a list of
- (name, value) pairs. See :ref:`the header normalization rules
- <headers-format>` for details.
-
- Must be empty unless ``Transfer-Encoding: chunked`` is in use.
-
- """
-
- __slots__ = ("headers",)
-
- headers: Headers
-
- def __init__(
- self,
- *,
- headers: Union[
- Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]], None
- ] = None,
- _parsed: bool = False,
- ) -> None:
- super().__init__()
- if headers is None:
- headers = Headers([])
- elif not isinstance(headers, Headers):
- headers = normalize_and_validate(headers, _parsed=_parsed)
-
- object.__setattr__(self, "headers", headers)
-
- # This is an unhashable type.
- __hash__ = None # type: ignore
-
-
-@dataclass(frozen=True)
-class ConnectionClosed(Event):
- """This event indicates that the sender has closed their outgoing
- connection.
-
- Note that this does not necessarily mean that they can't *receive* further
- data, because TCP connections are composed to two one-way channels which
- can be closed independently. See :ref:`closing` for details.
-
- No fields.
- """
-
- pass
diff --git a/venv/lib/python3.11/site-packages/h11/_headers.py b/venv/lib/python3.11/site-packages/h11/_headers.py
deleted file mode 100644
index b97d020..0000000
--- a/venv/lib/python3.11/site-packages/h11/_headers.py
+++ /dev/null
@@ -1,278 +0,0 @@
-import re
-from typing import AnyStr, cast, List, overload, Sequence, Tuple, TYPE_CHECKING, Union
-
-from ._abnf import field_name, field_value
-from ._util import bytesify, LocalProtocolError, validate
-
-if TYPE_CHECKING:
- from ._events import Request
-
-try:
- from typing import Literal
-except ImportError:
- from typing_extensions import Literal # type: ignore
-
-
-# Facts
-# -----
-#
-# Headers are:
-# keys: case-insensitive ascii
-# values: mixture of ascii and raw bytes
-#
-# "Historically, HTTP has allowed field content with text in the ISO-8859-1
-# charset [ISO-8859-1], supporting other charsets only through use of
-# [RFC2047] encoding. In practice, most HTTP header field values use only a
-# subset of the US-ASCII charset [USASCII]. Newly defined header fields SHOULD
-# limit their field values to US-ASCII octets. A recipient SHOULD treat other
-# octets in field content (obs-text) as opaque data."
-# And it deprecates all non-ascii values
-#
-# Leading/trailing whitespace in header names is forbidden
-#
-# Values get leading/trailing whitespace stripped
-#
-# Content-Disposition actually needs to contain unicode semantically; to
-# accomplish this it has a terrifically weird way of encoding the filename
-# itself as ascii (and even this still has lots of cross-browser
-# incompatibilities)
-#
-# Order is important:
-# "a proxy MUST NOT change the order of these field values when forwarding a
-# message"
-# (and there are several headers where the order indicates a preference)
-#
-# Multiple occurences of the same header:
-# "A sender MUST NOT generate multiple header fields with the same field name
-# in a message unless either the entire field value for that header field is
-# defined as a comma-separated list [or the header is Set-Cookie which gets a
-# special exception]" - RFC 7230. (cookies are in RFC 6265)
-#
-# So every header aside from Set-Cookie can be merged by b", ".join if it
-# occurs repeatedly. But, of course, they can't necessarily be split by
-# .split(b","), because quoting.
-#
-# Given all this mess (case insensitive, duplicates allowed, order is
-# important, ...), there doesn't appear to be any standard way to handle
-# headers in Python -- they're almost like dicts, but... actually just
-# aren't. For now we punt and just use a super simple representation: headers
-# are a list of pairs
-#
-# [(name1, value1), (name2, value2), ...]
-#
-# where all entries are bytestrings, names are lowercase and have no
-# leading/trailing whitespace, and values are bytestrings with no
-# leading/trailing whitespace. Searching and updating are done via naive O(n)
-# methods.
-#
-# Maybe a dict-of-lists would be better?
-
-_content_length_re = re.compile(rb"[0-9]+")
-_field_name_re = re.compile(field_name.encode("ascii"))
-_field_value_re = re.compile(field_value.encode("ascii"))
-
-
-class Headers(Sequence[Tuple[bytes, bytes]]):
- """
- A list-like interface that allows iterating over headers as byte-pairs
- of (lowercased-name, value).
-
- Internally we actually store the representation as three-tuples,
- including both the raw original casing, in order to preserve casing
- over-the-wire, and the lowercased name, for case-insensitive comparisions.
-
- r = Request(
- method="GET",
- target="/",
- headers=[("Host", "example.org"), ("Connection", "keep-alive")],
- http_version="1.1",
- )
- assert r.headers == [
- (b"host", b"example.org"),
- (b"connection", b"keep-alive")
- ]
- assert r.headers.raw_items() == [
- (b"Host", b"example.org"),
- (b"Connection", b"keep-alive")
- ]
- """
-
- __slots__ = "_full_items"
-
- def __init__(self, full_items: List[Tuple[bytes, bytes, bytes]]) -> None:
- self._full_items = full_items
-
- def __bool__(self) -> bool:
- return bool(self._full_items)
-
- def __eq__(self, other: object) -> bool:
- return list(self) == list(other) # type: ignore
-
- def __len__(self) -> int:
- return len(self._full_items)
-
- def __repr__(self) -> str:
- return "<Headers(%s)>" % repr(list(self))
-
- def __getitem__(self, idx: int) -> Tuple[bytes, bytes]: # type: ignore[override]
- _, name, value = self._full_items[idx]
- return (name, value)
-
- def raw_items(self) -> List[Tuple[bytes, bytes]]:
- return [(raw_name, value) for raw_name, _, value in self._full_items]
-
-
-HeaderTypes = Union[
- List[Tuple[bytes, bytes]],
- List[Tuple[bytes, str]],
- List[Tuple[str, bytes]],
- List[Tuple[str, str]],
-]
-
-
-@overload
-def normalize_and_validate(headers: Headers, _parsed: Literal[True]) -> Headers:
- ...
-
-
-@overload
-def normalize_and_validate(headers: HeaderTypes, _parsed: Literal[False]) -> Headers:
- ...
-
-
-@overload
-def normalize_and_validate(
- headers: Union[Headers, HeaderTypes], _parsed: bool = False
-) -> Headers:
- ...
-
-
-def normalize_and_validate(
- headers: Union[Headers, HeaderTypes], _parsed: bool = False
-) -> Headers:
- new_headers = []
- seen_content_length = None
- saw_transfer_encoding = False
- for name, value in headers:
- # For headers coming out of the parser, we can safely skip some steps,
- # because it always returns bytes and has already run these regexes
- # over the data:
- if not _parsed:
- name = bytesify(name)
- value = bytesify(value)
- validate(_field_name_re, name, "Illegal header name {!r}", name)
- validate(_field_value_re, value, "Illegal header value {!r}", value)
- assert isinstance(name, bytes)
- assert isinstance(value, bytes)
-
- raw_name = name
- name = name.lower()
- if name == b"content-length":
- lengths = {length.strip() for length in value.split(b",")}
- if len(lengths) != 1:
- raise LocalProtocolError("conflicting Content-Length headers")
- value = lengths.pop()
- validate(_content_length_re, value, "bad Content-Length")
- if seen_content_length is None:
- seen_content_length = value
- new_headers.append((raw_name, name, value))
- elif seen_content_length != value:
- raise LocalProtocolError("conflicting Content-Length headers")
- elif name == b"transfer-encoding":
- # "A server that receives a request message with a transfer coding
- # it does not understand SHOULD respond with 501 (Not
- # Implemented)."
- # https://tools.ietf.org/html/rfc7230#section-3.3.1
- if saw_transfer_encoding:
- raise LocalProtocolError(
- "multiple Transfer-Encoding headers", error_status_hint=501
- )
- # "All transfer-coding names are case-insensitive"
- # -- https://tools.ietf.org/html/rfc7230#section-4
- value = value.lower()
- if value != b"chunked":
- raise LocalProtocolError(
- "Only Transfer-Encoding: chunked is supported",
- error_status_hint=501,
- )
- saw_transfer_encoding = True
- new_headers.append((raw_name, name, value))
- else:
- new_headers.append((raw_name, name, value))
- return Headers(new_headers)
-
-
-def get_comma_header(headers: Headers, name: bytes) -> List[bytes]:
- # Should only be used for headers whose value is a list of
- # comma-separated, case-insensitive values.
- #
- # The header name `name` is expected to be lower-case bytes.
- #
- # Connection: meets these criteria (including cast insensitivity).
- #
- # Content-Length: technically is just a single value (1*DIGIT), but the
- # standard makes reference to implementations that do multiple values, and
- # using this doesn't hurt. Ditto, case insensitivity doesn't things either
- # way.
- #
- # Transfer-Encoding: is more complex (allows for quoted strings), so
- # splitting on , is actually wrong. For example, this is legal:
- #
- # Transfer-Encoding: foo; options="1,2", chunked
- #
- # and should be parsed as
- #
- # foo; options="1,2"
- # chunked
- #
- # but this naive function will parse it as
- #
- # foo; options="1
- # 2"
- # chunked
- #
- # However, this is okay because the only thing we are going to do with
- # any Transfer-Encoding is reject ones that aren't just "chunked", so
- # both of these will be treated the same anyway.
- #
- # Expect: the only legal value is the literal string
- # "100-continue". Splitting on commas is harmless. Case insensitive.
- #
- out: List[bytes] = []
- for _, found_name, found_raw_value in headers._full_items:
- if found_name == name:
- found_raw_value = found_raw_value.lower()
- for found_split_value in found_raw_value.split(b","):
- found_split_value = found_split_value.strip()
- if found_split_value:
- out.append(found_split_value)
- return out
-
-
-def set_comma_header(headers: Headers, name: bytes, new_values: List[bytes]) -> Headers:
- # The header name `name` is expected to be lower-case bytes.
- #
- # Note that when we store the header we use title casing for the header
- # names, in order to match the conventional HTTP header style.
- #
- # Simply calling `.title()` is a blunt approach, but it's correct
- # here given the cases where we're using `set_comma_header`...
- #
- # Connection, Content-Length, Transfer-Encoding.
- new_headers: List[Tuple[bytes, bytes]] = []
- for found_raw_name, found_name, found_raw_value in headers._full_items:
- if found_name != name:
- new_headers.append((found_raw_name, found_raw_value))
- for new_value in new_values:
- new_headers.append((name.title(), new_value))
- return normalize_and_validate(new_headers)
-
-
-def has_expect_100_continue(request: "Request") -> bool:
- # https://tools.ietf.org/html/rfc7231#section-5.1.1
- # "A server that receives a 100-continue expectation in an HTTP/1.0 request
- # MUST ignore that expectation."
- if request.http_version < b"1.1":
- return False
- expect = get_comma_header(request.headers, b"expect")
- return b"100-continue" in expect
diff --git a/venv/lib/python3.11/site-packages/h11/_readers.py b/venv/lib/python3.11/site-packages/h11/_readers.py
deleted file mode 100644
index 08a9574..0000000
--- a/venv/lib/python3.11/site-packages/h11/_readers.py
+++ /dev/null
@@ -1,247 +0,0 @@
-# Code to read HTTP data
-#
-# Strategy: each reader is a callable which takes a ReceiveBuffer object, and
-# either:
-# 1) consumes some of it and returns an Event
-# 2) raises a LocalProtocolError (for consistency -- e.g. we call validate()
-# and it might raise a LocalProtocolError, so simpler just to always use
-# this)
-# 3) returns None, meaning "I need more data"
-#
-# If they have a .read_eof attribute, then this will be called if an EOF is
-# received -- but this is optional. Either way, the actual ConnectionClosed
-# event will be generated afterwards.
-#
-# READERS is a dict describing how to pick a reader. It maps states to either:
-# - a reader
-# - or, for body readers, a dict of per-framing reader factories
-
-import re
-from typing import Any, Callable, Dict, Iterable, NoReturn, Optional, Tuple, Type, Union
-
-from ._abnf import chunk_header, header_field, request_line, status_line
-from ._events import Data, EndOfMessage, InformationalResponse, Request, Response
-from ._receivebuffer import ReceiveBuffer
-from ._state import (
- CLIENT,
- CLOSED,
- DONE,
- IDLE,
- MUST_CLOSE,
- SEND_BODY,
- SEND_RESPONSE,
- SERVER,
-)
-from ._util import LocalProtocolError, RemoteProtocolError, Sentinel, validate
-
-__all__ = ["READERS"]
-
-header_field_re = re.compile(header_field.encode("ascii"))
-obs_fold_re = re.compile(rb"[ \t]+")
-
-
-def _obsolete_line_fold(lines: Iterable[bytes]) -> Iterable[bytes]:
- it = iter(lines)
- last: Optional[bytes] = None
- for line in it:
- match = obs_fold_re.match(line)
- if match:
- if last is None:
- raise LocalProtocolError("continuation line at start of headers")
- if not isinstance(last, bytearray):
- # Cast to a mutable type, avoiding copy on append to ensure O(n) time
- last = bytearray(last)
- last += b" "
- last += line[match.end() :]
- else:
- if last is not None:
- yield last
- last = line
- if last is not None:
- yield last
-
-
-def _decode_header_lines(
- lines: Iterable[bytes],
-) -> Iterable[Tuple[bytes, bytes]]:
- for line in _obsolete_line_fold(lines):
- matches = validate(header_field_re, line, "illegal header line: {!r}", line)
- yield (matches["field_name"], matches["field_value"])
-
-
-request_line_re = re.compile(request_line.encode("ascii"))
-
-
-def maybe_read_from_IDLE_client(buf: ReceiveBuffer) -> Optional[Request]:
- lines = buf.maybe_extract_lines()
- if lines is None:
- if buf.is_next_line_obviously_invalid_request_line():
- raise LocalProtocolError("illegal request line")
- return None
- if not lines:
- raise LocalProtocolError("no request line received")
- matches = validate(
- request_line_re, lines[0], "illegal request line: {!r}", lines[0]
- )
- return Request(
- headers=list(_decode_header_lines(lines[1:])), _parsed=True, **matches
- )
-
-
-status_line_re = re.compile(status_line.encode("ascii"))
-
-
-def maybe_read_from_SEND_RESPONSE_server(
- buf: ReceiveBuffer,
-) -> Union[InformationalResponse, Response, None]:
- lines = buf.maybe_extract_lines()
- if lines is None:
- if buf.is_next_line_obviously_invalid_request_line():
- raise LocalProtocolError("illegal request line")
- return None
- if not lines:
- raise LocalProtocolError("no response line received")
- matches = validate(status_line_re, lines[0], "illegal status line: {!r}", lines[0])
- http_version = (
- b"1.1" if matches["http_version"] is None else matches["http_version"]
- )
- reason = b"" if matches["reason"] is None else matches["reason"]
- status_code = int(matches["status_code"])
- class_: Union[Type[InformationalResponse], Type[Response]] = (
- InformationalResponse if status_code < 200 else Response
- )
- return class_(
- headers=list(_decode_header_lines(lines[1:])),
- _parsed=True,
- status_code=status_code,
- reason=reason,
- http_version=http_version,
- )
-
-
-class ContentLengthReader:
- def __init__(self, length: int) -> None:
- self._length = length
- self._remaining = length
-
- def __call__(self, buf: ReceiveBuffer) -> Union[Data, EndOfMessage, None]:
- if self._remaining == 0:
- return EndOfMessage()
- data = buf.maybe_extract_at_most(self._remaining)
- if data is None:
- return None
- self._remaining -= len(data)
- return Data(data=data)
-
- def read_eof(self) -> NoReturn:
- raise RemoteProtocolError(
- "peer closed connection without sending complete message body "
- "(received {} bytes, expected {})".format(
- self._length - self._remaining, self._length
- )
- )
-
-
-chunk_header_re = re.compile(chunk_header.encode("ascii"))
-
-
-class ChunkedReader:
- def __init__(self) -> None:
- self._bytes_in_chunk = 0
- # After reading a chunk, we have to throw away the trailing \r\n; if
- # this is >0 then we discard that many bytes before resuming regular
- # de-chunkification.
- self._bytes_to_discard = 0
- self._reading_trailer = False
-
- def __call__(self, buf: ReceiveBuffer) -> Union[Data, EndOfMessage, None]:
- if self._reading_trailer:
- lines = buf.maybe_extract_lines()
- if lines is None:
- return None
- return EndOfMessage(headers=list(_decode_header_lines(lines)))
- if self._bytes_to_discard > 0:
- data = buf.maybe_extract_at_most(self._bytes_to_discard)
- if data is None:
- return None
- self._bytes_to_discard -= len(data)
- if self._bytes_to_discard > 0:
- return None
- # else, fall through and read some more
- assert self._bytes_to_discard == 0
- if self._bytes_in_chunk == 0:
- # We need to refill our chunk count
- chunk_header = buf.maybe_extract_next_line()
- if chunk_header is None:
- return None
- matches = validate(
- chunk_header_re,
- chunk_header,
- "illegal chunk header: {!r}",
- chunk_header,
- )
- # XX FIXME: we discard chunk extensions. Does anyone care?
- self._bytes_in_chunk = int(matches["chunk_size"], base=16)
- if self._bytes_in_chunk == 0:
- self._reading_trailer = True
- return self(buf)
- chunk_start = True
- else:
- chunk_start = False
- assert self._bytes_in_chunk > 0
- data = buf.maybe_extract_at_most(self._bytes_in_chunk)
- if data is None:
- return None
- self._bytes_in_chunk -= len(data)
- if self._bytes_in_chunk == 0:
- self._bytes_to_discard = 2
- chunk_end = True
- else:
- chunk_end = False
- return Data(data=data, chunk_start=chunk_start, chunk_end=chunk_end)
-
- def read_eof(self) -> NoReturn:
- raise RemoteProtocolError(
- "peer closed connection without sending complete message body "
- "(incomplete chunked read)"
- )
-
-
-class Http10Reader:
- def __call__(self, buf: ReceiveBuffer) -> Optional[Data]:
- data = buf.maybe_extract_at_most(999999999)
- if data is None:
- return None
- return Data(data=data)
-
- def read_eof(self) -> EndOfMessage:
- return EndOfMessage()
-
-
-def expect_nothing(buf: ReceiveBuffer) -> None:
- if buf:
- raise LocalProtocolError("Got data when expecting EOF")
- return None
-
-
-ReadersType = Dict[
- Union[Type[Sentinel], Tuple[Type[Sentinel], Type[Sentinel]]],
- Union[Callable[..., Any], Dict[str, Callable[..., Any]]],
-]
-
-READERS: ReadersType = {
- (CLIENT, IDLE): maybe_read_from_IDLE_client,
- (SERVER, IDLE): maybe_read_from_SEND_RESPONSE_server,
- (SERVER, SEND_RESPONSE): maybe_read_from_SEND_RESPONSE_server,
- (CLIENT, DONE): expect_nothing,
- (CLIENT, MUST_CLOSE): expect_nothing,
- (CLIENT, CLOSED): expect_nothing,
- (SERVER, DONE): expect_nothing,
- (SERVER, MUST_CLOSE): expect_nothing,
- (SERVER, CLOSED): expect_nothing,
- SEND_BODY: {
- "chunked": ChunkedReader,
- "content-length": ContentLengthReader,
- "http/1.0": Http10Reader,
- },
-}
diff --git a/venv/lib/python3.11/site-packages/h11/_receivebuffer.py b/venv/lib/python3.11/site-packages/h11/_receivebuffer.py
deleted file mode 100644
index e5c4e08..0000000
--- a/venv/lib/python3.11/site-packages/h11/_receivebuffer.py
+++ /dev/null
@@ -1,153 +0,0 @@
-import re
-import sys
-from typing import List, Optional, Union
-
-__all__ = ["ReceiveBuffer"]
-
-
-# Operations we want to support:
-# - find next \r\n or \r\n\r\n (\n or \n\n are also acceptable),
-# or wait until there is one
-# - read at-most-N bytes
-# Goals:
-# - on average, do this fast
-# - worst case, do this in O(n) where n is the number of bytes processed
-# Plan:
-# - store bytearray, offset, how far we've searched for a separator token
-# - use the how-far-we've-searched data to avoid rescanning
-# - while doing a stream of uninterrupted processing, advance offset instead
-# of constantly copying
-# WARNING:
-# - I haven't benchmarked or profiled any of this yet.
-#
-# Note that starting in Python 3.4, deleting the initial n bytes from a
-# bytearray is amortized O(n), thanks to some excellent work by Antoine
-# Martin:
-#
-# https://bugs.python.org/issue19087
-#
-# This means that if we only supported 3.4+, we could get rid of the code here
-# involving self._start and self.compress, because it's doing exactly the same
-# thing that bytearray now does internally.
-#
-# BUT unfortunately, we still support 2.7, and reading short segments out of a
-# long buffer MUST be O(bytes read) to avoid DoS issues, so we can't actually
-# delete this code. Yet:
-#
-# https://pythonclock.org/
-#
-# (Two things to double-check first though: make sure PyPy also has the
-# optimization, and benchmark to make sure it's a win, since we do have a
-# slightly clever thing where we delay calling compress() until we've
-# processed a whole event, which could in theory be slightly more efficient
-# than the internal bytearray support.)
-blank_line_regex = re.compile(b"\n\r?\n", re.MULTILINE)
-
-
-class ReceiveBuffer:
- def __init__(self) -> None:
- self._data = bytearray()
- self._next_line_search = 0
- self._multiple_lines_search = 0
-
- def __iadd__(self, byteslike: Union[bytes, bytearray]) -> "ReceiveBuffer":
- self._data += byteslike
- return self
-
- def __bool__(self) -> bool:
- return bool(len(self))
-
- def __len__(self) -> int:
- return len(self._data)
-
- # for @property unprocessed_data
- def __bytes__(self) -> bytes:
- return bytes(self._data)
-
- def _extract(self, count: int) -> bytearray:
- # extracting an initial slice of the data buffer and return it
- out = self._data[:count]
- del self._data[:count]
-
- self._next_line_search = 0
- self._multiple_lines_search = 0
-
- return out
-
- def maybe_extract_at_most(self, count: int) -> Optional[bytearray]:
- """
- Extract a fixed number of bytes from the buffer.
- """
- out = self._data[:count]
- if not out:
- return None
-
- return self._extract(count)
-
- def maybe_extract_next_line(self) -> Optional[bytearray]:
- """
- Extract the first line, if it is completed in the buffer.
- """
- # Only search in buffer space that we've not already looked at.
- search_start_index = max(0, self._next_line_search - 1)
- partial_idx = self._data.find(b"\r\n", search_start_index)
-
- if partial_idx == -1:
- self._next_line_search = len(self._data)
- return None
-
- # + 2 is to compensate len(b"\r\n")
- idx = partial_idx + 2
-
- return self._extract(idx)
-
- def maybe_extract_lines(self) -> Optional[List[bytearray]]:
- """
- Extract everything up to the first blank line, and return a list of lines.
- """
- # Handle the case where we have an immediate empty line.
- if self._data[:1] == b"\n":
- self._extract(1)
- return []
-
- if self._data[:2] == b"\r\n":
- self._extract(2)
- return []
-
- # Only search in buffer space that we've not already looked at.
- match = blank_line_regex.search(self._data, self._multiple_lines_search)
- if match is None:
- self._multiple_lines_search = max(0, len(self._data) - 2)
- return None
-
- # Truncate the buffer and return it.
- idx = match.span(0)[-1]
- out = self._extract(idx)
- lines = out.split(b"\n")
-
- for line in lines:
- if line.endswith(b"\r"):
- del line[-1]
-
- assert lines[-2] == lines[-1] == b""
-
- del lines[-2:]
-
- return lines
-
- # In theory we should wait until `\r\n` before starting to validate
- # incoming data. However it's interesting to detect (very) invalid data
- # early given they might not even contain `\r\n` at all (hence only
- # timeout will get rid of them).
- # This is not a 100% effective detection but more of a cheap sanity check
- # allowing for early abort in some useful cases.
- # This is especially interesting when peer is messing up with HTTPS and
- # sent us a TLS stream where we were expecting plain HTTP given all
- # versions of TLS so far start handshake with a 0x16 message type code.
- def is_next_line_obviously_invalid_request_line(self) -> bool:
- try:
- # HTTP header line must not contain non-printable characters
- # and should not start with a space
- return self._data[0] < 0x21
- except IndexError:
- return False
diff --git a/venv/lib/python3.11/site-packages/h11/_state.py b/venv/lib/python3.11/site-packages/h11/_state.py
deleted file mode 100644
index 3593430..0000000
--- a/venv/lib/python3.11/site-packages/h11/_state.py
+++ /dev/null
@@ -1,367 +0,0 @@
-################################################################
-# The core state machine
-################################################################
-#
-# Rule 1: everything that affects the state machine and state transitions must
-# live here in this file. As much as possible goes into the table-based
-# representation, but for the bits that don't quite fit, the actual code and
-# state must nonetheless live here.
-#
-# Rule 2: this file does not know about what role we're playing; it only knows
-# about HTTP request/response cycles in the abstract. This ensures that we
-# don't cheat and apply different rules to local and remote parties.
-#
-#
-# Theory of operation
-# ===================
-#
-# Possibly the simplest way to think about this is that we actually have 5
-# different state machines here. Yes, 5. These are:
-#
-# 1) The client state, with its complicated automaton (see the docs)
-# 2) The server state, with its complicated automaton (see the docs)
-# 3) The keep-alive state, with possible states {True, False}
-# 4) The SWITCH_CONNECT state, with possible states {False, True}
-# 5) The SWITCH_UPGRADE state, with possible states {False, True}
-#
-# For (3)-(5), the first state listed is the initial state.
-#
-# (1)-(3) are stored explicitly in member variables. The last
-# two are stored implicitly in the pending_switch_proposals set as:
-# (state of 4) == (_SWITCH_CONNECT in pending_switch_proposals)
-# (state of 5) == (_SWITCH_UPGRADE in pending_switch_proposals)
-#
-# And each of these machines has two different kinds of transitions:
-#
-# a) Event-triggered
-# b) State-triggered
-#
-# Event triggered is the obvious thing that you'd think it is: some event
-# happens, and if it's the right event at the right time then a transition
-# happens. But there are somewhat complicated rules for which machines can
-# "see" which events. (As a rule of thumb, if a machine "sees" an event, this
-# means two things: the event can affect the machine, and if the machine is
-# not in a state where it expects that event then it's an error.) These rules
-# are:
-#
-# 1) The client machine sees all h11.events objects emitted by the client.
-#
-# 2) The server machine sees all h11.events objects emitted by the server.
-#
-# It also sees the client's Request event.
-#
-# And sometimes, server events are annotated with a _SWITCH_* event. For
-# example, we can have a (Response, _SWITCH_CONNECT) event, which is
-# different from a regular Response event.
-#
-# 3) The keep-alive machine sees the process_keep_alive_disabled() event
-# (which is derived from Request/Response events), and this event
-# transitions it from True -> False, or from False -> False. There's no way
-# to transition back.
-#
-# 4&5) The _SWITCH_* machines transition from False->True when we get a
-# Request that proposes the relevant type of switch (via
-# process_client_switch_proposals), and they go from True->False when we
-# get a Response that has no _SWITCH_* annotation.
-#
-# So that's event-triggered transitions.
-#
-# State-triggered transitions are less standard. What they do here is couple
-# the machines together. The way this works is, when certain *joint*
-# configurations of states are achieved, then we automatically transition to a
-# new *joint* state. So, for example, if we're ever in a joint state with
-#
-# client: DONE
-# keep-alive: False
-#
-# then the client state immediately transitions to:
-#
-# client: MUST_CLOSE
-#
-# This is fundamentally different from an event-based transition, because it
-# doesn't matter how we arrived at the {client: DONE, keep-alive: False} state
-# -- maybe the client transitioned SEND_BODY -> DONE, or keep-alive
-# transitioned True -> False. Either way, once this precondition is satisfied,
-# this transition is immediately triggered.
-#
-# What if two conflicting state-based transitions get enabled at the same
-# time? In practice there's only one case where this arises (client DONE ->
-# MIGHT_SWITCH_PROTOCOL versus DONE -> MUST_CLOSE), and we resolve it by
-# explicitly prioritizing the DONE -> MIGHT_SWITCH_PROTOCOL transition.
-#
-# Implementation
-# --------------
-#
-# The event-triggered transitions for the server and client machines are all
-# stored explicitly in a table. Ditto for the state-triggered transitions that
-# involve just the server and client state.
-#
-# The transitions for the other machines, and the state-triggered transitions
-# that involve the other machines, are written out as explicit Python code.
-#
-# It'd be nice if there were some cleaner way to do all this. This isn't
-# *too* terrible, but I feel like it could probably be better.
-#
-# WARNING
-# -------
-#
-# The script that generates the state machine diagrams for the docs knows how
-# to read out the EVENT_TRIGGERED_TRANSITIONS and STATE_TRIGGERED_TRANSITIONS
-# tables. But it can't automatically read the transitions that are written
-# directly in Python code. So if you touch those, you need to also update the
-# script to keep it in sync!
-from typing import cast, Dict, Optional, Set, Tuple, Type, Union
-
-from ._events import *
-from ._util import LocalProtocolError, Sentinel
-
-# Everything in __all__ gets re-exported as part of the h11 public API.
-__all__ = [
- "CLIENT",
- "SERVER",
- "IDLE",
- "SEND_RESPONSE",
- "SEND_BODY",
- "DONE",
- "MUST_CLOSE",
- "CLOSED",
- "MIGHT_SWITCH_PROTOCOL",
- "SWITCHED_PROTOCOL",
- "ERROR",
-]
-
-
-class CLIENT(Sentinel, metaclass=Sentinel):
- pass
-
-
-class SERVER(Sentinel, metaclass=Sentinel):
- pass
-
-
-# States
-class IDLE(Sentinel, metaclass=Sentinel):
- pass
-
-
-class SEND_RESPONSE(Sentinel, metaclass=Sentinel):
- pass
-
-
-class SEND_BODY(Sentinel, metaclass=Sentinel):
- pass
-
-
-class DONE(Sentinel, metaclass=Sentinel):
- pass
-
-
-class MUST_CLOSE(Sentinel, metaclass=Sentinel):
- pass
-
-
-class CLOSED(Sentinel, metaclass=Sentinel):
- pass
-
-
-class ERROR(Sentinel, metaclass=Sentinel):
- pass
-
-
-# Switch types
-class MIGHT_SWITCH_PROTOCOL(Sentinel, metaclass=Sentinel):
- pass
-
-
-class SWITCHED_PROTOCOL(Sentinel, metaclass=Sentinel):
- pass
-
-
-class _SWITCH_UPGRADE(Sentinel, metaclass=Sentinel):
- pass
-
-
-class _SWITCH_CONNECT(Sentinel, metaclass=Sentinel):
- pass
-
-
-EventTransitionType = Dict[
- Type[Sentinel],
- Dict[
- Type[Sentinel],
- Dict[Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]], Type[Sentinel]],
- ],
-]
-
-EVENT_TRIGGERED_TRANSITIONS: EventTransitionType = {
- CLIENT: {
- IDLE: {Request: SEND_BODY, ConnectionClosed: CLOSED},
- SEND_BODY: {Data: SEND_BODY, EndOfMessage: DONE},
- DONE: {ConnectionClosed: CLOSED},
- MUST_CLOSE: {ConnectionClosed: CLOSED},
- CLOSED: {ConnectionClosed: CLOSED},
- MIGHT_SWITCH_PROTOCOL: {},
- SWITCHED_PROTOCOL: {},
- ERROR: {},
- },
- SERVER: {
- IDLE: {
- ConnectionClosed: CLOSED,
- Response: SEND_BODY,
- # Special case: server sees client Request events, in this form
- (Request, CLIENT): SEND_RESPONSE,
- },
- SEND_RESPONSE: {
- InformationalResponse: SEND_RESPONSE,
- Response: SEND_BODY,
- (InformationalResponse, _SWITCH_UPGRADE): SWITCHED_PROTOCOL,
- (Response, _SWITCH_CONNECT): SWITCHED_PROTOCOL,
- },
- SEND_BODY: {Data: SEND_BODY, EndOfMessage: DONE},
- DONE: {ConnectionClosed: CLOSED},
- MUST_CLOSE: {ConnectionClosed: CLOSED},
- CLOSED: {ConnectionClosed: CLOSED},
- SWITCHED_PROTOCOL: {},
- ERROR: {},
- },
-}
-
-StateTransitionType = Dict[
- Tuple[Type[Sentinel], Type[Sentinel]], Dict[Type[Sentinel], Type[Sentinel]]
-]
-
-# NB: there are also some special-case state-triggered transitions hard-coded
-# into _fire_state_triggered_transitions below.
-STATE_TRIGGERED_TRANSITIONS: StateTransitionType = {
- # (Client state, Server state) -> new states
- # Protocol negotiation
- (MIGHT_SWITCH_PROTOCOL, SWITCHED_PROTOCOL): {CLIENT: SWITCHED_PROTOCOL},
- # Socket shutdown
- (CLOSED, DONE): {SERVER: MUST_CLOSE},
- (CLOSED, IDLE): {SERVER: MUST_CLOSE},
- (ERROR, DONE): {SERVER: MUST_CLOSE},
- (DONE, CLOSED): {CLIENT: MUST_CLOSE},
- (IDLE, CLOSED): {CLIENT: MUST_CLOSE},
- (DONE, ERROR): {CLIENT: MUST_CLOSE},
-}
-
-
-class ConnectionState:
- def __init__(self) -> None:
- # Extra bits of state that don't quite fit into the state model.
-
- # If this is False then it enables the automatic DONE -> MUST_CLOSE
- # transition. Don't set this directly; call .keep_alive_disabled()
- self.keep_alive = True
-
- # This is a subset of {UPGRADE, CONNECT}, containing the proposals
- # made by the client for switching protocols.
- self.pending_switch_proposals: Set[Type[Sentinel]] = set()
-
- self.states: Dict[Type[Sentinel], Type[Sentinel]] = {CLIENT: IDLE, SERVER: IDLE}
-
- def process_error(self, role: Type[Sentinel]) -> None:
- self.states[role] = ERROR
- self._fire_state_triggered_transitions()
-
- def process_keep_alive_disabled(self) -> None:
- self.keep_alive = False
- self._fire_state_triggered_transitions()
-
- def process_client_switch_proposal(self, switch_event: Type[Sentinel]) -> None:
- self.pending_switch_proposals.add(switch_event)
- self._fire_state_triggered_transitions()
-
- def process_event(
- self,
- role: Type[Sentinel],
- event_type: Type[Event],
- server_switch_event: Optional[Type[Sentinel]] = None,
- ) -> None:
- _event_type: Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]] = event_type
- if server_switch_event is not None:
- assert role is SERVER
- if server_switch_event not in self.pending_switch_proposals:
- raise LocalProtocolError(
- "Received server {} event without a pending proposal".format(
- server_switch_event
- )
- )
- _event_type = (event_type, server_switch_event)
- if server_switch_event is None and _event_type is Response:
- self.pending_switch_proposals = set()
- self._fire_event_triggered_transitions(role, _event_type)
- # Special case: the server state does get to see Request
- # events.
- if _event_type is Request:
- assert role is CLIENT
- self._fire_event_triggered_transitions(SERVER, (Request, CLIENT))
- self._fire_state_triggered_transitions()
-
- def _fire_event_triggered_transitions(
- self,
- role: Type[Sentinel],
- event_type: Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]],
- ) -> None:
- state = self.states[role]
- try:
- new_state = EVENT_TRIGGERED_TRANSITIONS[role][state][event_type]
- except KeyError:
- event_type = cast(Type[Event], event_type)
- raise LocalProtocolError(
- "can't handle event type {} when role={} and state={}".format(
- event_type.__name__, role, self.states[role]
- )
- ) from None
- self.states[role] = new_state
-
- def _fire_state_triggered_transitions(self) -> None:
- # We apply these rules repeatedly until converging on a fixed point
- while True:
- start_states = dict(self.states)
-
- # It could happen that both these special-case transitions are
- # enabled at the same time:
- #
- # DONE -> MIGHT_SWITCH_PROTOCOL
- # DONE -> MUST_CLOSE
- #
- # For example, this will always be true of a HTTP/1.0 client
- # requesting CONNECT. If this happens, the protocol switch takes
- # priority. From there the client will either go to
- # SWITCHED_PROTOCOL, in which case it's none of our business when
- # they close the connection, or else the server will deny the
- # request, in which case the client will go back to DONE and then
- # from there to MUST_CLOSE.
- if self.pending_switch_proposals:
- if self.states[CLIENT] is DONE:
- self.states[CLIENT] = MIGHT_SWITCH_PROTOCOL
-
- if not self.pending_switch_proposals:
- if self.states[CLIENT] is MIGHT_SWITCH_PROTOCOL:
- self.states[CLIENT] = DONE
-
- if not self.keep_alive:
- for role in (CLIENT, SERVER):
- if self.states[role] is DONE:
- self.states[role] = MUST_CLOSE
-
- # Tabular state-triggered transitions
- joint_state = (self.states[CLIENT], self.states[SERVER])
- changes = STATE_TRIGGERED_TRANSITIONS.get(joint_state, {})
- self.states.update(changes)
-
- if self.states == start_states:
- # Fixed point reached
- return
-
- def start_next_cycle(self) -> None:
- if self.states != {CLIENT: DONE, SERVER: DONE}:
- raise LocalProtocolError(
- "not in a reusable state. self.states={}".format(self.states)
- )
- # Can't reach DONE/DONE with any of these active, but still, let's be
- # sure.
- assert self.keep_alive
- assert not self.pending_switch_proposals
- self.states = {CLIENT: IDLE, SERVER: IDLE}
diff --git a/venv/lib/python3.11/site-packages/h11/_util.py b/venv/lib/python3.11/site-packages/h11/_util.py
deleted file mode 100644
index 6718445..0000000
--- a/venv/lib/python3.11/site-packages/h11/_util.py
+++ /dev/null
@@ -1,135 +0,0 @@
-from typing import Any, Dict, NoReturn, Pattern, Tuple, Type, TypeVar, Union
-
-__all__ = [
- "ProtocolError",
- "LocalProtocolError",
- "RemoteProtocolError",
- "validate",
- "bytesify",
-]
-
-
-class ProtocolError(Exception):
- """Exception indicating a violation of the HTTP/1.1 protocol.
-
- This as an abstract base class, with two concrete base classes:
- :exc:`LocalProtocolError`, which indicates that you tried to do something
- that HTTP/1.1 says is illegal, and :exc:`RemoteProtocolError`, which
- indicates that the remote peer tried to do something that HTTP/1.1 says is
- illegal. See :ref:`error-handling` for details.
-
- In addition to the normal :exc:`Exception` features, it has one attribute:
-
- .. attribute:: error_status_hint
-
- This gives a suggestion as to what status code a server might use if
- this error occurred as part of a request.
-
- For a :exc:`RemoteProtocolError`, this is useful as a suggestion for
- how you might want to respond to a misbehaving peer, if you're
- implementing a server.
-
- For a :exc:`LocalProtocolError`, this can be taken as a suggestion for
- how your peer might have responded to *you* if h11 had allowed you to
- continue.
-
- The default is 400 Bad Request, a generic catch-all for protocol
- violations.
-
- """
-
- def __init__(self, msg: str, error_status_hint: int = 400) -> None:
- if type(self) is ProtocolError:
- raise TypeError("tried to directly instantiate ProtocolError")
- Exception.__init__(self, msg)
- self.error_status_hint = error_status_hint
-
-
-# Strategy: there are a number of public APIs where a LocalProtocolError can
-# be raised (send(), all the different event constructors, ...), and only one
-# public API where RemoteProtocolError can be raised
-# (receive_data()). Therefore we always raise LocalProtocolError internally,
-# and then receive_data will translate this into a RemoteProtocolError.
-#
-# Internally:
-# LocalProtocolError is the generic "ProtocolError".
-# Externally:
-# LocalProtocolError is for local errors and RemoteProtocolError is for
-# remote errors.
-class LocalProtocolError(ProtocolError):
- def _reraise_as_remote_protocol_error(self) -> NoReturn:
- # After catching a LocalProtocolError, use this method to re-raise it
- # as a RemoteProtocolError. This method must be called from inside an
- # except: block.
- #
- # An easy way to get an equivalent RemoteProtocolError is just to
- # modify 'self' in place.
- self.__class__ = RemoteProtocolError # type: ignore
- # But the re-raising is somewhat non-trivial -- you might think that
- # now that we've modified the in-flight exception object, that just
- # doing 'raise' to re-raise it would be enough. But it turns out that
- # this doesn't work, because Python tracks the exception type
- # (exc_info[0]) separately from the exception object (exc_info[1]),
- # and we only modified the latter. So we really do need to re-raise
- # the new type explicitly.
- # On py3, the traceback is part of the exception object, so our
- # in-place modification preserved it and we can just re-raise:
- raise self
-
-
-class RemoteProtocolError(ProtocolError):
- pass
-
-
-def validate(
- regex: Pattern[bytes], data: bytes, msg: str = "malformed data", *format_args: Any
-) -> Dict[str, bytes]:
- match = regex.fullmatch(data)
- if not match:
- if format_args:
- msg = msg.format(*format_args)
- raise LocalProtocolError(msg)
- return match.groupdict()
-
-
-# Sentinel values
-#
-# - Inherit identity-based comparison and hashing from object
-# - Have a nice repr
-# - Have a *bonus property*: type(sentinel) is sentinel
-#
-# The bonus property is useful if you want to take the return value from
-# next_event() and do some sort of dispatch based on type(event).
-
-_T_Sentinel = TypeVar("_T_Sentinel", bound="Sentinel")
-
-
-class Sentinel(type):
- def __new__(
- cls: Type[_T_Sentinel],
- name: str,
- bases: Tuple[type, ...],
- namespace: Dict[str, Any],
- **kwds: Any
- ) -> _T_Sentinel:
- assert bases == (Sentinel,)
- v = super().__new__(cls, name, bases, namespace, **kwds)
- v.__class__ = v # type: ignore
- return v
-
- def __repr__(self) -> str:
- return self.__name__
-
-
-# Used for methods, request targets, HTTP versions, header names, and header
-# values. Accepts ascii-strings, or bytes/bytearray/memoryview/..., and always
-# returns bytes.
-def bytesify(s: Union[bytes, bytearray, memoryview, int, str]) -> bytes:
- # Fast-path:
- if type(s) is bytes:
- return s
- if isinstance(s, str):
- s = s.encode("ascii")
- if isinstance(s, int):
- raise TypeError("expected bytes-like object, not int")
- return bytes(s)
diff --git a/venv/lib/python3.11/site-packages/h11/_version.py b/venv/lib/python3.11/site-packages/h11/_version.py
deleted file mode 100644
index 4c89113..0000000
--- a/venv/lib/python3.11/site-packages/h11/_version.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# This file must be kept very simple, because it is consumed from several
-# places -- it is imported by h11/__init__.py, execfile'd by setup.py, etc.
-
-# We use a simple scheme:
-# 1.0.0 -> 1.0.0+dev -> 1.1.0 -> 1.1.0+dev
-# where the +dev versions are never released into the wild, they're just what
-# we stick into the VCS in between releases.
-#
-# This is compatible with PEP 440:
-# http://legacy.python.org/dev/peps/pep-0440/
-# via the use of the "local suffix" "+dev", which is disallowed on index
-# servers and causes 1.0.0+dev to sort after plain 1.0.0, which is what we
-# want. (Contrast with the special suffix 1.0.0.dev, which sorts *before*
-# 1.0.0.)
-
-__version__ = "0.14.0"
diff --git a/venv/lib/python3.11/site-packages/h11/_writers.py b/venv/lib/python3.11/site-packages/h11/_writers.py
deleted file mode 100644
index 939cdb9..0000000
--- a/venv/lib/python3.11/site-packages/h11/_writers.py
+++ /dev/null
@@ -1,145 +0,0 @@
-# Code to read HTTP data
-#
-# Strategy: each writer takes an event + a write-some-bytes function, which is
-# calls.
-#
-# WRITERS is a dict describing how to pick a reader. It maps states to either:
-# - a writer
-# - or, for body writers, a dict of framin-dependent writer factories
-
-from typing import Any, Callable, Dict, List, Tuple, Type, Union
-
-from ._events import Data, EndOfMessage, Event, InformationalResponse, Request, Response
-from ._headers import Headers
-from ._state import CLIENT, IDLE, SEND_BODY, SEND_RESPONSE, SERVER
-from ._util import LocalProtocolError, Sentinel
-
-__all__ = ["WRITERS"]
-
-Writer = Callable[[bytes], Any]
-
-
-def write_headers(headers: Headers, write: Writer) -> None:
- # "Since the Host field-value is critical information for handling a
- # request, a user agent SHOULD generate Host as the first header field
- # following the request-line." - RFC 7230
- raw_items = headers._full_items
- for raw_name, name, value in raw_items:
- if name == b"host":
- write(b"%s: %s\r\n" % (raw_name, value))
- for raw_name, name, value in raw_items:
- if name != b"host":
- write(b"%s: %s\r\n" % (raw_name, value))
- write(b"\r\n")
-
-
-def write_request(request: Request, write: Writer) -> None:
- if request.http_version != b"1.1":
- raise LocalProtocolError("I only send HTTP/1.1")
- write(b"%s %s HTTP/1.1\r\n" % (request.method, request.target))
- write_headers(request.headers, write)
-
-
-# Shared between InformationalResponse and Response
-def write_any_response(
- response: Union[InformationalResponse, Response], write: Writer
-) -> None:
- if response.http_version != b"1.1":
- raise LocalProtocolError("I only send HTTP/1.1")
- status_bytes = str(response.status_code).encode("ascii")
- # We don't bother sending ascii status messages like "OK"; they're
- # optional and ignored by the protocol. (But the space after the numeric
- # status code is mandatory.)
- #
- # XX FIXME: could at least make an effort to pull out the status message
- # from stdlib's http.HTTPStatus table. Or maybe just steal their enums
- # (either by import or copy/paste). We already accept them as status codes
- # since they're of type IntEnum < int.
- write(b"HTTP/1.1 %s %s\r\n" % (status_bytes, response.reason))
- write_headers(response.headers, write)
-
-
-class BodyWriter:
- def __call__(self, event: Event, write: Writer) -> None:
- if type(event) is Data:
- self.send_data(event.data, write)
- elif type(event) is EndOfMessage:
- self.send_eom(event.headers, write)
- else: # pragma: no cover
- assert False
-
- def send_data(self, data: bytes, write: Writer) -> None:
- pass
-
- def send_eom(self, headers: Headers, write: Writer) -> None:
- pass
-
-
-#
-# These are all careful not to do anything to 'data' except call len(data) and
-# write(data). This allows us to transparently pass-through funny objects,
-# like placeholder objects referring to files on disk that will be sent via
-# sendfile(2).
-#
-class ContentLengthWriter(BodyWriter):
- def __init__(self, length: int) -> None:
- self._length = length
-
- def send_data(self, data: bytes, write: Writer) -> None:
- self._length -= len(data)
- if self._length < 0:
- raise LocalProtocolError("Too much data for declared Content-Length")
- write(data)
-
- def send_eom(self, headers: Headers, write: Writer) -> None:
- if self._length != 0:
- raise LocalProtocolError("Too little data for declared Content-Length")
- if headers:
- raise LocalProtocolError("Content-Length and trailers don't mix")
-
-
-class ChunkedWriter(BodyWriter):
- def send_data(self, data: bytes, write: Writer) -> None:
- # if we encoded 0-length data in the naive way, it would look like an
- # end-of-message.
- if not data:
- return
- write(b"%x\r\n" % len(data))
- write(data)
- write(b"\r\n")
-
- def send_eom(self, headers: Headers, write: Writer) -> None:
- write(b"0\r\n")
- write_headers(headers, write)
-
-
-class Http10Writer(BodyWriter):
- def send_data(self, data: bytes, write: Writer) -> None:
- write(data)
-
- def send_eom(self, headers: Headers, write: Writer) -> None:
- if headers:
- raise LocalProtocolError("can't send trailers to HTTP/1.0 client")
- # no need to close the socket ourselves, that will be taken care of by
- # Connection: close machinery
-
-
-WritersType = Dict[
- Union[Tuple[Type[Sentinel], Type[Sentinel]], Type[Sentinel]],
- Union[
- Dict[str, Type[BodyWriter]],
- Callable[[Union[InformationalResponse, Response], Writer], None],
- Callable[[Request, Writer], None],
- ],
-]
-
-WRITERS: WritersType = {
- (CLIENT, IDLE): write_request,
- (SERVER, IDLE): write_any_response,
- (SERVER, SEND_RESPONSE): write_any_response,
- SEND_BODY: {
- "chunked": ChunkedWriter,
- "content-length": ContentLengthWriter,
- "http/1.0": Http10Writer,
- },
-}
diff --git a/venv/lib/python3.11/site-packages/h11/py.typed b/venv/lib/python3.11/site-packages/h11/py.typed
deleted file mode 100644
index f5642f7..0000000
--- a/venv/lib/python3.11/site-packages/h11/py.typed
+++ /dev/null
@@ -1 +0,0 @@
-Marker
diff --git a/venv/lib/python3.11/site-packages/h11/tests/__init__.py b/venv/lib/python3.11/site-packages/h11/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/__init__.py
+++ /dev/null
diff --git a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/tests/__pycache__/__init__.cpython-311.pyc
deleted file mode 100644
index b45464e..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/__init__.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/helpers.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/tests/__pycache__/helpers.cpython-311.pyc
deleted file mode 100644
index d8e415d..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/helpers.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_against_stdlib_http.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_against_stdlib_http.cpython-311.pyc
deleted file mode 100644
index 564b601..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_against_stdlib_http.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_connection.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_connection.cpython-311.pyc
deleted file mode 100644
index c9f269e..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_connection.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_events.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_events.cpython-311.pyc
deleted file mode 100644
index 90bb491..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_events.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_headers.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_headers.cpython-311.pyc
deleted file mode 100644
index 5c15287..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_headers.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_helpers.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_helpers.cpython-311.pyc
deleted file mode 100644
index dcefc5a..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_helpers.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_io.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_io.cpython-311.pyc
deleted file mode 100644
index 9abf567..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_io.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_receivebuffer.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_receivebuffer.cpython-311.pyc
deleted file mode 100644
index 8f1f6db..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_receivebuffer.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_state.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_state.cpython-311.pyc
deleted file mode 100644
index 1115678..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_state.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_util.cpython-311.pyc b/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_util.cpython-311.pyc
deleted file mode 100644
index b3bfd20..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/__pycache__/test_util.cpython-311.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/h11/tests/data/test-file b/venv/lib/python3.11/site-packages/h11/tests/data/test-file
deleted file mode 100644
index d0be0a6..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/data/test-file
+++ /dev/null
@@ -1 +0,0 @@
-92b12bc045050b55b848d37167a1a63947c364579889ce1d39788e45e9fac9e5
diff --git a/venv/lib/python3.11/site-packages/h11/tests/helpers.py b/venv/lib/python3.11/site-packages/h11/tests/helpers.py
deleted file mode 100644
index 571be44..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/helpers.py
+++ /dev/null
@@ -1,101 +0,0 @@
-from typing import cast, List, Type, Union, ValuesView
-
-from .._connection import Connection, NEED_DATA, PAUSED
-from .._events import (
- ConnectionClosed,
- Data,
- EndOfMessage,
- Event,
- InformationalResponse,
- Request,
- Response,
-)
-from .._state import CLIENT, CLOSED, DONE, MUST_CLOSE, SERVER
-from .._util import Sentinel
-
-try:
- from typing import Literal
-except ImportError:
- from typing_extensions import Literal # type: ignore
-
-
-def get_all_events(conn: Connection) -> List[Event]:
- got_events = []
- while True:
- event = conn.next_event()
- if event in (NEED_DATA, PAUSED):
- break
- event = cast(Event, event)
- got_events.append(event)
- if type(event) is ConnectionClosed:
- break
- return got_events
-
-
-def receive_and_get(conn: Connection, data: bytes) -> List[Event]:
- conn.receive_data(data)
- return get_all_events(conn)
-
-
-# Merges adjacent Data events, converts payloads to bytestrings, and removes
-# chunk boundaries.
-def normalize_data_events(in_events: List[Event]) -> List[Event]:
- out_events: List[Event] = []
- for event in in_events:
- if type(event) is Data:
- event = Data(data=bytes(event.data), chunk_start=False, chunk_end=False)
- if out_events and type(out_events[-1]) is type(event) is Data:
- out_events[-1] = Data(
- data=out_events[-1].data + event.data,
- chunk_start=out_events[-1].chunk_start,
- chunk_end=out_events[-1].chunk_end,
- )
- else:
- out_events.append(event)
- return out_events
-
-
-# Given that we want to write tests that push some events through a Connection
-# and check that its state updates appropriately... we might as make a habit
-# of pushing them through two Connections with a fake network link in
-# between.
-class ConnectionPair:
- def __init__(self) -> None:
- self.conn = {CLIENT: Connection(CLIENT), SERVER: Connection(SERVER)}
- self.other = {CLIENT: SERVER, SERVER: CLIENT}
-
- @property
- def conns(self) -> ValuesView[Connection]:
- return self.conn.values()
-
- # expect="match" if expect=send_events; expect=[...] to say what expected
- def send(
- self,
- role: Type[Sentinel],
- send_events: Union[List[Event], Event],
- expect: Union[List[Event], Event, Literal["match"]] = "match",
- ) -> bytes:
- if not isinstance(send_events, list):
- send_events = [send_events]
- data = b""
- closed = False
- for send_event in send_events:
- new_data = self.conn[role].send(send_event)
- if new_data is None:
- closed = True
- else:
- data += new_data
- # send uses b"" to mean b"", and None to mean closed
- # receive uses b"" to mean closed, and None to mean "try again"
- # so we have to translate between the two conventions
- if data:
- self.conn[self.other[role]].receive_data(data)
- if closed:
- self.conn[self.other[role]].receive_data(b"")
- got_events = get_all_events(self.conn[self.other[role]])
- if expect == "match":
- expect = send_events
- if not isinstance(expect, list):
- expect = [expect]
- assert got_events == expect
- return data
diff --git a/venv/lib/python3.11/site-packages/h11/tests/test_against_stdlib_http.py b/venv/lib/python3.11/site-packages/h11/tests/test_against_stdlib_http.py
deleted file mode 100644
index d2ee131..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/test_against_stdlib_http.py
+++ /dev/null
@@ -1,115 +0,0 @@
-import json
-import os.path
-import socket
-import socketserver
-import threading
-from contextlib import closing, contextmanager
-from http.server import SimpleHTTPRequestHandler
-from typing import Callable, Generator
-from urllib.request import urlopen
-
-import h11
-
-
-@contextmanager
-def socket_server(
- handler: Callable[..., socketserver.BaseRequestHandler]
-) -> Generator[socketserver.TCPServer, None, None]:
- httpd = socketserver.TCPServer(("127.0.0.1", 0), handler)
- thread = threading.Thread(
- target=httpd.serve_forever, kwargs={"poll_interval": 0.01}
- )
- thread.daemon = True
- try:
- thread.start()
- yield httpd
- finally:
- httpd.shutdown()
-
-
-test_file_path = os.path.join(os.path.dirname(__file__), "data/test-file")
-with open(test_file_path, "rb") as f:
- test_file_data = f.read()
-
-
-class SingleMindedRequestHandler(SimpleHTTPRequestHandler):
- def translate_path(self, path: str) -> str:
- return test_file_path
-
-
-def test_h11_as_client() -> None:
- with socket_server(SingleMindedRequestHandler) as httpd:
- with closing(socket.create_connection(httpd.server_address)) as s:
- c = h11.Connection(h11.CLIENT)
-
- s.sendall(
- c.send( # type: ignore[arg-type]
- h11.Request(
- method="GET", target="/foo", headers=[("Host", "localhost")]
- )
- )
- )
- s.sendall(c.send(h11.EndOfMessage())) # type: ignore[arg-type]
-
- data = bytearray()
- while True:
- event = c.next_event()
- print(event)
- if event is h11.NEED_DATA:
- # Use a small read buffer to make things more challenging
- # and exercise more paths :-)
- c.receive_data(s.recv(10))
- continue
- if type(event) is h11.Response:
- assert event.status_code == 200
- if type(event) is h11.Data:
- data += event.data
- if type(event) is h11.EndOfMessage:
- break
- assert bytes(data) == test_file_data
-
-
-class H11RequestHandler(socketserver.BaseRequestHandler):
- def handle(self) -> None:
- with closing(self.request) as s:
- c = h11.Connection(h11.SERVER)
- request = None
- while True:
- event = c.next_event()
- if event is h11.NEED_DATA:
- # Use a small read buffer to make things more challenging
- # and exercise more paths :-)
- c.receive_data(s.recv(10))
- continue
- if type(event) is h11.Request:
- request = event
- if type(event) is h11.EndOfMessage:
- break
- assert request is not None
- info = json.dumps(
- {
- "method": request.method.decode("ascii"),
- "target": request.target.decode("ascii"),
- "headers": {
- name.decode("ascii"): value.decode("ascii")
- for (name, value) in request.headers
- },
- }
- )
- s.sendall(c.send(h11.Response(status_code=200, headers=[]))) # type: ignore[arg-type]
- s.sendall(c.send(h11.Data(data=info.encode("ascii"))))
- s.sendall(c.send(h11.EndOfMessage()))
-
-
-def test_h11_as_server() -> None:
- with socket_server(H11RequestHandler) as httpd:
- host, port = httpd.server_address
- url = "http://{}:{}/some-path".format(host, port)
- with closing(urlopen(url)) as f:
- assert f.getcode() == 200
- data = f.read()
- info = json.loads(data.decode("ascii"))
- print(info)
- assert info["method"] == "GET"
- assert info["target"] == "/some-path"
- assert "urllib" in info["headers"]["user-agent"]
diff --git a/venv/lib/python3.11/site-packages/h11/tests/test_connection.py b/venv/lib/python3.11/site-packages/h11/tests/test_connection.py
deleted file mode 100644
index 73a27b9..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/test_connection.py
+++ /dev/null
@@ -1,1122 +0,0 @@
-from typing import Any, cast, Dict, List, Optional, Tuple, Type
-
-import pytest
-
-from .._connection import _body_framing, _keep_alive, Connection, NEED_DATA, PAUSED
-from .._events import (
- ConnectionClosed,
- Data,
- EndOfMessage,
- Event,
- InformationalResponse,
- Request,
- Response,
-)
-from .._state import (
- CLIENT,
- CLOSED,
- DONE,
- ERROR,
- IDLE,
- MIGHT_SWITCH_PROTOCOL,
- MUST_CLOSE,
- SEND_BODY,
- SEND_RESPONSE,
- SERVER,
- SWITCHED_PROTOCOL,
-)
-from .._util import LocalProtocolError, RemoteProtocolError, Sentinel
-from .helpers import ConnectionPair, get_all_events, receive_and_get
-
-
-def test__keep_alive() -> None:
- assert _keep_alive(
- Request(method="GET", target="/", headers=[("Host", "Example.com")])
- )
- assert not _keep_alive(
- Request(
- method="GET",
- target="/",
- headers=[("Host", "Example.com"), ("Connection", "close")],
- )
- )
- assert not _keep_alive(
- Request(
- method="GET",
- target="/",
- headers=[("Host", "Example.com"), ("Connection", "a, b, cLOse, foo")],
- )
- )
- assert not _keep_alive(
- Request(method="GET", target="/", headers=[], http_version="1.0") # type: ignore[arg-type]
- )
-
- assert _keep_alive(Response(status_code=200, headers=[])) # type: ignore[arg-type]
- assert not _keep_alive(Response(status_code=200, headers=[("Connection", "close")]))
- assert not _keep_alive(
- Response(status_code=200, headers=[("Connection", "a, b, cLOse, foo")])
- )
- assert not _keep_alive(Response(status_code=200, headers=[], http_version="1.0")) # type: ignore[arg-type]
-
-
-def test__body_framing() -> None:
- def headers(cl: Optional[int], te: bool) -> List[Tuple[str, str]]:
- headers = []
- if cl is not None:
- headers.append(("Content-Length", str(cl)))
- if te:
- headers.append(("Transfer-Encoding", "chunked"))
- return headers
-
- def resp(
- status_code: int = 200, cl: Optional[int] = None, te: bool = False
- ) -> Response:
- return Response(status_code=status_code, headers=headers(cl, te))
-
- def req(cl: Optional[int] = None, te: bool = False) -> Request:
- h = headers(cl, te)
- h += [("Host", "example.com")]
- return Request(method="GET", target="/", headers=h)
-
- # Special cases where the headers are ignored:
- for kwargs in [{}, {"cl": 100}, {"te": True}, {"cl": 100, "te": True}]:
- kwargs = cast(Dict[str, Any], kwargs)
- for meth, r in [
- (b"HEAD", resp(**kwargs)),
- (b"GET", resp(status_code=204, **kwargs)),
- (b"GET", resp(status_code=304, **kwargs)),
- ]:
- assert _body_framing(meth, r) == ("content-length", (0,))
-
- # Transfer-encoding
- for kwargs in [{"te": True}, {"cl": 100, "te": True}]:
- kwargs = cast(Dict[str, Any], kwargs)
- for meth, r in [(None, req(**kwargs)), (b"GET", resp(**kwargs))]: # type: ignore
- assert _body_framing(meth, r) == ("chunked", ())
-
- # Content-Length
- for meth, r in [(None, req(cl=100)), (b"GET", resp(cl=100))]: # type: ignore
- assert _body_framing(meth, r) == ("content-length", (100,))
-
- # No headers
- assert _body_framing(None, req()) == ("content-length", (0,)) # type: ignore
- assert _body_framing(b"GET", resp()) == ("http/1.0", ())
-
-
-def test_Connection_basics_and_content_length() -> None:
- with pytest.raises(ValueError):
- Connection("CLIENT") # type: ignore
-
- p = ConnectionPair()
- assert p.conn[CLIENT].our_role is CLIENT
- assert p.conn[CLIENT].their_role is SERVER
- assert p.conn[SERVER].our_role is SERVER
- assert p.conn[SERVER].their_role is CLIENT
-
- data = p.send(
- CLIENT,
- Request(
- method="GET",
- target="/",
- headers=[("Host", "example.com"), ("Content-Length", "10")],
- ),
- )
- assert data == (
- b"GET / HTTP/1.1\r\n" b"Host: example.com\r\n" b"Content-Length: 10\r\n\r\n"
- )
-
- for conn in p.conns:
- assert conn.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
- assert p.conn[CLIENT].our_state is SEND_BODY
- assert p.conn[CLIENT].their_state is SEND_RESPONSE
- assert p.conn[SERVER].our_state is SEND_RESPONSE
- assert p.conn[SERVER].their_state is SEND_BODY
-
- assert p.conn[CLIENT].their_http_version is None
- assert p.conn[SERVER].their_http_version == b"1.1"
-
- data = p.send(SERVER, InformationalResponse(status_code=100, headers=[])) # type: ignore[arg-type]
- assert data == b"HTTP/1.1 100 \r\n\r\n"
-
- data = p.send(SERVER, Response(status_code=200, headers=[("Content-Length", "11")]))
- assert data == b"HTTP/1.1 200 \r\nContent-Length: 11\r\n\r\n"
-
- for conn in p.conns:
- assert conn.states == {CLIENT: SEND_BODY, SERVER: SEND_BODY}
-
- assert p.conn[CLIENT].their_http_version == b"1.1"
- assert p.conn[SERVER].their_http_version == b"1.1"
-
- data = p.send(CLIENT, Data(data=b"12345"))
- assert data == b"12345"
- data = p.send(
- CLIENT, Data(data=b"67890"), expect=[Data(data=b"67890"), EndOfMessage()]
- )
- assert data == b"67890"
- data = p.send(CLIENT, EndOfMessage(), expect=[])
- assert data == b""
-
- for conn in p.conns:
- assert conn.states == {CLIENT: DONE, SERVER: SEND_BODY}
-
- data = p.send(SERVER, Data(data=b"1234567890"))
- assert data == b"1234567890"
- data = p.send(SERVER, Data(data=b"1"), expect=[Data(data=b"1"), EndOfMessage()])
- assert data == b"1"
- data = p.send(SERVER, EndOfMessage(), expect=[])
- assert data == b""
-
- for conn in p.conns:
- assert conn.states == {CLIENT: DONE, SERVER: DONE}
-
-
-def test_chunked() -> None:
- p = ConnectionPair()
-
- p.send(
- CLIENT,
- Request(
- method="GET",
- target="/",
- headers=[("Host", "example.com"), ("Transfer-Encoding", "chunked")],
- ),
- )
- data = p.send(CLIENT, Data(data=b"1234567890", chunk_start=True, chunk_end=True))
- assert data == b"a\r\n1234567890\r\n"
- data = p.send(CLIENT, Data(data=b"abcde", chunk_start=True, chunk_end=True))
- assert data == b"5\r\nabcde\r\n"
- data = p.send(CLIENT, Data(data=b""), expect=[])
- assert data == b""
- data = p.send(CLIENT, EndOfMessage(headers=[("hello", "there")]))
- assert data == b"0\r\nhello: there\r\n\r\n"
-
- p.send(
- SERVER, Response(status_code=200, headers=[("Transfer-Encoding", "chunked")])
- )
- p.send(SERVER, Data(data=b"54321", chunk_start=True, chunk_end=True))
- p.send(SERVER, Data(data=b"12345", chunk_start=True, chunk_end=True))
- p.send(SERVER, EndOfMessage())
-
- for conn in p.conns:
- assert conn.states == {CLIENT: DONE, SERVER: DONE}
-
-
-def test_chunk_boundaries() -> None:
- conn = Connection(our_role=SERVER)
-
- request = (
- b"POST / HTTP/1.1\r\n"
- b"Host: example.com\r\n"
- b"Transfer-Encoding: chunked\r\n"
- b"\r\n"
- )
- conn.receive_data(request)
- assert conn.next_event() == Request(
- method="POST",
- target="/",
- headers=[("Host", "example.com"), ("Transfer-Encoding", "chunked")],
- )
- assert conn.next_event() is NEED_DATA
-
- conn.receive_data(b"5\r\nhello\r\n")
- assert conn.next_event() == Data(data=b"hello", chunk_start=True, chunk_end=True)
-
- conn.receive_data(b"5\r\nhel")
- assert conn.next_event() == Data(data=b"hel", chunk_start=True, chunk_end=False)
-
- conn.receive_data(b"l")
- assert conn.next_event() == Data(data=b"l", chunk_start=False, chunk_end=False)
-
- conn.receive_data(b"o\r\n")
- assert conn.next_event() == Data(data=b"o", chunk_start=False, chunk_end=True)
-
- conn.receive_data(b"5\r\nhello")
- assert conn.next_event() == Data(data=b"hello", chunk_start=True, chunk_end=True)
-
- conn.receive_data(b"\r\n")
- assert conn.next_event() == NEED_DATA
-
- conn.receive_data(b"0\r\n\r\n")
- assert conn.next_event() == EndOfMessage()
-
-
-def test_client_talking_to_http10_server() -> None:
- c = Connection(CLIENT)
- c.send(Request(method="GET", target="/", headers=[("Host", "example.com")]))
- c.send(EndOfMessage())
- assert c.our_state is DONE
- # No content-length, so Http10 framing for body
- assert receive_and_get(c, b"HTTP/1.0 200 OK\r\n\r\n") == [
- Response(status_code=200, headers=[], http_version="1.0", reason=b"OK") # type: ignore[arg-type]
- ]
- assert c.our_state is MUST_CLOSE
- assert receive_and_get(c, b"12345") == [Data(data=b"12345")]
- assert receive_and_get(c, b"67890") == [Data(data=b"67890")]
- assert receive_and_get(c, b"") == [EndOfMessage(), ConnectionClosed()]
- assert c.their_state is CLOSED
-
-
-def test_server_talking_to_http10_client() -> None:
- c = Connection(SERVER)
- # No content-length, so no body
- # NB: no host header
- assert receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n") == [
- Request(method="GET", target="/", headers=[], http_version="1.0"), # type: ignore[arg-type]
- EndOfMessage(),
- ]
- assert c.their_state is MUST_CLOSE
-
- # We automatically Connection: close back at them
- assert (
- c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type]
- == b"HTTP/1.1 200 \r\nConnection: close\r\n\r\n"
- )
-
- assert c.send(Data(data=b"12345")) == b"12345"
- assert c.send(EndOfMessage()) == b""
- assert c.our_state is MUST_CLOSE
-
- # Check that it works if they do send Content-Length
- c = Connection(SERVER)
- # NB: no host header
- assert receive_and_get(c, b"POST / HTTP/1.0\r\nContent-Length: 10\r\n\r\n1") == [
- Request(
- method="POST",
- target="/",
- headers=[("Content-Length", "10")],
- http_version="1.0",
- ),
- Data(data=b"1"),
- ]
- assert receive_and_get(c, b"234567890") == [Data(data=b"234567890"), EndOfMessage()]
- assert c.their_state is MUST_CLOSE
- assert receive_and_get(c, b"") == [ConnectionClosed()]
-
-
-def test_automatic_transfer_encoding_in_response() -> None:
- # Check that in responses, the user can specify either Transfer-Encoding:
- # chunked or no framing at all, and in both cases we automatically select
- # the right option depending on whether the peer speaks HTTP/1.0 or
- # HTTP/1.1
- for user_headers in [
- [("Transfer-Encoding", "chunked")],
- [],
- # In fact, this even works if Content-Length is set,
- # because if both are set then Transfer-Encoding wins
- [("Transfer-Encoding", "chunked"), ("Content-Length", "100")],
- ]:
- user_headers = cast(List[Tuple[str, str]], user_headers)
- p = ConnectionPair()
- p.send(
- CLIENT,
- [
- Request(method="GET", target="/", headers=[("Host", "example.com")]),
- EndOfMessage(),
- ],
- )
- # When speaking to HTTP/1.1 client, all of the above cases get
- # normalized to Transfer-Encoding: chunked
- p.send(
- SERVER,
- Response(status_code=200, headers=user_headers),
- expect=Response(
- status_code=200, headers=[("Transfer-Encoding", "chunked")]
- ),
- )
-
- # When speaking to HTTP/1.0 client, all of the above cases get
- # normalized to no-framing-headers
- c = Connection(SERVER)
- receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n")
- assert (
- c.send(Response(status_code=200, headers=user_headers))
- == b"HTTP/1.1 200 \r\nConnection: close\r\n\r\n"
- )
- assert c.send(Data(data=b"12345")) == b"12345"
-
-
-def test_automagic_connection_close_handling() -> None:
- p = ConnectionPair()
- # If the user explicitly sets Connection: close, then we notice and
- # respect it
- p.send(
- CLIENT,
- [
- Request(
- method="GET",
- target="/",
- headers=[("Host", "example.com"), ("Connection", "close")],
- ),
- EndOfMessage(),
- ],
- )
- for conn in p.conns:
- assert conn.states[CLIENT] is MUST_CLOSE
- # And if the client sets it, the server automatically echoes it back
- p.send(
- SERVER,
- # no header here...
- [Response(status_code=204, headers=[]), EndOfMessage()], # type: ignore[arg-type]
- # ...but oh look, it arrived anyway
- expect=[
- Response(status_code=204, headers=[("connection", "close")]),
- EndOfMessage(),
- ],
- )
- for conn in p.conns:
- assert conn.states == {CLIENT: MUST_CLOSE, SERVER: MUST_CLOSE}
-
-
-def test_100_continue() -> None:
- def setup() -> ConnectionPair:
- p = ConnectionPair()
- p.send(
- CLIENT,
- Request(
- method="GET",
- target="/",
- headers=[
- ("Host", "example.com"),
- ("Content-Length", "100"),
- ("Expect", "100-continue"),
- ],
- ),
- )
- for conn in p.conns:
- assert conn.client_is_waiting_for_100_continue
- assert not p.conn[CLIENT].they_are_waiting_for_100_continue
- assert p.conn[SERVER].they_are_waiting_for_100_continue
- return p
-
- # Disabled by 100 Continue
- p = setup()
- p.send(SERVER, InformationalResponse(status_code=100, headers=[])) # type: ignore[arg-type]
- for conn in p.conns:
- assert not conn.client_is_waiting_for_100_continue
- assert not conn.they_are_waiting_for_100_continue
-
- # Disabled by a real response
- p = setup()
- p.send(
- SERVER, Response(status_code=200, headers=[("Transfer-Encoding", "chunked")])
- )
- for conn in p.conns:
- assert not conn.client_is_waiting_for_100_continue
- assert not conn.they_are_waiting_for_100_continue
-
- # Disabled by the client going ahead and sending stuff anyway
- p = setup()
- p.send(CLIENT, Data(data=b"12345"))
- for conn in p.conns:
- assert not conn.client_is_waiting_for_100_continue
- assert not conn.they_are_waiting_for_100_continue
-
-
-def test_max_incomplete_event_size_countermeasure() -> None:
- # Infinitely long headers are definitely not okay
- c = Connection(SERVER)
- c.receive_data(b"GET / HTTP/1.0\r\nEndless: ")
- assert c.next_event() is NEED_DATA
- with pytest.raises(RemoteProtocolError):
- while True:
- c.receive_data(b"a" * 1024)
- c.next_event()
-
- # Checking that the same header is accepted / rejected depending on the
- # max_incomplete_event_size setting:
- c = Connection(SERVER, max_incomplete_event_size=5000)
- c.receive_data(b"GET / HTTP/1.0\r\nBig: ")
- c.receive_data(b"a" * 4000)
- c.receive_data(b"\r\n\r\n")
- assert get_all_events(c) == [
- Request(
- method="GET", target="/", http_version="1.0", headers=[("big", "a" * 4000)]
- ),
- EndOfMessage(),
- ]
-
- c = Connection(SERVER, max_incomplete_event_size=4000)
- c.receive_data(b"GET / HTTP/1.0\r\nBig: ")
- c.receive_data(b"a" * 4000)
- with pytest.raises(RemoteProtocolError):
- c.next_event()
-
- # Temporarily exceeding the size limit is fine, as long as its done with
- # complete events:
- c = Connection(SERVER, max_incomplete_event_size=5000)
- c.receive_data(b"GET / HTTP/1.0\r\nContent-Length: 10000")
- c.receive_data(b"\r\n\r\n" + b"a" * 10000)
- assert get_all_events(c) == [
- Request(
- method="GET",
- target="/",
- http_version="1.0",
- headers=[("Content-Length", "10000")],
- ),
- Data(data=b"a" * 10000),
- EndOfMessage(),
- ]
-
- c = Connection(SERVER, max_incomplete_event_size=100)
- # Two pipelined requests to create a way-too-big receive buffer... but
- # it's fine because we're not checking
- c.receive_data(
- b"GET /1 HTTP/1.1\r\nHost: a\r\n\r\n"
- b"GET /2 HTTP/1.1\r\nHost: b\r\n\r\n" + b"X" * 1000
- )
- assert get_all_events(c) == [
- Request(method="GET", target="/1", headers=[("host", "a")]),
- EndOfMessage(),
- ]
- # Even more data comes in, still no problem
- c.receive_data(b"X" * 1000)
- # We can respond and reuse to get the second pipelined request
- c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type]
- c.send(EndOfMessage())
- c.start_next_cycle()
- assert get_all_events(c) == [
- Request(method="GET", target="/2", headers=[("host", "b")]),
- EndOfMessage(),
- ]
- # But once we unpause and try to read the next message, and find that it's
- # incomplete and the buffer is *still* way too large, then *that's* a
- # problem:
- c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type]
- c.send(EndOfMessage())
- c.start_next_cycle()
- with pytest.raises(RemoteProtocolError):
- c.next_event()
-
-
-def test_reuse_simple() -> None:
- p = ConnectionPair()
- p.send(
- CLIENT,
- [Request(method="GET", target="/", headers=[("Host", "a")]), EndOfMessage()],
- )
- p.send(
- SERVER,
- [
- Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]),
- EndOfMessage(),
- ],
- )
- for conn in p.conns:
- assert conn.states == {CLIENT: DONE, SERVER: DONE}
- conn.start_next_cycle()
-
- p.send(
- CLIENT,
- [
- Request(method="DELETE", target="/foo", headers=[("Host", "a")]),
- EndOfMessage(),
- ],
- )
- p.send(
- SERVER,
- [
- Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]),
- EndOfMessage(),
- ],
- )
-
-
-def test_pipelining() -> None:
- # Client doesn't support pipelining, so we have to do this by hand
- c = Connection(SERVER)
- assert c.next_event() is NEED_DATA
- # 3 requests all bunched up
- c.receive_data(
- b"GET /1 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n"
- b"12345"
- b"GET /2 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n"
- b"67890"
- b"GET /3 HTTP/1.1\r\nHost: a.com\r\n\r\n"
- )
- assert get_all_events(c) == [
- Request(
- method="GET",
- target="/1",
- headers=[("Host", "a.com"), ("Content-Length", "5")],
- ),
- Data(data=b"12345"),
- EndOfMessage(),
- ]
- assert c.their_state is DONE
- assert c.our_state is SEND_RESPONSE
-
- assert c.next_event() is PAUSED
-
- c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type]
- c.send(EndOfMessage())
- assert c.their_state is DONE
- assert c.our_state is DONE
-
- c.start_next_cycle()
-
- assert get_all_events(c) == [
- Request(
- method="GET",
- target="/2",
- headers=[("Host", "a.com"), ("Content-Length", "5")],
- ),
- Data(data=b"67890"),
- EndOfMessage(),
- ]
- assert c.next_event() is PAUSED
- c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type]
- c.send(EndOfMessage())
- c.start_next_cycle()
-
- assert get_all_events(c) == [
- Request(method="GET", target="/3", headers=[("Host", "a.com")]),
- EndOfMessage(),
- ]
- # Doesn't pause this time, no trailing data
- assert c.next_event() is NEED_DATA
- c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type]
- c.send(EndOfMessage())
-
- # Arrival of more data triggers pause
- assert c.next_event() is NEED_DATA
- c.receive_data(b"SADF")
- assert c.next_event() is PAUSED
- assert c.trailing_data == (b"SADF", False)
- # If EOF arrives while paused, we don't see that either:
- c.receive_data(b"")
- assert c.trailing_data == (b"SADF", True)
- assert c.next_event() is PAUSED
- c.receive_data(b"")
- assert c.next_event() is PAUSED
- # Can't call receive_data with non-empty buf after closing it
- with pytest.raises(RuntimeError):
- c.receive_data(b"FDSA")
-
-
-def test_protocol_switch() -> None:
- for (req, deny, accept) in [
- (
- Request(
- method="CONNECT",
- target="example.com:443",
- headers=[("Host", "foo"), ("Content-Length", "1")],
- ),
- Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]),
- Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]),
- ),
- (
- Request(
- method="GET",
- target="/",
- headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")],
- ),
- Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]),
- InformationalResponse(status_code=101, headers=[("Upgrade", "a")]),
- ),
- (
- Request(
- method="CONNECT",
- target="example.com:443",
- headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")],
- ),
- Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]),
- # Accept CONNECT, not upgrade
- Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]),
- ),
- (
- Request(
- method="CONNECT",
- target="example.com:443",
- headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")],
- ),
- Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]),
- # Accept Upgrade, not CONNECT
- InformationalResponse(status_code=101, headers=[("Upgrade", "b")]),
- ),
- ]:
-
- def setup() -> ConnectionPair:
- p = ConnectionPair()
- p.send(CLIENT, req)
- # No switch-related state change stuff yet; the client has to
- # finish the request before that kicks in
- for conn in p.conns:
- assert conn.states[CLIENT] is SEND_BODY
- p.send(CLIENT, [Data(data=b"1"), EndOfMessage()])
- for conn in p.conns:
- assert conn.states[CLIENT] is MIGHT_SWITCH_PROTOCOL
- assert p.conn[SERVER].next_event() is PAUSED
- return p
-
- # Test deny case
- p = setup()
- p.send(SERVER, deny)
- for conn in p.conns:
- assert conn.states == {CLIENT: DONE, SERVER: SEND_BODY}
- p.send(SERVER, EndOfMessage())
- # Check that re-use is still allowed after a denial
- for conn in p.conns:
- conn.start_next_cycle()
-
- # Test accept case
- p = setup()
- p.send(SERVER, accept)
- for conn in p.conns:
- assert conn.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL}
- conn.receive_data(b"123")
- assert conn.next_event() is PAUSED
- conn.receive_data(b"456")
- assert conn.next_event() is PAUSED
- assert conn.trailing_data == (b"123456", False)
-
- # Pausing in might-switch, then recovery
- # (weird artificial case where the trailing data actually is valid
- # HTTP for some reason, because this makes it easier to test the state
- # logic)
- p = setup()
- sc = p.conn[SERVER]
- sc.receive_data(b"GET / HTTP/1.0\r\n\r\n")
- assert sc.next_event() is PAUSED
- assert sc.trailing_data == (b"GET / HTTP/1.0\r\n\r\n", False)
- sc.send(deny)
- assert sc.next_event() is PAUSED
- sc.send(EndOfMessage())
- sc.start_next_cycle()
- assert get_all_events(sc) == [
- Request(method="GET", target="/", headers=[], http_version="1.0"), # type: ignore[arg-type]
- EndOfMessage(),
- ]
-
- # When we're DONE, have no trailing data, and the connection gets
- # closed, we report ConnectionClosed(). When we're in might-switch or
- # switched, we don't.
- p = setup()
- sc = p.conn[SERVER]
- sc.receive_data(b"")
- assert sc.next_event() is PAUSED
- assert sc.trailing_data == (b"", True)
- p.send(SERVER, accept)
- assert sc.next_event() is PAUSED
-
- p = setup()
- sc = p.conn[SERVER]
- sc.receive_data(b"")
- assert sc.next_event() is PAUSED
- sc.send(deny)
- assert sc.next_event() == ConnectionClosed()
-
- # You can't send after switching protocols, or while waiting for a
- # protocol switch
- p = setup()
- with pytest.raises(LocalProtocolError):
- p.conn[CLIENT].send(
- Request(method="GET", target="/", headers=[("Host", "a")])
- )
- p = setup()
- p.send(SERVER, accept)
- with pytest.raises(LocalProtocolError):
- p.conn[SERVER].send(Data(data=b"123"))
-
-
-def test_close_simple() -> None:
- # Just immediately closing a new connection without anything having
- # happened yet.
- for (who_shot_first, who_shot_second) in [(CLIENT, SERVER), (SERVER, CLIENT)]:
-
- def setup() -> ConnectionPair:
- p = ConnectionPair()
- p.send(who_shot_first, ConnectionClosed())
- for conn in p.conns:
- assert conn.states == {
- who_shot_first: CLOSED,
- who_shot_second: MUST_CLOSE,
- }
- return p
-
- # You can keep putting b"" into a closed connection, and you keep
- # getting ConnectionClosed() out:
- p = setup()
- assert p.conn[who_shot_second].next_event() == ConnectionClosed()
- assert p.conn[who_shot_second].next_event() == ConnectionClosed()
- p.conn[who_shot_second].receive_data(b"")
- assert p.conn[who_shot_second].next_event() == ConnectionClosed()
- # Second party can close...
- p = setup()
- p.send(who_shot_second, ConnectionClosed())
- for conn in p.conns:
- assert conn.our_state is CLOSED
- assert conn.their_state is CLOSED
- # But trying to receive new data on a closed connection is a
- # RuntimeError (not ProtocolError, because the problem here isn't
- # violation of HTTP, it's violation of physics)
- p = setup()
- with pytest.raises(RuntimeError):
- p.conn[who_shot_second].receive_data(b"123")
- # And receiving new data on a MUST_CLOSE connection is a ProtocolError
- p = setup()
- p.conn[who_shot_first].receive_data(b"GET")
- with pytest.raises(RemoteProtocolError):
- p.conn[who_shot_first].next_event()
-
-
-def test_close_different_states() -> None:
- req = [
- Request(method="GET", target="/foo", headers=[("Host", "a")]),
- EndOfMessage(),
- ]
- resp = [
- Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]),
- EndOfMessage(),
- ]
-
- # Client before request
- p = ConnectionPair()
- p.send(CLIENT, ConnectionClosed())
- for conn in p.conns:
- assert conn.states == {CLIENT: CLOSED, SERVER: MUST_CLOSE}
-
- # Client after request
- p = ConnectionPair()
- p.send(CLIENT, req)
- p.send(CLIENT, ConnectionClosed())
- for conn in p.conns:
- assert conn.states == {CLIENT: CLOSED, SERVER: SEND_RESPONSE}
-
- # Server after request -> not allowed
- p = ConnectionPair()
- p.send(CLIENT, req)
- with pytest.raises(LocalProtocolError):
- p.conn[SERVER].send(ConnectionClosed())
- p.conn[CLIENT].receive_data(b"")
- with pytest.raises(RemoteProtocolError):
- p.conn[CLIENT].next_event()
-
- # Server after response
- p = ConnectionPair()
- p.send(CLIENT, req)
- p.send(SERVER, resp)
- p.send(SERVER, ConnectionClosed())
- for conn in p.conns:
- assert conn.states == {CLIENT: MUST_CLOSE, SERVER: CLOSED}
-
- # Both after closing (ConnectionClosed() is idempotent)
- p = ConnectionPair()
- p.send(CLIENT, req)
- p.send(SERVER, resp)
- p.send(CLIENT, ConnectionClosed())
- p.send(SERVER, ConnectionClosed())
- p.send(CLIENT, ConnectionClosed())
- p.send(SERVER, ConnectionClosed())
-
- # In the middle of sending -> not allowed
- p = ConnectionPair()
- p.send(
- CLIENT,
- Request(
- method="GET", target="/", headers=[("Host", "a"), ("Content-Length", "10")]
- ),
- )
- with pytest.raises(LocalProtocolError):
- p.conn[CLIENT].send(ConnectionClosed())
- p.conn[SERVER].receive_data(b"")
- with pytest.raises(RemoteProtocolError):
- p.conn[SERVER].next_event()
-
-
-# Receive several requests and then client shuts down their side of the
-# connection; we can respond to each
-def test_pipelined_close() -> None:
- c = Connection(SERVER)
- # 2 requests then a close
- c.receive_data(
- b"GET /1 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n"
- b"12345"
- b"GET /2 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n"
- b"67890"
- )
- c.receive_data(b"")
- assert get_all_events(c) == [
- Request(
- method="GET",
- target="/1",
- headers=[("host", "a.com"), ("content-length", "5")],
- ),
- Data(data=b"12345"),
- EndOfMessage(),
- ]
- assert c.states[CLIENT] is DONE
- c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type]
- c.send(EndOfMessage())
- assert c.states[SERVER] is DONE
- c.start_next_cycle()
- assert get_all_events(c) == [
- Request(
- method="GET",
- target="/2",
- headers=[("host", "a.com"), ("content-length", "5")],
- ),
- Data(data=b"67890"),
- EndOfMessage(),
- ConnectionClosed(),
- ]
- assert c.states == {CLIENT: CLOSED, SERVER: SEND_RESPONSE}
- c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type]
- c.send(EndOfMessage())
- assert c.states == {CLIENT: CLOSED, SERVER: MUST_CLOSE}
- c.send(ConnectionClosed())
- assert c.states == {CLIENT: CLOSED, SERVER: CLOSED}
-
-
-def test_sendfile() -> None:
- class SendfilePlaceholder:
- def __len__(self) -> int:
- return 10
-
- placeholder = SendfilePlaceholder()
-
- def setup(
- header: Tuple[str, str], http_version: str
- ) -> Tuple[Connection, Optional[List[bytes]]]:
- c = Connection(SERVER)
- receive_and_get(
- c, "GET / HTTP/{}\r\nHost: a\r\n\r\n".format(http_version).encode("ascii")
- )
- headers = []
- if header:
- headers.append(header)
- c.send(Response(status_code=200, headers=headers))
- return c, c.send_with_data_passthrough(Data(data=placeholder)) # type: ignore
-
- c, data = setup(("Content-Length", "10"), "1.1")
- assert data == [placeholder] # type: ignore
- # Raises an error if the connection object doesn't think we've sent
- # exactly 10 bytes
- c.send(EndOfMessage())
-
- _, data = setup(("Transfer-Encoding", "chunked"), "1.1")
- assert placeholder in data # type: ignore
- data[data.index(placeholder)] = b"x" * 10 # type: ignore
- assert b"".join(data) == b"a\r\nxxxxxxxxxx\r\n" # type: ignore
-
- c, data = setup(None, "1.0") # type: ignore
- assert data == [placeholder] # type: ignore
- assert c.our_state is SEND_BODY
-
-
-def test_errors() -> None:
- # After a receive error, you can't receive
- for role in [CLIENT, SERVER]:
- c = Connection(our_role=role)
- c.receive_data(b"gibberish\r\n\r\n")
- with pytest.raises(RemoteProtocolError):
- c.next_event()
- # Now any attempt to receive continues to raise
- assert c.their_state is ERROR
- assert c.our_state is not ERROR
- print(c._cstate.states)
- with pytest.raises(RemoteProtocolError):
- c.next_event()
- # But we can still yell at the client for sending us gibberish
- if role is SERVER:
- assert (
- c.send(Response(status_code=400, headers=[])) # type: ignore[arg-type]
- == b"HTTP/1.1 400 \r\nConnection: close\r\n\r\n"
- )
-
- # After an error sending, you can no longer send
- # (This is especially important for things like content-length errors,
- # where there's complex internal state being modified)
- def conn(role: Type[Sentinel]) -> Connection:
- c = Connection(our_role=role)
- if role is SERVER:
- # Put it into the state where it *could* send a response...
- receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n")
- assert c.our_state is SEND_RESPONSE
- return c
-
- for role in [CLIENT, SERVER]:
- if role is CLIENT:
- # This HTTP/1.0 request won't be detected as bad until after we go
- # through the state machine and hit the writing code
- good = Request(method="GET", target="/", headers=[("Host", "example.com")])
- bad = Request(
- method="GET",
- target="/",
- headers=[("Host", "example.com")],
- http_version="1.0",
- )
- elif role is SERVER:
- good = Response(status_code=200, headers=[]) # type: ignore[arg-type,assignment]
- bad = Response(status_code=200, headers=[], http_version="1.0") # type: ignore[arg-type,assignment]
- # Make sure 'good' actually is good
- c = conn(role)
- c.send(good)
- assert c.our_state is not ERROR
- # Do that again, but this time sending 'bad' first
- c = conn(role)
- with pytest.raises(LocalProtocolError):
- c.send(bad)
- assert c.our_state is ERROR
- assert c.their_state is not ERROR
- # Now 'good' is not so good
- with pytest.raises(LocalProtocolError):
- c.send(good)
-
- # And check send_failed() too
- c = conn(role)
- c.send_failed()
- assert c.our_state is ERROR
- assert c.their_state is not ERROR
- # This is idempotent
- c.send_failed()
- assert c.our_state is ERROR
- assert c.their_state is not ERROR
-
-
-def test_idle_receive_nothing() -> None:
- # At one point this incorrectly raised an error
- for role in [CLIENT, SERVER]:
- c = Connection(role)
- assert c.next_event() is NEED_DATA
-
-
-def test_connection_drop() -> None:
- c = Connection(SERVER)
- c.receive_data(b"GET /")
- assert c.next_event() is NEED_DATA
- c.receive_data(b"")
- with pytest.raises(RemoteProtocolError):
- c.next_event()
-
-
-def test_408_request_timeout() -> None:
- # Should be able to send this spontaneously as a server without seeing
- # anything from client
- p = ConnectionPair()
- p.send(SERVER, Response(status_code=408, headers=[(b"connection", b"close")]))
-
-
-# This used to raise IndexError
-def test_empty_request() -> None:
- c = Connection(SERVER)
- c.receive_data(b"\r\n")
- with pytest.raises(RemoteProtocolError):
- c.next_event()
-
-
-# This used to raise IndexError
-def test_empty_response() -> None:
- c = Connection(CLIENT)
- c.send(Request(method="GET", target="/", headers=[("Host", "a")]))
- c.receive_data(b"\r\n")
- with pytest.raises(RemoteProtocolError):
- c.next_event()
-
-
-@pytest.mark.parametrize(
- "data",
- [
- b"\x00",
- b"\x20",
- b"\x16\x03\x01\x00\xa5", # Typical start of a TLS Client Hello
- ],
-)
-def test_early_detection_of_invalid_request(data: bytes) -> None:
- c = Connection(SERVER)
- # Early detection should occur before even receiving a `\r\n`
- c.receive_data(data)
- with pytest.raises(RemoteProtocolError):
- c.next_event()
-
-
-@pytest.mark.parametrize(
- "data",
- [
- b"\x00",
- b"\x20",
- b"\x16\x03\x03\x00\x31", # Typical start of a TLS Server Hello
- ],
-)
-def test_early_detection_of_invalid_response(data: bytes) -> None:
- c = Connection(CLIENT)
- # Early detection should occur before even receiving a `\r\n`
- c.receive_data(data)
- with pytest.raises(RemoteProtocolError):
- c.next_event()
-
-
-# This used to give different headers for HEAD and GET.
-# The correct way to handle HEAD is to put whatever headers we *would* have
-# put if it were a GET -- even though we know that for HEAD, those headers
-# will be ignored.
-def test_HEAD_framing_headers() -> None:
- def setup(method: bytes, http_version: bytes) -> Connection:
- c = Connection(SERVER)
- c.receive_data(
- method + b" / HTTP/" + http_version + b"\r\n" + b"Host: example.com\r\n\r\n"
- )
- assert type(c.next_event()) is Request
- assert type(c.next_event()) is EndOfMessage
- return c
-
- for method in [b"GET", b"HEAD"]:
- # No Content-Length, HTTP/1.1 peer, should use chunked
- c = setup(method, b"1.1")
- assert (
- c.send(Response(status_code=200, headers=[])) == b"HTTP/1.1 200 \r\n" # type: ignore[arg-type]
- b"Transfer-Encoding: chunked\r\n\r\n"
- )
-
- # No Content-Length, HTTP/1.0 peer, frame with connection: close
- c = setup(method, b"1.0")
- assert (
- c.send(Response(status_code=200, headers=[])) == b"HTTP/1.1 200 \r\n" # type: ignore[arg-type]
- b"Connection: close\r\n\r\n"
- )
-
- # Content-Length + Transfer-Encoding, TE wins
- c = setup(method, b"1.1")
- assert (
- c.send(
- Response(
- status_code=200,
- headers=[
- ("Content-Length", "100"),
- ("Transfer-Encoding", "chunked"),
- ],
- )
- )
- == b"HTTP/1.1 200 \r\n"
- b"Transfer-Encoding: chunked\r\n\r\n"
- )
-
-
-def test_special_exceptions_for_lost_connection_in_message_body() -> None:
- c = Connection(SERVER)
- c.receive_data(
- b"POST / HTTP/1.1\r\n" b"Host: example.com\r\n" b"Content-Length: 100\r\n\r\n"
- )
- assert type(c.next_event()) is Request
- assert c.next_event() is NEED_DATA
- c.receive_data(b"12345")
- assert c.next_event() == Data(data=b"12345")
- c.receive_data(b"")
- with pytest.raises(RemoteProtocolError) as excinfo:
- c.next_event()
- assert "received 5 bytes" in str(excinfo.value)
- assert "expected 100" in str(excinfo.value)
-
- c = Connection(SERVER)
- c.receive_data(
- b"POST / HTTP/1.1\r\n"
- b"Host: example.com\r\n"
- b"Transfer-Encoding: chunked\r\n\r\n"
- )
- assert type(c.next_event()) is Request
- assert c.next_event() is NEED_DATA
- c.receive_data(b"8\r\n012345")
- assert c.next_event().data == b"012345" # type: ignore
- c.receive_data(b"")
- with pytest.raises(RemoteProtocolError) as excinfo:
- c.next_event()
- assert "incomplete chunked read" in str(excinfo.value)
diff --git a/venv/lib/python3.11/site-packages/h11/tests/test_events.py b/venv/lib/python3.11/site-packages/h11/tests/test_events.py
deleted file mode 100644
index bc6c313..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/test_events.py
+++ /dev/null
@@ -1,150 +0,0 @@
-from http import HTTPStatus
-
-import pytest
-
-from .. import _events
-from .._events import (
- ConnectionClosed,
- Data,
- EndOfMessage,
- Event,
- InformationalResponse,
- Request,
- Response,
-)
-from .._util import LocalProtocolError
-
-
-def test_events() -> None:
- with pytest.raises(LocalProtocolError):
- # Missing Host:
- req = Request(
- method="GET", target="/", headers=[("a", "b")], http_version="1.1"
- )
- # But this is okay (HTTP/1.0)
- req = Request(method="GET", target="/", headers=[("a", "b")], http_version="1.0")
- # fields are normalized
- assert req.method == b"GET"
- assert req.target == b"/"
- assert req.headers == [(b"a", b"b")]
- assert req.http_version == b"1.0"
-
- # This is also okay -- has a Host (with weird capitalization, which is ok)
- req = Request(
- method="GET",
- target="/",
- headers=[("a", "b"), ("hOSt", "example.com")],
- http_version="1.1",
- )
- # we normalize header capitalization
- assert req.headers == [(b"a", b"b"), (b"host", b"example.com")]
-
- # Multiple host is bad too
- with pytest.raises(LocalProtocolError):
- req = Request(
- method="GET",
- target="/",
- headers=[("Host", "a"), ("Host", "a")],
- http_version="1.1",
- )
- # Even for HTTP/1.0
- with pytest.raises(LocalProtocolError):
- req = Request(
- method="GET",
- target="/",
- headers=[("Host", "a"), ("Host", "a")],
- http_version="1.0",
- )
-
- # Header values are validated
- for bad_char in "\x00\r\n\f\v":
- with pytest.raises(LocalProtocolError):
- req = Request(
- method="GET",
- target="/",
- headers=[("Host", "a"), ("Foo", "asd" + bad_char)],
- http_version="1.0",
- )
-
- # But for compatibility we allow non-whitespace control characters, even
- # though they're forbidden by the spec.
- Request(
- method="GET",
- target="/",
- headers=[("Host", "a"), ("Foo", "asd\x01\x02\x7f")],
- http_version="1.0",
- )
-
- # Request target is validated
- for bad_byte in b"\x00\x20\x7f\xee":
- target = bytearray(b"/")
- target.append(bad_byte)
- with pytest.raises(LocalProtocolError):
- Request(
- method="GET", target=target, headers=[("Host", "a")], http_version="1.1"
- )
-
- # Request method is validated
- with pytest.raises(LocalProtocolError):
- Request(
- method="GET / HTTP/1.1",
- target=target,
- headers=[("Host", "a")],
- http_version="1.1",
- )
-
- ir = InformationalResponse(status_code=100, headers=[("Host", "a")])
- assert ir.status_code == 100
- assert ir.headers == [(b"host", b"a")]
- assert ir.http_version == b"1.1"
-
- with pytest.raises(LocalProtocolError):
- InformationalResponse(status_code=200, headers=[("Host", "a")])
-
- resp = Response(status_code=204, headers=[], http_version="1.0") # type: ignore[arg-type]
- assert resp.status_code == 204
- assert resp.headers == []
- assert resp.http_version == b"1.0"
-
- with pytest.raises(LocalProtocolError):
- resp = Response(status_code=100, headers=[], http_version="1.0") # type: ignore[arg-type]
-
- with pytest.raises(LocalProtocolError):
- Response(status_code="100", headers=[], http_version="1.0") # type: ignore[arg-type]
-
- with pytest.raises(LocalProtocolError):
- InformationalResponse(status_code=b"100", headers=[], http_version="1.0") # type: ignore[arg-type]
-
- d = Data(data=b"asdf")
- assert d.data == b"asdf"
-
- eom = EndOfMessage()
- assert eom.headers == []
-
- cc = ConnectionClosed()
- assert repr(cc) == "ConnectionClosed()"
-
-
-def test_intenum_status_code() -> None:
- # https://github.com/python-hyper/h11/issues/72
-
- r = Response(status_code=HTTPStatus.OK, headers=[], http_version="1.0") # type: ignore[arg-type]
- assert r.status_code == HTTPStatus.OK
- assert type(r.status_code) is not type(HTTPStatus.OK)
- assert type(r.status_code) is int
-
-
-def test_header_casing() -> None:
- r = Request(
- method="GET",
- target="/",
- headers=[("Host", "example.org"), ("Connection", "keep-alive")],
- http_version="1.1",
- )
- assert len(r.headers) == 2
- assert r.headers[0] == (b"host", b"example.org")
- assert r.headers == [(b"host", b"example.org"), (b"connection", b"keep-alive")]
- assert r.headers.raw_items() == [
- (b"Host", b"example.org"),
- (b"Connection", b"keep-alive"),
- ]
diff --git a/venv/lib/python3.11/site-packages/h11/tests/test_headers.py b/venv/lib/python3.11/site-packages/h11/tests/test_headers.py
deleted file mode 100644
index ba53d08..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/test_headers.py
+++ /dev/null
@@ -1,157 +0,0 @@
-import pytest
-
-from .._events import Request
-from .._headers import (
- get_comma_header,
- has_expect_100_continue,
- Headers,
- normalize_and_validate,
- set_comma_header,
-)
-from .._util import LocalProtocolError
-
-
-def test_normalize_and_validate() -> None:
- assert normalize_and_validate([("foo", "bar")]) == [(b"foo", b"bar")]
- assert normalize_and_validate([(b"foo", b"bar")]) == [(b"foo", b"bar")]
-
- # no leading/trailing whitespace in names
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([(b"foo ", "bar")])
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([(b" foo", "bar")])
-
- # no weird characters in names
- with pytest.raises(LocalProtocolError) as excinfo:
- normalize_and_validate([(b"foo bar", b"baz")])
- assert "foo bar" in str(excinfo.value)
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([(b"foo\x00bar", b"baz")])
- # Not even 8-bit characters:
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([(b"foo\xffbar", b"baz")])
- # And not even the control characters we allow in values:
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([(b"foo\x01bar", b"baz")])
-
- # no return or NUL characters in values
- with pytest.raises(LocalProtocolError) as excinfo:
- normalize_and_validate([("foo", "bar\rbaz")])
- assert "bar\\rbaz" in str(excinfo.value)
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("foo", "bar\nbaz")])
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("foo", "bar\x00baz")])
- # no leading/trailing whitespace
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("foo", "barbaz ")])
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("foo", " barbaz")])
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("foo", "barbaz\t")])
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("foo", "\tbarbaz")])
-
- # content-length
- assert normalize_and_validate([("Content-Length", "1")]) == [
- (b"content-length", b"1")
- ]
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("Content-Length", "asdf")])
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("Content-Length", "1x")])
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("Content-Length", "1"), ("Content-Length", "2")])
- assert normalize_and_validate(
- [("Content-Length", "0"), ("Content-Length", "0")]
- ) == [(b"content-length", b"0")]
- assert normalize_and_validate([("Content-Length", "0 , 0")]) == [
- (b"content-length", b"0")
- ]
- with pytest.raises(LocalProtocolError):
- normalize_and_validate(
- [("Content-Length", "1"), ("Content-Length", "1"), ("Content-Length", "2")]
- )
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("Content-Length", "1 , 1,2")])
-
- # transfer-encoding
- assert normalize_and_validate([("Transfer-Encoding", "chunked")]) == [
- (b"transfer-encoding", b"chunked")
- ]
- assert normalize_and_validate([("Transfer-Encoding", "cHuNkEd")]) == [
- (b"transfer-encoding", b"chunked")
- ]
- with pytest.raises(LocalProtocolError) as excinfo:
- normalize_and_validate([("Transfer-Encoding", "gzip")])
- assert excinfo.value.error_status_hint == 501 # Not Implemented
- with pytest.raises(LocalProtocolError) as excinfo:
- normalize_and_validate(
- [("Transfer-Encoding", "chunked"), ("Transfer-Encoding", "gzip")]
- )
- assert excinfo.value.error_status_hint == 501 # Not Implemented
-
-
-def test_get_set_comma_header() -> None:
- headers = normalize_and_validate(
- [
- ("Connection", "close"),
- ("whatever", "something"),
- ("connectiON", "fOo,, , BAR"),
- ]
- )
-
- assert get_comma_header(headers, b"connection") == [b"close", b"foo", b"bar"]
-
- headers = set_comma_header(headers, b"newthing", ["a", "b"]) # type: ignore
-
- with pytest.raises(LocalProtocolError):
- set_comma_header(headers, b"newthing", [" a", "b"]) # type: ignore
-
- assert headers == [
- (b"connection", b"close"),
- (b"whatever", b"something"),
- (b"connection", b"fOo,, , BAR"),
- (b"newthing", b"a"),
- (b"newthing", b"b"),
- ]
-
- headers = set_comma_header(headers, b"whatever", ["different thing"]) # type: ignore
-
- assert headers == [
- (b"connection", b"close"),
- (b"connection", b"fOo,, , BAR"),
- (b"newthing", b"a"),
- (b"newthing", b"b"),
- (b"whatever", b"different thing"),
- ]
-
-
-def test_has_100_continue() -> None:
- assert has_expect_100_continue(
- Request(
- method="GET",
- target="/",
- headers=[("Host", "example.com"), ("Expect", "100-continue")],
- )
- )
- assert not has_expect_100_continue(
- Request(method="GET", target="/", headers=[("Host", "example.com")])
- )
- # Case insensitive
- assert has_expect_100_continue(
- Request(
- method="GET",
- target="/",
- headers=[("Host", "example.com"), ("Expect", "100-Continue")],
- )
- )
- # Doesn't work in HTTP/1.0
- assert not has_expect_100_continue(
- Request(
- method="GET",
- target="/",
- headers=[("Host", "example.com"), ("Expect", "100-continue")],
- http_version="1.0",
- )
- )
diff --git a/venv/lib/python3.11/site-packages/h11/tests/test_helpers.py b/venv/lib/python3.11/site-packages/h11/tests/test_helpers.py
deleted file mode 100644
index c329c76..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/test_helpers.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from .._events import (
- ConnectionClosed,
- Data,
- EndOfMessage,
- Event,
- InformationalResponse,
- Request,
- Response,
-)
-from .helpers import normalize_data_events
-
-
-def test_normalize_data_events() -> None:
- assert normalize_data_events(
- [
- Data(data=bytearray(b"1")),
- Data(data=b"2"),
- Response(status_code=200, headers=[]), # type: ignore[arg-type]
- Data(data=b"3"),
- Data(data=b"4"),
- EndOfMessage(),
- Data(data=b"5"),
- Data(data=b"6"),
- Data(data=b"7"),
- ]
- ) == [
- Data(data=b"12"),
- Response(status_code=200, headers=[]), # type: ignore[arg-type]
- Data(data=b"34"),
- EndOfMessage(),
- Data(data=b"567"),
- ]
diff --git a/venv/lib/python3.11/site-packages/h11/tests/test_io.py b/venv/lib/python3.11/site-packages/h11/tests/test_io.py
deleted file mode 100644
index 2b47c0e..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/test_io.py
+++ /dev/null
@@ -1,572 +0,0 @@
-from typing import Any, Callable, Generator, List
-
-import pytest
-
-from .._events import (
- ConnectionClosed,
- Data,
- EndOfMessage,
- Event,
- InformationalResponse,
- Request,
- Response,
-)
-from .._headers import Headers, normalize_and_validate
-from .._readers import (
- _obsolete_line_fold,
- ChunkedReader,
- ContentLengthReader,
- Http10Reader,
- READERS,
-)
-from .._receivebuffer import ReceiveBuffer
-from .._state import (
- CLIENT,
- CLOSED,
- DONE,
- IDLE,
- MIGHT_SWITCH_PROTOCOL,
- MUST_CLOSE,
- SEND_BODY,
- SEND_RESPONSE,
- SERVER,
- SWITCHED_PROTOCOL,
-)
-from .._util import LocalProtocolError
-from .._writers import (
- ChunkedWriter,
- ContentLengthWriter,
- Http10Writer,
- write_any_response,
- write_headers,
- write_request,
- WRITERS,
-)
-from .helpers import normalize_data_events
-
-SIMPLE_CASES = [
- (
- (CLIENT, IDLE),
- Request(
- method="GET",
- target="/a",
- headers=[("Host", "foo"), ("Connection", "close")],
- ),
- b"GET /a HTTP/1.1\r\nHost: foo\r\nConnection: close\r\n\r\n",
- ),
- (
- (SERVER, SEND_RESPONSE),
- Response(status_code=200, headers=[("Connection", "close")], reason=b"OK"),
- b"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n",
- ),
- (
- (SERVER, SEND_RESPONSE),
- Response(status_code=200, headers=[], reason=b"OK"), # type: ignore[arg-type]
- b"HTTP/1.1 200 OK\r\n\r\n",
- ),
- (
- (SERVER, SEND_RESPONSE),
- InformationalResponse(
- status_code=101, headers=[("Upgrade", "websocket")], reason=b"Upgrade"
- ),
- b"HTTP/1.1 101 Upgrade\r\nUpgrade: websocket\r\n\r\n",
- ),
- (
- (SERVER, SEND_RESPONSE),
- InformationalResponse(status_code=101, headers=[], reason=b"Upgrade"), # type: ignore[arg-type]
- b"HTTP/1.1 101 Upgrade\r\n\r\n",
- ),
-]
-
-
-def dowrite(writer: Callable[..., None], obj: Any) -> bytes:
- got_list: List[bytes] = []
- writer(obj, got_list.append)
- return b"".join(got_list)
-
-
-def tw(writer: Any, obj: Any, expected: Any) -> None:
- got = dowrite(writer, obj)
- assert got == expected
-
-
-def makebuf(data: bytes) -> ReceiveBuffer:
- buf = ReceiveBuffer()
- buf += data
- return buf
-
-
-def tr(reader: Any, data: bytes, expected: Any) -> None:
- def check(got: Any) -> None:
- assert got == expected
- # Headers should always be returned as bytes, not e.g. bytearray
- # https://github.com/python-hyper/wsproto/pull/54#issuecomment-377709478
- for name, value in getattr(got, "headers", []):
- assert type(name) is bytes
- assert type(value) is bytes
-
- # Simple: consume whole thing
- buf = makebuf(data)
- check(reader(buf))
- assert not buf
-
- # Incrementally growing buffer
- buf = ReceiveBuffer()
- for i in range(len(data)):
- assert reader(buf) is None
- buf += data[i : i + 1]
- check(reader(buf))
-
- # Trailing data
- buf = makebuf(data)
- buf += b"trailing"
- check(reader(buf))
- assert bytes(buf) == b"trailing"
-
-
-def test_writers_simple() -> None:
- for ((role, state), event, binary) in SIMPLE_CASES:
- tw(WRITERS[role, state], event, binary)
-
-
-def test_readers_simple() -> None:
- for ((role, state), event, binary) in SIMPLE_CASES:
- tr(READERS[role, state], binary, event)
-
-
-def test_writers_unusual() -> None:
- # Simple test of the write_headers utility routine
- tw(
- write_headers,
- normalize_and_validate([("foo", "bar"), ("baz", "quux")]),
- b"foo: bar\r\nbaz: quux\r\n\r\n",
- )
- tw(write_headers, Headers([]), b"\r\n")
-
- # We understand HTTP/1.0, but we don't speak it
- with pytest.raises(LocalProtocolError):
- tw(
- write_request,
- Request(
- method="GET",
- target="/",
- headers=[("Host", "foo"), ("Connection", "close")],
- http_version="1.0",
- ),
- None,
- )
- with pytest.raises(LocalProtocolError):
- tw(
- write_any_response,
- Response(
- status_code=200, headers=[("Connection", "close")], http_version="1.0"
- ),
- None,
- )
-
-
-def test_readers_unusual() -> None:
- # Reading HTTP/1.0
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.0\r\nSome: header\r\n\r\n",
- Request(
- method="HEAD",
- target="/foo",
- headers=[("Some", "header")],
- http_version="1.0",
- ),
- )
-
- # check no-headers, since it's only legal with HTTP/1.0
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.0\r\n\r\n",
- Request(method="HEAD", target="/foo", headers=[], http_version="1.0"), # type: ignore[arg-type]
- )
-
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.0 200 OK\r\nSome: header\r\n\r\n",
- Response(
- status_code=200,
- headers=[("Some", "header")],
- http_version="1.0",
- reason=b"OK",
- ),
- )
-
- # single-character header values (actually disallowed by the ABNF in RFC
- # 7230 -- this is a bug in the standard that we originally copied...)
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.0 200 OK\r\n" b"Foo: a a a a a \r\n\r\n",
- Response(
- status_code=200,
- headers=[("Foo", "a a a a a")],
- http_version="1.0",
- reason=b"OK",
- ),
- )
-
- # Empty headers -- also legal
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.0 200 OK\r\n" b"Foo:\r\n\r\n",
- Response(
- status_code=200, headers=[("Foo", "")], http_version="1.0", reason=b"OK"
- ),
- )
-
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.0 200 OK\r\n" b"Foo: \t \t \r\n\r\n",
- Response(
- status_code=200, headers=[("Foo", "")], http_version="1.0", reason=b"OK"
- ),
- )
-
- # Tolerate broken servers that leave off the response code
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.0 200\r\n" b"Foo: bar\r\n\r\n",
- Response(
- status_code=200, headers=[("Foo", "bar")], http_version="1.0", reason=b""
- ),
- )
-
- # Tolerate headers line endings (\r\n and \n)
- # \n\r\b between headers and body
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.1 200 OK\r\nSomeHeader: val\n\r\n",
- Response(
- status_code=200,
- headers=[("SomeHeader", "val")],
- http_version="1.1",
- reason="OK",
- ),
- )
-
- # delimited only with \n
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.1 200 OK\nSomeHeader1: val1\nSomeHeader2: val2\n\n",
- Response(
- status_code=200,
- headers=[("SomeHeader1", "val1"), ("SomeHeader2", "val2")],
- http_version="1.1",
- reason="OK",
- ),
- )
-
- # mixed \r\n and \n
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.1 200 OK\r\nSomeHeader1: val1\nSomeHeader2: val2\n\r\n",
- Response(
- status_code=200,
- headers=[("SomeHeader1", "val1"), ("SomeHeader2", "val2")],
- http_version="1.1",
- reason="OK",
- ),
- )
-
- # obsolete line folding
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1\r\n"
- b"Host: example.com\r\n"
- b"Some: multi-line\r\n"
- b" header\r\n"
- b"\tnonsense\r\n"
- b" \t \t\tI guess\r\n"
- b"Connection: close\r\n"
- b"More-nonsense: in the\r\n"
- b" last header \r\n\r\n",
- Request(
- method="HEAD",
- target="/foo",
- headers=[
- ("Host", "example.com"),
- ("Some", "multi-line header nonsense I guess"),
- ("Connection", "close"),
- ("More-nonsense", "in the last header"),
- ],
- ),
- )
-
- with pytest.raises(LocalProtocolError):
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1\r\n" b" folded: line\r\n\r\n",
- None,
- )
-
- with pytest.raises(LocalProtocolError):
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1\r\n" b"foo : line\r\n\r\n",
- None,
- )
- with pytest.raises(LocalProtocolError):
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1\r\n" b"foo\t: line\r\n\r\n",
- None,
- )
- with pytest.raises(LocalProtocolError):
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1\r\n" b"foo\t: line\r\n\r\n",
- None,
- )
- with pytest.raises(LocalProtocolError):
- tr(READERS[CLIENT, IDLE], b"HEAD /foo HTTP/1.1\r\n" b": line\r\n\r\n", None)
-
-
-def test__obsolete_line_fold_bytes() -> None:
- # _obsolete_line_fold has a defensive cast to bytearray, which is
- # necessary to protect against O(n^2) behavior in case anyone ever passes
- # in regular bytestrings... but right now we never pass in regular
- # bytestrings. so this test just exists to get some coverage on that
- # defensive cast.
- assert list(_obsolete_line_fold([b"aaa", b"bbb", b" ccc", b"ddd"])) == [
- b"aaa",
- bytearray(b"bbb ccc"),
- b"ddd",
- ]
-
-
-def _run_reader_iter(
- reader: Any, buf: bytes, do_eof: bool
-) -> Generator[Any, None, None]:
- while True:
- event = reader(buf)
- if event is None:
- break
- yield event
- # body readers have undefined behavior after returning EndOfMessage,
- # because this changes the state so they don't get called again
- if type(event) is EndOfMessage:
- break
- if do_eof:
- assert not buf
- yield reader.read_eof()
-
-
-def _run_reader(*args: Any) -> List[Event]:
- events = list(_run_reader_iter(*args))
- return normalize_data_events(events)
-
-
-def t_body_reader(thunk: Any, data: bytes, expected: Any, do_eof: bool = False) -> None:
- # Simple: consume whole thing
- print("Test 1")
- buf = makebuf(data)
- assert _run_reader(thunk(), buf, do_eof) == expected
-
- # Incrementally growing buffer
- print("Test 2")
- reader = thunk()
- buf = ReceiveBuffer()
- events = []
- for i in range(len(data)):
- events += _run_reader(reader, buf, False)
- buf += data[i : i + 1]
- events += _run_reader(reader, buf, do_eof)
- assert normalize_data_events(events) == expected
-
- is_complete = any(type(event) is EndOfMessage for event in expected)
- if is_complete and not do_eof:
- buf = makebuf(data + b"trailing")
- assert _run_reader(thunk(), buf, False) == expected
-
-
-def test_ContentLengthReader() -> None:
- t_body_reader(lambda: ContentLengthReader(0), b"", [EndOfMessage()])
-
- t_body_reader(
- lambda: ContentLengthReader(10),
- b"0123456789",
- [Data(data=b"0123456789"), EndOfMessage()],
- )
-
-
-def test_Http10Reader() -> None:
- t_body_reader(Http10Reader, b"", [EndOfMessage()], do_eof=True)
- t_body_reader(Http10Reader, b"asdf", [Data(data=b"asdf")], do_eof=False)
- t_body_reader(
- Http10Reader, b"asdf", [Data(data=b"asdf"), EndOfMessage()], do_eof=True
- )
-
-
-def test_ChunkedReader() -> None:
- t_body_reader(ChunkedReader, b"0\r\n\r\n", [EndOfMessage()])
-
- t_body_reader(
- ChunkedReader,
- b"0\r\nSome: header\r\n\r\n",
- [EndOfMessage(headers=[("Some", "header")])],
- )
-
- t_body_reader(
- ChunkedReader,
- b"5\r\n01234\r\n"
- + b"10\r\n0123456789abcdef\r\n"
- + b"0\r\n"
- + b"Some: header\r\n\r\n",
- [
- Data(data=b"012340123456789abcdef"),
- EndOfMessage(headers=[("Some", "header")]),
- ],
- )
-
- t_body_reader(
- ChunkedReader,
- b"5\r\n01234\r\n" + b"10\r\n0123456789abcdef\r\n" + b"0\r\n\r\n",
- [Data(data=b"012340123456789abcdef"), EndOfMessage()],
- )
-
- # handles upper and lowercase hex
- t_body_reader(
- ChunkedReader,
- b"aA\r\n" + b"x" * 0xAA + b"\r\n" + b"0\r\n\r\n",
- [Data(data=b"x" * 0xAA), EndOfMessage()],
- )
-
- # refuses arbitrarily long chunk integers
- with pytest.raises(LocalProtocolError):
- # Technically this is legal HTTP/1.1, but we refuse to process chunk
- # sizes that don't fit into 20 characters of hex
- t_body_reader(ChunkedReader, b"9" * 100 + b"\r\nxxx", [Data(data=b"xxx")])
-
- # refuses garbage in the chunk count
- with pytest.raises(LocalProtocolError):
- t_body_reader(ChunkedReader, b"10\x00\r\nxxx", None)
-
- # handles (and discards) "chunk extensions" omg wtf
- t_body_reader(
- ChunkedReader,
- b"5; hello=there\r\n"
- + b"xxxxx"
- + b"\r\n"
- + b'0; random="junk"; some=more; canbe=lonnnnngg\r\n\r\n',
- [Data(data=b"xxxxx"), EndOfMessage()],
- )
-
- t_body_reader(
- ChunkedReader,
- b"5 \r\n01234\r\n" + b"0\r\n\r\n",
- [Data(data=b"01234"), EndOfMessage()],
- )
-
-
-def test_ContentLengthWriter() -> None:
- w = ContentLengthWriter(5)
- assert dowrite(w, Data(data=b"123")) == b"123"
- assert dowrite(w, Data(data=b"45")) == b"45"
- assert dowrite(w, EndOfMessage()) == b""
-
- w = ContentLengthWriter(5)
- with pytest.raises(LocalProtocolError):
- dowrite(w, Data(data=b"123456"))
-
- w = ContentLengthWriter(5)
- dowrite(w, Data(data=b"123"))
- with pytest.raises(LocalProtocolError):
- dowrite(w, Data(data=b"456"))
-
- w = ContentLengthWriter(5)
- dowrite(w, Data(data=b"123"))
- with pytest.raises(LocalProtocolError):
- dowrite(w, EndOfMessage())
-
- w = ContentLengthWriter(5)
- dowrite(w, Data(data=b"123")) == b"123"
- dowrite(w, Data(data=b"45")) == b"45"
- with pytest.raises(LocalProtocolError):
- dowrite(w, EndOfMessage(headers=[("Etag", "asdf")]))
-
-
-def test_ChunkedWriter() -> None:
- w = ChunkedWriter()
- assert dowrite(w, Data(data=b"aaa")) == b"3\r\naaa\r\n"
- assert dowrite(w, Data(data=b"a" * 20)) == b"14\r\n" + b"a" * 20 + b"\r\n"
-
- assert dowrite(w, Data(data=b"")) == b""
-
- assert dowrite(w, EndOfMessage()) == b"0\r\n\r\n"
-
- assert (
- dowrite(w, EndOfMessage(headers=[("Etag", "asdf"), ("a", "b")]))
- == b"0\r\nEtag: asdf\r\na: b\r\n\r\n"
- )
-
-
-def test_Http10Writer() -> None:
- w = Http10Writer()
- assert dowrite(w, Data(data=b"1234")) == b"1234"
- assert dowrite(w, EndOfMessage()) == b""
-
- with pytest.raises(LocalProtocolError):
- dowrite(w, EndOfMessage(headers=[("Etag", "asdf")]))
-
-
-def test_reject_garbage_after_request_line() -> None:
- with pytest.raises(LocalProtocolError):
- tr(READERS[SERVER, SEND_RESPONSE], b"HTTP/1.0 200 OK\x00xxxx\r\n\r\n", None)
-
-
-def test_reject_garbage_after_response_line() -> None:
- with pytest.raises(LocalProtocolError):
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1 xxxxxx\r\n" b"Host: a\r\n\r\n",
- None,
- )
-
-
-def test_reject_garbage_in_header_line() -> None:
- with pytest.raises(LocalProtocolError):
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1\r\n" b"Host: foo\x00bar\r\n\r\n",
- None,
- )
-
-
-def test_reject_non_vchar_in_path() -> None:
- for bad_char in b"\x00\x20\x7f\xee":
- message = bytearray(b"HEAD /")
- message.append(bad_char)
- message.extend(b" HTTP/1.1\r\nHost: foobar\r\n\r\n")
- with pytest.raises(LocalProtocolError):
- tr(READERS[CLIENT, IDLE], message, None)
-
-
-# https://github.com/python-hyper/h11/issues/57
-def test_allow_some_garbage_in_cookies() -> None:
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1\r\n"
- b"Host: foo\r\n"
- b"Set-Cookie: ___utmvafIumyLc=kUd\x01UpAt; path=/; Max-Age=900\r\n"
- b"\r\n",
- Request(
- method="HEAD",
- target="/foo",
- headers=[
- ("Host", "foo"),
- ("Set-Cookie", "___utmvafIumyLc=kUd\x01UpAt; path=/; Max-Age=900"),
- ],
- ),
- )
-
-
-def test_host_comes_first() -> None:
- tw(
- write_headers,
- normalize_and_validate([("foo", "bar"), ("Host", "example.com")]),
- b"Host: example.com\r\nfoo: bar\r\n\r\n",
- )
diff --git a/venv/lib/python3.11/site-packages/h11/tests/test_receivebuffer.py b/venv/lib/python3.11/site-packages/h11/tests/test_receivebuffer.py
deleted file mode 100644
index 21a3870..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/test_receivebuffer.py
+++ /dev/null
@@ -1,135 +0,0 @@
-import re
-from typing import Tuple
-
-import pytest
-
-from .._receivebuffer import ReceiveBuffer
-
-
-def test_receivebuffer() -> None:
- b = ReceiveBuffer()
- assert not b
- assert len(b) == 0
- assert bytes(b) == b""
-
- b += b"123"
- assert b
- assert len(b) == 3
- assert bytes(b) == b"123"
-
- assert bytes(b) == b"123"
-
- assert b.maybe_extract_at_most(2) == b"12"
- assert b
- assert len(b) == 1
- assert bytes(b) == b"3"
-
- assert bytes(b) == b"3"
-
- assert b.maybe_extract_at_most(10) == b"3"
- assert bytes(b) == b""
-
- assert b.maybe_extract_at_most(10) is None
- assert not b
-
- ################################################################
- # maybe_extract_until_next
- ################################################################
-
- b += b"123\n456\r\n789\r\n"
-
- assert b.maybe_extract_next_line() == b"123\n456\r\n"
- assert bytes(b) == b"789\r\n"
-
- assert b.maybe_extract_next_line() == b"789\r\n"
- assert bytes(b) == b""
-
- b += b"12\r"
- assert b.maybe_extract_next_line() is None
- assert bytes(b) == b"12\r"
-
- b += b"345\n\r"
- assert b.maybe_extract_next_line() is None
- assert bytes(b) == b"12\r345\n\r"
-
- # here we stopped at the middle of b"\r\n" delimiter
-
- b += b"\n6789aaa123\r\n"
- assert b.maybe_extract_next_line() == b"12\r345\n\r\n"
- assert b.maybe_extract_next_line() == b"6789aaa123\r\n"
- assert b.maybe_extract_next_line() is None
- assert bytes(b) == b""
-
- ################################################################
- # maybe_extract_lines
- ################################################################
-
- b += b"123\r\na: b\r\nfoo:bar\r\n\r\ntrailing"
- lines = b.maybe_extract_lines()
- assert lines == [b"123", b"a: b", b"foo:bar"]
- assert bytes(b) == b"trailing"
-
- assert b.maybe_extract_lines() is None
-
- b += b"\r\n\r"
- assert b.maybe_extract_lines() is None
-
- assert b.maybe_extract_at_most(100) == b"trailing\r\n\r"
- assert not b
-
- # Empty body case (as happens at the end of chunked encoding if there are
- # no trailing headers, e.g.)
- b += b"\r\ntrailing"
- assert b.maybe_extract_lines() == []
- assert bytes(b) == b"trailing"
-
-
-@pytest.mark.parametrize(
- "data",
- [
- pytest.param(
- (
- b"HTTP/1.1 200 OK\r\n",
- b"Content-type: text/plain\r\n",
- b"Connection: close\r\n",
- b"\r\n",
- b"Some body",
- ),
- id="with_crlf_delimiter",
- ),
- pytest.param(
- (
- b"HTTP/1.1 200 OK\n",
- b"Content-type: text/plain\n",
- b"Connection: close\n",
- b"\n",
- b"Some body",
- ),
- id="with_lf_only_delimiter",
- ),
- pytest.param(
- (
- b"HTTP/1.1 200 OK\n",
- b"Content-type: text/plain\r\n",
- b"Connection: close\n",
- b"\n",
- b"Some body",
- ),
- id="with_mixed_crlf_and_lf",
- ),
- ],
-)
-def test_receivebuffer_for_invalid_delimiter(data: Tuple[bytes]) -> None:
- b = ReceiveBuffer()
-
- for line in data:
- b += line
-
- lines = b.maybe_extract_lines()
-
- assert lines == [
- b"HTTP/1.1 200 OK",
- b"Content-type: text/plain",
- b"Connection: close",
- ]
- assert bytes(b) == b"Some body"
diff --git a/venv/lib/python3.11/site-packages/h11/tests/test_state.py b/venv/lib/python3.11/site-packages/h11/tests/test_state.py
deleted file mode 100644
index bc974e6..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/test_state.py
+++ /dev/null
@@ -1,271 +0,0 @@
-import pytest
-
-from .._events import (
- ConnectionClosed,
- Data,
- EndOfMessage,
- Event,
- InformationalResponse,
- Request,
- Response,
-)
-from .._state import (
- _SWITCH_CONNECT,
- _SWITCH_UPGRADE,
- CLIENT,
- CLOSED,
- ConnectionState,
- DONE,
- IDLE,
- MIGHT_SWITCH_PROTOCOL,
- MUST_CLOSE,
- SEND_BODY,
- SEND_RESPONSE,
- SERVER,
- SWITCHED_PROTOCOL,
-)
-from .._util import LocalProtocolError
-
-
-def test_ConnectionState() -> None:
- cs = ConnectionState()
-
- # Basic event-triggered transitions
-
- assert cs.states == {CLIENT: IDLE, SERVER: IDLE}
-
- cs.process_event(CLIENT, Request)
- # The SERVER-Request special case:
- assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
-
- # Illegal transitions raise an error and nothing happens
- with pytest.raises(LocalProtocolError):
- cs.process_event(CLIENT, Request)
- assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
-
- cs.process_event(SERVER, InformationalResponse)
- assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
-
- cs.process_event(SERVER, Response)
- assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_BODY}
-
- cs.process_event(CLIENT, EndOfMessage)
- cs.process_event(SERVER, EndOfMessage)
- assert cs.states == {CLIENT: DONE, SERVER: DONE}
-
- # State-triggered transition
-
- cs.process_event(SERVER, ConnectionClosed)
- assert cs.states == {CLIENT: MUST_CLOSE, SERVER: CLOSED}
-
-
-def test_ConnectionState_keep_alive() -> None:
- # keep_alive = False
- cs = ConnectionState()
- cs.process_event(CLIENT, Request)
- cs.process_keep_alive_disabled()
- cs.process_event(CLIENT, EndOfMessage)
- assert cs.states == {CLIENT: MUST_CLOSE, SERVER: SEND_RESPONSE}
-
- cs.process_event(SERVER, Response)
- cs.process_event(SERVER, EndOfMessage)
- assert cs.states == {CLIENT: MUST_CLOSE, SERVER: MUST_CLOSE}
-
-
-def test_ConnectionState_keep_alive_in_DONE() -> None:
- # Check that if keep_alive is disabled when the CLIENT is already in DONE,
- # then this is sufficient to immediately trigger the DONE -> MUST_CLOSE
- # transition
- cs = ConnectionState()
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, EndOfMessage)
- assert cs.states[CLIENT] is DONE
- cs.process_keep_alive_disabled()
- assert cs.states[CLIENT] is MUST_CLOSE
-
-
-def test_ConnectionState_switch_denied() -> None:
- for switch_type in (_SWITCH_CONNECT, _SWITCH_UPGRADE):
- for deny_early in (True, False):
- cs = ConnectionState()
- cs.process_client_switch_proposal(switch_type)
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, Data)
- assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
-
- assert switch_type in cs.pending_switch_proposals
-
- if deny_early:
- # before client reaches DONE
- cs.process_event(SERVER, Response)
- assert not cs.pending_switch_proposals
-
- cs.process_event(CLIENT, EndOfMessage)
-
- if deny_early:
- assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY}
- else:
- assert cs.states == {
- CLIENT: MIGHT_SWITCH_PROTOCOL,
- SERVER: SEND_RESPONSE,
- }
-
- cs.process_event(SERVER, InformationalResponse)
- assert cs.states == {
- CLIENT: MIGHT_SWITCH_PROTOCOL,
- SERVER: SEND_RESPONSE,
- }
-
- cs.process_event(SERVER, Response)
- assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY}
- assert not cs.pending_switch_proposals
-
-
-_response_type_for_switch = {
- _SWITCH_UPGRADE: InformationalResponse,
- _SWITCH_CONNECT: Response,
- None: Response,
-}
-
-
-def test_ConnectionState_protocol_switch_accepted() -> None:
- for switch_event in [_SWITCH_UPGRADE, _SWITCH_CONNECT]:
- cs = ConnectionState()
- cs.process_client_switch_proposal(switch_event)
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, Data)
- assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
-
- cs.process_event(CLIENT, EndOfMessage)
- assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE}
-
- cs.process_event(SERVER, InformationalResponse)
- assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE}
-
- cs.process_event(SERVER, _response_type_for_switch[switch_event], switch_event)
- assert cs.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL}
-
-
-def test_ConnectionState_double_protocol_switch() -> None:
- # CONNECT + Upgrade is legal! Very silly, but legal. So we support
- # it. Because sometimes doing the silly thing is easier than not.
- for server_switch in [None, _SWITCH_UPGRADE, _SWITCH_CONNECT]:
- cs = ConnectionState()
- cs.process_client_switch_proposal(_SWITCH_UPGRADE)
- cs.process_client_switch_proposal(_SWITCH_CONNECT)
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, EndOfMessage)
- assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE}
- cs.process_event(
- SERVER, _response_type_for_switch[server_switch], server_switch
- )
- if server_switch is None:
- assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY}
- else:
- assert cs.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL}
-
-
-def test_ConnectionState_inconsistent_protocol_switch() -> None:
- for client_switches, server_switch in [
- ([], _SWITCH_CONNECT),
- ([], _SWITCH_UPGRADE),
- ([_SWITCH_UPGRADE], _SWITCH_CONNECT),
- ([_SWITCH_CONNECT], _SWITCH_UPGRADE),
- ]:
- cs = ConnectionState()
- for client_switch in client_switches: # type: ignore[attr-defined]
- cs.process_client_switch_proposal(client_switch)
- cs.process_event(CLIENT, Request)
- with pytest.raises(LocalProtocolError):
- cs.process_event(SERVER, Response, server_switch)
-
-
-def test_ConnectionState_keepalive_protocol_switch_interaction() -> None:
- # keep_alive=False + pending_switch_proposals
- cs = ConnectionState()
- cs.process_client_switch_proposal(_SWITCH_UPGRADE)
- cs.process_event(CLIENT, Request)
- cs.process_keep_alive_disabled()
- cs.process_event(CLIENT, Data)
- assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
-
- # the protocol switch "wins"
- cs.process_event(CLIENT, EndOfMessage)
- assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE}
-
- # but when the server denies the request, keep_alive comes back into play
- cs.process_event(SERVER, Response)
- assert cs.states == {CLIENT: MUST_CLOSE, SERVER: SEND_BODY}
-
-
-def test_ConnectionState_reuse() -> None:
- cs = ConnectionState()
-
- with pytest.raises(LocalProtocolError):
- cs.start_next_cycle()
-
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, EndOfMessage)
-
- with pytest.raises(LocalProtocolError):
- cs.start_next_cycle()
-
- cs.process_event(SERVER, Response)
- cs.process_event(SERVER, EndOfMessage)
-
- cs.start_next_cycle()
- assert cs.states == {CLIENT: IDLE, SERVER: IDLE}
-
- # No keepalive
-
- cs.process_event(CLIENT, Request)
- cs.process_keep_alive_disabled()
- cs.process_event(CLIENT, EndOfMessage)
- cs.process_event(SERVER, Response)
- cs.process_event(SERVER, EndOfMessage)
-
- with pytest.raises(LocalProtocolError):
- cs.start_next_cycle()
-
- # One side closed
-
- cs = ConnectionState()
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, EndOfMessage)
- cs.process_event(CLIENT, ConnectionClosed)
- cs.process_event(SERVER, Response)
- cs.process_event(SERVER, EndOfMessage)
-
- with pytest.raises(LocalProtocolError):
- cs.start_next_cycle()
-
- # Succesful protocol switch
-
- cs = ConnectionState()
- cs.process_client_switch_proposal(_SWITCH_UPGRADE)
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, EndOfMessage)
- cs.process_event(SERVER, InformationalResponse, _SWITCH_UPGRADE)
-
- with pytest.raises(LocalProtocolError):
- cs.start_next_cycle()
-
- # Failed protocol switch
-
- cs = ConnectionState()
- cs.process_client_switch_proposal(_SWITCH_UPGRADE)
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, EndOfMessage)
- cs.process_event(SERVER, Response)
- cs.process_event(SERVER, EndOfMessage)
-
- cs.start_next_cycle()
- assert cs.states == {CLIENT: IDLE, SERVER: IDLE}
-
-
-def test_server_request_is_illegal() -> None:
- # There used to be a bug in how we handled the Request special case that
- # made this allowed...
- cs = ConnectionState()
- with pytest.raises(LocalProtocolError):
- cs.process_event(SERVER, Request)
diff --git a/venv/lib/python3.11/site-packages/h11/tests/test_util.py b/venv/lib/python3.11/site-packages/h11/tests/test_util.py
deleted file mode 100644
index 79bc095..0000000
--- a/venv/lib/python3.11/site-packages/h11/tests/test_util.py
+++ /dev/null
@@ -1,112 +0,0 @@
-import re
-import sys
-import traceback
-from typing import NoReturn
-
-import pytest
-
-from .._util import (
- bytesify,
- LocalProtocolError,
- ProtocolError,
- RemoteProtocolError,
- Sentinel,
- validate,
-)
-
-
-def test_ProtocolError() -> None:
- with pytest.raises(TypeError):
- ProtocolError("abstract base class")
-
-
-def test_LocalProtocolError() -> None:
- try:
- raise LocalProtocolError("foo")
- except LocalProtocolError as e:
- assert str(e) == "foo"
- assert e.error_status_hint == 400
-
- try:
- raise LocalProtocolError("foo", error_status_hint=418)
- except LocalProtocolError as e:
- assert str(e) == "foo"
- assert e.error_status_hint == 418
-
- def thunk() -> NoReturn:
- raise LocalProtocolError("a", error_status_hint=420)
-
- try:
- try:
- thunk()
- except LocalProtocolError as exc1:
- orig_traceback = "".join(traceback.format_tb(sys.exc_info()[2]))
- exc1._reraise_as_remote_protocol_error()
- except RemoteProtocolError as exc2:
- assert type(exc2) is RemoteProtocolError
- assert exc2.args == ("a",)
- assert exc2.error_status_hint == 420
- new_traceback = "".join(traceback.format_tb(sys.exc_info()[2]))
- assert new_traceback.endswith(orig_traceback)
-
-
-def test_validate() -> None:
- my_re = re.compile(rb"(?P<group1>[0-9]+)\.(?P<group2>[0-9]+)")
- with pytest.raises(LocalProtocolError):
- validate(my_re, b"0.")
-
- groups = validate(my_re, b"0.1")
- assert groups == {"group1": b"0", "group2": b"1"}
-
- # successful partial matches are an error - must match whole string
- with pytest.raises(LocalProtocolError):
- validate(my_re, b"0.1xx")
- with pytest.raises(LocalProtocolError):
- validate(my_re, b"0.1\n")
-
-
-def test_validate_formatting() -> None:
- my_re = re.compile(rb"foo")
-
- with pytest.raises(LocalProtocolError) as excinfo:
- validate(my_re, b"", "oops")
- assert "oops" in str(excinfo.value)
-
- with pytest.raises(LocalProtocolError) as excinfo:
- validate(my_re, b"", "oops {}")
- assert "oops {}" in str(excinfo.value)
-
- with pytest.raises(LocalProtocolError) as excinfo:
- validate(my_re, b"", "oops {} xx", 10)
- assert "oops 10 xx" in str(excinfo.value)
-
-
-def test_make_sentinel() -> None:
- class S(Sentinel, metaclass=Sentinel):
- pass
-
- assert repr(S) == "S"
- assert S == S
- assert type(S).__name__ == "S"
- assert S in {S}
- assert type(S) is S
-
- class S2(Sentinel, metaclass=Sentinel):
- pass
-
- assert repr(S2) == "S2"
- assert S != S2
- assert S not in {S2}
- assert type(S) is not type(S2)
-
-
-def test_bytesify() -> None:
- assert bytesify(b"123") == b"123"
- assert bytesify(bytearray(b"123")) == b"123"
- assert bytesify("123") == b"123"
-
- with pytest.raises(UnicodeEncodeError):
- bytesify("\u1234")
-
- with pytest.raises(TypeError):
- bytesify(10)