aio-libs / aiohttp

Asynchronous HTTP client/server framework for asyncio and Python
https://docs.aiohttp.org
Other
14.96k stars 1.99k forks source link

How to force close underlying connection #3433

Closed postlund closed 1 month ago

postlund commented 5 years ago

Long story short

As part of my Apple TV library (pyatv), I can request the device to play AirPlay streams by sending "some data" (HTTP) to it over TCP port 7000. This part works fine but the problem is that the device keeps showing a white spinner on the screen as long as the TCP connection remains active, even though the video/music stream has ended. So I want to force-close the underlying connection to indicate that the playback is done. Is this possible?

I would prefer not having to create a new ClientSession for each playback session as a) my library is used by other projects in which I have no control of the session b) authentication must be made on on the same connection prior to playback, which makes the design rather awkward to implement.

Expected behaviour

Some method to force close a particular connection.

Actual behaviour

Connection is kept alive for some time (not sure how long or how I could affect it).

Steps to reproduce

This problem is seen in Home Assistant, if for instance Text-To-Speech (TTS), is used from the Apple TV component.

Your environment

Running latest aiohttp from PyPi with Home Assistant on raspbian and Mac OS. Same behavior. Everything is on local network, no proxies or such.

aio-libs-bot commented 5 years ago

GitMate.io thinks the contributors most likely able to help are @asvetlov, and @fafhrd91.

Possibly related issues are https://github.com/aio-libs/aiohttp/issues/3363 (How send "WebSocket Connection Close[FIN]"???), https://github.com/aio-libs/aiohttp/issues/1799 (Unclosed connection), https://github.com/aio-libs/aiohttp/issues/1814 (Close websocket connection when pong not received), https://github.com/aio-libs/aiohttp/issues/3052 (SSL with closed connections), and https://github.com/aio-libs/aiohttp/issues/1768 (websocket connection is closing.).

webknjaz commented 5 years ago

request.transport?

asvetlov commented 5 years ago

It is a client API. resp.close() forces closing the socket

postlund commented 5 years ago

I'm not totally sure it does what I need it to. Let me give you an example:

#!/usr/bin/env python3

import asyncio
import aiohttp

async def do_stuff():
    async with aiohttp.ClientSession() as session:
        async with session.get('http://10.0.10.253') as resp:
            text = await resp.text()
            # do something with text
            resp.close()  # Strictly not needed because of `with`, right?
        await asyncio.sleep(30)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(do_stuff())

So I do a request, close the response but do not end the session instance by sleeping for 30 seconds. When checking for active connections using lsof in another terminal, I can see:

$ lsof -i -n | grep 10.0.10.253:http
python3.6 40853 postlund    6u  IPv4 0x5c0d9cedcc112963      0t0  TCP 10.0.10.50:52473->10.0.10.253:http (ESTABLISHED)

It dies after some time, maybe 10 seconds or so, but I would expect it to die immediately when doing close. Is there anything I'm missing with this?

joeyorlando commented 4 years ago

I'm commenting ~2 years later but maybe try:

async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(force_close=True)) as session:
  ...

from aiohttp.BaseConnector:

force_close - Set to True to force close and do reconnect after each request (and between redirects).
Dreamsorcerer commented 1 month ago

I'm not totally sure it does what I need it to. Let me give you an example:

The problem here is that after reading the request body, the HTTP message is complete and aiohttp releases the connection back to the pool. So, the call to resp.close() does nothing as the connection is already released.

Using a connector with force_close as described above would be the most reliable way to ensure the connection is actually closed upon completion.