From 08ac32ee4e2650e6b6ba6d9ba4525f17098bce3b Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Thu, 5 Jan 2017 13:03:21 -0800 Subject: [PATCH] Prevent traceback in object-server on client disconnect This only affects chunked transfers to replicated policies. EC policies already have comparable error handling in swift.common.utils._MultipartMimeFileLikeObject.read See also: https://github.com/eventlet/eventlet/commit/c3ce3ee but note that eventlet.wsgi.ChunkReadError and swift.common.exceptions.ChunkReadError are entirely unrelated. Change-Id: Ic9bcb73ea518c672a58a71d2b92152892f1ce2e4 --- swift/obj/server.py | 5 ++++- test/unit/obj/test_server.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/swift/obj/server.py b/swift/obj/server.py index 157cca4d93..be0f68575e 100644 --- a/swift/obj/server.py +++ b/swift/obj/server.py @@ -407,7 +407,10 @@ class ObjectController(BaseStorageServer): def _make_timeout_reader(self, file_like): def timeout_reader(): with ChunkReadTimeout(self.client_timeout): - return file_like.read(self.network_chunk_size) + try: + return file_like.read(self.network_chunk_size) + except (IOError, ValueError): + raise ChunkReadError return timeout_reader def _read_put_commit_message(self, mime_documents_iter): diff --git a/test/unit/obj/test_server.py b/test/unit/obj/test_server.py index da9a6b9afc..76c9a6c811 100644 --- a/test/unit/obj/test_server.py +++ b/test/unit/obj/test_server.py @@ -1919,6 +1919,25 @@ class TestObjectController(unittest.TestCase): resp = req.get_response(self.object_controller) self.assertEqual(resp.status_int, 408) + def test_PUT_client_closed_connection(self): + class fake_input(object): + def read(self, *a, **kw): + # On client disconnect during a chunked transfer, eventlet + # may raise a ValueError (or ChunkReadError, following + # https://github.com/eventlet/eventlet/commit/c3ce3ee -- but + # that inherits from ValueError) + raise ValueError + + timestamp = normalize_timestamp(time()) + req = Request.blank( + '/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'}, + headers={'X-Timestamp': timestamp, + 'Content-Type': 'text/plain', + 'Content-Length': '6'}) + req.environ['wsgi.input'] = fake_input() + resp = req.get_response(self.object_controller) + self.assertEqual(resp.status_int, 499) + def test_PUT_system_metadata(self): # check that sysmeta is stored in diskfile timestamp = normalize_timestamp(time())