Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python: Add Django 5.x compatibility #1563

Merged
merged 1 commit into from
Feb 21, 2025
Merged

python: Add Django 5.x compatibility #1563

merged 1 commit into from
Feb 21, 2025

Conversation

ac000
Copy link
Member

@ac000 ac000 commented Feb 21, 2025

    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 <[email protected]>

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] nginx#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: <nginx#564>
Fixes: 5675452 ("Python: fixing ASGI receive() issues.")
Closes: nginx#1561
Signed-off-by: Andrew Clayton <[email protected]>
@ac000 ac000 linked an issue Feb 21, 2025 that may be closed by this pull request
@ac000 ac000 marked this pull request as ready for review February 21, 2025 01:34
@ac000 ac000 requested a review from hongzhidao February 21, 2025 01:35
Copy link
Contributor

@hongzhidao hongzhidao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

@ac000 ac000 merged commit 3fea47e into nginx:master Feb 21, 2025
25 checks passed
@ac000 ac000 deleted the django-5x branch February 21, 2025 16:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Django ASGI: Invalid ASGI message after request body: http.request
2 participants