Repository: aio-libs/aiohttp

Issue:
Don't cancel web handler on disconnection
Raise an error on reading/writing instead.
CancelledError
### Describe the bug

Using the aiohttp web server, I struggle with `asyncio.CancelledError()` when a client disconnect for some time.
I shielded what needed to be but it has its limits.
Today I found this old PR:
https://github.com/aio-libs/aiohttp/pull/4080

It looks like my issue.
If I understand correctly  `asyncio.CancelledError()` was fixed at the beginning of v3.7.0 (1th October 2019).
But I still have frequent `asyncio.CancelledError()` in my server.

I have investigated a bit more and I find another PR, at the end of v3.7.0 (just before v3.7.0b0 I think) that for me re-introduce the issue:
https://github.com/aio-libs/aiohttp/pull/4415

For me the wrong code is the following:
```py
        if self._task_handler is not None:
            self._task_handler.cancel()
```    

It was removed by https://github.com/aio-libs/aiohttp/pull/4080 (and with all the documentation regarding this issue) but re-introduced silently by https://github.com/aio-libs/aiohttp/pull/4415.

I'm clearly not an expert so I'm not sure of what I'm saying, but does it make sense for you?

### To Reproduce

NA

### Expected behavior

NA

### Logs/tracebacks

```python-traceback
NA
```


### Python Version

```console
3.10.4
```


### aiohttp Version

```console
3.8.1
```


### multidict Version

```console
5.1.0
```


### yarl Version

```console
1.6.3
```


### OS

Fedora 35

### Related component

Server

### Additional context

_No response_

### Code of Conduct

- [X] I agree to follow the aio-libs Code of Conduct
`OSError: AF_UNIX path too long` under MacOS
Surprisingly Travis fails on MacOS: https://travis-ci.com/aio-libs/aiohttp/builds/98188999?utm_medium=notification&utm_source=email

Looks like it is the result of my https://github.com/aio-libs/aiohttp/pull/3548 PR.
I have no Mac available, the investigation can be slow.
If somebody can run tests locally on Mac and debug what is wrong -- it would be awesome and save my personal time a lot.


