diff --git a/cheroot/errors.py b/cheroot/errors.py index 4395e56528..91fac316ba 100644 --- a/cheroot/errors.py +++ b/cheroot/errors.py @@ -56,3 +56,24 @@ def plat_specific_errors(*errnames): if sys.platform == 'darwin': socket_errors_to_ignore.extend(plat_specific_errors('EPROTOTYPE')) socket_errors_nonblocking.extend(plat_specific_errors('EPROTOTYPE')) + + +acceptable_sock_shutdown_error_codes = { + errno.ENOTCONN, + errno.EPIPE, errno.ESHUTDOWN, # corresponds to BrokenPipeError in Python 3 + errno.ECONNRESET, # corresponds to ConnectionResetError in Python 3 +} +"""Errors that may happen during the connection close sequence. + +* ENOTCONN — client is no longer connected +* EPIPE — write on a pipe while the other end has been closed +* ESHUTDOWN — write on a socket which has been shutdown for writing +* ECONNRESET — connection is reset by the peer + +Ref: https://github.com/cherrypy/cheroot/issues/341#issuecomment-735884889 +""" + +try: # py3 + broken_connection_exceptions = (BrokenPipeError, ConnectionResetError) +except NameError: # py2 + broken_connection_exceptions = () diff --git a/cheroot/server.py b/cheroot/server.py index a5c31b2e65..8ecf9265a2 100644 --- a/cheroot/server.py +++ b/cheroot/server.py @@ -75,7 +75,6 @@ import platform import contextlib import threading -import errno try: from functools import lru_cache @@ -1478,9 +1477,10 @@ def _close_kernel_socket(self): try: shutdown(socket.SHUT_RDWR) # actually send a TCP FIN + except errors.broken_connection_exceptions: + pass except socket.error as e: - # Suppress "client is no longer connected" - if e.errno != errno.ENOTCONN: + if e.errno not in errors.acceptable_sock_shutdown_error_codes: raise