Spaces:
Sleeping
Sleeping
File size: 5,380 Bytes
b958c14 7d1f8cf b958c14 7d1f8cf b958c14 7d1f8cf b958c14 7d1f8cf b958c14 7d1f8cf b958c14 7d1f8cf b958c14 7d1f8cf b958c14 7d1f8cf b958c14 7d1f8cf b958c14 7d1f8cf b958c14 7d1f8cf b958c14 8ae2e16 b958c14 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | from fastapi import FastAPI, Request, Response
from fastapi.responses import JSONResponse, PlainTextResponse
import httpx
import os
import sys
from collections import deque
app = FastAPI()
# 内存日志队列(最多保存1000条)
log_buffer = deque(maxlen=1000)
def log_message(msg):
"""记录日志到内存和标准输出"""
log_buffer.append(msg)
print(msg, flush=True)
@app.get("/")
@app.head("/")
async def root():
return {"status": "ok", "service": "HTTP Proxy Node"}
@app.get("/logs")
async def get_logs():
"""获取最近的日志"""
return PlainTextResponse("\n".join(log_buffer))
@app.post("/api/proxy")
async def proxy(request: Request):
"""标准 HTTP 代理 API"""
import time
start_time = time.time()
try:
# 解析请求
parse_start = time.time()
data = await request.json()
target_url = data.get("url")
method = data.get("method", "GET")
headers = data.get("headers", {})
body = data.get("body")
log_message(f"[PROXY] {method} {target_url[:80]} - parse: {(time.time()-parse_start)*1000:.0f}ms")
if not target_url:
return JSONResponse(
{"error": "Missing url parameter"},
status_code=400
)
# 转换 body(如果是 base64)
if body and isinstance(body, str):
import base64
try:
body = base64.b64decode(body)
except:
body = body.encode('utf-8')
# 发送请求(异步)
fetch_start = time.time()
log_message(f"[FETCH_START] {method} {target_url[:80]}")
try:
# 设置更详细的超时
timeout_config = httpx.Timeout(
connect=10.0, # 连接超时
read=30.0, # 读取超时
write=10.0, # 写入超时
pool=10.0 # 连接池超时
)
async with httpx.AsyncClient(timeout=timeout_config, follow_redirects=True) as client:
resp = await client.request(
method=method,
url=target_url,
headers=headers,
content=body
)
log_message(f"[FETCH] {resp.status_code} {len(resp.content)} bytes - {(time.time()-fetch_start)*1000:.0f}ms")
except httpx.TimeoutException as e:
error_msg = f"{type(e).__name__}: {str(e) or repr(e)}"
log_message(f"[FETCH_TIMEOUT] {error_msg} - {(time.time()-fetch_start)*1000:.0f}ms")
raise
except httpx.HTTPError as e:
error_msg = f"{type(e).__name__}: {str(e) or repr(e)}"
log_message(f"[FETCH_HTTP_ERROR] {error_msg} - {(time.time()-fetch_start)*1000:.0f}ms")
raise
except Exception as fetch_error:
error_msg = f"{type(fetch_error).__name__}: {str(fetch_error) or repr(fetch_error)}"
log_message(f"[FETCH_ERROR] {error_msg} - {(time.time()-fetch_start)*1000:.0f}ms")
import traceback
log_message(f"[TRACEBACK] {traceback.format_exc()}")
raise
# 返回响应(过滤掉会冲突的 headers)
response_headers = {}
for k, v in resp.headers.items():
k_lower = k.lower()
# 跳过这些 headers,让 FastAPI 自动处理
if k_lower not in ['content-length', 'content-encoding', 'transfer-encoding']:
response_headers[k] = v
log_message(f"[DONE] Total: {(time.time()-start_time)*1000:.0f}ms")
return Response(
content=resp.content,
status_code=resp.status_code,
headers=response_headers
)
except Exception as e:
log_message(f"[ERROR] {str(e)} - {(time.time()-start_time)*1000:.0f}ms")
return JSONResponse(
{"error": str(e)},
status_code=500
)
if __name__ == "__main__":
import uvicorn
import threading
import time
# 启动时自动注册
def auto_register():
time.sleep(10) # 等待服务启动
try:
import httpx
# 从 HF 环境变量获取 Space 信息
space_id = os.environ.get("SPACE_ID", "") # pyrq/ws-test-xxx
if space_id:
space_url = f"https://{space_id.replace('/', '-')}.hf.space"
# 注册到 Worker(新格式)
with httpx.Client(timeout=10.0) as client:
client.post(
"https://proxy-worker.busitest135.workers.dev/api/instances",
json={
"nodeId": space_id,
"url": space_url,
"platform": "hf",
"tags": ["auto"]
}
)
log_message(f"[AUTO_REGISTER] Registered {space_id} -> {space_url}")
except Exception as e:
log_message(f"[AUTO_REGISTER] Failed: {e}")
threading.Thread(target=auto_register, daemon=True).start()
port = int(os.environ.get("PORT", 7860))
uvicorn.run(app, host="0.0.0.0", port=port)
|