84 lines
2.3 KiB
Python
84 lines
2.3 KiB
Python
import httpx
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.responses import Response
|
|
from starlette.responses import JSONResponse
|
|
|
|
from client import AsyncCustomHost, NameSolver
|
|
from const import OFFICIAL_API
|
|
|
|
app = FastAPI()
|
|
|
|
# Configure upstream client
|
|
official_client = httpx.AsyncClient(
|
|
verify=True,
|
|
timeout=httpx.Timeout(30.0, connect=10.0),
|
|
limits=httpx.Limits(max_keepalive_connections=50, max_connections=100),
|
|
transport=AsyncCustomHost(NameSolver())
|
|
)
|
|
|
|
# Hop-by-hop headers we must strip
|
|
HOP_BY_HOP_HEADERS = {
|
|
"connection",
|
|
"keep-alive",
|
|
"proxy-authenticate",
|
|
"proxy-authorization",
|
|
"te",
|
|
"trailer",
|
|
"transfer-encoding",
|
|
"upgrade",
|
|
"content-length", # let httpx calculate
|
|
"host", # must be rewritten
|
|
}
|
|
|
|
|
|
@app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"])
|
|
async def catch_all(request: Request, path: str):
|
|
# Copy request body
|
|
body = await request.body()
|
|
|
|
# Copy headers, filtering hop-by-hop
|
|
req_headers = {
|
|
k: v
|
|
for k, v in request.headers.items()
|
|
if k.lower() not in HOP_BY_HOP_HEADERS
|
|
}
|
|
|
|
# Rewrite Host header to match upstream
|
|
req_headers["host"] = httpx.URL(OFFICIAL_API).host
|
|
|
|
# Forward request upstream
|
|
upstream_response = await official_client.request(
|
|
method=request.method,
|
|
url=f"{OFFICIAL_API}/{path}",
|
|
headers=req_headers,
|
|
content=body,
|
|
params=request.query_params,
|
|
cookies=request.cookies,
|
|
)
|
|
|
|
# Copy response headers, filtering hop-by-hop
|
|
resp_headers = {
|
|
k: v
|
|
for k, v in upstream_response.headers.items()
|
|
if k.lower() not in HOP_BY_HOP_HEADERS
|
|
}
|
|
|
|
content_type = upstream_response.headers.get("content-type", "")
|
|
|
|
resp_headers.pop("content-encoding", None) # remove, since body is decoded
|
|
if content_type.startswith("application/json"):
|
|
response = JSONResponse(
|
|
content=upstream_response.json(),
|
|
status_code=upstream_response.status_code,
|
|
headers=resp_headers
|
|
)
|
|
else:
|
|
response = Response(
|
|
content=upstream_response.content,
|
|
status_code=upstream_response.status_code,
|
|
headers=resp_headers,
|
|
media_type=content_type
|
|
)
|
|
|
|
return response
|