Discussion:
# [Codecov](https://codecov.io/gh/aio-libs/aiohttp/pull/4080?src=pr&el=h1) Report
> Merging [#4080](https://codecov.io/gh/aio-libs/aiohttp/pull/4080?src=pr&el=desc) into [master](https://codecov.io/gh/aio-libs/aiohttp/commit/3bc5bd6db16ebaf31deb351043abbfb29b461656?src=pr&el=desc) will **increase** coverage by `0.03%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/aio-libs/aiohttp/pull/4080/graphs/tree.svg?width=650&token=LJn22G3rd7&height=150&src=pr)](https://codecov.io/gh/aio-libs/aiohttp/pull/4080?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #4080      +/-   ##
==========================================
+ Coverage   97.81%   97.85%   +0.03%     
==========================================
  Files          43       43              
  Lines        8807     8820      +13     
  Branches     1378     1381       +3     
==========================================
+ Hits         8615     8631      +16     
+ Misses         78       76       -2     
+ Partials      114      113       -1
```


| [Impacted Files](https://codecov.io/gh/aio-libs/aiohttp/pull/4080?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [aiohttp/http\_websocket.py](https://codecov.io/gh/aio-libs/aiohttp/pull/4080/diff?src=pr&el=tree#diff-YWlvaHR0cC9odHRwX3dlYnNvY2tldC5weQ==) | `98.64% <100%> (+0.01%)` | :arrow_up: |
| [aiohttp/web\_request.py](https://codecov.io/gh/aio-libs/aiohttp/pull/4080/diff?src=pr&el=tree#diff-YWlvaHR0cC93ZWJfcmVxdWVzdC5weQ==) | `97.43% <100%> (+0.01%)` | :arrow_up: |
| [aiohttp/web\_protocol.py](https://codecov.io/gh/aio-libs/aiohttp/pull/4080/diff?src=pr&el=tree#diff-YWlvaHR0cC93ZWJfcHJvdG9jb2wucHk=) | `92.66% <100%> (+1.07%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/aio-libs/aiohttp/pull/4080?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/aio-libs/aiohttp/pull/4080?src=pr&el=footer). Last update [3bc5bd6...49584c3](https://codecov.io/gh/aio-libs/aiohttp/pull/4080?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

The PR is ready for review

[GitMate.io](https://gitmate.io) thinks the contributors most likely able to help are @webknjaz, and @aio-libs-bot.

Possibly related issues are https://github.com/aio-libs/aiohttp/issues/3138 (aiohttp-master failing to build on macOS High Sierra 10.13.5.), https://github.com/aio-libs/aiohttp/issues/2148 (Failed tests), https://github.com/aio-libs/aiohttp/issues/2280 (Test fails on ppc64le), https://github.com/aio-libs/aiohttp/issues/2920 (AIOHttp failing after some requests), and https://github.com/aio-libs/aiohttp/issues/3241 (Failed building wheel for aiohttp==3.4.3).
I think we've already seen the issue with fs paths for unix sockets being too long in the past...

```console
[1m[31m_________________________ test_unix_connector[pyloop] __________________________[0m



unix_server = <function unix_server.<locals>.go at 0x10bfd58c8>

unix_sockname = '/private/var/folders/nz/vv4_9tw56nv9k3tkvyszvwg80000gn/T/pytest-of-travis/pytest-0/test_unix_connector_pyloop_0/socket.sock'



[1m    @pytest.mark.skipif(not hasattr(socket, 'AF_UNIX'),[0m

[1m                        reason='requires UNIX sockets')[0m

[1m    async def test_unix_connector(unix_server, unix_sockname) -> None:[0m

[1m        async def handler(request):[0m

[1m            return web.Response()[0m

[1m    [0m

[1m        app = web.Application()[0m

[1m        app.router.add_get('/', handler)[0m

[1m>       await unix_server(app)[0m



[1m[31mtests/test_connector.py[0m:2106: 

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

[1m[31mtests/test_connector.py[0m:60: in go

[1m    await site.start()[0m

[1m[31maiohttp/web_runner.py[0m:128: in start

[1m    ssl=self._ssl_context, backlog=self._backlog)[0m

[1m[31m../../../.pyenv/versions/3.5.3/lib/python3.5/asyncio/coroutines.py[0m:210: in coro

[1m    res = func(*args, **kw)[0m

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 



self = <_UnixSelectorEventLoop running=False closed=False debug=False>

protocol_factory = <aiohttp.web_server.Server object at 0x10c0b8048>

path = '/private/var/folders/nz/vv4_9tw56nv9k3tkvyszvwg80000gn/T/pytest-of-travis/pytest-0/test_unix_connector_pyloop_0/socket.sock'



[1m    @coroutine[0m

[1m    def create_unix_server(self, protocol_factory, path=None, *,[0m

[1m                           sock=None, backlog=100, ssl=None):[0m

[1m        if isinstance(ssl, bool):[0m

[1m            raise TypeError('ssl argument must be an SSLContext or None')[0m

[1m    [0m

[1m        if path is not None:[0m

[1m            if sock is not None:[0m

[1m                raise ValueError([0m

[1m                    'path and sock can not be specified at the same time')[0m

[1m    [0m

[1m            path = _fspath(path)[0m

[1m            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)[0m

[1m    [0m

[1m            # Check for abstract socket. `str` and `bytes` paths are supported.[0m

[1m            if path[0] not in (0, '\x00'):[0m

[1m                try:[0m

[1m                    if stat.S_ISSOCK(os.stat(path).st_mode):[0m

[1m                        os.remove(path)[0m

[1m                except FileNotFoundError:[0m

[1m                    pass[0m

[1m                except OSError as err:[0m

[1m                    # Directory may have permissions only to create socket.[0m

[1m                    logger.error('Unable to check or remove stale UNIX socket %r: %r', path, err)[0m

[1m    [0m

[1m            try:[0m

[1m>               sock.bind(path)[0m

[1m[31mE               OSError: AF_UNIX path too long[0m



[1m[31m../../../.pyenv/versions/3.5.3/lib/python3.5/asyncio/unix_events.py[0m:281: OSError

[1m[31m____________________________ test_addresses[pyloop] ____________________________[0m



make_runner = <function make_runner.<locals>.go at 0x10c39f6a8>

tmpdir = local('/private/var/folders/nz/vv4_9tw56nv9k3tkvyszvwg80000gn/T/pytest-of-travis/pytest-0/test_addresses_pyloop_0')



[1m    @pytest.mark.skipif(platform.system() == "Windows",[0m

[1m                        reason="Unix socket support is required")[0m

[1m    async def test_addresses(make_runner, tmpdir) -> None:[0m

[1m        _sock = get_unused_port_socket('127.0.0.1')[0m

[1m        runner = make_runner()[0m

[1m        await runner.setup()[0m

[1m        tcp = web.SockSite(runner, _sock)[0m

[1m        await tcp.start()[0m

[1m        path = str(tmpdir / 'tmp.sock')[0m

[1m        unix = web.UnixSite(runner, path)[0m

[1m>       await unix.start()[0m



[1m[31mtests/test_web_runner.py[0m:113: 

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

[1m[31maiohttp/web_runner.py[0m:128: in start

[1m    ssl=self._ssl_context, backlog=self._backlog)[0m

[1m[31m../../../.pyenv/versions/3.5.3/lib/python3.5/asyncio/coroutines.py[0m:210: in coro

[1m    res = func(*args, **kw)[0m

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 



self = <_UnixSelectorEventLoop running=False closed=False debug=False>

protocol_factory = <aiohttp.web_server.Server object at 0x10c2d0240>

path = '/private/var/folders/nz/vv4_9tw56nv9k3tkvyszvwg80000gn/T/pytest-of-travis/pytest-0/test_addresses_pyloop_0/tmp.sock'



[1m    @coroutine[0m

[1m    def create_unix_server(self, protocol_factory, path=None, *,[0m

[1m                           sock=None, backlog=100, ssl=None):[0m

[1m        if isinstance(ssl, bool):[0m

[1m            raise TypeError('ssl argument must be an SSLContext or None')[0m

[1m    [0m

[1m        if path is not None:[0m

[1m            if sock is not None:[0m

[1m                raise ValueError([0m

[1m                    'path and sock can not be specified at the same time')[0m

[1m    [0m

[1m            path = _fspath(path)[0m

[1m            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)[0m

[1m    [0m

[1m            # Check for abstract socket. `str` and `bytes` paths are supported.[0m

[1m            if path[0] not in (0, '\x00'):[0m

[1m                try:[0m

[1m                    if stat.S_ISSOCK(os.stat(path).st_mode):[0m

[1m                        os.remove(path)[0m

[1m                except FileNotFoundError:[0m

[1m                    pass[0m

[1m                except OSError as err:[0m

[1m                    # Directory may have permissions only to create socket.[0m

[1m                    logger.error('Unable to check or remove stale UNIX socket %r: %r', path, err)[0m

[1m    [0m

[1m            try:[0m

[1m>               sock.bind(path)[0m

[1m[31mE               OSError: AF_UNIX path too long[0m



[1m[31m../../../.pyenv/versions/3.5.3/lib/python3.5/asyncio/unix_events.py[0m:281: OSError
```
To fix this we'll need to use `tmpdir_factory` fixture: https://github.com/agronholm/anyio/pull/42/files#diff-f0c1c7497545019b8489fa8bc06be6cdR208
Ref: https://github.com/ethereum/trinity/issues/404#issuecomment-473344700
@webknjaz if you want to provide some information adding a link without any comment is not very helpful
Sorry, missed your prev comment.
As a part of #3827 I've just skipped these two failed tests on MacOSX.
Don't want to spend time by fighting with MacOS weirdness.
@asvetlov it's not just macOS, all NIXes have different limits. You can hit it under Linux too. I almost have a PR for this. So stay tuned :)
I don't care too much.
If you have a PR -- let's review/apply it.
Otherwise just skipping is good enough for 2 very minor tests

Resolving patch:
diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py
--- a/aiohttp/__init__.py
+++ b/aiohttp/__init__.py
@@ -1,4 +1,4 @@
-__version__ = "4.0.0a1"
+__version__ = "4.0.0a2.dev0"
 
 from typing import Tuple
 
diff --git a/docs/conf.py b/docs/conf.py
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -66,6 +66,7 @@
 
 
 intersphinx_mapping = {
+    "pytest": ("http://docs.pytest.org/en/latest/", None),
     "python": ("http://docs.python.org/3", None),
     "multidict": ("https://multidict.readthedocs.io/en/stable/", None),
     "yarl": ("https://yarl.readthedocs.io/en/stable/", None),
diff --git a/examples/curl.py b/examples/curl.py
--- a/examples/curl.py
+++ b/examples/curl.py
@@ -2,6 +2,7 @@
 
 import argparse
 import asyncio
+import sys
 
 import aiohttp
 
@@ -25,9 +26,10 @@ async def curl(url: str) -> None:
     )
     options = ARGS.parse_args()
 
-    if options.iocp:
+    if options.iocp and sys.platform == "win32":
         from asyncio import events, windows_events
 
+        # https://github.com/python/mypy/issues/12286
         el = windows_events.ProactorEventLoop()
         events.set_event_loop(el)
 


Test patch:
diff --git a/tests/test_run_app.py b/tests/test_run_app.py
--- a/tests/test_run_app.py
+++ b/tests/test_run_app.py
@@ -14,6 +14,7 @@
 from uuid import uuid4
 
 import pytest
+from conftest import needs_unix
 
 from aiohttp import web
 from aiohttp.test_utils import make_mocked_coro
@@ -515,37 +516,39 @@ def test_run_app_custom_backlog_unix(patched_loop: Any) -> None:
 
 
 @skip_if_no_unix_socks
-def test_run_app_http_unix_socket(patched_loop: Any, tmp_path: Any) -> None:
+def test_run_app_http_unix_socket(patched_loop: Any, unix_sockname: Any) -> None:
     app = web.Application()
 
-    sock_path = tmp_path / "socket.sock"
     printer = mock.Mock(wraps=stopper(patched_loop))
-    web.run_app(app, path=sock_path, print=printer, loop=patched_loop)
+    web.run_app(app, path=unix_sockname, print=printer, loop=patched_loop)
 
     patched_loop.create_unix_server.assert_called_with(
-        mock.ANY, sock_path, ssl=None, backlog=128
+        mock.ANY, unix_sockname, ssl=None, backlog=128
     )
-    assert f"http://unix:{sock_path}:" in printer.call_args[0][0]
+    assert f"http://unix:{unix_sockname}:" in printer.call_args[0][0]
 
 
 @skip_if_no_unix_socks
-def test_run_app_https_unix_socket(patched_loop: Any, tmp_path: Any) -> None:
+def test_run_app_https_unix_socket(patched_loop: Any, unix_sockname: Any) -> None:
     app = web.Application()
 
-    sock_path = tmp_path / "socket.sock"
     ssl_context = ssl.create_default_context()
     printer = mock.Mock(wraps=stopper(patched_loop))
     web.run_app(
-        app, path=sock_path, ssl_context=ssl_context, print=printer, loop=patched_loop
+        app,
+        path=unix_sockname,
+        ssl_context=ssl_context,
+        print=printer,
+        loop=patched_loop,
     )
 
     patched_loop.create_unix_server.assert_called_with(
-        mock.ANY, sock_path, ssl=ssl_context, backlog=128
+        mock.ANY, unix_sockname, ssl=ssl_context, backlog=128
     )
-    assert f"https://unix:{sock_path}:" in printer.call_args[0][0]
+    assert f"https://unix:{unix_sockname}:" in printer.call_args[0][0]
 
 
-@skip_if_no_unix_socks
+@needs_unix
 @skip_if_no_abstract_paths
 def test_run_app_abstract_linux_socket(patched_loop: Any) -> None:
     sock_path = b"\x00" + uuid4().hex.encode("ascii")