diff --git a/swift/common/middleware/versioned_writes.py b/swift/common/middleware/versioned_writes.py index 3ece3524ee..ac36ef97e6 100644 --- a/swift/common/middleware/versioned_writes.py +++ b/swift/common/middleware/versioned_writes.py @@ -338,6 +338,7 @@ class VersionedWritesContext(WSGIContext): lreq.environ['QUERY_STRING'] += '&reverse=on' lresp = lreq.get_response(self.app) if not is_success(lresp.status_int): + close_if_possible(lresp.app_iter) if lresp.status_int == HTTP_NOT_FOUND: raise ListingIterNotFound() elif is_client_error(lresp.status_int): @@ -378,6 +379,7 @@ class VersionedWritesContext(WSGIContext): if source_resp.content_length is None or \ source_resp.content_length > MAX_FILE_SIZE: + close_if_possible(source_resp.app_iter) return HTTPRequestEntityTooLarge(request=req) return source_resp @@ -391,7 +393,9 @@ class VersionedWritesContext(WSGIContext): copy_header_subset(source_resp, put_req, lambda k: k.lower() != 'x-timestamp') put_req.environ['wsgi.input'] = FileLikeIter(source_resp.app_iter) - return put_req.get_response(self.app) + put_resp = put_req.get_response(self.app) + close_if_possible(source_resp.app_iter) + return put_resp def _check_response_error(self, req, resp): """ @@ -399,6 +403,7 @@ class VersionedWritesContext(WSGIContext): """ if is_success(resp.status_int): return + close_if_possible(resp.app_iter) if is_client_error(resp.status_int): # missing container or bad permissions raise HTTPPreconditionFailed(request=req) @@ -456,6 +461,7 @@ class VersionedWritesContext(WSGIContext): put_resp = self._put_versioned_obj(req, put_path_info, get_resp) self._check_response_error(req, put_resp) + close_if_possible(put_resp.app_iter) def handle_obj_versions_put(self, req, versions_cont, api_version, account_name, object_name): @@ -514,6 +520,7 @@ class VersionedWritesContext(WSGIContext): marker_req.environ['swift.content_type_overridden'] = True marker_resp = marker_req.get_response(self.app) self._check_response_error(req, marker_resp) + close_if_possible(marker_resp.app_iter) # successfully copied and created delete marker; safe to delete return self.app @@ -527,6 +534,7 @@ class VersionedWritesContext(WSGIContext): # if the version isn't there, keep trying with previous version if get_resp.status_int == HTTP_NOT_FOUND: + close_if_possible(get_resp.app_iter) return False self._check_response_error(req, get_resp) @@ -537,6 +545,7 @@ class VersionedWritesContext(WSGIContext): req, put_path_info, get_resp) self._check_response_error(req, put_resp) + close_if_possible(put_resp.app_iter) return get_path def handle_obj_versions_delete_pop(self, req, versions_cont, api_version, @@ -582,6 +591,7 @@ class VersionedWritesContext(WSGIContext): req.environ, path=req.path_info, method='HEAD', headers=obj_head_headers, swift_source='VW') hresp = head_req.get_response(self.app) + close_if_possible(hresp.app_iter) if hresp.status_int != HTTP_NOT_FOUND: self._check_response_error(req, hresp) @@ -606,6 +616,7 @@ class VersionedWritesContext(WSGIContext): req.environ, path=restored_path, method='DELETE', headers=auth_token_header, swift_source='VW') del_resp = old_del_req.get_response(self.app) + close_if_possible(del_resp.app_iter) if del_resp.status_int != HTTP_NOT_FOUND: self._check_response_error(req, del_resp) # else, well, it existed long enough to do the diff --git a/swift/common/swob.py b/swift/common/swob.py index c4515aa7fd..c93b890eef 100644 --- a/swift/common/swob.py +++ b/swift/common/swob.py @@ -52,7 +52,7 @@ from six.moves import urllib from swift.common.header_key_dict import HeaderKeyDict from swift.common.utils import reiterate, split_path, Timestamp, pairs, \ - close_if_possible + close_if_possible, closing_if_possible from swift.common.exceptions import InvalidTimestamp @@ -308,7 +308,8 @@ def _resp_body_property(): if not self._body: if not self._app_iter: return '' - self._body = ''.join(self._app_iter) + with closing_if_possible(self._app_iter): + self._body = ''.join(self._app_iter) self._app_iter = None return self._body diff --git a/test/unit/common/middleware/test_versioned_writes.py b/test/unit/common/middleware/test_versioned_writes.py index e23def4b80..1e32c7633c 100644 --- a/test/unit/common/middleware/test_versioned_writes.py +++ b/test/unit/common/middleware/test_versioned_writes.py @@ -62,7 +62,10 @@ class VersionedWritesBaseTestCase(unittest.TestCase): conf = {'allow_versioned_writes': 'true'} self.vw = versioned_writes.filter_factory(conf)(self.app) - def call_app(self, req, app=None, expect_exception=False): + def tearDown(self): + self.assertEqual(self.app.unclosed_requests, {}) + + def call_app(self, req, app=None): if app is None: app = self.app @@ -84,24 +87,13 @@ class VersionedWritesBaseTestCase(unittest.TestCase): headers[0] = h body_iter = app(req.environ, start_response) - body = '' - caught_exc = None - try: - for chunk in body_iter: - body += chunk - except Exception as exc: - if expect_exception: - caught_exc = exc - else: - raise + with utils.closing_if_possible(body_iter): + body = b''.join(body_iter) - if expect_exception: - return status[0], headers[0], body, caught_exc - else: - return status[0], headers[0], body + return status[0], headers[0], body - def call_vw(self, req, **kwargs): - return self.call_app(req, app=self.vw, **kwargs) + def call_vw(self, req): + return self.call_app(req, app=self.vw) def assertRequestEqual(self, req, other): self.assertEqual(req.method, other.method)