aiohttp may respond to requests sent after the client asks for the connection to be closed
Describe the bug
From RFC 9112, section 9.6:
A server that receives a "close" connection option MUST initiate closure of the connection (see below) after it sends the final response to the request that contained the "close" connection option. The server SHOULD send a "close" connection option in its final response on that connection. The server MUST NOT process any further requests received on that connection.
When aiohttp receives a pipeline with a request containing Connection: close, followed by an invalid request, aiohttp responds only to the second (invalid) request, even though the standard requires that aiohttp respond only to the first one.
To Reproduce
- Start the following web werver:
from aiohttp import web
async def respond(request):
return web.Response(text="hello world")
app = web.Application()
app.add_routes([web.route("*", "/{whatever:.*}", respond)])
web.run_app(app, port=8080)
- Send it a pipeline consisting of a valid request with
Connection: closeset, followed by an invalid request:
printf 'GET / HTTP/1.1\r\nConnection: close\r\n\r\nInvalid\r\n\r\n' | nc localhost 8080
- Observe that the only response received is intended for the invalid request:
HTTP/1.0 400 Bad Request
Content-Type: text/plain; charset=utf-8
Content-Length: 25
Date: Mon, 29 Jan 2024 03:20:29 GMT
Server: Python/3.11 aiohttp/4.0.0a2.dev0
Bad status line 'Invalid'
Expected behavior
The server should respond only to the first request, and then close the connection.
Logs/tracebacks
Error handling request
Traceback (most recent call last):
File "/app/aiohttp/env/lib/python3.11/site-packages/aiohttp/web_protocol.py", line 366, in data_received
messages, upgraded, tail = self._request_parser.feed_data(data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/aiohttp/env/lib/python3.11/site-packages/aiohttp/http_parser.py", line 325, in feed_data
msg: _MsgT = self.parse_message(self._lines)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/aiohttp/env/lib/python3.11/site-packages/aiohttp/http_parser.py", line 558, in parse_message
raise BadStatusLine(line) from None
aiohttp.http_exceptions.BadStatusLine: 400, message:
Bad status line 'Invalid'
Python Version
$ python --version
Python 3.11.2
aiohttp Version
$ python -m pip show aiohttp
Name: aiohttp
Version: 4.0.0a2.dev0
Summary: Async http client/server framework (asyncio)
Home-page: https://github.com/aio-libs/aiohttp
Author:
Author-email:
License: Apache 2
Location: /app/aiohttp/env/lib/python3.11/site-packages
Requires: aiohappyeyeballs, aiosignal, frozenlist, multidict, yarl
Required-by:
multidict Version
$ python -m pip show multidict
Name: multidict
Version: 6.0.4
Summary: multidict implementation
Home-page: https://github.com/aio-libs/multidict
Author: Andrew Svetlov
Author-email: [email protected]
License: Apache 2
Location: /app/aiohttp/env/lib/python3.11/site-packages
Requires:
Required-by: aiohttp, yarl
yarl Version
$ python -m pip show yarl
Name: yarl
Version: 1.9.4
Summary: Yet another URL library
Home-page: https://github.com/aio-libs/yarl
Author: Andrew Svetlov
Author-email: [email protected]
License: Apache-2.0
Location: /app/aiohttp/env/lib/python3.11/site-packages
Requires: idna, multidict
Required-by: aiohttp
OS
Debian 12 (running in Docker on Arch Linux) Linux 6.7.2
Related component
Server
Additional context
Some other HTTP implementations that handle this correctly: Apache httpd, Boost::Beast, Daphne, H2O, Lighttpd, Nginx, Tornado, OpenWrt uhttpd, Waitress
Some other HTTP implementations that also have this bug: Mongoose, Uvicorn
Code of Conduct
- [X] I agree to follow the aio-libs Code of Conduct