Read decrypted/origin-form HTTP requests and relay them.
(self, host: str, port: int, reader, writer)
| 1004 | await self._relay_http_stream(host, port, reader, writer) |
| 1005 | |
| 1006 | async def _relay_http_stream(self, host: str, port: int, reader, writer): |
| 1007 | """Read decrypted/origin-form HTTP requests and relay them.""" |
| 1008 | # Read and relay HTTP requests from the browser (now decrypted) |
| 1009 | while True: |
| 1010 | try: |
| 1011 | first_line = await asyncio.wait_for( |
| 1012 | reader.readline(), timeout=CLIENT_IDLE_TIMEOUT |
| 1013 | ) |
| 1014 | if not first_line: |
| 1015 | break |
| 1016 | |
| 1017 | header_block = first_line |
| 1018 | oversized_headers = False |
| 1019 | while True: |
| 1020 | line = await asyncio.wait_for(reader.readline(), timeout=10) |
| 1021 | header_block += line |
| 1022 | if len(header_block) > MAX_HEADER_BYTES: |
| 1023 | oversized_headers = True |
| 1024 | break |
| 1025 | if line in (b"\r\n", b"\n", b""): |
| 1026 | break |
| 1027 | |
| 1028 | # Reject truncated / oversized header blocks cleanly rather |
| 1029 | # than forwarding a half-parsed request to the relay — doing |
| 1030 | # so would send malformed JSON payloads to Apps Script and |
| 1031 | # leave the client hanging until its own timeout fires. |
| 1032 | if oversized_headers: |
| 1033 | log.warning( |
| 1034 | "MITM header block exceeds %d bytes — closing (%s)", |
| 1035 | MAX_HEADER_BYTES, host, |
| 1036 | ) |
| 1037 | try: |
| 1038 | writer.write( |
| 1039 | b"HTTP/1.1 431 Request Header Fields Too Large\r\n" |
| 1040 | b"Connection: close\r\n" |
| 1041 | b"Content-Length: 0\r\n\r\n" |
| 1042 | ) |
| 1043 | await writer.drain() |
| 1044 | except Exception: |
| 1045 | pass |
| 1046 | break |
| 1047 | |
| 1048 | # Read body |
| 1049 | body = b"" |
| 1050 | if has_unsupported_transfer_encoding(header_block): |
| 1051 | log.warning("Unsupported Transfer-Encoding → %s:%d", host, port) |
| 1052 | writer.write( |
| 1053 | b"HTTP/1.1 501 Not Implemented\r\n" |
| 1054 | b"Connection: close\r\n" |
| 1055 | b"Content-Length: 0\r\n\r\n" |
| 1056 | ) |
| 1057 | await writer.drain() |
| 1058 | break |
| 1059 | length = parse_content_length(header_block) |
| 1060 | if length > MAX_REQUEST_BODY_BYTES: |
| 1061 | raise ValueError(f"Request body too large: {length} bytes") |
| 1062 | if length > 0: |
| 1063 | body = await reader.readexactly(length) |
no test coverage detected