diff --git a/CHANGES/3218.bugfix b/CHANGES/3218.bugfix new file mode 100644 index 00000000000..990e082fbc0 --- /dev/null +++ b/CHANGES/3218.bugfix @@ -0,0 +1 @@ +Fix empty header parsing regression. diff --git a/aiohttp/_http_parser.pyx b/aiohttp/_http_parser.pyx index b0a214c1c16..817b4827e5e 100644 --- a/aiohttp/_http_parser.pyx +++ b/aiohttp/_http_parser.pyx @@ -260,6 +260,7 @@ cdef class HttpParser: bytearray _raw_name bytearray _raw_value + bint _has_value object _protocol object _loop @@ -327,6 +328,7 @@ cdef class HttpParser: self._raw_name = bytearray() self._raw_value = bytearray() + self._has_value = False self._max_line_size = max_line_size self._max_headers = max_headers @@ -364,12 +366,13 @@ cdef class HttpParser: PyByteArray_Resize(self._raw_name, 0) PyByteArray_Resize(self._raw_value, 0) + self._has_value = False self._raw_headers.append((raw_name, raw_value)) cdef _on_header_field(self, char* at, size_t length): cdef Py_ssize_t size cdef char *buf - if self._raw_value: + if self._has_value: self._process_header() size = PyByteArray_Size(self._raw_name) @@ -385,6 +388,7 @@ cdef class HttpParser: PyByteArray_Resize(self._raw_value, size + length) buf = PyByteArray_AsString(self._raw_value) memcpy(buf + size, at, length) + self._has_value = True cdef _on_headers_complete(self): self._process_header() diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 05588453d30..33d27b401a5 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -495,6 +495,26 @@ async def handler(request): resp.close() +async def test_empty_header_values(aiohttp_client): + async def handler(request): + resp = web.Response() + resp.headers['X-Empty'] = '' + return resp + + app = web.Application() + app.router.add_route('GET', '/', handler) + client = await aiohttp_client(app) + resp = await client.get('/') + assert resp.status == 200 + raw_headers = tuple((bytes(h), bytes(v)) for h, v in resp.raw_headers) + assert raw_headers == ((b'X-Empty', b''), + (b'Content-Length', b'0'), + (b'Content-Type', b'application/octet-stream'), + (b'Date', mock.ANY), + (b'Server', mock.ANY)) + resp.close() + + async def test_204_with_gzipped_content_encoding(aiohttp_client): async def handler(request): resp = web.StreamResponse(status=204)