|
| 1 | +# import logging |
| 2 | +# from fastapi import FastAPI, Request, Response, HTTPException |
| 3 | +# from fastapi.responses import RedirectResponse, JSONResponse |
| 4 | +# import os |
| 5 | + |
| 6 | +# # Configure logging |
| 7 | +# logging.basicConfig(level=logging.INFO) |
| 8 | +# logger = logging.getLogger(__name__) |
| 9 | + |
| 10 | +# app = FastAPI() |
| 11 | + |
| 12 | +# MIDDLEWARE_BACKEND_URL = os.getenv("MIDDLEWARE_BACKEND_URL") # e.g. http://middleware_endpoint_handler:5000 |
| 13 | +# API_SERVER_URL = os.getenv("API_SERVER_URL") # e.g. http://api_server_gpt_5:8080 |
| 14 | + |
| 15 | +# @app.get("/health") |
| 16 | +# async def health_check(): |
| 17 | +# return {"status": "healthy", "service": "middleware_route_filter"} |
| 18 | + |
| 19 | +# @app.middleware("http") |
| 20 | +# async def route_redirect(request: Request, call_next): |
| 21 | +# try: |
| 22 | +# path = request.url.path |
| 23 | +# query = request.url.query |
| 24 | + |
| 25 | +# # Block docs endpoints to avoid exposing backend docs |
| 26 | +# if path in ["/docs", "/openapi.json", "/api/docs", "/api/openapi.json"]: |
| 27 | +# raise HTTPException(status_code=404, detail="Not found") |
| 28 | + |
| 29 | +# # Allow health endpoint locally |
| 30 | +# if path == "/health": |
| 31 | +# return await call_next(request) |
| 32 | + |
| 33 | +# logger.info(f"Raw incoming path: {path}") |
| 34 | + |
| 35 | + |
| 36 | +# # Decide target backend URL based on path |
| 37 | +# if path.startswith("/middleware"): |
| 38 | +# target_url = f"{MIDDLEWARE_BACKEND_URL}{path[len('/middleware'):] or '/'}" |
| 39 | +# elif path.startswith("/api"): |
| 40 | +# target_url = f"{API_SERVER_URL}{path}" |
| 41 | +# else: |
| 42 | +# target_url = f"{API_SERVER_URL}{path}" |
| 43 | + |
| 44 | +# # Append query parameters if present |
| 45 | +# if query: |
| 46 | +# target_url = f"{target_url}?{query}" |
| 47 | + |
| 48 | +# logger.info(f"Redirecting {path} to {target_url}") |
| 49 | + |
| 50 | +# # Redirect client to backend server URL |
| 51 | +# return RedirectResponse(url=target_url) |
| 52 | + |
| 53 | +# except HTTPException as http_exc: |
| 54 | +# logger.warning(f"HTTPException: {http_exc.detail} for path {request.url.path}") |
| 55 | +# return JSONResponse(status_code=http_exc.status_code, content={"detail": http_exc.detail}) |
| 56 | + |
| 57 | +# except Exception as exc: |
| 58 | +# logger.error(f"Unhandled error: {exc}") |
| 59 | +# return JSONResponse(status_code=500, content={"detail": "Internal server error"}) |
| 60 | + |
| 61 | +import os |
| 62 | +import logging |
| 63 | +from fastapi import FastAPI, Request, HTTPException |
| 64 | +from fastapi.responses import JSONResponse, Response |
| 65 | +import httpx |
| 66 | + |
| 67 | +# Configure logging |
| 68 | +logging.basicConfig(level=logging.INFO) |
| 69 | +logger = logging.getLogger(__name__) |
| 70 | + |
| 71 | +app = FastAPI() |
| 72 | + |
| 73 | +# Set these as ENV vars in your docker-compose for the middleware container! |
| 74 | +MIDDLEWARE_BACKEND_URL = os.getenv("MIDDLEWARE_BACKEND_URL", "http://middleware_endpoint_handler:5000") |
| 75 | +API_SERVER_URL = os.getenv("API_SERVER_URL", "http://api_server_gpt_5:8080") |
| 76 | + |
| 77 | +@app.get("/health") |
| 78 | +async def health_check(): |
| 79 | + return {"status": "healthy", "service": "middleware_route_filter"} |
| 80 | + |
| 81 | +@app.middleware("http") |
| 82 | +async def proxy_middleware(request: Request, call_next): |
| 83 | + try: |
| 84 | + path = request.url.path |
| 85 | + query = request.url.query |
| 86 | + |
| 87 | + # Block docs endpoints to avoid exposing backend docs |
| 88 | + if path in ["/docs", "/openapi.json", "/api/docs", "/api/openapi.json"]: |
| 89 | + raise HTTPException(status_code=404, detail="Not found") |
| 90 | + |
| 91 | + if path == "/health": |
| 92 | + return await call_next(request) |
| 93 | + |
| 94 | + logger.info(f"Raw incoming path: {path}") |
| 95 | + |
| 96 | + # Decide target backend URL based on path |
| 97 | + if path.startswith("/middleware"): |
| 98 | + target_url = f"{MIDDLEWARE_BACKEND_URL}{path[len('/middleware'):] or '/'}" |
| 99 | + else: |
| 100 | + target_url = f"{API_SERVER_URL}{path}" |
| 101 | + |
| 102 | + if query: |
| 103 | + target_url = f"{target_url}?{query}" |
| 104 | + |
| 105 | + logger.info(f"Proxying {path} to {target_url}") |
| 106 | + |
| 107 | + # Prepare headers (remove host header to avoid conflicts) |
| 108 | + headers = dict(request.headers) |
| 109 | + headers.pop("host", None) |
| 110 | + |
| 111 | + # Proxy the request using httpx |
| 112 | + async with httpx.AsyncClient() as client: |
| 113 | + proxied_response = await client.request( |
| 114 | + method=request.method, |
| 115 | + url=target_url, |
| 116 | + headers=headers, |
| 117 | + content=await request.body(), |
| 118 | + timeout=None # Consider setting a reasonable timeout |
| 119 | + ) |
| 120 | + |
| 121 | + # Copy headers except for transfer-encoding, content-encoding, connection |
| 122 | + excluded_headers = {"content-encoding", "transfer-encoding", "connection"} |
| 123 | + response_headers = {k: v for k, v in proxied_response.headers.items() if k.lower() not in excluded_headers} |
| 124 | + |
| 125 | + return Response( |
| 126 | + content=proxied_response.content, |
| 127 | + status_code=proxied_response.status_code, |
| 128 | + headers=response_headers, |
| 129 | + media_type=proxied_response.headers.get("content-type") |
| 130 | + ) |
| 131 | + |
| 132 | + except HTTPException as http_exc: |
| 133 | + logger.warning(f"HTTPException: {http_exc.detail} for path {request.url.path}") |
| 134 | + return JSONResponse(status_code=http_exc.status_code, content={"detail": http_exc.detail}) |
| 135 | + except Exception as exc: |
| 136 | + logger.error(f"Unhandled error: {exc}") |
| 137 | + return JSONResponse(status_code=500, content={"detail": "Internal server error"}) |
0 commit comments