From 3fea47eaa3edd916ecf0b339626dd5f963838295 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Fri, 21 Feb 2025 00:14:17 +0000 Subject: 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: Link: Fixes: 567545213 ("Python: fixing ASGI receive() issues.") Closes: https://github.com/nginx/unit/issues/1561 Signed-off-by: Andrew Clayton --- src/python/nxt_python_asgi_http.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') 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; } -- cgit