Skip to content

Commit cf9aa61

Browse files
committed
Handle connection resets on Windows
Refactor OSError re-throwing and wrap all instances of 'ov.getresult()' with the decorator so that client-side disconnects can be gracefully handled. Update the Proactor server to handle the connection and continue looping and serving (client disconnects are not a fatal error).
1 parent 6f4d64b commit cf9aa61

File tree

2 files changed

+27
-8
lines changed

2 files changed

+27
-8
lines changed

Lib/asyncio/proactor_events.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,10 @@ def loop(f=None):
858858
if self.is_closed():
859859
return
860860
f = self._proactor.accept(sock)
861+
except exceptions.CancelledError:
862+
# Effectively ignore connections that throw a cancelled error
863+
# during setup, loop back around and continue serving.
864+
sock.close()
861865
except OSError as exc:
862866
if sock.fileno() != -1:
863867
self.call_exception_handler({

Lib/asyncio/windows_events.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,24 @@ async def _make_subprocess_transport(self, protocol, args, shell,
414414
return transp
415415

416416

417+
def overlapped_connection_reset_error_handler(func):
418+
"""
419+
Rethrow common connection errors that come from clients
420+
disconnecting unexpectedly. This is a common error that
421+
can be safely ignored in most cases.
422+
"""
423+
def wrapper(*args, **kwargs):
424+
try:
425+
return func(*args, **kwargs)
426+
except OSError as exc:
427+
if exc.winerror in (_overlapped.ERROR_NETNAME_DELETED,
428+
_overlapped.ERROR_OPERATION_ABORTED):
429+
raise ConnectionResetError(*exc.args)
430+
else:
431+
raise
432+
return wrapper
433+
434+
417435
class IocpProactor:
418436
"""Proactor implementation using IOCP."""
419437

@@ -458,15 +476,9 @@ def _result(self, value):
458476
return fut
459477

460478
@staticmethod
479+
@overlapped_connection_reset_error_handler
461480
def finish_socket_func(trans, key, ov):
462-
try:
463-
return ov.getresult()
464-
except OSError as exc:
465-
if exc.winerror in (_overlapped.ERROR_NETNAME_DELETED,
466-
_overlapped.ERROR_OPERATION_ABORTED):
467-
raise ConnectionResetError(*exc.args)
468-
else:
469-
raise
481+
return ov.getresult()
470482

471483
@classmethod
472484
def _finish_recvfrom(cls, trans, key, ov, *, empty_result):
@@ -552,6 +564,7 @@ def accept(self, listener):
552564
ov = _overlapped.Overlapped(NULL)
553565
ov.AcceptEx(listener.fileno(), conn.fileno())
554566

567+
@overlapped_connection_reset_error_handler
555568
def finish_accept(trans, key, ov):
556569
ov.getresult()
557570
# Use SO_UPDATE_ACCEPT_CONTEXT so getsockname() etc work.
@@ -596,6 +609,7 @@ def connect(self, conn, address):
596609
ov = _overlapped.Overlapped(NULL)
597610
ov.ConnectEx(conn.fileno(), address)
598611

612+
@overlapped_connection_reset_error_handler
599613
def finish_connect(trans, key, ov):
600614
ov.getresult()
601615
# Use SO_UPDATE_CONNECT_CONTEXT so getsockname() etc work.
@@ -628,6 +642,7 @@ def accept_pipe(self, pipe):
628642
# completion of the connection.
629643
return self._result(pipe)
630644

645+
@overlapped_connection_reset_error_handler
631646
def finish_accept_pipe(trans, key, ov):
632647
ov.getresult()
633648
return pipe

0 commit comments

Comments
 (0)