gradio-app / gradio

Build and share delightful machine learning apps, all in Python. 🌟 Star to support our work!
http://www.gradio.app
Apache License 2.0
30.81k stars 2.29k forks source link

Gradio mounted with fastapi via https in nginx returns wrong path #8073

Open aka7774 opened 2 months ago

aka7774 commented 2 months ago

Describe the bug

I use https connection and BASIC authentication to securely so many ai apps.

https://fastapi.example.com:18000/gradio is accessed by the nginx proxy to http://127.0.0.1:8000/gradio is called.

http://127.0.0.1:8000/ is fastapi and gradio is mounted on /gradio.

For example, a connection that should access /gradio/info becomes /info and returns a 404 error.

Have you searched existing issues? 🔎

Reproduction

nginx.conf

server {
  server_name fastapi.example.com;
  listen 18000 ssl;

  location / {
        proxy_pass http://127.0.0.1:8000/;
        proxy_buffering off;
        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host 'fastapi.example.com:18000';
        proxy_set_header X-Forwarded-Host 'fastapi.example.com:18000';
        proxy_set_header X-Forwarded-Proto $scheme;

        auth_basic "Basic Authentication";
        auth_basic_user_file "/etc/nginx/conf.d/.htpasswd";
  }
}

main.py

import gradio as gr
from app import demo
app = FastAPI()
...
gr.mount_gradio_app(app, demo, path="/gradio")

app.py

import gradio as gr
with gr.Blocks() as demo:
...

if __name__ == '__main__':
    demo.launch()

Temporary workaround

  location = /info {
    rewrite ^ /gradio/info;
  }
  location = /theme.css {
    rewrite ^ /gradio/theme.css;
  }
  location /assets/ {
    rewrite (.*) /gradio$1;
  }
  location /heartbeat/ {
    rewrite (.*) /gradio$1;
  }
  location /queue/ {
    rewrite (.*) /gradio$1;
  }

Screenshot

image

Logs

No response

System Info

gradio==4.27.0
fastapi==0.110.2
uvicorn==0.29.0

nginx version: nginx/1.24.0
built by gcc 11.2.0 (Ubuntu 11.2.0-19ubuntu1)
built with OpenSSL 3.0.2 15 Mar 2022

Severity

I can work around it

abidlabs commented 2 months ago

Will take a look! Thanks for providing the detailed repro

insistence commented 2 months ago

Similar issue, after setting root_path to uvicorn, mounting the gradio app in fastapi shows 404 error. The same error still occurs when the front-end uses the Vite proxy.

from fastapi import FastAPI
import gradio as gr
import uvicorn

app = FastAPI()

def greet(name):
    return "Hello " + name + "!"

gradio_app = gr.Interface(fn=greet, inputs="text", outputs="text")

gr.mount_gradio_app(app, gradio_app, path="/gradio")

if __name__ == "__main__":
    uvicorn.run('app:app', host="0.0.0.0", port=8000, root_path='/dev-api')

I think this issue seems to be related to the bug mentioned in this PR(https://github.com/tiangolo/fastapi/pull/11160).

abidlabs commented 1 month ago

Just to make sure I understand @aka7774, you are serving your fastapi app on the root / and within that fastapi app, you are serving a gradio app on the /gradio route?

aka7774 commented 1 month ago

Yes!

ex. https://huggingface.co/spaces/aka7774/katanuki

/ : not use /katanuki : api /gradio : webui

abidlabs commented 1 month ago

Could you try to see if the issue is fixed in this version of gradio?

pip install https://gradio-builds.s3.amazonaws.com/3fbf2e8e7059f3bab64b0839ab7a0f9f864f0a66/gradio-4.31.5-py3-none-any.whl
green-sh commented 1 month ago

same issue here. I tried the version in the above comment and version gradio==4.32.0 using both 'root_path' and 'path' in 'mount_gradio_app' results in the gradio app being unreachable.

I tried the follwing:

# Using the intended way
app = FastAPI()

block = webapp.getLayout()
gr.mount_gradio_app(app=app, blocks=block, path="/demo/", root_path="http://localhost:8000/demo/")
app = FastAPI()
app2 = FastAPI()

block = webapp.getLayout()
gr.mount_gradio_app(app=app2, blocks=block, path="/", root_path="http://localhost:8000/demo/")

app.mount("/demo", app2)

Both examples work just fine without root_path. Adding it will results in 404's from FastAPI.

green-sh commented 1 month ago

I used a quick fix: monkeypatch the root_path function and provide an absolut root_path without host instead. Since I am not specifieing the host it worked behind my proxy.

import gradio
def get_root_url(
    request: fastapi.Request, route_path: str, root_path: str | None
):
    return root_path
import gradio.route_utils 
gradio.route_utils.get_root_url = get_root_url
... # fastapi and block defnition
gr.mount_gradio_app(app=app, blocks=block, path="/demo", root_path="/demo")

Note that this might have sideeffects im not aware of.