From f7a820ed3a72992948e22e7366a9a1780ad40388 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Fri, 12 Aug 2016 05:46:33 +0000 Subject: [PATCH] Wait for a non-empty chunk in WSGIContext._app_call We're functioning as a WSGI server here, so this bit from PEP-3333 seems to apply: > The start_response callable must not actually transmit the response > headers. Instead, it must store them for the server or gateway to > transmit only after the first iteration of the application return > value that yields a non-empty bytestrin ... . In other words, response > headers must not be sent until there is actual body data available, or > until the application's returned iterable is exhausted. Plus, it mirrors what swob.Request.call_application does. Change-Id: I1e8501f8ce91ea912780db64fee1c56bef809a98 --- swift/common/wsgi.py | 17 ++++++----------- test/unit/common/test_wsgi.py | 2 ++ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/swift/common/wsgi.py b/swift/common/wsgi.py index 88e61f2293..7749f1562c 100644 --- a/swift/common/wsgi.py +++ b/swift/common/wsgi.py @@ -42,7 +42,7 @@ from swift.common.swob import Request from swift.common.utils import capture_stdio, disable_fallocate, \ drop_privileges, get_logger, NullLogger, config_true_value, \ validate_configuration, get_hub, config_auto_int_value, \ - CloseableChain + reiterate # Set maximum line size of message headers to be accepted. wsgi.MAX_HEADER_LINE = constraints.MAX_HEADER_SIZE @@ -1053,16 +1053,11 @@ class WSGIContext(object): self._response_headers = None self._response_exc_info = None resp = self.app(env, self._start_response) - # if start_response has been called, just return the iter - if self._response_status is not None: - return resp - resp = iter(resp) - try: - first_chunk = next(resp) - except StopIteration: - return iter([]) - else: # We got a first_chunk - return CloseableChain([first_chunk], resp) + # if start_response has not been called, iterate until we've got a + # non-empty chunk, by which time the app *should* have called it + if self._response_status is None: + resp = reiterate(resp) + return resp def _get_status_int(self): """ diff --git a/test/unit/common/test_wsgi.py b/test/unit/common/test_wsgi.py index cc33833714..51ac12599d 100644 --- a/test/unit/common/test_wsgi.py +++ b/test/unit/common/test_wsgi.py @@ -1242,6 +1242,8 @@ class TestWSGIContext(unittest.TestCase): def test_app_iter_is_closable(self): def app(env, start_response): + yield '' + yield '' start_response('200 OK', [('Content-Length', '25')]) yield 'aaaaa' yield 'bbbbb'