aiohttp icon indicating copy to clipboard operation
aiohttp copied to clipboard

aiohttp may respond to requests sent after the client asks for the connection to be closed

Open kenballus opened this issue 1 year ago • 0 comments

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

  1. 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)
  1. Send it a pipeline consisting of a valid request with Connection: close set, followed by an invalid request:
printf 'GET / HTTP/1.1\r\nConnection: close\r\n\r\nInvalid\r\n\r\n' | nc localhost 8080
  1. 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

kenballus avatar Jan 29 '24 03:01 kenballus