diff --git a/CHANGES.rst b/CHANGES.rst index 134e3ff27f..189e79a7be 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,9 @@ waiting for a ``tick`` event, addressing performance degradation introduced in v8.1.0. +- :issue:`341` via :pr:`342`: Suppress legitimate OS errors + expected on shutdown. + .. scm-version-title:: v8.4.8 - :issue:`317` via :pr:`337`: Fixed a regression in diff --git a/cheroot/errors.py b/cheroot/errors.py index 4395e56528..4101fe3e4a 100644 --- a/cheroot/errors.py +++ b/cheroot/errors.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Collection of exceptions raised and/or processed by Cheroot.""" from __future__ import absolute_import, division, print_function @@ -56,3 +57,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 + acceptable_sock_shutdown_exceptions = (BrokenPipeError, ConnectionResetError) +except NameError: # py2 + acceptable_sock_shutdown_exceptions = () diff --git a/cheroot/server.py b/cheroot/server.py index fb3bc421a2..c75b2eba28 100644 --- a/cheroot/server.py +++ b/cheroot/server.py @@ -76,7 +76,6 @@ import platform import contextlib import threading -import errno try: from functools import lru_cache @@ -1479,9 +1478,10 @@ def _close_kernel_socket(self): try: shutdown(socket.SHUT_RDWR) # actually send a TCP FIN + except errors.acceptable_sock_shutdown_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