-
Couldn't load subscription status.
- Fork 42
Description
When root-path is set through uvicorn's --root-path flag, some of the links returned from stac-fastapi-pgstac are malformed and contain the root path twice.
Steps to reproduce
To reproduce this behavior locally, apply the following diff to the docker-compose file:
diff --git a/docker-compose.yml b/docker-compose.yml
index 2dcafa9..ae2628c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -89,7 +89,8 @@ services:
app-nginx:
extends:
service: app
- command: bash -c "./scripts/wait-for-it.sh database:5432 && uvicorn stac_fastapi.pgstac.app:app --host 0.0.0.0 --port 8082 --proxy-headers --forwarded-allow-ips=*"
+ container_name: stac-fastapi-pgstac-nginx
+ command: bash -c "./scripts/wait-for-it.sh database:5432 && uvicorn stac_fastapi.pgstac.app:app --host 0.0.0.0 --port 8082 --proxy-headers --forwarded-allow-ips=* --root-path=/api/v1/pgstac"
environment:
- ROOT_PATH=/api/v1/pgstacThis sets the --root-path flag.
Now start the app-nginx service: docker compose up --build app-nginx nginx
Inspect the links returned from the collections endpoint:
curl -s http://0.0.0.0:8080/api/v1/pgstac/collections | jq '.links'
[
{
"rel": "root",
"type": "application/json",
"href": "http://0.0.0.0:8080/api/v1/pgstac/"
},
{
"rel": "self",
"type": "application/json",
"href": "http://0.0.0.0:8080/api/v1/pgstac/api/v1/pgstac/collections"
}
]
You would notice that the href in the second link entry is malformed and contains the root path /api/v1/pgstac twice.
Probable cause
There was a refactoring into how root path is handled by uvicorn in Kludex/uvicorn#2213
This was released as part of uvicorn 0.26.0 (https://github.com/encode/uvicorn/blob/master/docs/release-notes.md#0260-january-16-2024)
This was integrated into stac-fastapi-pgstac in 7135059 and released as part of the 4.0.0 release in #196
After uvicorn 0.26.0 and stac-fastapi-pgstac 4.0.0, all requests going through uvicorn go to /{root-path}/requested-path. This also explains behavior in developmentseed/eoapi-k8s#195 (comment)
Possible fix
One way to get around this is of course to not set root path through uvicorn's --root-path flag and use the ROOT_PATH env var instead. But I don't think that's a good solution.
Here's a patch for how to handle this during link generation:
diff --git a/stac_fastapi/pgstac/models/links.py b/stac_fastapi/pgstac/models/links.py
index 6697488..5d72de7 100644
--- a/stac_fastapi/pgstac/models/links.py
+++ b/stac_fastapi/pgstac/models/links.py
@@ -51,7 +51,30 @@ class BaseLinks:
@property
def url(self):
"""Get the current request url."""
- url = urljoin(str(self.request.base_url), self.request.url.path.lstrip("/"))
+ # base_url is of the form http{s}://host{:port}/{root_path}
+ base_url = self.request.base_url
+ path = self.request.url.path
+ # root path can be set in the request scope in two different ways:
+ # by uvicorn when running with --root-path
+ # or by FastAPI when running with FastAPI(root_path="...")
+ # When root path is set by uvicorn, request.url.path will have the root path prefix.
+ # eg. if root path is "/api" and the path is "/collections",
+ # the request.url.path will be "/api/collections"
+ # We need to remove the root path prefix from the path before
+ # joining the base_url and path to get the full url to avoid
+ # having root_path twice in the url
+ if self.request.scope.get("root_path") and not self.request.app.root_path:
+ # self.request.app.root_path is set by FastAPI when running with FastAPI(root_path="...")
+ # If self.request.app.root_path is not set but self.request.scope.get("root_path") is set,
+ # then the root path is set by uvicorn
+ # So we need to remove the root path prefix from the path before
+ # joining the base_url and path to get the full url
+ root_path = self.request.scope["root_path"]
+ if path.startswith(root_path):
+ path = path[len(root_path) :]
+ path = path.lstrip("/")
+
+ url = urljoin(str(base_url), path)
if qs := self.request.url.query:
url += f"?{qs}"We deployed an image with this patch applied for IFRC Montandon and it worked quite well. @vincentsarago let me know if this looks ok and I'd love to create a PR