fix(web): always drain visit request body to keep keep-alive connections aligned
This commit is contained in:
1 parent
a3e7589ad3
commit
e3bd2e601b
2 files changed
+33
-4
No files matched your search
@@ -323,10 +323,9 @@ border-color:#3a2c22;color:#f3e9dc}button{background:#e9b97c;color:#2a1a10}}
|
||||
return
|
||||
|
||||
path_value = _param(query, "path", "")
|
||||
if not path_value:
|
||||
body = self._read_small_body()
|
||||
if body:
|
||||
path_value = _json_path(body)
|
||||
body = self._read_small_body()
|
||||
if not path_value and body:
|
||||
path_value = _json_path(body)
|
||||
|
||||
user_agent = self.headers.get("User-Agent")
|
||||
if not track.count_bots and is_bot(user_agent):
|
||||
@@ -360,6 +359,8 @@ border-color:#3a2c22;color:#f3e9dc}button{background:#e9b97c;color:#2a1a10}}
|
||||
if length <= 0:
|
||||
return b""
|
||||
if length > VISIT_BODY_LIMIT:
|
||||
# 不读取超长请求体,直接关闭连接,避免 keep-alive 上残留字节错位。
|
||||
self.close_connection = True
|
||||
raise ApiError(HTTPStatus.BAD_REQUEST, "bad_request", "请求体过大")
|
||||
return self.rfile.read(length)
|
||||
|
||||
|
||||
@@ -270,6 +270,33 @@ def test_visit_endpoint_and_stats():
|
||||
running.close()
|
||||
|
||||
|
||||
def test_visit_query_path_drains_body():
|
||||
"""path 也可来自查询串;请求体必须被读走,避免 keep-alive 连接错位。"""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
running = RunningServer(tmp)
|
||||
try:
|
||||
ua = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/155.0"}
|
||||
conn = http.client.HTTPConnection("127.0.0.1", running.port, timeout=5)
|
||||
conn.request(
|
||||
"POST", "/api/visit?path=/about",
|
||||
body=json.dumps({"path": "/ignored"}),
|
||||
headers={**ua, "Content-Type": "application/json"},
|
||||
)
|
||||
first = conn.getresponse()
|
||||
first_payload = json.loads(first.read())
|
||||
assert first.status == 200
|
||||
assert first_payload["data"]["route"] == "/about"
|
||||
|
||||
# 同一连接继续发请求:若上一次请求体未被读走,这里会解析错位
|
||||
conn.request("GET", "/api/health")
|
||||
second = conn.getresponse()
|
||||
health = json.loads(second.read())
|
||||
conn.close()
|
||||
assert second.status == 200 and health["ok"] is True
|
||||
finally:
|
||||
running.close()
|
||||
|
||||
|
||||
def test_token_auth_and_login():
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
running = RunningServer(tmp, token="test-token")
|
||||
@@ -385,6 +412,7 @@ def test_preview_and_loopback_guards():
|
||||
def main_test():
|
||||
test_readonly_routes_and_static()
|
||||
test_visit_endpoint_and_stats()
|
||||
test_visit_query_path_drains_body()
|
||||
test_token_auth_and_login()
|
||||
test_preview_and_loopback_guards()
|
||||
print("OK: Web 只读路由/鉴权/预览/访问统计/安全边界测试全部通过")
|
||||
|
||||
Reference in new issue
Block a user