futranbg commited on
Commit
72c54d6
·
verified ·
1 Parent(s): 788951d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +36 -68
app.py CHANGED
@@ -9,23 +9,19 @@ from starlette.background import BackgroundTask
9
  from contextlib import asynccontextmanager
10
 
11
  # --- CONFIGURATION ---
12
- TARGET_URL = os.environ.get("TARGET_URL")
13
- HF_TOKEN = os.environ.get("HF_TOKEN")
14
 
15
  if not TARGET_URL:
16
- raise ValueError("❌ TARGET_URL is missing. Set it in Space Settings > Secrets.")
17
 
18
- # Global HTTP Client
19
  http_client = None
20
 
21
  @asynccontextmanager
22
  async def lifespan(app: FastAPI):
23
  global http_client
24
- http_client = httpx.AsyncClient(
25
- timeout=httpx.Timeout(60.0, connect=10.0),
26
- follow_redirects=True,
27
- limits=httpx.Limits(max_keepalive_connections=20, max_connections=40)
28
- )
29
  yield
30
  await http_client.aclose()
31
 
@@ -33,96 +29,68 @@ app = FastAPI(lifespan=lifespan)
33
 
34
  @app.middleware("http")
35
  async def proxy_http(request: Request, call_next):
36
- # 1. Build Target URL
37
- # We append the HF_TOKEN as a query parameter '?token=...'
38
- # This unlocks the Private Space Gate without using the Authorization header
39
- path = request.url.path
40
- query = request.url.query
 
 
 
 
 
 
 
41
 
42
- # Logic to merge existing queries with the token
43
- if query:
44
- target_query = f"{query}&token={HF_TOKEN}"
45
- else:
46
- target_query = f"token={HF_TOKEN}"
47
-
48
- url = f"{TARGET_URL}{path}?{target_query}"
49
-
50
- # 2. Filter Headers
51
- # We allow 'authorization' to pass through untouched (for your User Login)
52
- # We allow 'content-type' (for the Login Form)
53
- excluded_headers = ['host', 'content-length', 'connection', 'upgrade', 'accept-encoding']
54
- proxy_headers = {
55
- k: v for k, v in request.headers.items()
56
- if k.lower() not in excluded_headers
57
- }
58
 
59
  try:
60
- # 3. Build Request
61
  rp_req = http_client.build_request(
62
  method=request.method,
63
  url=url,
64
- headers=proxy_headers,
65
  content=request.stream(),
66
  cookies=request.cookies
67
  )
68
-
69
- # 4. Send Request
70
  rp_resp = await http_client.send(rp_req, stream=True)
71
-
72
- # 5. Filter Response Headers
73
  res_excluded = ['content-encoding', 'content-length', 'transfer-encoding', 'connection']
74
- res_headers = {
75
- k: v for k, v in rp_resp.headers.items()
76
- if k.lower() not in res_excluded
77
- }
78
 
79
- # 6. Return Response
80
  return StreamingResponse(
81
  rp_resp.aiter_raw(),
82
  status_code=rp_resp.status_code,
83
  headers=res_headers,
84
  background=BackgroundTask(rp_resp.aclose)
85
  )
86
-
87
  except Exception as e:
88
- print(f"❌ Proxy Error: {e}")
89
  return Response(f"Proxy Error: {str(e)}", status_code=502)
90
 
91
  @app.websocket_route("/{path:path}")
92
  async def proxy_ws(websocket: WebSocket):
93
  await websocket.accept()
94
-
95
- # Same logic for Websockets: Pass token in URL
96
  ws_url = TARGET_URL.replace("https://", "wss://").replace("http://", "ws://")
97
- target_ws_url = f"{ws_url}/{websocket.path_params['path']}?token={HF_TOKEN}"
98
 
 
99
  headers = dict(websocket.headers)
100
-
 
 
 
101
  try:
102
  async with websockets.connect(target_ws_url, additional_headers=headers) as target_ws:
103
- async def forward_to_target():
104
- try:
105
- while True:
106
- data = await websocket.receive_text()
107
- await target_ws.send(data)
108
- except Exception:
109
- pass
110
-
111
- async def forward_to_client():
112
  try:
113
  while True:
