Skip to content

Commit a4985fb

Browse files
Fix is_connection_dropped with loads of useful contextual comments
1 parent fb95be2 commit a4985fb

File tree

2 files changed

+22
-1
lines changed

2 files changed

+22
-1
lines changed

httpx/concurrency/asyncio.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,20 @@ async def write(
122122
raise WriteTimeout() from None
123123

124124
def is_connection_dropped(self) -> bool:
125+
# Counter-intuitively, what we really want to know here is whether the socket is
126+
# *readable*, i.e. whether it would return immediately with empty bytes if we
127+
# called ``.recv()` on it, indicating that the other end has closed the socket.
128+
# See: https://github.com/encode/httpx/pull/143#issuecomment-515181778
129+
#
130+
# As it turns out, asyncio checks for readability in the background
131+
# (see: https://github.com/encode/httpx/pull/276#discussion_r322000402),
132+
# so checking for EOF or readability here would yield the same result.
133+
#
134+
# At the cost of rigour, we check for EOF instead of readability because asyncio
135+
# does not expose any public API to check for readability.
136+
# (For a solution that uses private asyncio APIs, see:
137+
# https://github.com/encode/httpx/pull/143#issuecomment-515202982)
138+
125139
return self.stream_reader.at_eof()
126140

127141
async def close(self) -> None:

httpx/concurrency/trio.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,19 @@ async def read(
6363
raise ReadTimeout() from None
6464

6565
def is_connection_dropped(self) -> bool:
66+
# Adapted from: https://github.com/encode/httpx/pull/143#issuecomment-515202982
6667
stream = self.stream
68+
6769
# Peek through any SSLStream wrappers to get the underlying SocketStream.
6870
while hasattr(stream, "transport_stream"):
6971
stream = stream.transport_stream
7072
assert isinstance(stream, trio.SocketStream)
71-
return not stream.socket.is_readable()
73+
74+
# Counter-intuitively, what we really want to know here is whether the socket is
75+
# *readable*, i.e. whether it would return immediately with empty bytes if
76+
# we called .recv() on it, indicating that the other end has closed the socket.
77+
# See: https://github.com/encode/httpx/pull/143#issuecomment-515181778
78+
return stream.socket.is_readable()
7279

7380
def write_no_block(self, data: bytes) -> None:
7481
self.write_buffer += data

0 commit comments

Comments
 (0)