summaryrefslogtreecommitdiffhomepage
path: root/src/python/nxt_python_asgi_http.c
diff options
context:
space:
mode:
authorAndrew Clayton <a.clayton@nginx.com>2025-02-21 00:14:17 +0000
committerAndrew Clayton <a.clayton@nginx.com>2025-02-21 01:20:31 +0000
commit3fea47eaa3edd916ecf0b339626dd5f963838295 (patch)
tree6b4fd454949b9fb66ee577a16c2ab0e30f162e6c /src/python/nxt_python_asgi_http.c
parent7a0addd7422386cb7a8de68b8198a143c2f11a08 (diff)
downloadunit-3fea47eaa3edd916ecf0b339626dd5f963838295.tar.gz
unit-3fea47eaa3edd916ecf0b339626dd5f963838295.tar.bz2
python: Add Django 5.x compatibility
Note: This may not be *specific* to Django 5.x but is where the issue showed up. @codedoga on GitHub reported an issue with Unit and Django 5.x When trying to perform a simple POST/PUT request with body data, Unit was throwing the following error 2025/02/16 11:07:14 [error] 6#6 [unit] #9: Python failed to call 'future.result()' Traceback (most recent call last): File "/usr/local/lib/python3.13/site-packages/django/core/handlers/asgi.py", line 162, in __call__ await self.handle(scope, receive, send) File "/usr/local/lib/python3.13/site-packages/django/core/handlers/asgi.py", line 208, in handle task.result() ~~~~~~~~~~~^^ File "/usr/local/lib/python3.13/site-packages/django/core/handlers/asgi.py", line 239, in listen_for_disconnect assert False, "Invalid ASGI message after request body: %s" % message["type"] ^^^^^ AssertionError: Invalid ASGI message after request body: http.request There is no such issue with Django 4.x The issue was caused when Django started doing an 'async receive()' just after we have handled the initial request and passed it to the application. Django is then looking to see if/when we send it a 'http.disconnect' message. We were not prepared for this and would go through all the motions of handling the request again which would result in the erroneous 'http.request' message. What we need to do is track when we've handled the initial request. We can then use that information coupled with the fact if we get a request with 0 content length then we basically have nothing to do. For this we create a new nxt_py_asgi_http_t member, request_received. We can repurpose 'empty_body_received' for this if we rename it and change where we set it as now if 'request_received' is true then so would 'empty_body_received'. 'empty_body_received' was actually part of a previous commit that was addressing various receive() issues. I've checked that the provided reproducer application still works. Link: <https://github.com/django/django/commit/1d1ddffc27cd55c011298cd09bfa4de3fa73cf7a> Link: <https://github.com/nginx/unit/issues/564> Fixes: 567545213 ("Python: fixing ASGI receive() issues.") Closes: https://github.com/nginx/unit/issues/1561 Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
Diffstat (limited to 'src/python/nxt_python_asgi_http.c')
-rw-r--r--src/python/nxt_python_asgi_http.c10
1 files changed, 5 insertions, 5 deletions
diff --git a/src/python/nxt_python_asgi_http.c b/src/python/nxt_python_asgi_http.c
index cdd6357e..fc489bc1 100644
--- a/src/python/nxt_python_asgi_http.c
+++ b/src/python/nxt_python_asgi_http.c
@@ -27,7 +27,7 @@ typedef struct {
Py_ssize_t send_body_off;
uint8_t complete;
uint8_t closed;
- uint8_t empty_body_received;
+ uint8_t request_received;
} nxt_py_asgi_http_t;
@@ -102,7 +102,7 @@ nxt_py_asgi_http_create(nxt_unit_request_info_t *req)
http->send_body_off = 0;
http->complete = 0;
http->closed = 0;
- http->empty_body_received = 0;
+ http->request_received = 0;
}
return (PyObject *) http;
@@ -177,11 +177,9 @@ nxt_py_asgi_http_read_msg(nxt_py_asgi_http_t *http)
}
if (size == 0) {
- if (http->empty_body_received) {
+ if (http->request_received) {
Py_RETURN_NONE;
}
-
- http->empty_body_received = 1;
}
if (size > 0) {
@@ -234,6 +232,8 @@ nxt_py_asgi_http_read_msg(nxt_py_asgi_http_t *http)
Py_XDECREF(body);
+ http->request_received = 1;
+
return msg;
}