114
- data = await target_ws.recv()
115
- await websocket.send_text(data)
116
- except Exception:
117
- pass
118
-
119
- await asyncio.gather(forward_to_target(), forward_to_client())
120
-
121
- except Exception as e:
122
- try:
123
- await websocket.close()
124
- except:
125
- pass
126
 
127
  if __name__ == '__main__':
128
  uvicorn.run(app, host='0.0.0.0', port=7860)
 
9
  from contextlib import asynccontextmanager
10
 
11
  # --- CONFIGURATION ---
12
+ TARGET_URL = os.environ.get("TARGET_URL") # Your Private Direct URL
13
+ HF_TOKEN = os.environ.get("HF_TOKEN") # Your HF Read Token
14
 
15
  if not TARGET_URL:
16
+ raise ValueError("❌ TARGET_URL is missing.")
17
 
18
+ # Global Client
19
  http_client = None
20
 
21
  @asynccontextmanager
22
  async def lifespan(app: FastAPI):
23
  global http_client
24
+ http_client = httpx.AsyncClient(timeout=60.0, follow_redirects=True)
 
 
 
 
25
  yield
26
  await http_client.aclose()
27
 
 
29
 
30
  @app.middleware("http")
31
  async def proxy_http(request: Request, call_next):
32
+ url = f"{TARGET_URL}{request.url.path}"
33
+ if request.url.query:
34
+ url += f"?{request.url.query}"
35
+
36
+ # 1. Prepare Headers
37
+ excluded = ['host', 'content-length', 'connection', 'upgrade', 'accept-encoding']
38
+ proxy_headers = {k: v for k, v in request.headers.items() if k.lower() not in excluded}
39
+
40
+ # --- THE HEADER SWAP ---
41
+ # A. If the user sent a token, hide it in 'X-User-Auth'
42
+ if "authorization" in proxy_headers:
43
+ proxy_headers["X-User-Auth"] = proxy_headers["authorization"]
44
 
45
+ # B. Put the HF Token in the main 'Authorization' slot to pass the Gate
46
+ proxy_headers["Authorization"] = f"Bearer {HF_TOKEN}"
47
+ # -----------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
  try:
 
50
  rp_req = http_client.build_request(
51
  method=request.method,
52
  url=url,
53
+ headers=proxy_headers, # Sending modified headers
54
  content=request.stream(),
55
  cookies=request.cookies
56
  )
 
 
57
  rp_resp = await http_client.send(rp_req, stream=True)
58
+
 
59
  res_excluded = ['content-encoding', 'content-length', 'transfer-encoding', 'connection']
60
+ res_headers = {k: v for k, v in rp_resp.headers.items() if k.lower() not in res_excluded}
 
 
 
61
 
 
62
  return StreamingResponse(
63
  rp_resp.aiter_raw(),
64
  status_code=rp_resp.status_code,
65
  headers=res_headers,
66
  background=BackgroundTask(rp_resp.aclose)
67
  )
 
68
  except Exception as e:
 
69
  return Response(f"Proxy Error: {str(e)}", status_code=502)
70
 
71
  @app.websocket_route("/{path:path}")
72
  async def proxy_ws(websocket: WebSocket):
73
  await websocket.accept()
 
 
74
  ws_url = TARGET_URL.replace("https://", "wss://").replace("http://", "ws://")
75
+ target_ws_url = f"{ws_url}/{websocket.path_params['path']}"
76
 
77
+ # Same Swap for Websockets
78
  headers = dict(websocket.headers)
79
+ if "authorization" in headers:
80
+ headers["X-User-Auth"] = headers["authorization"]
81
+ headers["Authorization"] = f"Bearer {HF_TOKEN}"
82
+
83
  try:
84
  async with websockets.connect(target_ws_url, additional_headers=headers) as target_ws:
85
+ async def forward(src, dst):
 
 
 
 
 
 
 
 
86
  try:
87
  while True:
88
+ data = await src.recv() if hasattr(src, 'recv') else await src.receive_text()
89
+ await dst.send(data) if hasattr(dst, 'send') else await dst.send_text(data)
90
+ except: pass
91
+ await asyncio.gather(forward(websocket, target_ws), forward(target_ws, websocket))
92
+ except:
93
+ await websocket.close()
 
 
 
 
 
 
94
 
95
  if __name__ == '__main__':
96
  uvicorn.run(app, host='0.0.0.0', port=7860)