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

View File

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