Skip to content

3.3 socket read timeouts not working correctly #3053

Closed
@thehesiod

Description

@asvetlov looks like the new sock_read timeout implementation is not working, see testcase:

import multiprocessing
import asyncio
import aiohttp
import socket

host = "localhost"


def get_free_tcp_port():
    sckt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sckt.bind((host, 0))
    addr, port = sckt.getsockname()
    sckt.close()
    return port


class AIOServer(multiprocessing.Process):
    """
    This is a mock AWS service which will 5 seconds before returning
    a response to test socket timeouts.
    """
    def __init__(self):
        super().__init__(target=self._run)
        self._loop = None
        self._port = get_free_tcp_port()
        self.endpoint_url = 'http://{}:{}'.format(host, self._port)
        self.daemon = True  # die when parent dies

    def _run(self):
        asyncio.set_event_loop(asyncio.new_event_loop())
        app = aiohttp.web.Application()
        app.router.add_route('*', '/ok', self.ok)
        app.router.add_route('*', '/{anything:.*}', self.stream_handler)

        try:
            aiohttp.web.run_app(app, host=host, port=self._port,
                                handle_signals=False)
        except BaseException:
            pytest.fail('unable to start and connect to aiohttp server')
            raise

    async def __aenter__(self):
        self.start()
        await self._wait_until_up()
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        try:
            self.terminate()
        except:
            pytest.fail("Unable to shut down server")
            raise

    async def ok(self, request):
        return aiohttp.web.Response()

    async def stream_handler(self, request):
        # Without the Content-Type, most (all?) browsers will not render
        # partially downloaded content. Note, the response type is
        # StreamResponse not Response.
        resp = aiohttp.web.StreamResponse(status=200, reason='OK',
                              headers={'Content-Type': 'text/html'})

        await resp.prepare(request)
        await asyncio.sleep(5, loop=self._loop)
        await resp.drain()
        return resp

    async def _wait_until_up(self):
        async with aiohttp.ClientSession() as session:
            for i in range(0, 30):
                if self.exitcode is not None:
                    pytest.fail('unable to start and connect to aiohttp server')
                    return

                try:
                    # we need to bypass the proxies due to monkey patches
                    await session.get(self.endpoint_url + '/ok', timeout=0.5)
                    return
                except (aiohttp.ClientConnectionError, asyncio.TimeoutError):
                    await asyncio.sleep(0.5)
                except BaseException:
                    pytest.fail('unable to start and connect to aiohttp server')
                    raise

        pytest.fail('unable to start and connect to aiohttp server')


async def main():
    async with AIOServer() as server, \
            aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(sock_connect=1, sock_read=1)) as session:

        try:
            response = await session.get(server.endpoint_url)
            await response.read()
        except asyncio.TimeoutError:
            print("Success")

        print("failure")


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

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions