Skip to content

Commit

Permalink
python: Add Django 5.x compatibility
Browse files Browse the repository at this point in the history
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: <django/django@1d1ddff>
Link: <#564>
Fixes: 5675452 ("Python: fixing ASGI receive() issues.")
Closes: #1561
Signed-off-by: Andrew Clayton <[email protected]>
  • Loading branch information
ac000 committed Feb 21, 2025
1 parent 7a0addd commit 3fea47e
Showing 1 changed file with 5 additions and 5 deletions.
10 changes: 5 additions & 5 deletions src/python/nxt_python_asgi_http.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;


Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}

Expand Down

0 comments on commit 3fea47e

Please sign in to comment.