# Change Log
+## Unreleased
+
+### _Blackd_
+
+- Remove dependency on aiohttp-cors (#2500)
+
## 21.9b0
### Packaging
[packages]
aiohttp = ">=3.6.0"
-aiohttp-cors = ">=0.4.0"
platformdirs= ">=2"
click = ">=8.0.0"
mypy_extensions = ">=0.4.3"
{
"_meta": {
"hash": {
- "sha256": "ebf216584cfb2c962a1792d0682f3c08b44c7ae27305a03a54eacd6f42df27db"
+ "sha256": "8b28e41c5a63f0c30361d2a0ed29dc1e3f0468223ef150ae68586839e2ccf1c9"
},
"pipfile-spec": 6,
"requires": {},
"index": "pypi",
"version": "==3.7.4.post0"
},
- "aiohttp-cors": {
- "hashes": [
- "sha256:0451ba59fdf6909d0e2cd21e4c0a43752bc0703d33fc78ae94d9d9321710193e",
- "sha256:4d39c6d7100fd9764ed1caf8cebf0eb01bf5e3f24e2e073fda6234bc48b19f5d"
- ],
- "index": "pypi",
- "version": "==0.7.0"
- },
"async-timeout": {
"hashes": [
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"index": "pypi",
"version": "==0.4.3"
},
+ "packaging": {
+ "hashes": [
+ "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7",
+ "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==21.0"
+ },
"pathspec": {
"hashes": [
"sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a",
"index": "pypi",
"version": "==2.2.0"
},
+ "pyparsing": {
+ "hashes": [
+ "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
+ "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
+ ],
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.4.7"
+ },
"regex": {
"hashes": [
"sha256:03840a07a402576b8e3a6261f17eb88abd653ad4e18ec46ef10c9a63f8c99ebd",
"version": "==2021.8.21"
},
"setuptools-scm": {
+ "extras": [
+ "toml"
+ ],
"hashes": [
"sha256:c3bd5f701c8def44a5c0bfe8d407bef3f80342217ef3492b951f3777bd2d915c",
"sha256:d1925a69cb07e9b29416a275b9fadb009a23c148ace905b2fb220649a6c18e92"
"index": "pypi",
"version": "==3.7.4.post0"
},
- "aiohttp-cors": {
- "hashes": [
- "sha256:0451ba59fdf6909d0e2cd21e4c0a43752bc0703d33fc78ae94d9d9321710193e",
- "sha256:4d39c6d7100fd9764ed1caf8cebf0eb01bf5e3f24e2e073fda6234bc48b19f5d"
- ],
- "index": "pypi",
- "version": "==0.7.0"
- },
"alabaster": {
"hashes": [
"sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
"sha256:2480d8e07d1890056cb53c96e3de44fead9c62f2ba949b0f2e4c4345f4afa977",
"sha256:a65882a4d0fe5fbf702273456ba2ce74fe44892c25e42e057aca526b702a6d4b"
],
- "markers": "python_version < '3.7' and python_version < '3.7'",
+ "markers": "python_version < '3.7'",
"version": "==5.2.2"
},
"iniconfig": {
"version": "==3.3.1"
},
"setuptools-scm": {
+ "extras": [
+ "toml"
+ ],
"hashes": [
"sha256:c3bd5f701c8def44a5c0bfe8d407bef3f80342217ef3492b951f3777bd2d915c",
"sha256:d1925a69cb07e9b29416a275b9fadb009a23c148ace905b2fb220649a6c18e92"
"mypy_extensions>=0.4.3",
],
extras_require={
- "d": ["aiohttp>=3.6.0", "aiohttp-cors>=0.4.0"],
+ "d": ["aiohttp>=3.6.0"],
"colorama": ["colorama>=0.4.3"],
"python2": ["typed-ast>=1.4.2"],
"uvloop": ["uvloop>=0.15.2"],
try:
from aiohttp import web
- import aiohttp_cors
+ from .middlewares import cors
except ImportError as ie:
raise ImportError(
f"aiohttp dependency is not installed: {ie}. "
def make_app() -> web.Application:
- app = web.Application()
- executor = ProcessPoolExecutor()
-
- cors = aiohttp_cors.setup(app)
- resource = cors.add(app.router.add_resource("/"))
- cors.add(
- resource.add_route("POST", partial(handle, executor=executor)),
- {
- "*": aiohttp_cors.ResourceOptions(
- allow_headers=(*BLACK_HEADERS, "Content-Type"), expose_headers="*"
- )
- },
+ app = web.Application(
+ middlewares=[cors(allow_headers=(*BLACK_HEADERS, "Content-Type"))]
)
-
+ executor = ProcessPoolExecutor()
+ app.add_routes([web.post("/", partial(handle, executor=executor))])
return app
--- /dev/null
+from typing import Iterable, Awaitable, Callable
+from aiohttp.web_response import StreamResponse
+from aiohttp.web_request import Request
+from aiohttp.web_middlewares import middleware
+
+Handler = Callable[[Request], Awaitable[StreamResponse]]
+Middleware = Callable[[Request, Handler], Awaitable[StreamResponse]]
+
+
+def cors(allow_headers: Iterable[str]) -> Middleware:
+ @middleware
+ async def impl(request: Request, handler: Handler) -> StreamResponse:
+ is_options = request.method == "OPTIONS"
+ is_preflight = is_options and "Access-Control-Request-Method" in request.headers
+ if is_preflight:
+ resp = StreamResponse()
+ else:
+ resp = await handler(request)
+
+ origin = request.headers.get("Origin")
+ if not origin:
+ return resp
+
+ resp.headers["Access-Control-Allow-Origin"] = "*"
+ resp.headers["Access-Control-Expose-Headers"] = "*"
+ if is_options:
+ resp.headers["Access-Control-Allow-Headers"] = ", ".join(allow_headers)
+ resp.headers["Access-Control-Allow-Methods"] = ", ".join(
+ ("OPTIONS", "POST")
+ )
+
+ return resp
+
+ return impl # type: ignore
async def test_blackd_response_black_version_header(self) -> None:
response = await self.client.post("/")
self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
+
+ @unittest_run_loop
+ async def test_cors_preflight(self) -> None:
+ response = await self.client.options(
+ "/",
+ headers={
+ "Access-Control-Request-Method": "POST",
+ "Origin": "*",
+ "Access-Control-Request-Headers": "Content-Type",
+ },
+ )
+ self.assertEqual(response.status, 200)
+ self.assertIsNotNone(response.headers.get("Access-Control-Allow-Origin"))
+ self.assertIsNotNone(response.headers.get("Access-Control-Allow-Headers"))
+ self.assertIsNotNone(response.headers.get("Access-Control-Allow-Methods"))
+
+ @unittest_run_loop
+ async def test_cors_headers_present(self) -> None:
+ response = await self.client.post("/", headers={"Origin": "*"})
+ self.assertIsNotNone(response.headers.get("Access-Control-Allow-Origin"))
+ self.assertIsNotNone(response.headers.get("Access-Control-Expose-Headers"))