|
import requests |
|
from fastapi import FastAPI, Request, Response |
|
from fastapi.responses import StreamingResponse |
|
import uvicorn |
|
import os |
|
import logging |
|
import json |
|
|
|
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') |
|
|
|
app = FastAPI() |
|
|
|
|
|
|
|
|
|
@app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]) |
|
async def reverse_proxy(request: Request, path: str): |
|
""" |
|
反向代理到目标 URL。 |
|
从路径中提取目标 URL,例如 /google.com/search?q=test -> https://google.com/search?q=test |
|
""" |
|
target_url_str = path |
|
|
|
|
|
if not ('.' in target_url_str or target_url_str.startswith(('http://', 'https://'))): |
|
return Response(content="Invalid target URL format in path.", status_code=400) |
|
|
|
|
|
if not target_url_str.startswith(('http://', 'https://')): |
|
target_url_str = f"https://{target_url_str}" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
headers = {key: value for key, value in request.headers.items() if key.lower() != 'host'} |
|
|
|
|
|
|
|
|
|
body = await request.body() |
|
|
|
|
|
if request.method == "POST": |
|
logging.info(f"Received POST request to {path}. Body follows:") |
|
try: |
|
|
|
body_text = body.decode('utf-8') |
|
try: |
|
|
|
body_json = json.loads(body_text) |
|
|
|
formatted_json = json.dumps(body_json, indent=2, ensure_ascii=False) |
|
logging.info(formatted_json) |
|
except json.JSONDecodeError: |
|
|
|
logging.info(body_text) |
|
except UnicodeDecodeError: |
|
|
|
logging.info(f"Body (bytes): {body}") |
|
|
|
try: |
|
|
|
target_response = requests.request( |
|
method=request.method, |
|
url=target_url_str, |
|
headers=headers, |
|
data=body, |
|
stream=True, |
|
allow_redirects=True, |
|
verify=True |
|
) |
|
|
|
|
|
response_headers = { |
|
k: v for k, v in target_response.headers.items() |
|
if k.lower() not in ['transfer-encoding', 'content-encoding', 'content-length'] |
|
} |
|
|
|
|
|
return StreamingResponse( |
|
target_response.iter_content(chunk_size=8192), |
|
status_code=target_response.status_code, |
|
headers=response_headers, |
|
media_type=target_response.headers.get('content-type') |
|
) |
|
|
|
except requests.exceptions.RequestException as e: |
|
return Response(content=f"Error connecting to target server: {e}", status_code=502) |
|
except Exception as e: |
|
return Response(content=f"An unexpected error occurred: {e}", status_code=500) |
|
|
|
if __name__ == "__main__": |
|
port = int(os.environ.get("PORT", 7860)) |
|
uvicorn.run(app, host="0.0.0.0", port=port) |
|
|