Parse a WebSocket handshake request. This is a generator-based coroutine. The request path isn't URL-decoded or validated in any way. The request path and headers are expected to contain only ASCII characters. Other characters are represented with surrogat
(
cls,
read_line: Callable[[int], Generator[None, None, bytes | bytearray]],
)
| 101 | |
| 102 | @classmethod |
| 103 | def parse( |
| 104 | cls, |
| 105 | read_line: Callable[[int], Generator[None, None, bytes | bytearray]], |
| 106 | ) -> Generator[None, None, Request]: |
| 107 | """ |
| 108 | Parse a WebSocket handshake request. |
| 109 | |
| 110 | This is a generator-based coroutine. |
| 111 | |
| 112 | The request path isn't URL-decoded or validated in any way. |
| 113 | |
| 114 | The request path and headers are expected to contain only ASCII |
| 115 | characters. Other characters are represented with surrogate escapes. |
| 116 | |
| 117 | :meth:`parse` doesn't attempt to read the request body because |
| 118 | WebSocket handshake requests don't have one. If the request contains a |
| 119 | body, it may be read from the data stream after :meth:`parse` returns. |
| 120 | |
| 121 | Args: |
| 122 | read_line: Generator-based coroutine that reads a LF-terminated |
| 123 | line or raises an exception if there isn't enough data |
| 124 | |
| 125 | Raises: |
| 126 | EOFError: If the connection is closed without a full HTTP request. |
| 127 | SecurityError: If the request exceeds a security limit. |
| 128 | ValueError: If the request isn't well formatted. |
| 129 | |
| 130 | """ |
| 131 | # https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.1 |
| 132 | |
| 133 | # Parsing is simple because fixed values are expected for method and |
| 134 | # version and because path isn't checked. Since WebSocket software tends |
| 135 | # to implement HTTP/1.1 strictly, there's little need for lenient parsing. |
| 136 | |
| 137 | try: |
| 138 | request_line = yield from parse_line(read_line) |
| 139 | except EOFError as exc: |
| 140 | raise EOFError("connection closed while reading HTTP request line") from exc |
| 141 | |
| 142 | try: |
| 143 | method, raw_path, protocol = request_line.split(b" ", 2) |
| 144 | except ValueError: # not enough values to unpack (expected 3, got 1-2) |
| 145 | raise ValueError(f"invalid HTTP request line: {d(request_line)}") from None |
| 146 | if protocol != b"HTTP/1.1": |
| 147 | raise ValueError( |
| 148 | f"unsupported protocol; expected HTTP/1.1: {d(request_line)}" |
| 149 | ) |
| 150 | if method != b"GET": |
| 151 | raise ValueError(f"unsupported HTTP method; expected GET; got {d(method)}") |
| 152 | path = raw_path.decode("ascii", "surrogateescape") |
| 153 | |
| 154 | headers = yield from parse_headers(read_line) |
| 155 | |
| 156 | # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3 |
| 157 | |
| 158 | if "Transfer-Encoding" in headers: |
| 159 | raise NotImplementedError("transfer codings aren't supported") |
| 160 |
nothing calls this directly
no test coverage detected