Repository: dask/distributed

Issue:
Support for aiohttp 3.8.6
`distributed/http/scheduler/tests/test_scheduler_http.py::test_api` has started failing since aiohttp was bumped from 3.8.5 to 3.8.6:

https://github.com/dask/distributed/actions/runs/6457646619/job/17529638800?pr=8246

```
>               assert (await resp.text()) == "API V1"

distributed/http/scheduler/tests/test_scheduler_http.py:419: 
[...]
E           DeprecationWarning: Automatic charset detection will be removed in 3.9, see: https://docs.aiohttp.org/en/stable/client_advanced.html#character-set-detection
```


Discussion:
xref https://github.com/dask/distributed/pull/8249

Resolving patch:
diff --git a/distributed/http/health.py b/distributed/http/health.py
--- a/distributed/http/health.py
+++ b/distributed/http/health.py
@@ -6,7 +6,7 @@
 class HealthHandler(web.RequestHandler):
     def get(self):
         self.write("ok")
-        self.set_header("Content-Type", "text/plain")
+        self.set_header("Content-Type", "text/plain; charset=utf-8")
 
 
 routes: list[tuple] = [("/health", HealthHandler, {})]
diff --git a/distributed/http/scheduler/api.py b/distributed/http/scheduler/api.py
--- a/distributed/http/scheduler/api.py
+++ b/distributed/http/scheduler/api.py
@@ -8,7 +8,7 @@
 class APIHandler(RequestHandler):
     def get(self):
         self.write("API V1")
-        self.set_header("Content-Type", "text/plain")
+        self.set_header("Content-Type", "text/plain; charset=utf-8")
 
 
 class RetireWorkersHandler(RequestHandler):


Test patch:
diff --git a/distributed/http/scheduler/tests/test_scheduler_http.py b/distributed/http/scheduler/tests/test_scheduler_http.py
--- a/distributed/http/scheduler/tests/test_scheduler_http.py
+++ b/distributed/http/scheduler/tests/test_scheduler_http.py
@@ -306,18 +306,17 @@ async def fetch_metrics():
     await ev.set()
 
 
-@gen_cluster(client=True, clean_kwargs={"threads": False})
-async def test_health(c, s, a, b):
-    http_client = AsyncHTTPClient()
-
-    response = await http_client.fetch(
-        "http://localhost:%d/health" % s.http_server.port
-    )
-    assert response.code == 200
-    assert response.headers["Content-Type"] == "text/plain"
+@gen_cluster(nthreads=[])
+async def test_health(s):
+    aiohttp = pytest.importorskip("aiohttp")
 
-    txt = response.body.decode("utf8")
-    assert txt == "ok"
+    async with (
+        aiohttp.ClientSession() as session,
+        session.get(f"http://localhost:{s.http_server.port}/health") as resp,
+    ):
+        assert resp.status == 200
+        assert resp.headers["Content-Type"] == "text/plain; charset=utf-8"
+        assert (await resp.text()) == "ok"
 
 
 @gen_cluster()
@@ -400,23 +399,22 @@ def test_api_disabled_by_default():
 
 
 @gen_cluster(
-    client=True,
-    clean_kwargs={"threads": False},
+    nthreads=[],
     config={
         "distributed.scheduler.http.routes": DEFAULT_ROUTES
         + ["distributed.http.scheduler.api"]
     },
 )
-async def test_api(c, s, a, b):
+async def test_api(s):
     aiohttp = pytest.importorskip("aiohttp")
 
-    async with aiohttp.ClientSession() as session:
-        async with session.get(
-            "http://localhost:%d/api/v1" % s.http_server.port
-        ) as resp:
-            assert resp.status == 200
-            assert resp.headers["Content-Type"] == "text/plain"
-            assert (await resp.text()) == "API V1"
+    async with (
+        aiohttp.ClientSession() as session,
+        session.get(f"http://localhost:{s.http_server.port}/api/v1") as resp,
+    ):
+        assert resp.status == 200
+        assert resp.headers["Content-Type"] == "text/plain; charset=utf-8"
+        assert (await resp.text()) == "API V1"
 
 
 @gen_cluster(
diff --git a/distributed/http/worker/tests/test_worker_http.py b/distributed/http/worker/tests/test_worker_http.py
--- a/distributed/http/worker/tests/test_worker_http.py
+++ b/distributed/http/worker/tests/test_worker_http.py
@@ -1,10 +1,8 @@
 from __future__ import annotations
 
-import json
 from unittest import mock
 
 import pytest
-from tornado.httpclient import AsyncHTTPClient
 
 from distributed import Event, Worker, wait
 from distributed.sizeof import sizeof
@@ -133,28 +131,30 @@ async def assert_metrics(**expect):
     await assert_metrics()
 
 
-@gen_cluster(client=True)
-async def test_health(c, s, a, b):
-    http_client = AsyncHTTPClient()
+@gen_cluster(nthreads=[("", 1)])
+async def test_health(s, a):
+    aiohttp = pytest.importorskip("aiohttp")
 
-    response = await http_client.fetch(
-        "http://localhost:%d/health" % a.http_server.port
-    )
-    assert response.code == 200
-    assert response.headers["Content-Type"] == "text/plain"
+    async with (
+        aiohttp.ClientSession() as session,
+        session.get(f"http://localhost:{a.http_server.port}/health") as resp,
+    ):
+        assert resp.status == 200
+        assert resp.headers["Content-Type"] == "text/plain; charset=utf-8"
+        assert (await resp.text()) == "ok"
 
-    txt = response.body.decode("utf8")
-    assert txt == "ok"
 
+@gen_cluster(nthreads=[("", 1)])
+async def test_sitemap(s, a):
+    aiohttp = pytest.importorskip("aiohttp")
 
-@gen_cluster()
-async def test_sitemap(s, a, b):
-    http_client = AsyncHTTPClient()
+    async with (
+        aiohttp.ClientSession() as session,
+        session.get(f"http://localhost:{a.http_server.port}/sitemap.json") as resp,
+    ):
+        assert resp.status == 200
+        out = await resp.json()
 
-    response = await http_client.fetch(
-        "http://localhost:%d/sitemap.json" % a.http_server.port
-    )
-    out = json.loads(response.body.decode())
     assert "paths" in out
     assert "/sitemap.json" in out["paths"]
     assert "/health" in out["paths"]