Make proxy_logging close the WSGI iterator

PEP 333 says that the WSGI framework will call .close() on the
iterator returned by a WSGI application once it's done, provided such
a method exists. So, if our code wraps an iterator, then we have to
call .close() on it once we're done with it. proxy_logging wasn't.

Since WSGIContext gets it right, I looked at making proxy_logging use
WSGIContext. However, WSGIContext is all about forcing the first chunk
out of the iterator so that it can capture the final HTTP status and
headers; it doesn't help if you want to look at every chunk.
proxy_logging wants every chunk so it can count the bytes sent.

This didn't hurt anything in Swift, but pconstantine was complaining
in IRC that our failure to call .close() was goofing up some other
middleware he had.

Change-Id: Ic6ea0795ccef6cda2b5c6737697ef7d58eac9ab4
This commit is contained in:
Samuel Merritt 2015-02-20 11:04:24 -08:00
parent a6091c0f39
commit db29ffc983
2 changed files with 24 additions and 0 deletions

View File

@ -289,6 +289,9 @@ class ProxyLoggingMiddleware(object):
self.log_request(
req, status_int, input_proxy.bytes_received, bytes_sent,
start_time, time.time(), resp_headers=resp_headers)
close_method = getattr(iterable, 'close', None)
if callable(close_method):
close_method()
try:
iterable = self.app(env, my_start_response)

View File

@ -430,6 +430,27 @@ class TestProxyLogging(unittest.TestCase):
self.assertEquals(log_parts[0], '1.2.3.4') # client ip
self.assertEquals(log_parts[1], '1.2.3.4') # remote addr
def test_iterator_closing(self):
class CloseableBody(object):
def __init__(self):
self.closed = False
def close(self):
self.closed = True
def __iter__(self):
return iter(["CloseableBody"])
body = CloseableBody()
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(body), {})
req = Request.blank('/', environ={'REQUEST_METHOD': 'GET',
'REMOTE_ADDR': '1.2.3.4'})
resp = app(req.environ, start_response)
# exhaust generator
[x for x in resp]
self.assertTrue(body.closed)
def test_proxy_client_logging(self):
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {})
app.access_logger = FakeLogger()