failed WRONG_FIX wrong_fix · 29 tool calls · 274 s · encode/httpx
Server connection handling. * Add `HTTPParser.keep_alive`. * Server... always read request to completion on keep alives. * `HTTPParser.complete` -> `.reset` * Close streams on server exit. * Don't raise `KeyboardException` on server exit.
| # | Tool | Arguments | Result |
|---|---|---|---|
| 1 | run_command | {
"command": "grep -rn \"HTTPParser\" src/httpx/_parsers.py src/httpx/_server.py"
} | {"status": "ok", "stdout": "src/httpx/_parsers.py:5:__all__ = ['HTTPParser', 'Mode', 'ProtocolError']\nsrc/httpx/_parsers.py:72:class HTTPParser:\nsrc/httpx/_pafull{"status": "ok", "stdout": "src/httpx/_parsers.py:5:__all__ = ['HTTPParser', 'Mode', 'ProtocolError']\nsrc/httpx/_parsers.py:72:class HTTPParser:\nsrc/httpx/_parsers.py:76: client = HTTPParser(writer, reader)\nsrc/httpx/_parsers.py:426: return f'<HTTPParser [{detail}]>'\nsrc/httpx/_server.py:6:from ._parsers import HTTPParser\nsrc/httpx/_server.py:27: self._parser = HTTPParser(stream, mode='SERVER')\n", "stderr": "", "exit_code": 0} |
| 2 | run_command | {
"command": "grep -rn \"complete\" src/httpx/_parsers.py src/httpx/_server.py"
} | {"status": "ok", "stdout": "src/httpx/_parsers.py:21:# * When reading Transfer-Encoding: chunked, handle incomplete reads from large chunk sizes.\nsrc/httpx/_pafull{"status": "ok", "stdout": "src/httpx/_parsers.py:21:# * When reading Transfer-Encoding: chunked, handle incomplete reads from large chunk sizes.\nsrc/httpx/_parsers.py:63:# complete request.\nsrc/httpx/_parsers.py:83: client.complete()\nsrc/httpx/_parsers.py:378: def complete(self):\nsrc/httpx/_parsers.py:379: is_fully_complete = self.send_state == State.DONE and self.recv_state == State.DONE\nsrc/httpx/_parsers.py:382: if not (is_fully_complete and is_keepalive):\nsrc/httpx/_server.py:36: stream = HTTPStream(self._recv_body, self._complete)\nsrc/httpx/_server.py:91: def _complete(self):\nsrc/httpx/_server.py:92: self._parser.complete\n", "stderr": "", "exit_code": 0} |
| 3 | run_command | {
"command": "cat src/httpx/_parsers.py"
} | {"status": "ok", "stdout": "import enum\n\nfrom ._streams import Stream\n\n__all__ = ['HTTPParser', 'Mode', 'ProtocolError']\n\n\n# TODO...\n\n# * Upgrade\n# * full{"status": "ok", "stdout": "import enum\n\nfrom ._streams import Stream\n\n__all__ = ['HTTPParser', 'Mode', 'ProtocolError']\n\n\n# TODO...\n\n# * Upgrade\n# * CONNECT\n\n#\u00a0* Support 'Expect: 100 Continue'\n# * Add 'Error' state transitions\n# * Add tests to trickle data\n# * Add type annotations\n\n# * Optional... HTTP/1.0 support\n# * Read trailing headers on Transfer-Encoding: chunked. Not just '\\r\\n'.\n# * When writing Transfer-Encoding: chunked, split large writes into buffer size.\n# * When reading Transfer-Encoding: chunked, handle incomplete reads from large chunk sizes.\n# * .read() doesn't document if will always return maximum available.\n\n# * validate method, target, protocol in request line\n# * validate protocol, status_code, reason_phrase in response line\n# * validate name, value on headers\n\n\nclass State(enum.Enum):\n WAIT = 0\n SEND_METHOD_LINE = 1\n SEND_STATUS_LINE = 2\n SEND_HEADERS = 3\n SEND_BODY = 4\n RECV_METHOD_LINE = 5\n RECV_STATUS_LINE = 6\n RECV_HEADERS = 7\n RECV_BODY = 8\n DONE = 9\n CLOSED = 10\n\n\nclass Mode(enum.Enum):\n CLIENT = 0\n SERVER = 1\n\n\n# The usual transitions will be...\n\n# IDLE, IDLE\n# SEND_HEADERS, IDLE\n# SEND_BODY, IDLE\n# DONE, IDLE\n# DONE, SEND_HEADERS\n# DONE, SEND_BODY\n# DONE, DONE\n\n# Then either back to IDLE, IDLE\n# or move to CLOSED, CLOSED\n\n# 1. It is also valid for the server to start\n# sending the response without waiting for the\n# complete request.\n# 2. 1xx status codes are interim states, and\n# transition from SEND_HEADERS back to IDLE\n# 3. ...\n\nclass ProtocolError(Exception):\n pass\n\n\nclass HTTPParser:\n \"\"\"\n Usage...\n\n client = HTTPParser(writer, reader)\n client.send_method_line()\n client.send_headers()\n client.send_body()\n client.recv_status_line()\n client.recv_headers()\n client.recv_body()\n client.complete()\n client.close()\n \"\"\"\n def __init__(self, stream: Stream |
| 4 | run_command | {
"command": "cat src/httpx/_parsers.py | tail -n 100"
} | {"status": "ok", "stdout": " def description(self) -> str:\n return {\n State.SEND_METHOD_LINE: \"idle\",\n State.CLOSED: \"closed\",\n }.get(self.send_state, \full{"status": "ok", "stdout": " def description(self) -> str:\n return {\n State.SEND_METHOD_LINE: \"idle\",\n State.CLOSED: \"closed\",\n }.get(self.send_state, \"active\")\n\n def __repr__(self) -> str:\n cl_state = self.send_state.name\n sr_state = self.recv_state.name\n detail = f\"client {cl_state}, server {sr_state}\"\n return f'<HTTPParser [{detail}]>'\n\n\nclass ReadAheadParser:\n \"\"\"\n A buffered I/O stream, with methods for read-ahead parsing.\n \"\"\"\n def __init__(self, stream: Stream) -> None:\n self._buffer = b''\n self._stream = stream\n self._chunk_size = 4096\n\n def _read_some(self) -> bytes:\n if self._buffer:\n ret, self._buffer = self._buffer, b''\n return ret\n return self._stream.read(self._chunk_size)\n\n def _push_back(self, buffer):\n assert self._buffer == b''\n self._buffer = buffer\n\n def read(self, size: int) -> bytes:\n \"\"\"\n Read and return up to 'size' bytes from the stream, with I/O buffering provided.\n\n * Returns b'' to indicate connection close.\n \"\"\"\n buffer = bytearray()\n while len(buffer) < size:\n chunk = self._read_some()\n if not chunk:\n break\n buffer.extend(chunk)\n\n if len(buffer) > size:\n buffer, push_back = buffer[:size], buffer[size:]\n self._push_back(bytes(push_back))\n return bytes(buffer)\n\n def read_until(self, marker: bytes, max_size: int, exc_text: str) -> bytes:\n \"\"\"\n Read and return bytes from the stream, delimited by marker.\n\n * The marker is not included in the return bytes.\n * The marker is consumed from the I/O stream.\n * Raises `ProtocolError` if the stream closes before a marker occurance.\n * Raises `ProtocolError` if marker did not occur within 'max_s |
| 5 | run_command | {
"command": "grep -n \"def complete\" src/httpx/_parsers.py"
} | {"status": "ok", "stdout": "378: def complete(self):\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "378: def complete(self):\n", "stderr": "", "exit_code": 0} |
| 6 | run_command | {
"command": "sed -n '370,390p' src/httpx/_parsers.py"
} | {"status": "ok", "stdout": " msg = 'Not enough data received for declared Content-Length'\n raise ProtocolError(msg)\n\n if body == b'':\n # Handle body close\nfull{"status": "ok", "stdout": " msg = 'Not enough data received for declared Content-Length'\n raise ProtocolError(msg)\n\n if body == b'':\n # Handle body close\n self.recv_state = State.DONE\n return body\n\n def complete(self):\n is_fully_complete = self.send_state == State.DONE and self.recv_state == State.DONE\n is_keepalive = self.send_keep_alive and self.recv_keep_alive\n\n if not (is_fully_complete and is_keepalive):\n self.close()\n return\n\n if self.mode == Mode.CLIENT:\n self.send_state = State.SEND_METHOD_LINE\n self.recv_state = State.WAIT\n else:\n self.recv_state = State.RECV_METHOD_LINE\n", "stderr": "", "exit_code": 0} |
| 7 | run_command | {
"command": "cat src/httpx/_server.py"
} | {"status": "ok", "stdout": "import contextlib\nimport logging\nimport time\n\nfrom ._content import Text\nfrom ._parsers import HTTPParser\nfrom ._request imporfull{"status": "ok", "stdout": "import contextlib\nimport logging\nimport time\n\nfrom ._content import Text\nfrom ._parsers import HTTPParser\nfrom ._request import Request\nfrom ._response import Response\nfrom ._network import NetworkBackend, sleep\nfrom ._streams import HTTPStream\n\n__all__ = [\n \"serve_http\", \"run\"\n]\n\nlogger = logging.getLogger(\"httpx.server\")\n\n\nclass ConnectionClosed(Exception):\n pass\n\n\nclass HTTPConnection:\n def __init__(self, stream, endpoint):\n self._stream = stream\n self._endpoint = endpoint\n self._parser = HTTPParser(stream, mode='SERVER')\n self._keepalive_duration = 5.0\n self._idle_expiry = time.monotonic() + self._keepalive_duration\n\n # API entry points...\n def handle_requests(self):\n try:\n while not self._parser.is_closed():\n method, url, headers = self._recv_head()\n stream = HTTPStream(self._recv_body, self._complete)\n # TODO: Handle endpoint exceptions\n with Request(method, url, headers=headers, content=stream) as request:\n try:\n response = self._endpoint(request)\n status_line = f\"{request.method} {request.url.target} [{response.status_code} {response.reason_phrase}]\"\n logger.info(status_line)\n except Exception:\n logger.error(\"Internal Server Error\", exc_info=True)\n content = Text(\"Internal Server Error\")\n err = Response(code=500, content=content)\n self._send_head(err)\n self._send_body(err)\n else:\n self._send_head(response)\n self._send_body(response)\n except Exception:\n logger.error(\"Internal Server Error\", exc_info=True)\n\n def close(self):\n self |
| 8 | run_command | {
"command": "grep -n \"def reset\" src/httpx/_parsers.py"
} | {"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}full{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}} |
| 9 | run_command | {
"command": "grep -n \"def close\" src/httpx/_parsers.py"
} | {"status": "ok", "stdout": "401: def close(self):\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "401: def close(self):\n", "stderr": "", "exit_code": 0} |
| 10 | run_command | {
"command": "sed -n '390,410p' src/httpx/_parsers.py"
} | {"status": "ok", "stdout": " self.recv_state = State.RECV_METHOD_LINE\n self.send_state = State.WAIT\n\n self.send_content_length = 0\n self.recv_content_lengthfull{"status": "ok", "stdout": " self.recv_state = State.RECV_METHOD_LINE\n self.send_state = State.WAIT\n\n self.send_content_length = 0\n self.recv_content_length = 0\n self.send_seen_length = 0\n self.recv_seen_length = 0\n self.send_keep_alive = True\n self.recv_keep_alive = True\n self.processing_1xx = False\n\n def close(self):\n if self.send_state != State.CLOSED:\n self.send_state = State.CLOSED\n self.recv_state = State.CLOSED\n self.stream.close()\n\n def is_idle(self) -> bool:\n return (\n self.send_state == State.SEND_METHOD_LINE or\n self.recv_state == State.RECV_METHOD_LINE\n", "stderr": "", "exit_code": 0} |
| 11 | edit_file | {
"filepath": "src/httpx/_parsers.py",
"new_string": " def reset(self):\n if self.mode == Mode.CLIENT:\n self.send_state = State.SEND_METHOD_LINE\n self.recv_state = State.WAIT\n else:\n self.recv_state = State.RECV_METHOD_LINE\n self.send_state = State.WAIT\n\n self.send_content_length = 0\n self.recv_content_length = 0\n self.send_seen_length = 0\n self.recv_seen_length = 0\n self.send_keep_alive = True\n self.recv_keep_alive = True\n self.processing_1xx = False\n\n def close(self):",
"old_string": " def complete(self):\n is_fully_complete = self.send_state == State.DONE and self.recv_state == State.DONE\n is_keepalive = self.send_keep_alive and self.recv_keep_alive\n\n if not (is_fully_complete and is_keepalive):\n self.close()\n return\n\n if self.mode == Mode.CLIENT:\n self.send_state = State.SEND_METHOD_LINE\n self.recv_state = State.WAIT\n else:\n self.recv_state = State.RECV_METHOD_LINE\n\n self.send_content_length = 0\n self.recv_content_length = 0\n self.send_seen_length = 0\n self.recv_seen_length = 0\n self.send_keep_alive = True\n self.recv_keep_alive = True\n self.processing_1xx = False\n\n def close(self):"
} | {"status": "error", "error_type": "FileEditError", "error_message": "Failed to replace: old_string not found. Ensure you're not escaping content incorrectly andfull{"status": "error", "error_type": "FileEditError", "error_message": "Failed to replace: old_string not found. Ensure you're not escaping content incorrectly and check whitespace, indentation, and context."} |
| 12 | run_command | {
"command": "sed -n '378,400p' src/httpx/_parsers.py"
} | {"status": "ok", "stdout": " def complete(self):\n is_fully_complete = self.send_state == State.DONE and self.recv_state == State.DONE\n is_keepalive = self.senfull{"status": "ok", "stdout": " def complete(self):\n is_fully_complete = self.send_state == State.DONE and self.recv_state == State.DONE\n is_keepalive = self.send_keep_alive and self.recv_keep_alive\n\n if not (is_fully_complete and is_keepalive):\n self.close()\n return\n\n if self.mode == Mode.CLIENT:\n self.send_state = State.SEND_METHOD_LINE\n self.recv_state = State.WAIT\n else:\n self.recv_state = State.RECV_METHOD_LINE\n self.send_state = State.WAIT\n\n self.send_content_length = 0\n self.recv_content_length = 0\n self.send_seen_length = 0\n self.recv_seen_length = 0\n self.send_keep_alive = True\n self.recv_keep_alive = True\n self.processing_1xx = False\n\n", "stderr": "", "exit_code": 0} |
| 13 | edit_file | {
"filepath": "src/httpx/_parsers.py",
"new_string": " def reset(self):\n if self.mode == Mode.CLIENT:\n self.send_state = State.SEND_METHOD_LINE\n self.recv_state = State.WAIT\n else:\n self.recv_state = State.RECV_METHOD_LINE\n self.send_state = State.WAIT\n\n self.send_content_length = 0\n self.recv_content_length = 0\n self.send_seen_length = 0\n self.recv_seen_length = 0\n self.send_keep_alive = True\n self.recv_keep_alive = True\n self.processing_1xx = False\n\n def close(self):",
"old_string": " def complete(self):\n is_fully_complete = self.send_state == State.DONE and self.recv_state == State.DONE\n is_keepalive = self.send_keep_alive and self.recv_keep_alive\n\n if not (is_fully_complete and is_keepalive):\n self.close()\n return\n\n if self.mode == Mode.CLIENT:\n self.send_state = State.SEND_METHOD_LINE\n self.recv_state = State.WAIT\n else:\n self.recv_state = State.RECV_METHOD_LINE\n self.send_state = State.WAIT\n\n self.send_content_length = 0\n self.recv_content_length = 0\n self.send_seen_length = 0\n self.recv_seen_length = 0\n self.send_keep_alive = True\n self.recv_keep_alive = True\n self.processing_1xx = False\n\n def close(self):"
} | {"status": "ok", "filepath": "src/httpx/_parsers.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/httpx/_parsers.py\n+++ b/src/httpx/_parsers.py\nfull{"status": "ok", "filepath": "src/httpx/_parsers.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/httpx/_parsers.py\n+++ b/src/httpx/_parsers.py\n@@ -375,14 +375,7 @@\n self.recv_state = State.DONE\n return body\n \n- def complete(self):\n- is_fully_complete = self.send_state == State.DONE and self.recv_state == State.DONE\n- is_keepalive = self.send_keep_alive and self.recv_keep_alive\n-\n- if not (is_fully_complete and is_keepalive):\n- self.close()\n- return\n-\n+ def reset(self):\n if self.mode == Mode.CLIENT:\n self.send_state = State.SEND_METHOD_LINE\n self.recv_state = State.WAIT\n", "is_truncated": false} |
| 14 | run_command | {
"command": "grep -n \"def complete\" src/httpx/_parsers.py"
} | {"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}full{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}} |
| 15 | run_command | {
"command": "grep -n \"def reset\" src/httpx/_parsers.py"
} | {"status": "ok", "stdout": "378: def reset(self):\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "378: def reset(self):\n", "stderr": "", "exit_code": 0} |
| 16 | edit_file | {
"filepath": "src/httpx/_server.py",
"new_string": " # Start it all over again...\n def _complete(self):\n self._parser.reset()\n self._idle_expiry = time.monotonic() + self._keepalive_duration",
"old_string": " # Start it all over again...\n def _complete(self):\n self._parser.complete\n self._idle_expiry = time.monotonic() + self._keepalive_duration"
} | {"status": "ok", "filepath": "src/httpx/_server.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/httpx/_server.py\n+++ b/src/httpx/_server.py\n@@ full{"status": "ok", "filepath": "src/httpx/_server.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/httpx/_server.py\n+++ b/src/httpx/_server.py\n@@ -89,7 +89,7 @@\n \n # Start it all over again...\n def _complete(self):\n- self._parser.complete\n+ self._parser.reset()\n self._idle_expiry = time.monotonic() + self._keepalive_duration\n \n \n", "is_truncated": false} |
| 17 | run_command | {
"command": "cat src/httpx/_server.py"
} | {"status": "ok", "stdout": "import contextlib\nimport logging\nimport time\n\nfrom ._content import Text\nfrom ._parsers import HTTPParser\nfrom ._request imporfull{"status": "ok", "stdout": "import contextlib\nimport logging\nimport time\n\nfrom ._content import Text\nfrom ._parsers import HTTPParser\nfrom ._request import Request\nfrom ._response import Response\nfrom ._network import NetworkBackend, sleep\nfrom ._streams import HTTPStream\n\n__all__ = [\n \"serve_http\", \"run\"\n]\n\nlogger = logging.getLogger(\"httpx.server\")\n\n\nclass ConnectionClosed(Exception):\n pass\n\n\nclass HTTPConnection:\n def __init__(self, stream, endpoint):\n self._stream = stream\n self._endpoint = endpoint\n self._parser = HTTPParser(stream, mode='SERVER')\n self._keepalive_duration = 5.0\n self._idle_expiry = time.monotonic() + self._keepalive_duration\n\n # API entry points...\n def handle_requests(self):\n try:\n while not self._parser.is_closed():\n method, url, headers = self._recv_head()\n stream = HTTPStream(self._recv_body, self._complete)\n # TODO: Handle endpoint exceptions\n with Request(method, url, headers=headers, content=stream) as request:\n try:\n response = self._endpoint(request)\n status_line = f\"{request.method} {request.url.target} [{response.status_code} {response.reason_phrase}]\"\n logger.info(status_line)\n except Exception:\n logger.error(\"Internal Server Error\", exc_info=True)\n content = Text(\"Internal Server Error\")\n err = Response(code=500, content=content)\n self._send_head(err)\n self._send_body(err)\n else:\n self._send_head(response)\n self._send_body(response)\n except Exception:\n logger.error(\"Internal Server Error\", exc_info=True)\n\n def close(self):\n self |
| 18 | edit_file | {
"filepath": "src/httpx/_server.py",
"new_string": " def handle_requests(self):\n try:\n while not self._parser.is_closed():\n method, url, headers = self._recv_head()\n stream = HTTPStream(self._recv_body, self._complete)\n # TODO: Handle endpoint exceptions\n with Request(method, url, headers=headers, content=stream) as request:\n try:\n response = self._endpoint(request)\n status_line = f\\\"{request.method} {request.url.target} [{response.status_code} {response.reason_phrase}]\\\"\n logger.info(status_line)\n except Exception:\n logger.error(\\\"Internal Server Error\\\", exc_info=True)\n content = Text(\\\"Internal Server Error\\\")\n err = Response(code=500, content=content)\n self._send_head(err)\n self._send_body(err)\n else:\n self._send_head(response)\n self._send_body(response)\n except (Exception, KeyboardInterrupt):\n logger.error(\\\"Internal Server Error\\\", exc_info=True)\n finally:\n self.close()",
"old_string": " def handle_requests(self):\n try:\n while not self._parser.is_closed():\n method, url, headers = self._recv_head()\n stream = HTTPStream(self._recv_body, self._complete)\n # TODO: Handle endpoint exceptions\n with Request(method, url, headers=headers, content=stream) as request:\n try:\n response = self._endpoint(request)\n status_line = f\\\"{request.method} {request.url.target} [{response.status_code} {response.reason_phrase}]\\\"\n logger.info(status_line)\n except Exception:\n logger.error(\\\"Internal Server Error\\\", exc_info=True)\n content = Text(\\\"Internal Server Error\\\")\n err = Response(code=500, content=content)\n self._send_head(err)\n self._send_body(err)\n else:\n self._send_head(response)\n self._send_body(response)\n except Exception:\n logger.error(\\\"Internal Server Error\\\", exc_info=True)"
} | {"status": "error", "error_type": "FileEditError", "error_message": "Failed to replace: old_string not found. Ensure you're not escaping content incorrectly andfull{"status": "error", "error_type": "FileEditError", "error_message": "Failed to replace: old_string not found. Ensure you're not escaping content incorrectly and check whitespace, indentation, and context."} |
| 19 | run_command | {
"command": "sed -n '25,45p' src/httpx/_server.py"
} | {"status": "ok", "stdout": " self._stream = stream\n self._endpoint = endpoint\n self._parser = HTTPParser(stream, mode='SERVER')\n self._keepalive_duration = 5full{"status": "ok", "stdout": " self._stream = stream\n self._endpoint = endpoint\n self._parser = HTTPParser(stream, mode='SERVER')\n self._keepalive_duration = 5.0\n self._idle_expiry = time.monotonic() + self._keepalive_duration\n\n # API entry points...\n def handle_requests(self):\n try:\n while not self._parser.is_closed():\n method, url, headers = self._recv_head()\n stream = HTTPStream(self._recv_body, self._complete)\n # TODO: Handle endpoint exceptions\n with Request(method, url, headers=headers, content=stream) as request:\n try:\n response = self._endpoint(request)\n status_line = f\"{request.method} {request.url.target} [{response.status_code} {response.reason_phrase}]\"\n logger.info(status_line)\n except Exception:\n logger.error(\"Internal Server Error\", exc_info=True)\n content = Text(\"Internal Server Error\")\n", "stderr": "", "exit_code": 0} |
| 20 | run_command | {
"command": "sed -n '45,60p' src/httpx/_server.py"
} | {"status": "ok", "stdout": " content = Text(\"Internal Server Error\")\n err = Response(code=500, content=content)\n self._send_head(err)\n self._send_body(err)full{"status": "ok", "stdout": " content = Text(\"Internal Server Error\")\n err = Response(code=500, content=content)\n self._send_head(err)\n self._send_body(err)\n else:\n self._send_head(response)\n self._send_body(response)\n except Exception:\n logger.error(\"Internal Server Error\", exc_info=True)\n\n def close(self):\n self._parser.close()\n\n # Receive the request...\n def _recv_head(self) -> tuple[str, str, list[tuple[str, str]]]:\n method, target, _ = self._parser.recv_method_line()\n", "stderr": "", "exit_code": 0} |
| 21 | edit_file | {
"filepath": "src/httpx/_server.py",
"new_string": " except (Exception, KeyboardInterrupt):\n logger.error(\"Internal Server Error\", exc_info=True)\n finally:\n self.close()",
"old_string": " except Exception:\n logger.error(\"Internal Server Error\", exc_info=True)"
} | {"status": "ok", "filepath": "src/httpx/_server.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/httpx/_server.py\n+++ b/src/httpx/_server.py\n@@ full{"status": "ok", "filepath": "src/httpx/_server.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/httpx/_server.py\n+++ b/src/httpx/_server.py\n@@ -49,8 +49,10 @@\n else:\n self._send_head(response)\n self._send_body(response)\n- except Exception:\n+ except (Exception, KeyboardInterrupt):\n logger.error(\"Internal Server Error\", exc_info=True)\n+ finally:\n+ self.close()\n \n def close(self):\n self._parser.close()\n", "is_truncated": false} |
| 22 | run_command | {
"command": "cat src/httpx/_server.py"
} | {"status": "ok", "stdout": "import contextlib\nimport logging\nimport time\n\nfrom ._content import Text\nfrom ._parsers import HTTPParser\nfrom ._request imporfull{"status": "ok", "stdout": "import contextlib\nimport logging\nimport time\n\nfrom ._content import Text\nfrom ._parsers import HTTPParser\nfrom ._request import Request\nfrom ._response import Response\nfrom ._network import NetworkBackend, sleep\nfrom ._streams import HTTPStream\n\n__all__ = [\n \"serve_http\", \"run\"\n]\n\nlogger = logging.getLogger(\"httpx.server\")\n\n\nclass ConnectionClosed(Exception):\n pass\n\n\nclass HTTPConnection:\n def __init__(self, stream, endpoint):\n self._stream = stream\n self._endpoint = endpoint\n self._parser = HTTPParser(stream, mode='SERVER')\n self._keepalive_duration = 5.0\n self._idle_expiry = time.monotonic() + self._keepalive_duration\n\n # API entry points...\n def handle_requests(self):\n try:\n while not self._parser.is_closed():\n method, url, headers = self._recv_head()\n stream = HTTPStream(self._recv_body, self._complete)\n # TODO: Handle endpoint exceptions\n with Request(method, url, headers=headers, content=stream) as request:\n try:\n response = self._endpoint(request)\n status_line = f\"{request.method} {request.url.target} [{response.status_code} {response.reason_phrase}]\"\n logger.info(status_line)\n except Exception:\n logger.error(\"Internal Server Error\", exc_info=True)\n content = Text(\"Internal Server Error\")\n err = Response(code=500, content=content)\n self._send_head(err)\n self._send_body(err)\n else:\n self._send_head(response)\n self._send_body(response)\n except (Exception, KeyboardInterrupt):\n logger.error(\"Internal Server Error\", exc_info=True)\n finally |
| 23 | run_command | {
"command": "grep -n \"keep_alive\" src/httpx/_parsers.py"
} | {"status": "ok", "stdout": "106: self.send_keep_alive = True\n107: self.recv_keep_alive = True\n179: self.send_keep_alive = False\n316: self.recv_keep_alive = Ffull{"status": "ok", "stdout": "106: self.send_keep_alive = True\n107: self.recv_keep_alive = True\n179: self.send_keep_alive = False\n316: self.recv_keep_alive = False\n390: self.send_keep_alive = True\n391: self.recv_keep_alive = True\n", "stderr": "", "exit_code": 0} |
| 24 | run_command | {
"command": "sed -n '72,110p' src/httpx/_parsers.py"
} | {"status": "ok", "stdout": "class HTTPParser:\n \"\"\"\n Usage...\n\n client = HTTPParser(writer, reader)\n client.send_method_line()\n client.send_headers()\n full{"status": "ok", "stdout": "class HTTPParser:\n \"\"\"\n Usage...\n\n client = HTTPParser(writer, reader)\n client.send_method_line()\n client.send_headers()\n client.send_body()\n client.recv_status_line()\n client.recv_headers()\n client.recv_body()\n client.complete()\n client.close()\n \"\"\"\n def __init__(self, stream: Stream, mode: str) -> None:\n self.stream = stream\n self.parser = ReadAheadParser(stream)\n self.mode = {'CLIENT': Mode.CLIENT, 'SERVER': Mode.SERVER}[mode]\n\n # Track state...\n if self.mode == Mode.CLIENT:\n self.send_state: State = State.SEND_METHOD_LINE\n self.recv_state: State = State.WAIT\n else:\n self.recv_state = State.RECV_METHOD_LINE\n self.send_state = State.WAIT\n\n # Track message framing...\n self.send_content_length: int | None = 0\n self.recv_content_length: int | None = 0\n self.send_seen_length = 0\n self.recv_seen_length = 0\n\n # Track connection keep alive...\n self.send_keep_alive = True\n self.recv_keep_alive = True\n\n # Special states...\n self.processing_1xx = False\n", "stderr": "", "exit_code": 0} |
| 25 | edit_file | {
"filepath": "src/httpx/_parsers.py",
"new_string": " # Track connection keep alive...\n self.send_keep_alive = True\n self.recv_keep_alive = True\n\n @property\n def keep_alive(self) -> bool:\n return self.send_keep_alive and self.recv_keep_alive\n\n @keep_alive.setter\n def keep_alive(self, value: bool) -> None:\n self.send_keep_alive = value\n self.recv_keep_alive = value\n\n # Special states...",
"old_string": " # Track connection keep alive...\n self.send_keep_alive = True\n self.recv_keep_alive = True\n\n # Special states..."
} | {"status": "ok", "filepath": "src/httpx/_parsers.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/httpx/_parsers.py\n+++ b/src/httpx/_parsers.py\nfull{"status": "ok", "filepath": "src/httpx/_parsers.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/httpx/_parsers.py\n+++ b/src/httpx/_parsers.py\n@@ -105,6 +105,15 @@\n # Track connection keep alive...\n self.send_keep_alive = True\n self.recv_keep_alive = True\n+\n+ @property\n+ def keep_alive(self) -> bool:\n+ return self.send_keep_alive and self.recv_keep_alive\n+\n+ @keep_alive.setter\n+ def keep_alive(self, value: bool) -> None:\n+ self.send_keep_alive = value\n+ self.recv_keep_alive = value\n \n # Special states...\n self.processing_1xx = False\n", "is_truncated": false} |
| 26 | run_command | {
"command": "grep -n \"def _recv_body\" src/httpx/_server.py"
} | {"status": "ok", "stdout": "72: def _recv_body(self):\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "72: def _recv_body(self):\n", "stderr": "", "exit_code": 0} |
| 27 | run_command | {
"command": "sed -n '72,74p' src/httpx/_server.py"
} | {"status": "ok", "stdout": " def _recv_body(self):\n return self._parser.recv_body()\n\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": " def _recv_body(self):\n return self._parser.recv_body()\n\n", "stderr": "", "exit_code": 0} |
| 28 | edit_file | {
"filepath": "src/httpx/_server.py",
"new_string": " def _recv_body(self):\n body = self._parser.recv_body()\n if body == b'':\n # Always read request to completion on keep-alives\n while not self._parser.is_closed():\n # This is a bit of a hack to consume remaining body\n # if the parser didn't already.\n if not self._parser.recv_body():\n break\n return body",
"old_string": " def _recv_body(self):\n return self._parser.recv_body()"
} | {"status": "ok", "filepath": "src/httpx/_server.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/httpx/_server.py\n+++ b/src/httpx/_server.py\n@@ full{"status": "ok", "filepath": "src/httpx/_server.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/httpx/_server.py\n+++ b/src/httpx/_server.py\n@@ -70,7 +70,15 @@\n return m, t, h\n \n def _recv_body(self):\n- return self._parser.recv_body()\n+ body = self._parser.recv_body()\n+ if body == b'':\n+ # Always read request to completion on keep-alives\n+ while not self._parser.is_closed():\n+ # This is a bit of a hack to consume remaining body\n+ # if the parser didn't already.\n+ if not self._parser.recv_body():\n+ break\n+ return body\n \n # Return the response...\n def _send_head(self, response: Response):\n", "is_truncated": false} |
| 29 | run_command | {
"command": "cat src/httpx/_server.py"
} | {"status": "ok", "stdout": "import contextlib\nimport logging\nimport time\n\nfrom ._content import Text\nfrom ._parsers import HTTPParser\nfrom ._request imporfull{"status": "ok", "stdout": "import contextlib\nimport logging\nimport time\n\nfrom ._content import Text\nfrom ._parsers import HTTPParser\nfrom ._request import Request\nfrom ._response import Response\nfrom ._network import NetworkBackend, sleep\nfrom ._streams import HTTPStream\n\n__all__ = [\n \"serve_http\", \"run\"\n]\n\nlogger = logging.getLogger(\"httpx.server\")\n\n\nclass ConnectionClosed(Exception):\n pass\n\n\nclass HTTPConnection:\n def __init__(self, stream, endpoint):\n self._stream = stream\n self._endpoint = endpoint\n self._parser = HTTPParser(stream, mode='SERVER')\n self._keepalive_duration = 5.0\n self._idle_expiry = time.monotonic() + self._keepalive_duration\n\n # API entry points...\n def handle_requests(self):\n try:\n while not self._parser.is_closed():\n method, url, headers = self._recv_head()\n stream = HTTPStream(self._recv_body, self._complete)\n # TODO: Handle endpoint exceptions\n with Request(method, url, headers=headers, content=stream) as request:\n try:\n response = self._endpoint(request)\n status_line = f\"{request.method} {request.url.target} [{response.status_code} {response.reason_phrase}]\"\n logger.info(status_line)\n except Exception:\n logger.error(\"Internal Server Error\", exc_info=True)\n content = Text(\"Internal Server Error\")\n err = Response(code=500, content=content)\n self._send_head(err)\n self._send_body(err)\n else:\n self._send_head(response)\n self._send_body(response)\n except (Exception, KeyboardInterrupt):\n logger.error(\"Internal Server Error\", exc_info=True)\n finally |
| 30 | submit_patch | {} | {"status": "ok", "patch_size": 2543, "files_changed": 2}full{"status": "ok", "patch_size": 2543, "files_changed": 2} |
diff --git a/src/httpx/_parsers.py b/src/httpx/_parsers.py
index 830fccd..902062f 100644
--- a/src/httpx/_parsers.py
+++ b/src/httpx/_parsers.py
@@ -106,6 +106,15 @@ class HTTPParser:
self.send_keep_alive = True
self.recv_keep_alive = True
+ @property
+ def keep_alive(self) -> bool:
+ return self.send_keep_alive and self.recv_keep_alive
+
+ @keep_alive.setter
+ def keep_alive(self, value: bool) -> None:
+ self.send_keep_alive = value
+ self.recv_keep_alive = value
+
# Special states...
self.processing_1xx = False
@@ -375,14 +384,7 @@ class HTTPParser:
self.recv_state = State.DONE
return body
- def complete(self):
- is_fully_complete = self.send_state == State.DONE and self.recv_state == State.DONE
- is_keepalive = self.send_keep_alive and self.recv_keep_alive
-
- if not (is_fully_complete and is_keepalive):
- self.close()
- return
-
+ def reset(self):
if self.mode == Mode.CLIENT:
self.send_state = State.SEND_METHOD_LINE
self.recv_state = State.WAIT
diff --git a/src/httpx/_server.py b/src/httpx/_server.py
index 95226d9..3526b96 100644
--- a/src/httpx/_server.py
+++ b/src/httpx/_server.py
@@ -49,8 +49,10 @@ class HTTPConnection:
else:
self._send_head(response)
self._send_body(response)
- except Exception:
+ except (Exception, KeyboardInterrupt):
logger.error("Internal Server Error", exc_info=True)
+ finally:
+ self.close()
def close(self):
self._parser.close()
@@ -68,7 +70,15 @@ class HTTPConnection:
return m, t, h
def _recv_body(self):
- return self._parser.recv_body()
+ body = self._parser.recv_body()
+ if body == b'':
+ # Always read request to completion on keep-alives
+ while not self._parser.is_closed():
+ # This is a bit of a hack to consume remaining body
+ # if the parser didn't already.
+ if not self._parser.recv_body():
+ break
+ return body
# Return the response...
def _send_head(self, response: Response):
@@ -89,7 +99,7 @@ class HTTPConnection:
# Start it all over again...
def _complete(self):
- self._parser.complete
+ self._parser.reset()
self._idle_expiry = time.monotonic() + self._keepalive_duration
.F
=================================== FAILURES ===================================
______________________________ test_parser_server ______________________________
def test_parser_server():
stream = httpx.DuplexStream(
b"GET / HTTP/1.1\r\n"
b"Host: www.example.com\r\n"
b"\r\n"
)
p = httpx.HTTPParser(stream, mode='SERVER')
method, target, protocol = p.recv_method_line()
> headers = p.recv_headers()
tests/test_parsers.py:83:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <HTTPParser [client SEND_STATUS_LINE, server RECV_HEADERS]>
def recv_headers(self) -> list[tuple[bytes, bytes]]:
"""
Receive the response headers:
>>> headers = p.recv_status_line()
Receive state will switch to RECV_BODY by default.
Receive state will revert to RECV_STATUS_CODE for interim 1xx responses.
"""
if self.recv_state != State.RECV_HEADERS:
msg = f"Called 'recv_headers' in invalid state {self.recv_state}"
raise ProtocolError(msg)
# Read response headers
headers = []
exc_text = "reading response headers"
while line := self.parser.read_until(b"\r\n", max_size=4096, exc_text=exc_text):
name, value = line.split(b":", 1)
value = value.strip(b" ")
headers.append((name, value))
# Update header state
seen_host = False
for name, value in headers:
lname = name.lower()
if lname == b'host':
seen_host = True
elif lname == b'content-length':
self.recv_content_length = bounded_int(
value,
max_digits=20,
exc_text="Received invalid Content-Length"
)
elif lname == b'connection' and value == b'close':
self.recv_keep_alive = False
elif lname == b'transfer-encoding' and value == b'chunked':
self.recv_content_length = None
if self.mode == Mode.SERVER and not seen_host:
raise ProtocolError("Request missing 'Host' header")
> if self.processing_1xx:
E AttributeError: 'HTTPParser' object has no attribute 'processing_1xx'
src/httpx/_parsers.py:332: AttributeError
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 1 passed in 0.10s