Comments (11)
Agree 💯, they are considered equal
from asgiref.
I put this in the WSGI compat docs very deliberately:
- SCRIPT_NAME is root_path
- PATH_INFO can be derived by stripping root_path from path
The WSGI adapter in asgiref
does not support hosting things under a prefix/non-root-path; that was mostly because I told myself at the time I'd go back and add it later on. As you can see, I still haven't...
from asgiref.
Standards are obtained by consensus, not by dictat, so if there's strong consensus on another way of doing it from all the main servers we can change it.
from asgiref.
Hi @abersheeran no problem with mentioning me, thank you for bringing this up.
I can confirm how my web framework is using root_path
today: it is used to obtain the full URL of a web request, including any path fragment a proxy server might use, like "/example-api" in the following example:
https://api.foo.example/example-api/
--> going to back-end exposed athttps://example.com/
I started using root_path
especially to obtain a redirect URL to the app itself when using OpenID Connect integration. This is mentioned here Neoteroi/BlackSheep#372.
Personally, I find natural to think about the "path" as the path from application's perspective, therefore not including the root path, but I don´t have a problem in updating my code to follow the specification.
from asgiref.
Hello all!
Just to make sure I'm on the same page, I'm gonna share my understanding of the current state, differences, and the current question.
Mounting
I see that the discussion/difference is in the end mainly about WSGI's PATH_INFO
vs ASGI's path
. Where PATH_INFO
includes only the last part of the path after any prefix and path
has everything, including any path prefix.
I see the discussion/problem and questions are only relevant to "mounting" ASGI apps, where "mounting" refers to putting the ASGI app behind a path prefix in a situation where the app's code doesn't know about this prefix.
For example, the app defines a route for /users/
in its code, but it is actually behind some frontend proxy like Nginx, under a path prefix of /api/v1/
, so, the client URL path is actually /api/v1/users/
even though the app's code doesn't define anything with /api/v1/
.
WSGI
In WSGI, PATH_INFO
refers to the last part of the path when an app is "mounted" (behind a path prefix that the app is not aware of, e.g. behind a frontend proxy like Nginx in a path /api/v1/
), so, if the app is behind Nginx under /api/v1/
, and the client requests /api/v1/users/
, the WSGI app would get in PATH_INFO
only the last part: /users/
.
When the WSGI app wants to check which of its own route/path handlers should handle that request, it checks the value of PATH_INFO
and matches it as-is with a route handler for that value, in this case, for /users/
. So, when finding a route to handle a request, it doesn't need to use SCRIPT_NAME
.
The WSGI app would get the path prefix in SCRIPT_NAME
, in this example, the value would be /api/v1/
. So, if the app wanted to provide the url_for
some specific route that the client should use (including the prefix), it would find the route handler for that path, and then concatenate SCRIPT_NAME
and PATH_INFO
.
ASGI
In ASGI, path
contains the full path from the URL the client used, so, in the example above, it would include the prefix of /api/v1/
, and the value would be /api/v1/users/
.
The ASGI app would receive the path prefix at path_root
, containing the same value that SCRIPT_NAME
would have contained, in this example, /api/v1/
.
When the ASGI app needs to find the route/path handler for a request, it would remove the prefix value in the root_path
from the path
, so, removing /api/v1/
from /api/v1/users/
, to get /users/
and then find the route/path handler in its code for /users/
.
When the ASGI app needs to find the url_for
a route/path, it would, again, remove the prefix in root_path
to find the route/path handler, and then it would concatenate the root_path
with the newly found sub-path from the router.
Relevance
Now, this is mainly relevant for things that are mounted behind a proxy, or behind/mounted in another app.
For example, say there's a Starlette app at /
, and it mounts a BlackShip app at /api/v2/
. Then a client requests for /api/v2/users/
. In this case, Starlette would take this request, and transfer it to BlackSheep.
In the current (unmodified) state of the spec, Starlette should send a scope including a root_path
with a value of /api/v2/
and a path
with a value of /api/v2/users/
. And then BlackSheep would use those two values to find its own route/path handler for the path /users/
.
The same for a2wsgi (which it already does), e.g. mounting a Flask app inside a FastAPI app, under /api/v1/
.
Moving Forward
Even though it could mean a couple of changes and "work" for all of us, I would consider it better to follow the spec as it is, without changing it, and fix this (small) detail in the tools as a "bug fix".
I fear otherwise it could become a scenario of "in version X of SuperASGITool it used the ASGI spec 2.3 as of 2023, but in version Y of SuperASGITool it started following the updated version of the ASGI spec 2.4 updated in 2024".
Something very similar happened with OpenAPI and JSON Schema
OpenAPI followed an old version of JSON Schema that didn't include examples
and defined its own examples
field with some structure. Then JSON Schema defined its own examples
field and OpenAPI was still for a long while using its own examples
field, so many things would be partially compatible with one or the other.
Now the latest OpenAPI follows the latest JSON Schema, and things are much simpler, but still, there are some places that accept the old examples
field from OpenAPI, with a slightly different format than JSON Schema. So, in a single OpenAPI document, there might be places with a field called examples
in one format, and in other specific places other examples
fields with another slightly different format.
All that is not terrible, and the difference is very small, but it ends up being somewhat confusing, in particular to users, and requires long text (like this one) explaining all the state of things, what works and what doesn't, when, how, etc.
More details about all the examples
stuff with OpenAPI, JSON Schema, FastAPI, and Pydantic here: https://fastapi.tiangolo.com/tutorial/schema-extra-example/#technical-details
...that's the type of long explanations I would hope we can save our users from. 😅
Starlette was recently using the old behavior, but it was fixed to follow the spec by @Kludex and me in encode/starlette#2352 and encode/starlette#2400.
Again, the places that need changing are only those that might be related to "mounting" apps under a path prefix (when that's supported).
from asgiref.
Line 60 in d95e378
asgiref itself seems to consider path and PATH_INFO to be completely equivalent. But if it wants to comply with ASGI's current specification, it should be modified as in this PR - remove SCRIPT_NAME
from PATH_INFO
. abersheeran/a2wsgi#40
https://asgi.readthedocs.io/en/latest/specs/www.html#wsgi-compatibility
from asgiref.
I put this in the WSGI compat docs very deliberately:
- SCRIPT_NAME is root_path
- PATH_INFO can be derived by stripping root_path from path
I mean, it seems that most of the existing ASGI-related procedures do not comply with this standard. Do we still need to insist on this?
from asgiref.
Okay, let's wait for the other people's opinions. As far as the code I looked up, most people still use exactly the same approach as WSGI.
from asgiref.
Yes, it is good to follow the rules. However, among the existing codes in the community, only a few frameworks or servers comply with this specification. Personally I think it's better to use exactly the same approach as WSGI. (Although I have made a2wsgi compliant spec)
WSGI has been used for more than ten years, and it is really confusing to be similar and different from WSGI in such small details. And this change does not seem to have any good impact. Instead, it makes the framework make more judgments when processing PATH.
We should let as few people as possible modify the code, rather than specify a specification that most people don't follow and then say "Hey, your code is wrong" when their users have problems.
from asgiref.
This was one of the few explicit divergences I made from WSGI - along with things like improving the string/bytes separation - and it was because I believed that having a "true" HTTP path was the most helpful thing to have in the path
attribute rather than some subset path that you can't actually build URLs on without knowing the mounting root anyway.
In ASGI, direct path manipulation with relative paths works perfectly fine; in WSGI, it does not, and you can easily shoot yourself in the foot. Ideally, in both specifications you are going to work out the third missing piece of info, but I regarded it as more important to have the path right out of the gate for simpler ASGI applications (assuming frameworks will have the relevant 3 lines of code to do URL matching, as they tend to).
ASGI itself is now seven years old, so an appeal to how old and fixed something is may not be entirely the right approach!
from asgiref.
FWIW this behaviour was brought into line in the last Daphne and Channels releases too. Django also is compliant.
from asgiref.
Related Issues (20)
- Add Type Annotations to `asgiref` Module HOT 1
- Add Type Annotations to `asgiref` Module Functions HOT 2
- Parameters to Generic[...] must all be type variables when use with sys.setprofile HOT 3
- Can asig http extensions be used in WebSocket Denial Response? HOT 2
- Using asyncio.shield hangs/deadlocks when used with sync middleware. HOT 10
- What am I allowed to do with the send/receive callables? HOT 1
- Wording: should “extra coroutines” actually be “extra tasks”? HOT 4
- async_to_sync does not use the correct thread when used as decorator HOT 3
- Contradiction between general and HTTP-specific sections on handling of send on a closed connection HOT 2
- `PATH_INFO` set incorrectly by `WsgiToAsgiInstance.build_environ()` HOT 1
- Compatibility with gevent monkey-patching? HOT 1
- Failure in exception handling in current_thread_executor.py _WorkItem.run() HOT 1
- Why `http.response.start` exists? HOT 1
- sdist is missing `tox.ini` HOT 1
- Task was destroyed but it is pending! HOT 1
- Regression in v3.8.0 HOT 9
- Issue in sync.py's SyncToAsync class as new ThreadPoolExecutor executors with daemon threads getting created for requests. HOT 4
- Spec question: `websocket.disconnect` doesn't support the `reason` field HOT 10
- Suggestion: set `NotRequired` on all fields that aren't required HOT 8
- DatabaseError with new version of asgiref HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from asgiref.