diff --git a/devtools/fake-backend/fake_backend.py b/devtools/fake-backend/fake_backend.py index d49b02e..8677000 100644 --- a/devtools/fake-backend/fake_backend.py +++ b/devtools/fake-backend/fake_backend.py @@ -319,11 +319,17 @@ class JsonlLog: def __init__(self, path: str): self.path = path self.lock = threading.Lock() - os.makedirs(os.path.dirname(path), exist_ok=True) + self.dir = os.path.dirname(path) + os.makedirs(self.dir, exist_ok=True) def append(self, record: dict) -> None: line = json.dumps(record, ensure_ascii=False) with self.lock: + # 目录**每次写都补一次**:README 说「删掉 `out/` 即清空」,而人在服务跑着的时候删掉 + # 它就再也不会自己回来——之后每一条请求都会在写日志时抛 FileNotFoundError, + # 表现成「假后端活着但什么都不回」(W76 实测踩到)。重建目录只是一次 stat, + # 代价可以忽略;让它自己长回来,比要求人先停服务再删要可靠。 + os.makedirs(self.dir, exist_ok=True) with open(self.path, "a", encoding="utf-8", newline="\n") as handle: handle.write(line + "\n") @@ -1084,6 +1090,20 @@ class Handler(BaseHTTPRequestHandler): query = parse_qs(parts.query, keep_blank_values=True) return parts.path, query + def handle_one_request(self): + """每个请求进来时**先把 body 缓存清掉**。 + + `protocol_version` 是 HTTP/1.1,同一条 keep-alive 连接上的多个请求由**同一个 Handler 实例** + 依次处理(`handle_one_request` 循环),所以 `_read_body` 的缓存必须按请求隔离。少了这一步, + 第 2 个请求会拿到第 1 个请求的 body(摘要记成上一份),而它自己那份**根本没人从 `rfile` 读走** + ——残留字节留在内核缓冲里,被下一个请求的请求行一并吃掉,`BaseHTTPRequestHandler` 于是回 + `501 Unsupported method ('{"sessionId":...}POST')`。客户端表现成「3 张图只投出去 2 张」, + 而真正的锅在这里。 + """ + self._body_read = False + self._body_cache = b"" + super().handle_one_request() + def _read_body(self): """读出请求体,**并缓存**:`_handle` 记录摘要时要读它,各 handler 里的 `_parse_json_body` 随后再读——不缓存的话第二次只会读到空串(body 只能读一次)。"""