Uh oh!
There was an error while loading. Please reload this page.
asgi: Send http.disconnect message on receive() after response complete - #909
Conversation
florimondmanca
left a comment
There was a problem hiding this comment.
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 Kludex/starlette#912 fix the issue you were seeing with StreamingResponse? |
JayH5
commented
Apr 28, 2020
@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 Kludex/starlette#908. My tests all pass with Kludex/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 😰 |
JayH5
commented
Apr 28, 2020
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 Kludex/starlette#908. Starlette's test client doesn't suffer from this problem because it doesyield to the event loop. httpx is not really able to do that because we can't just call |
florimondmanca
commented
May 1, 2020
@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 Kludex/starlette#912. Are we okay to merge this? |
lovelydinosaur
commented
May 1, 2020
Seems valid to me too, yup. |
JayH5
commented
May 1, 2020
I think we're ok to merge this one, the Starlette one I'm less sure about 👍 |
lovelydinosaur
commented
May 1, 2020
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.) |
lovelydinosaur
commented
May 1, 2020
(Which would resolve the issue, since waiting for the event really would be an async checkpoint) |
JayH5
commented
May 1, 2020
@tomchristie yeah I'm in agreement, I just don't know how to implement an "event"? |
lovelydinosaur
commented
May 1, 2020
Sketching out some of it... defget_event():
ifsniffio.current_async_library() =="trio":
returntrio.Event()
else:
returnasyncio.Event()
...
asyncdefrequest(...):
# ...response_complete_event=get_event()
# To set the event.response_complete_event.set()
# To wait for the event.awaitresponse_complete_event.wait() |
lovelydinosaur
commented
May 1, 2020
We've got similar stuff in the |
JayH5
commented
May 1, 2020
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 |
lovelydinosaur
commented
May 1, 2020
Well noticed, yes. I'm okay with |
JayH5
commented
May 1, 2020
OK, should get to another PR a bit later today. |
JayH5
commented
May 3, 2020
@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 Kludex/starlette#908.
There are kind of two different problems:
receive()(to check for disconnects) andsend()(to send the response) concurrently in itsStreamingResponsetype. 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 Kludex/starlette#912 to attempt to work around that.httpx's ASGI dispatch never sends anhttp.disconnectmessage. 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.