rkihacker commited on
Commit
5e1596b
·
verified ·
1 Parent(s): adb5311

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +9 -38
main.py CHANGED
@@ -30,10 +30,9 @@ async def lifespan(app: FastAPI):
30
  """
31
  Manages the lifecycle of the HTTPX client.
32
  The client is created on startup and gracefully closed on shutdown.
33
- WARNING: This client has no timeout and no explicit connection pool limits.
 
34
  """
35
- # timeout=None disables all client-side timeouts.
36
- # The absence of a `limits` parameter means we rely on system defaults.
37
  async with httpx.AsyncClient(base_url=TARGET_URL, timeout=None) as client:
38
  app.state.http_client = client
39
  yield
@@ -53,25 +52,15 @@ async def _reverse_proxy(request: Request):
53
  url = httpx.URL(path=request.url.path, query=request.url.query.encode("utf-8"))
54
 
55
  # --- Header Processing ---
56
- # Start with headers from the incoming request.
57
  request_headers = dict(request.headers)
 
58
 
59
- # 1. CRITICAL: Remove host header.
60
- # The 'host' header is managed by httpx.
61
- request_headers.pop("host", None)
62
-
63
- # 2. Get the user's authorization key from the incoming request.
64
  authorization_header = request.headers.get("authorization")
65
-
66
- # 3. Generate a random IP for spoofing headers.
67
  random_ip = generate_random_ip()
68
 
69
- # --- ADDED LOGGING ---
70
- # Log the original client IP and the spoofed IP being used for the request.
71
  logging.info(f"Client '{request.client.host}' is being proxied with spoofed IP: {random_ip}")
72
 
73
- # 4. Set the specific, required headers for the target API.
74
- # This will overwrite any conflicting headers from the original request.
75
  specific_headers = {
76
  "accept": "application/json, text/plain, */*",
77
  "accept-language": "en-US,en;q=0.9,ru;q=0.8",
@@ -91,29 +80,24 @@ async def _reverse_proxy(request: Request):
91
  }
92
  request_headers.update(specific_headers)
93
 
94
- # 5. Add the user's authorization key to the headers if it exists.
95
  if authorization_header:
96
  request_headers["authorization"] = authorization_header
97
 
98
- # Read the request body once, as it will be reused in case of retries.
99
  body = await request.body()
100
 
101
  # --- Retry Logic ---
102
  last_exception = None
103
  for attempt in range(MAX_RETRIES):
104
  try:
105
- # Build the request for each attempt to ensure the content stream is fresh.
106
  rp_req = client.build_request(
107
  method=request.method,
108
  url=url,
109
  headers=request_headers,
110
  content=body,
111
  )
112
- # Send the request and get a streaming response.
113
  rp_resp = await client.send(rp_req, stream=True)
114
 
115
- # If the status code is not in our retry list, we can return the response.
116
- if rp_resp.status_code not in RETRY_STATUS_CODES:
117
  return StreamingResponse(
118
  rp_resp.aiter_raw(),
119
  status_code=rp_resp.status_code,
@@ -121,26 +105,16 @@ async def _reverse_proxy(request: Request):
121
  background=BackgroundTask(rp_resp.aclose),
122
  )
123
 
124
- # If it's a retryable error but this is the last attempt, return the error response.
125
- if attempt == MAX_RETRIES - 1:
126
- return StreamingResponse(
127
- rp_resp.aiter_raw(),
128
- status_code=rp_resp.status_code,
129
- headers=rp_resp.headers,
130
- background=BackgroundTask(rp_resp.aclose),
131
- )
132
-
133
- # Otherwise, close the unsuccessful response before trying again.
134
  await rp_resp.aclose()
135
 
136
  except httpx.ConnectError as e:
137
  last_exception = e
138
- # Continue to the next attempt if a connection error occurs.
139
 
140
- # If all retry attempts failed with a connection error, raise a final exception.
141
  raise HTTPException(
142
  status_code=502,
143
- detail=f"Bad Gateway: Cannot connect to target service after {MAX_RETRIES} attempts. {last_exception}"
144
  )
145
 
146
 
@@ -156,10 +130,7 @@ async def chat_proxy_handler(request: Request):
156
  """
157
  return await _reverse_proxy(request)
158
 
159
- # A simple root endpoint for health checks.
160
  @app.get("/")
161
  async def health_check():
162
  """Provides a basic health check endpoint."""
163
- return {"status": "ok", "proxying_endpoint": "/completions", "target": "TypeGPT"}
164
-
165
- # Any request to a path other than "/completions" or "/" will result in a 404 Not Found.
 
30
  """
31
  Manages the lifecycle of the HTTPX client.
32
  The client is created on startup and gracefully closed on shutdown.
33
+ NOTE: The client has no timeout, which is a deliberate choice for a proxy
34
+ that should not impose its own timeout limits on long-running requests.
35
  """
 
 
36
  async with httpx.AsyncClient(base_url=TARGET_URL, timeout=None) as client:
37
  app.state.http_client = client
38
  yield
 
52
  url = httpx.URL(path=request.url.path, query=request.url.query.encode("utf-8"))
53
 
54
  # --- Header Processing ---
 
55
  request_headers = dict(request.headers)
56
+ request_headers.pop("host", None) # The 'host' header is managed by httpx.
57
 
 
 
 
 
 
58
  authorization_header = request.headers.get("authorization")
 
 
59
  random_ip = generate_random_ip()
60
 
 
 
61
  logging.info(f"Client '{request.client.host}' is being proxied with spoofed IP: {random_ip}")
62
 
63
+ # Set the specific, required headers for the target API.
 
64
  specific_headers = {
65
  "accept": "application/json, text/plain, */*",
66
  "accept-language": "en-US,en;q=0.9,ru;q=0.8",
 
80
  }
81
  request_headers.update(specific_headers)
82
 
 
83
  if authorization_header:
84
  request_headers["authorization"] = authorization_header
85
 
 
86
  body = await request.body()
87
 
88
  # --- Retry Logic ---
89
  last_exception = None
90
  for attempt in range(MAX_RETRIES):
91
  try:
 
92
  rp_req = client.build_request(
93
  method=request.method,
94
  url=url,
95
  headers=request_headers,
96
  content=body,
97
  )
 
98
  rp_resp = await client.send(rp_req, stream=True)
99
 
100
+ if rp_resp.status_code not in RETRY_STATUS_CODES or attempt == MAX_RETRIES - 1:
 
101
  return StreamingResponse(
102
  rp_resp.aiter_raw(),
103
  status_code=rp_resp.status_code,
 
105
  background=BackgroundTask(rp_resp.aclose),
106
  )
107
 
 
 
 
 
 
 
 
 
 
 
108
  await rp_resp.aclose()
109
 
110
  except httpx.ConnectError as e:
111
  last_exception = e
112
+ logging.warning(f"Attempt {attempt + 1}/{MAX_RETRIES} failed with connection error: {e}")
113
 
114
+ # If all retry attempts failed, raise a final exception.
115
  raise HTTPException(
116
  status_code=502,
117
+ detail=f"Bad Gateway: Cannot connect to target service after {MAX_RETRIES} attempts. Last error: {last_exception}"
118
  )
119
 
120
 
 
130
  """
131
  return await _reverse_proxy(request)
132
 
 
133
  @app.get("/")
134
  async def health_check():
135
  """Provides a basic health check endpoint."""
136
+ return {"status": "ok", "proxying_endpoint": "/completions", "target": "TypeGPT"}