Fix tracking of HTTP stage when writing to transport fails.

This commit is contained in:
L. Kärkkäinen 2020-03-20 17:20:20 +02:00
parent 9098493da9
commit 23e54fc546
2 changed files with 28 additions and 20 deletions

View File

@ -64,6 +64,10 @@ class Http:
self.exception = None self.exception = None
self.url = None self.url = None
def __bool__(self):
"""Test if request handling is in progress"""
return self.stage in (Stage.HANDLER, Stage.RESPONSE)
async def http1(self): async def http1(self):
"""HTTP 1.1 connection handler""" """HTTP 1.1 connection handler"""
while True: # As long as connection stays keep-alive while True: # As long as connection stays keep-alive
@ -175,7 +179,7 @@ class Http:
self.stage = Stage.HANDLER self.stage = Stage.HANDLER
self.request = request self.request = request
def http1_response_header(self, data, end_stream) -> bytes: async def http1_response_header(self, data, end_stream) -> bytes:
res = self.response res = self.response
# Compatibility with simple response body # Compatibility with simple response body
if not data and getattr(res, "body", None): if not data and getattr(res, "body", None):
@ -231,8 +235,8 @@ class Http:
# Send response # Send response
if self.protocol.access_log: if self.protocol.access_log:
self.log_response() self.log_response()
await self._send(ret)
self.stage = Stage.IDLE if end_stream else Stage.RESPONSE self.stage = Stage.IDLE if end_stream else Stage.RESPONSE
return ret
def head_response_ignored(self, data, end_stream): def head_response_ignored(self, data, end_stream):
"""HEAD response: body data silently ignored.""" """HEAD response: body data silently ignored."""
@ -240,29 +244,35 @@ class Http:
self.response_func = None self.response_func = None
self.stage = Stage.IDLE self.stage = Stage.IDLE
def http1_response_chunked(self, data, end_stream) -> bytes: async def http1_response_chunked(self, data, end_stream) -> bytes:
"""Format a part of response body in chunked encoding.""" """Format a part of response body in chunked encoding."""
# Chunked encoding # Chunked encoding
size = len(data) size = len(data)
if end_stream: if end_stream:
await self._send(
b"%x\r\n%b\r\n0\r\n\r\n" % (size, data)
if size else
b"0\r\n\r\n"
)
self.response_func = None self.response_func = None
self.stage = Stage.IDLE self.stage = Stage.IDLE
if size: elif size:
return b"%x\r\n%b\r\n0\r\n\r\n" % (size, data) await self._send(b"%x\r\n%b\r\n" % (size, data))
return b"0\r\n\r\n"
return b"%x\r\n%b\r\n" % (size, data) if size else b""
def http1_response_normal(self, data: bytes, end_stream: bool) -> bytes: async def http1_response_normal(self, data: bytes, end_stream: bool) -> bytes:
"""Format / keep track of non-chunked response.""" """Format / keep track of non-chunked response."""
self.response_bytes_left -= len(data) bytes_left = self.response_bytes_left - len(data)
if self.response_bytes_left <= 0: if bytes_left <= 0:
if self.response_bytes_left < 0: if bytes_left < 0:
raise ServerError("Response was bigger than content-length") raise ServerError("Response was bigger than content-length")
await self._send(data)
self.response_func = None self.response_func = None
self.stage = Stage.IDLE self.stage = Stage.IDLE
elif end_stream: else:
if end_stream:
raise ServerError("Response was smaller than content-length") raise ServerError("Response was smaller than content-length")
return data await self._send(data)
self.response_bytes_left = bytes_left
async def error_response(self, exception): async def error_response(self, exception):
# Disconnect after an error if in any other state than handler # Disconnect after an error if in any other state than handler
@ -391,8 +401,6 @@ class Http:
self.response, response.stream = response, self self.response, response.stream = response, self
return response return response
async def send(self, data, end_stream): @property
data = self.response_func(data, end_stream) def send(self):
if not data: return self.response_func
return
await self._send(data)

View File

@ -137,7 +137,7 @@ class HttpProtocol(asyncio.Protocol):
except Exception: except Exception:
logger.exception("protocol.connection_task uncaught") logger.exception("protocol.connection_task uncaught")
finally: finally:
if self._debug and self._http and self._http.request: if self._debug and self._http:
ip = self.transport.get_extra_info("peername") ip = self.transport.get_extra_info("peername")
logger.error( logger.error(
"Connection lost before response written" "Connection lost before response written"