Skip to content

Commit f3eb693

Browse files
committed
Fix remote browser JSON control-plane delivery
1 parent cbf9cd1 commit f3eb693

4 files changed

Lines changed: 30 additions & 12 deletions

File tree

src/api/admin.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Admin API routes"""
22
import asyncio
33
import json
4+
import httpx
45
from fastapi import APIRouter, Depends, HTTPException, Header, Request
56
from fastapi.responses import JSONResponse
67
from pydantic import BaseModel
@@ -189,7 +190,6 @@ async def _sync_json_http_request(
189190
request_kwargs: Dict[str, Any] = {
190191
"headers": req_headers,
191192
"timeout": timeout,
192-
"impersonate": "chrome120",
193193
}
194194

195195
if payload is not None:
@@ -198,7 +198,9 @@ async def _sync_json_http_request(
198198
request_kwargs["json"] = payload
199199

200200
try:
201-
async with AsyncSession() as session:
201+
# remote_browser 控制面是服务间 JSON API,使用 httpx 避免 curl_cffi 在当前
202+
# Windows + impersonate 场景下 POST body 丢失导致 FastAPI 直接判定 body 缺失。
203+
async with httpx.AsyncClient(follow_redirects=True) as session:
202204
response = await session.request(
203205
method=request_method,
204206
url=url,

src/services/flow_client.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import asyncio
33
import json
44
import contextvars
5+
import httpx
56
import time
67
import uuid
78
import random
@@ -2040,7 +2041,6 @@ async def _sync_json_http_request(
20402041
request_kwargs: Dict[str, Any] = {
20412042
"headers": req_headers,
20422043
"timeout": timeout,
2043-
"impersonate": "chrome120",
20442044
}
20452045

20462046
if payload is not None:
@@ -2049,7 +2049,9 @@ async def _sync_json_http_request(
20492049
request_kwargs["json"] = payload
20502050

20512051
try:
2052-
async with AsyncSession() as session:
2052+
# remote_browser 控制面只需要稳定传输 JSON,不需要浏览器指纹伪装。
2053+
# 使用 httpx 可以避免 curl_cffi 在当前环境下 POST body 被吞掉。
2054+
async with httpx.AsyncClient(follow_redirects=True) as session:
20532055
response = await session.request(
20542056
method=request_method,
20552057
url=url,

tests/test_api_routes.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def test_flexible_auth_accepts_x_goog_api_key(monkeypatch):
8585
) == "secret"
8686

8787

88-
def test_admin_remote_browser_helper_uses_asyncsession(monkeypatch):
88+
def test_admin_remote_browser_helper_uses_httpx(monkeypatch):
8989
calls = []
9090

9191
class FakeResponse:
@@ -95,7 +95,10 @@ class FakeResponse:
9595
def json(self):
9696
return {"success": True, "token": "abc"}
9797

98-
class FakeSession:
98+
class FakeAsyncClient:
99+
def __init__(self, **kwargs):
100+
calls.append({"client_kwargs": kwargs})
101+
99102
async def __aenter__(self):
100103
return self
101104

@@ -110,7 +113,7 @@ async def request(self, method, url, **kwargs):
110113
})
111114
return FakeResponse()
112115

113-
monkeypatch.setattr(admin_module, "AsyncSession", FakeSession)
116+
monkeypatch.setattr(admin_module.httpx, "AsyncClient", FakeAsyncClient)
114117

115118
status_code, payload, response_text = asyncio.run(
116119
admin_module._sync_json_http_request(
@@ -126,6 +129,11 @@ async def request(self, method, url, **kwargs):
126129
assert payload == {"success": True, "token": "abc"}
127130
assert response_text == '{"success": true, "token": "abc"}'
128131
assert calls == [
132+
{
133+
"client_kwargs": {
134+
"follow_redirects": True,
135+
},
136+
},
129137
{
130138
"method": "POST",
131139
"url": "https://example.com/api/v1/custom-score",
@@ -136,7 +144,6 @@ async def request(self, method, url, **kwargs):
136144
"Content-Type": "application/json; charset=utf-8",
137145
},
138146
"timeout": 15,
139-
"impersonate": "chrome120",
140147
"json": {"website_url": "https://example.com"},
141148
},
142149
}

tests/test_flow_client_control_plane.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ async def run():
4949
assert [call["timeout"] for call in calls] == [10, 15, 10, 10]
5050

5151

52-
def test_remote_browser_http_helper_uses_asyncsession(monkeypatch):
52+
def test_remote_browser_http_helper_uses_httpx(monkeypatch):
5353
calls = []
5454

5555
class FakeResponse:
@@ -59,7 +59,10 @@ class FakeResponse:
5959
def json(self):
6060
return {"ok": True}
6161

62-
class FakeSession:
62+
class FakeAsyncClient:
63+
def __init__(self, **kwargs):
64+
calls.append({"client_kwargs": kwargs})
65+
6366
async def __aenter__(self):
6467
return self
6568

@@ -74,7 +77,7 @@ async def request(self, method, url, **kwargs):
7477
})
7578
return FakeResponse()
7679

77-
monkeypatch.setattr(flow_client_module, "AsyncSession", FakeSession)
80+
monkeypatch.setattr(flow_client_module.httpx, "AsyncClient", FakeAsyncClient)
7881

7982
status_code, payload, response_text = asyncio.run(
8083
FlowClient._sync_json_http_request(
@@ -90,6 +93,11 @@ async def request(self, method, url, **kwargs):
9093
assert payload == {"ok": True}
9194
assert response_text == '{"ok": true}'
9295
assert calls == [
96+
{
97+
"client_kwargs": {
98+
"follow_redirects": True,
99+
},
100+
},
93101
{
94102
"method": "POST",
95103
"url": "https://example.com/api/v1/solve",
@@ -100,7 +108,6 @@ async def request(self, method, url, **kwargs):
100108
"Content-Type": "application/json; charset=utf-8",
101109
},
102110
"timeout": 12,
103-
"impersonate": "chrome120",
104111
"json": {"project_id": "project-123"},
105112
},
106113
}

0 commit comments

Comments
 (0)