3

I am trying to proxy an external website (Flower monitoring URL running on different container) using python Fast API framework:

client = AsyncClient(base_url=f'http://containername:7800/monitor')

@app.get(“/monitor/{path:path}”)
async def tile_request(path: str):
    req = client.build_request("GET", path)
    r = await client.send(req, stream=True)
    return StreamingResponse(
        r.aiter_raw(),
        background=BackgroundTask(r.aclose),
        headers=r.headers
   )

It is able to proxy the container URL for every path. For ex.

http://python_server:8001/monitor/dashboard --> http://containername:7800/monitor/dashboard

http://python_server:8001/monitor/tasks --> http://containername:7800/monitor/tasks

It works well. But it fails when the PATH has some query params in the URL.

For ex.

http://python_server:8001/monitor/dashboard?json=1&_=1641485992460 --> redirects to http://containername:7800/monitor/dashboard 

(Please note that no query params are appended to the URL).

Can anyone please help with how we can proxy this external website's any path with any query param.

4
  • you only pass the path, you dont capture or pass the query params. you can get them from the request object, Commented Jan 6, 2022 at 16:33
  • Does this answer your question? FastAPI variable query parameters Commented Jan 6, 2022 at 16:33
  • Thanks, I think it should work.. need to check how i can pass the query param to httpx client. Commented Jan 6, 2022 at 16:36
  • Please have a look at related answers here, as well as here and here Commented Jun 18, 2023 at 13:58

1 Answer 1

7

This code works for me and is used in production:

import httpx
from httpx import AsyncClient
from fastapi import Request
from fastapi.responses import StreamingResponse
from starlette.background import BackgroundTask

app = FastAPI()
HTTP_SERVER = AsyncClient(base_url="http://localhost:8000/")

async def _reverse_proxy(request: Request):
    url = httpx.URL(path=request.url.path, query=request.url.query.encode("utf-8"))
    rp_req = HTTP_SERVER.build_request(
        request.method, url, headers=request.headers.raw, content=await request.body()
    )
    rp_resp = await HTTP_SERVER.send(rp_req, stream=True)
    return StreamingResponse(
        rp_resp.aiter_raw(),
        status_code=rp_resp.status_code,
        headers=rp_resp.headers,
        background=BackgroundTask(rp_resp.aclose),
    )

app.add_route("/{path:path}", _reverse_proxy, ["GET", "POST"])
Sign up to request clarification or add additional context in comments.

2 Comments

When does that client HTTP_SERVER ever get closed? I'm always confused as to the right context to close (session or every request) but seems like problems could start by never closing??
the HTTP_SERVER response get's closed as a background task in the StreamingResponse as you can see above.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.