# -*- coding: utf-8 -*- r'''修 /rescue/ 鉴权失败时"不发响应"导致的挂起。 == 现象 == 加上鉴权之后,不带登录请求 /rescue/xxx.txt 会**挂到超时**: > GET /rescue/19700101-000000-demo.txt HTTP/1.1 * Operation timed out after 8001 milliseconds with 0 bytes received curl 报 HTTP 000。 == 根因 == _admin_gate() **只返回布尔值,不负责发响应** —— 它是个判断函数。 所以每个调用点都必须自己在 false 时把响应发出去。 看其它调用点,全都是这么写的: if not self._admin_gate(): self._json(401, {"ok": False, "error": "请先登录"}) return 而我加的那两处写成了: if not self._admin_gate(): return <- do_GET:一个字节都没发 ... if not self._admin_gate(): return no() <- do_HEAD:no() 默认 404,状态码也不对 裸 return 在 BaseHTTPRequestHandler 里就是"什么都不做" —— 连接保持着、没有响应头、客户端一直等。表现成"网站卡住", 比报 401 严重得多。 == 改法 == do_GET -> self._json(401, ...) 然后 return do_HEAD -> return no(401) (no() 本来就支持传状态码) ''' from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent RELAY = ROOT / "relay" / "relay.py" GET_OLD = ''' if not self._admin_gate(): return wanted = urllib.parse.unquote(path[len("/rescue/"):])''' GET_NEW = ''' # ⚠ _admin_gate() 只返回布尔值,**不负责发响应** —— # 每个调用点都要自己发。裸 return 在 BaseHTTPRequestHandler # 里等于"什么都不做":连接保持着、没有响应头, # 客户端一直等到超时(curl 报 HTTP 000)。 # 第一版就是这么写的,实测 /rescue/ 会挂住 8 秒。 if not self._admin_gate(): self._json(401, {"ok": False, "error": "请先登录"}) return wanted = urllib.parse.unquote(path[len("/rescue/"):])''' HEAD_OLD = ''' if not self._admin_gate(): return no() wanted = urllib.parse.unquote(path[len("/rescue/"):])''' HEAD_NEW = ''' # 同上:必须自己发响应,而且状态码要是 401 不是 404 # (no() 默认 404,探测方会以为是"文件不存在"而不是"没登录")。 if not self._admin_gate(): return no(401) wanted = urllib.parse.unquote(path[len("/rescue/"):])''' def main() -> int: raw = RELAY.read_bytes() bom = raw.startswith(b"\xef\xbb\xbf") text = raw.decode("utf-8-sig") ok = True for old, new, what in ((GET_OLD, GET_NEW, "do_GET"), (HEAD_OLD, HEAD_NEW, "do_HEAD")): if old not in text: print(" ✗ %s: 找不到锚点" % what) ok = False continue text = text.replace(old, new, 1) print(" ✓ %s: 鉴权失败时现在会发响应" % what) if not ok: return 1 RELAY.write_bytes((b"\xef\xbb\xbf" if bom else b"") + text.encode("utf-8")) print(" 已写回 relay.py") return 0 if __name__ == "__main__": sys.exit(main())