Skip to content

Commit f9c4393

Browse files
fantix1st1
authored andcommitted
Fix error caused by short-circuit on EPOLLHUP
1 parent b996e0f commit f9c4393

File tree

2 files changed

+57
-1
lines changed

2 files changed

+57
-1
lines changed

tests/test_unix.py

+51
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,57 @@ def test_create_unix_server_path_stream_bittype(self):
471471
finally:
472472
os.unlink(fn)
473473

474+
@unittest.skipUnless(sys.platform.startswith('linux'), 'requires epoll')
475+
def test_epollhup(self):
476+
SIZE = 50
477+
eof = False
478+
done = False
479+
recvd = b''
480+
481+
class Proto(asyncio.BaseProtocol):
482+
def connection_made(self, tr):
483+
tr.write(b'hello')
484+
self.data = bytearray(SIZE)
485+
self.buf = memoryview(self.data)
486+
487+
def get_buffer(self, sizehint):
488+
return self.buf
489+
490+
def buffer_updated(self, nbytes):
491+
nonlocal recvd
492+
recvd += self.buf[:nbytes]
493+
494+
def eof_received(self):
495+
nonlocal eof
496+
eof = True
497+
498+
def connection_lost(self, exc):
499+
nonlocal done
500+
done = exc
501+
502+
async def test():
503+
with tempfile.TemporaryDirectory() as td:
504+
sock_name = os.path.join(td, 'sock')
505+
srv = await self.loop.create_unix_server(Proto, sock_name)
506+
507+
s = socket.socket(socket.AF_UNIX)
508+
with s:
509+
s.setblocking(False)
510+
await self.loop.sock_connect(s, sock_name)
511+
d = await self.loop.sock_recv(s, 100)
512+
self.assertEqual(d, b'hello')
513+
514+
# IMPORTANT: overflow recv buffer and close immediately
515+
await self.loop.sock_sendall(s, b'a' * (SIZE + 1))
516+
517+
srv.close()
518+
await srv.wait_closed()
519+
520+
self.loop.run_until_complete(test())
521+
self.assertTrue(eof)
522+
self.assertIsNone(done)
523+
self.assertEqual(recvd, b'a' * (SIZE + 1))
524+
474525

475526
class Test_AIO_Unix(_TestUnix, tb.AIOTestCase):
476527
pass

uvloop/handles/stream.pyx

+6-1
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,12 @@ cdef void __uv_stream_buffered_on_read(uv.uv_stream_t* stream,
953953
return
954954

955955
try:
956-
if not sc._read_pybuf_acquired:
956+
if nread > 0 and not sc._read_pybuf_acquired:
957+
# From libuv docs:
958+
# nread is > 0 if there is data available or < 0 on error. When
959+
# we’ve reached EOF, nread will be set to UV_EOF. When
960+
# nread < 0, the buf parameter might not point to a valid
961+
# buffer; in that case buf.len and buf.base are both set to 0.
957962
raise RuntimeError(
958963
f'no python buffer is allocated in on_read; nread={nread}')
959964

0 commit comments

Comments
 (0)