From 1df1fb16316749bbc5436d1625e48f312eee8b70 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Thu, 23 May 2019 16:12:20 -0700 Subject: [PATCH] wsgi: minimize API changes for 100-continue fix (#569) In commit b9bf369778ec9798b3b6cffe59b7fd15f6159013, we stopped sending 100 Continue responses in the middle of a response when the application was over-eager to start sending back bytes, but I did that by pulling the headers_sent state out of handle_one_response(), up into handle_one_request(), and plumbing it through to * get_environ(), * Input.__init__(), and finally * handle_one_response(). This works, but updates a whole bunch of HttpProtocol APIs in ways that consumers may not have been expecting. For example, if someone wanted to subclass HttpProtocol and override get_environ(), they may not have bothered to include *args and **kwargs to accommodate future API changes. That code should certainly be fixed, but we shouldn't break them gratuitously. Now, wait to introduce the headers_sent state until handle_one_response() once more, and push it directly into the request's Input. All the same protections with minimal API disruption. --- eventlet/wsgi.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/eventlet/wsgi.py b/eventlet/wsgi.py index ffd9cdea8d..ee8ad2d6ad 100644 --- a/eventlet/wsgi.py +++ b/eventlet/wsgi.py @@ -92,8 +92,7 @@ def __init__(self, sock, wfile=None, wfile_line=None, - chunked_input=False, - headers_sent=None): + chunked_input=False): self.rfile = rfile self._sock = sock @@ -113,9 +112,10 @@ def __init__(self, self.hundred_continue_headers = None self.is_hundred_continue_response_sent = False - # Hold on to a ref to the response state so we know whether we can - # still send the 100 Continue - self.headers_sent = headers_sent + # handle_one_response should give us a ref to the response state so we + # know whether we can still send the 100 Continue; until then, though, + # we're flying blind + self.headers_sent = None def send_hundred_continue_response(self): if self.headers_sent: @@ -458,13 +458,12 @@ def handle_one_request(self): self.close_connection = 1 return - headers_sent = [] - self.environ = self.get_environ(headers_sent) + self.environ = self.get_environ() self.application = self.server.app try: self.server.outstanding_requests += 1 try: - self.handle_one_response(headers_sent) + self.handle_one_response() except socket.error as e: # Broken pipe, connection reset by peer if support.get_errno(e) not in BROKEN_SOCK: @@ -472,9 +471,13 @@ def handle_one_request(self): finally: self.server.outstanding_requests -= 1 - def handle_one_response(self, headers_sent): + def handle_one_response(self): start = time.time() headers_set = [] + headers_sent = [] + # Push the headers-sent state into the Input so it won't send a + # 100 Continue response if we've already started a response. + self.environ['wsgi.input'].headers_sent = headers_sent wfile = self.wfile result = None @@ -655,7 +658,7 @@ def get_client_address(self): host = forward + ',' + host return (host, port) - def get_environ(self, headers_sent): + def get_environ(self): env = self.server.get_environ() env['REQUEST_METHOD'] = self.command env['SCRIPT_NAME'] = '' @@ -719,7 +722,7 @@ def get_environ(self, headers_sent): chunked = env.get('HTTP_TRANSFER_ENCODING', '').lower() == 'chunked' env['wsgi.input'] = env['eventlet.input'] = Input( self.rfile, length, self.connection, wfile=wfile, wfile_line=wfile_line, - chunked_input=chunked, headers_sent=headers_sent) + chunked_input=chunked) env['eventlet.posthooks'] = [] return env