NitinBot001 commited on
Commit
67f7621
Β·
verified Β·
1 Parent(s): ac8a919

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +140 -0
app.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import httpx
2
+ import asyncio
3
+ from fastapi import FastAPI, Request, Response
4
+ from fastapi.middleware.cors import CORSMiddleware
5
+ from fastapi.responses import StreamingResponse
6
+ import urllib.parse
7
+
8
+ app = FastAPI(title="Universal CORS Proxy", version="1.0.0")
9
+
10
+ # ─── CORS β€” sab domains allow ───────────────────────────────────────────────
11
+ app.add_middleware(
12
+ CORSMiddleware,
13
+ allow_origins=["*"],
14
+ allow_credentials=True,
15
+ allow_methods=["*"],
16
+ allow_headers=["*"],
17
+ expose_headers=["*"],
18
+ )
19
+
20
+ # ─── Headers jo proxy nahi karega ────────────────────────────────────────────
21
+ HOP_BY_HOP_HEADERS = {
22
+ "connection", "keep-alive", "proxy-authenticate", "proxy-authorization",
23
+ "te", "trailers", "transfer-encoding", "upgrade",
24
+ "host", # host hum khud set karenge
25
+ }
26
+
27
+ TIMEOUT = httpx.Timeout(30.0, connect=10.0)
28
+
29
+
30
+ def extract_target_url(path: str, query_string: str) -> str:
31
+ """
32
+ Path se target URL nikalo.
33
+ Formats supported:
34
+ /https://api.example.com/v1/users
35
+ /http://localhost:3000/data
36
+ """
37
+ # leading slash hata do
38
+ raw = path.lstrip("/")
39
+
40
+ # agar URL encoded hai toh decode karo
41
+ decoded = urllib.parse.unquote(raw)
42
+
43
+ # scheme verify karo
44
+ if not (decoded.startswith("http://") or decoded.startswith("https://")):
45
+ return None
46
+
47
+ # query string attach karo agar hai
48
+ if query_string:
49
+ decoded = f"{decoded}?{query_string}"
50
+
51
+ return decoded
52
+
53
+
54
+ def filter_headers(headers: dict, skip: set = HOP_BY_HOP_HEADERS) -> dict:
55
+ """Hop-by-hop headers remove karo, baaki forward karo."""
56
+ return {k: v for k, v in headers.items() if k.lower() not in skip}
57
+
58
+
59
+ @app.get("/")
60
+ async def root():
61
+ return {
62
+ "service": "Universal CORS Proxy",
63
+ "usage": "/{target_url}",
64
+ "example": "/https://api.github.com/users/octocat",
65
+ "methods": ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"],
66
+ }
67
+
68
+
69
+ @app.api_route("/{full_path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"])
70
+ async def proxy(full_path: str, request: Request):
71
+ # ── 1. Target URL extract karo ──────────────────────────────────────────
72
+ query_string = request.url.query
73
+ target_url = extract_target_url(full_path, query_string)
74
+
75
+ if not target_url:
76
+ return Response(
77
+ content='{"error": "Invalid URL. Use: /{full_url} e.g. /https://api.example.com/endpoint"}',
78
+ status_code=400,
79
+ media_type="application/json",
80
+ )
81
+
82
+ # ── 2. Request body lo ──────────────────────────────────────────────────
83
+ body = await request.body()
84
+
85
+ # ── 3. Headers filter karo ──────────────────────────────────────────────
86
+ forward_headers = filter_headers(dict(request.headers))
87
+
88
+ # ── 4. Actual request bhejo ─────────────────────────────────────────────
89
+ async with httpx.AsyncClient(
90
+ timeout=TIMEOUT,
91
+ follow_redirects=True,
92
+ verify=True, # SSL verify on β€” production ke liye
93
+ ) as client:
94
+ try:
95
+ upstream_response = await client.request(
96
+ method=request.method,
97
+ url=target_url,
98
+ headers=forward_headers,
99
+ content=body if body else None,
100
+ )
101
+ except httpx.ConnectError as e:
102
+ return Response(
103
+ content=f'{{"error": "Could not connect to target", "detail": "{str(e)}"}}',
104
+ status_code=502,
105
+ media_type="application/json",
106
+ )
107
+ except httpx.TimeoutException:
108
+ return Response(
109
+ content='{"error": "Request to target timed out"}',
110
+ status_code=504,
111
+ media_type="application/json",
112
+ )
113
+ except Exception as e:
114
+ return Response(
115
+ content=f'{{"error": "Proxy error", "detail": "{str(e)}"}}',
116
+ status_code=500,
117
+ media_type="application/json",
118
+ )
119
+
120
+ # ── 5. Response headers filter karo + CORS inject karo ──────────────────
121
+ response_headers = filter_headers(dict(upstream_response.headers))
122
+ response_headers["Access-Control-Allow-Origin"] = "*"
123
+ response_headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS"
124
+ response_headers["Access-Control-Allow-Headers"] = "*"
125
+ response_headers["X-Proxied-By"] = "universal-cors-proxy"
126
+ response_headers["X-Original-URL"] = target_url.split("?")[0] # query strip for safety
127
+
128
+ # ── 6. Response return karo ─────────────────────────────────────────────
129
+ return Response(
130
+ content=upstream_response.content,
131
+ status_code=upstream_response.status_code,
132
+ headers=response_headers,
133
+ media_type=upstream_response.headers.get("content-type", "application/octet-stream"),
134
+ )
135
+
136
+
137
+ # ─── Run ─────────────────────────────────────────────────────────────────────
138
+ if __name__ == "__main__":
139
+ import uvicorn
140
+ uvicorn.run("proxy_server:app", host="0.0.0.0", port=7860, reload=True)