-
-
Notifications
You must be signed in to change notification settings - Fork 845
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
asgi: Send http.disconnect message on receive() after response complete #909
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sounds good to me - right now we’re indeed missing the http.disconnect part from the ASGI spec so its worth fixing in itself. Thanks!
Does this PR and encode/starlette#912 fix the issue you were seeing with StreamingResponse? |
@florimondmanca unfortunately, it looks like this only partially fixes my problem there 😞 I have a custom Starlette middleware library (unfortunately closed-source) that I'm testing using httpx. Before this change, any test that called the app with middleware installed would hang. After this change, one of the tests will pass, but then subsequent tests still hang. Some callback somewhere or something is obviously not completing. The tests are all very simple and similar to the one in encode/starlette#908. My tests all pass with encode/starlette#912 (even without this change) but it's obviously not the nicest fix so it'd be good to get to the bottom of why this PR doesn't fix things. I'm still digging...just somewhat stuck in async callback complexity 😰 |
OK so I think I have some idea of what's happening. My point number 1 in the description above is still accurate, but also it's worse than that. When Starlette calls So I think this PR is a valid fix but doesn't solve encode/starlette#908. Starlette's test client doesn't suffer from this problem because it does yield to the event loop. httpx is not really able to do that because we can't just call |
@JayH5 I think this is a valid fix in itself. It's very likely that we need a Starlette fix as well - most probably via encode/starlette#912. Are we okay to merge this? |
Seems valid to me too, yup. |
I think we're ok to merge this one, the Starlette one I'm less sure about 👍 |
Well, maybe. It's possible that actually we do want the fix to that to be at the HTTPX really shouldn't just keep sending (And either error if we're called once again after that, or just block forever.) |
(Which would resolve the issue, since waiting for the event really would be an async checkpoint) |
@tomchristie yeah I'm in agreement, I just don't know how to implement an "event"? |
Sketching out some of it... def get_event():
if sniffio.current_async_library() == "trio":
return trio.Event()
else:
return asyncio.Event()
...
async def request(...):
# ...
response_complete_event = get_event()
# To set the event.
response_complete_event.set()
# To wait for the event.
await response_complete_event.wait() |
We've got similar stuff in the |
Cool, yeah, I was uncertain about where that event abstraction should live since now most of the async-runtime-specific stuff is in httpcore. I can do what you're suggesting. One concern is that |
Well noticed, yes. I'm okay with |
OK, should get to another PR a bit later today. |
@tomchristie this discussion resulted in #919 |
I've been trying to track down what broke for me when using httpx with starlette==0.13.3 with ASGI dispatch, see encode/starlette#908.
There are kind of two different problems:
receive()
(to check for disconnects) andsend()
(to send the response) concurrently in itsStreamingResponse
type. It tries to wait for either of those two options to complete (usingasyncio.wait({tasks}, return_when=asyncio.FIRST_COMPLETED)
). Because the ASGI dispatch in httpx is generally effectively synchronous, bothsend()
andreceive()
may never return control to the event loop when called which means that if either task that is waited on does not complete, thenasyncio.wait()
will not return. I've made Fix infinite loop when using StreamingResponse with ASGI client starlette#912 to attempt to work around that.httpx
's ASGI dispatch never sends anhttp.disconnect
message. The ASGI docs say this should happen ifreceive()
is called after a response is complete. This is what this PR does.Starlette's test client does this already, although it also checks that the request is complete before sending
http.disconnect
--I'm not really sure why that check is necessary, though.