GithubHelp home page GithubHelp logo

aio-libs / aiohttp-cors Goto Github PK

View Code? Open in Web Editor NEW
203.0 14.0 56.0 256 KB

CORS support for aiohttp

License: Apache License 2.0

Python 87.10% PowerShell 2.93% HTML 9.90% Makefile 0.07%
aiohttp asyncio cors

aiohttp-cors's Introduction

CORS support for aiohttp

aiohttp_cors library implements Cross Origin Resource Sharing (CORS) support for aiohttp asyncio-powered asynchronous HTTP server.

Jump directly to Usage part to see how to use aiohttp_cors.

Same-origin policy

Web security model is tightly connected to Same-origin policy (SOP). In short: web pages cannot Read resources which origin doesn't match origin of requested page, but can Embed (or Execute) resources and have limited ability to Write resources.

Origin of a page is defined in the Standard as tuple (schema, host, port) (there is a notable exception with Internet Explorer: it doesn't use port to define origin, but uses it's own Security Zones).

Can Embed means that resource from other origin can be embedded into the page, e.g. by using <script src="...">, <img src="...">, <iframe src="...">.

Cannot Read means that resource from other origin source cannot be obtained by page (source โ€” any information that would allow to reconstruct resource). E.g. the page can Embed image with <img src="...">, but it can't get information about specific pixels, so page can't reconstruct original image (though some information from the other resource may still be leaked: e.g. the page can read embedded image dimensions).

Limited ability to Write means, that the page can send POST requests to other origin with limited set of Content-Type values and headers.

Restriction to Read resource from other origin is related to authentication mechanism that is used by browsers: when browser reads (downloads) resource he automatically sends all security credentials that user previously authorized for that resource (e.g. cookies, HTTP Basic Authentication).

For example, if Read would be allowed and user is authenticated in some internet banking, malicious page would be able to embed internet banking page with iframe (since authentication is done by the browser it may be embedded as if user is directly navigated to internet banking page), then read user private information by reading source of the embedded page (which may be not only source code, but, for example, screenshot of the embedded internet banking page).

Cross-origin resource sharing

Cross-origin Resource Sharing (CORS) allows to override SOP for specific resources.

In short, CORS works in the following way.

When page https://client.example.com request (Read) resource https://server.example.com/resource that have other origin, browser implicitly appends Origin: https://client.example.com header to the HTTP request, effectively requesting server to give read permission for the resource to the https://client.example.com page:

GET /resource HTTP/1.1
Origin: https://client.example.com
Host: server.example.com

If server allows access from the page to the resource, it responds with resource with Access-Control-Allow-Origin: https://client.example.com HTTP header (optionally allowing exposing custom server headers to the page and enabling use of the user credentials on the server resource):

Access-Control-Allow-Origin: https://client.example.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: X-Server-Header

Browser checks, if server responded with proper Access-Control-Allow-Origin header and accordingly allows or denies access for the obtained resource to the page.

CORS specification designed in a way that servers that are not aware of CORS will not expose any additional information, except allowed by the SOP.

To request resources with custom headers or using custom HTTP methods (e.g. PUT, DELETE) that are not allowed by SOP, CORS-enabled browser first send preflight request to the resource using OPTIONS method, in which he queries access to the resource with specific method and headers:

OPTIONS / HTTP/1.1
Origin: https://client.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Client-Header

CORS-enabled server responds is requested method is allowed and which of the specified headers are allowed:

Access-Control-Allow-Origin: https://client.example.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Headers: X-Client-Header
Access-Control-Max-Age: 3600

Browser checks response to preflight request, and, if actual request allowed, does actual request.

Installation

You can install aiohttp_cors as a typical Python library from PyPI or from git:

$ pip install aiohttp_cors

Note that aiohttp_cors requires versions of Python >= 3.4.1 and aiohttp >= 1.1.

Usage

To use aiohttp_cors you need to configure the application and enable CORS on resources and routes that you want to expose:

import asyncio
from aiohttp import web
import aiohttp_cors

@asyncio.coroutine
def handler(request):
    return web.Response(
        text="Hello!",
        headers={
            "X-Custom-Server-Header": "Custom data",
        })

app = web.Application()

# `aiohttp_cors.setup` returns `aiohttp_cors.CorsConfig` instance.
# The `cors` instance will store CORS configuration for the
# application.
cors = aiohttp_cors.setup(app)

# To enable CORS processing for specific route you need to add
# that route to the CORS configuration object and specify its
# CORS options.
resource = cors.add(app.router.add_resource("/hello"))
route = cors.add(
    resource.add_route("GET", handler), {
        "http://client.example.org": aiohttp_cors.ResourceOptions(
            allow_credentials=True,
            expose_headers=("X-Custom-Server-Header",),
            allow_headers=("X-Requested-With", "Content-Type"),
            max_age=3600,
        )
    })

Each route has it's own CORS configuration passed in CorsConfig.add() method.

CORS configuration is a mapping from origins to options for that origins.

In the example above CORS is configured for the resource under path /hello and HTTP method GET, and in the context of CORS:

  • This resource will be available using CORS only to http://client.example.org origin.
  • Passing of credentials to this resource will be allowed.
  • The resource will expose to the client X-Custom-Server-Header server header.
  • The client will be allowed to pass X-Requested-With and Content-Type headers to the server.
  • Preflight requests will be allowed to be cached by client for 3600 seconds.

Resource will be available only to the explicitly specified origins. You can specify "all other origins" using special * origin:

cors.add(route, {
        "*":
            aiohttp_cors.ResourceOptions(allow_credentials=False),
        "http://client.example.org":
            aiohttp_cors.ResourceOptions(allow_credentials=True),
    })

Here the resource specified by route will be available to all origins with disallowed credentials passing, and with allowed credentials passing only to http://client.example.org.

By default ResourceOptions will be constructed without any allowed CORS options. This means, that resource will be available using CORS to specified origin, but client will not be allowed to send either credentials, or send non-simple headers, or read from server non-simple headers.

To enable sending or receiving all headers you can specify special value * instead of sequence of headers:

cors.add(route, {
        "http://client.example.org":
            aiohttp_cors.ResourceOptions(
                expose_headers="*",
                allow_headers="*"),
    })

You can specify default CORS-enabled resource options using aiohttp_cors.setup()'s defaults argument:

cors = aiohttp_cors.setup(app, defaults={
        # Allow all to read all CORS-enabled resources from
        # http://client.example.org.
        "http://client.example.org": aiohttp_cors.ResourceOptions(),
    })

# Enable CORS on routes.

# According to defaults POST and PUT will be available only to
# "http://client.example.org".
hello_resource = cors.add(app.router.add_resource("/hello"))
cors.add(hello_resource.add_route("POST", handler_post))
cors.add(hello_resource.add_route("PUT", handler_put))

# In addition to "http://client.example.org", GET request will be
# allowed from "http://other-client.example.org" origin.
cors.add(hello_resource.add_route("GET", handler), {
        "http://other-client.example.org":
            aiohttp_cors.ResourceOptions(),
    })

# CORS will be enabled only on the resources added to `CorsConfig`,
# so following resource will be NOT CORS-enabled.
app.router.add_route("GET", "/private", handler)

Also you can specify default options for resources:

# Allow POST and PUT requests from "http://client.example.org" origin.
hello_resource = cors.add(app.router.add_resource("/hello"), {
        "http://client.example.org": aiohttp_cors.ResourceOptions(),
    })
cors.add(hello_resource.add_route("POST", handler_post))
cors.add(hello_resource.add_route("PUT", handler_put))

Resource CORS configuration allows to use allow_methods option that explicitly specifies list of allowed HTTP methods for origin (or * for all HTTP methods). By using this option it is not required to add all resource routes to CORS configuration object:

# Allow POST and PUT requests from "http://client.example.org" origin.
hello_resource = cors.add(app.router.add_resource("/hello"), {
        "http://client.example.org":
            aiohttp_cors.ResourceOptions(allow_methods=["POST", "PUT"]),
    })
# No need to add POST and PUT routes into CORS configuration object.
hello_resource.add_route("POST", handler_post)
hello_resource.add_route("PUT", handler_put)
# Still you can add additional methods to CORS configuration object:
cors.add(hello_resource.add_route("DELETE", handler_delete))

Here is an example of how to enable CORS for all origins with all CORS features:

cors = aiohttp_cors.setup(app, defaults={
    "*": aiohttp_cors.ResourceOptions(
            allow_credentials=True,
            expose_headers="*",
            allow_headers="*",
        )
})

# Add all resources to `CorsConfig`.
resource = cors.add(app.router.add_resource("/hello"))
cors.add(resource.add_route("GET", handler_get))
cors.add(resource.add_route("PUT", handler_put))
cors.add(resource.add_route("POST", handler_put))
cors.add(resource.add_route("DELETE", handler_delete))

Old routes API is supported โ€” you can use router.add_router and router.register_route as before, though this usage is discouraged:

cors.add(
    app.router.add_route("GET", "/hello", handler), {
        "http://client.example.org": aiohttp_cors.ResourceOptions(
            allow_credentials=True,
            expose_headers=("X-Custom-Server-Header",),
            allow_headers=("X-Requested-With", "Content-Type"),
            max_age=3600,
        )
    })

You can enable CORS for all added routes by accessing routes list in the router:

# Setup application routes.
app.router.add_route("GET", "/hello", handler_get)
app.router.add_route("PUT", "/hello", handler_put)
app.router.add_route("POST", "/hello", handler_put)
app.router.add_route("DELETE", "/hello", handler_delete)

# Configure default CORS settings.
cors = aiohttp_cors.setup(app, defaults={
    "*": aiohttp_cors.ResourceOptions(
            allow_credentials=True,
            expose_headers="*",
            allow_headers="*",
        )
})

# Configure CORS on all routes.
for route in list(app.router.routes()):
    cors.add(route)

You can also use CorsViewMixin on web.View:

class CorsView(web.View, CorsViewMixin):

    cors_config = {
        "*": ResourceOption(
            allow_credentials=True,
            allow_headers="X-Request-ID",
        )
    }

    @asyncio.coroutine
    def get(self):
        return web.Response(text="Done")

    @custom_cors({
        "*": ResourceOption(
            allow_credentials=True,
            allow_headers="*",
        )
    })
    @asyncio.coroutine
    def post(self):
        return web.Response(text="Done")

cors = aiohttp_cors.setup(app, defaults={
    "*": aiohttp_cors.ResourceOptions(
            allow_credentials=True,
            expose_headers="*",
            allow_headers="*",
        )
})

cors.add(
    app.router.add_route("*", "/resource", CorsView),
    webview=True)

Security

TODO: fill this

Development

To setup development environment:

# Clone sources repository:
git clone https://github.com/aio-libs/aiohttp_cors.git .
# Create and activate virtual Python environment:
python3 -m venv env
source env/bin/activate
# Install requirements and aiohttp_cors into virtual environment
pip install -r requirements-dev.txt

To run tests:

tox

To run only runtime tests in current environment:

py.test

To run only static code analysis checks:

tox -e check

Running Selenium tests

To run Selenium tests with Firefox web driver you need to install Firefox.

To run Selenium tests with Chromium web driver you need to:

  1. Install Chrome driver. On Ubuntu 14.04 it's in chromium-chromedriver package.
  2. Either add chromedriver to PATH or set WEBDRIVER_CHROMEDRIVER_PATH environment variable to chromedriver, e.g. on Ubuntu 14.04 WEBDRIVER_CHROMEDRIVER_PATH=/usr/lib/chromium-browser/chromedriver.

Release process

To release version vA.B.C from the current version of master branch you need to:

  1. Create local branch vA.B.C.

  2. In CHANGES.rst set release date to today.

  3. In aiohttp_cors/__about__.py change version from A.B.Ca0 to A.B.C.

  4. Create pull request with vA.B.C branch, wait for all checks to successfully finish (Travis and Appveyor).

  5. Merge pull request to master.

  6. Update and checkout master branch.

  7. Create and push tag for release version to GitHub:

    git tag vA.B.C
    git push --tags

    Now Travis should ran tests again, and build and deploy wheel on PyPI.

    If Travis release doesn't work for some reason, use following steps for manual release upload.

    1. Install fresh versions of setuptools and pip. Install wheel for building wheels. Install twine for uploading to PyPI.

      pip install -U pip setuptools twine wheel
    2. Configure PyPI credentials in ~/.pypirc.

    3. Build distribution:

      rm -rf build dist; python setup.py sdist bdist_wheel
    4. Upload new release to PyPI:

      twine upload dist/*
  8. Edit release description on GitHub if needed.

  9. Announce new release on the aio-libs mailing list: https://groups.google.com/forum/#!forum/aio-libs.

Post release steps:

  1. In CHANGES.rst add template for the next release.
  2. In aiohttp_cors/__about__.py change version from A.B.C to A.(B + 1).0a0.

Bugs

Please report bugs, issues, feature requests, etc. on GitHub.

License

Copyright 2015 Vladimir Rutsky <[email protected]>.

Licensed under the Apache License, Version 2.0, see LICENSE file for details.

aiohttp-cors's People

Contributors

alefteris avatar asvetlov avatar dependabot-preview[bot] avatar dependabot[bot] avatar diogommartins avatar dreamsorcerer avatar edwardbetts avatar jettify avatar lemming avatar mgorny avatar pedrokiefer avatar pyup-bot avatar rutsky avatar sloria avatar vovanbo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

aiohttp-cors's Issues

Rename `aiohttp_cors` package to `aiohttp-cors`

See #43 for reasoning.

@asvetlov thanks for making the change c34202e.

In order to complete renameing we need to:

  • rename package in sources (c34202e)
  • rename GitHub repository
  • test that this change will not break anything (if it will, it's OK, but we need to explicitly state that)
  • add notice to change log
  • rename package on PyPI (how?)
  • publish renamed package (last time I released aiohttp-cors I worked on publishing to PyPI from Travis: it should be enough to push master branch with tag in order to force uploading on PyPI)

@asvetlov which parts you want to complete by yourself right now and which part can you want to delegate to me (during this week)?

Authentication middleware prevent CORS to be send correctly

I have an authentication middleware. In the middleware if the request method is OPTIONS I am returning the handler intact with the aim that aiohttp-cors handle the preflight request and return the correct response headers. However, the response headers are not being sent correctly by the signals.

It is quite possible that I am doing something wrong in my middleware, and the OPTIONS call need to be handled differently. This is my middleware:

@middleware
async def auth_middleware(request, handler):
    if isinstance(request.match_info.route, SystemRoute):  # eg. 404
        return await handler(request)

    if request.method == hdrs.METH_OPTIONS:
        return await handler(request)

    try:
        request['claims'] = await authenticate(request)
    except ValueError as e:
        raise HTTPUnauthorized(PayloadErrors(e.args[0]))

    return await handler(request)

I am creating the app as following:

def create_app():
    app = Application(middlewares=middlewares)
    setup_cors(app)
    return app

And this is how I am setting up cors:

def setup_cors(app: Application):
    resources = [
        'http://localhost:8100',
        'http://www.example.com',
    ]

    cors = aiohttp_cors.setup(app, defaults={
        resource: aiohttp_cors.ResourceOptions(
            allow_credentials=True,
            expose_headers='*',
            allow_methods='*',
            allow_headers='*',
        ) for resource in resources
    })

    for route in app.router.routes():
        cors.add(route)

However, whenever I make a call I get the following error:

Unhandled exception
Traceback (most recent call last):
  File "/python3.7/site-packages/aiohttp/web_protocol.py", line 398, in start
    await resp.prepare(request)
  File "/python3.7/site-packages/aiohttp/web_response.py", line 299, in prepare
    await request._prepare_hook(self)
  File "/python3.7/site-packages/aiohttp/web_request.py", line 686, in _prepare_hook
    await app.on_response_prepare.send(self, response)
  File "/python3.7/site-packages/aiohttp/signals.py", line 35, in send
    await receiver(*args, **kwargs)
  File "/python3.7/site-packages/aiohttp_cors/cors_config.py", line 171, in _on_response_prepare
    assert hdrs.ACCESS_CONTROL_ALLOW_ORIGIN not in response.headers
AssertionError
Unhandled exception
Traceback (most recent call last):
  File "/python3.7/site-packages/aiohttp/web_protocol.py", line 398, in start
    await resp.prepare(request)
  File "/python3.7/site-packages/aiohttp/web_response.py", line 299, in prepare
    await request._prepare_hook(self)
  File "/python3.7/site-packages/aiohttp/web_request.py", line 686, in _prepare_hook
    await app.on_response_prepare.send(self, response)
  File "/python3.7/site-packages/aiohttp/signals.py", line 35, in send
    await receiver(*args, **kwargs)
  File "/python3.7/site-packages/aiohttp_cors/cors_config.py", line 171, in _on_response_prepare
    assert hdrs.ACCESS_CONTROL_ALLOW_ORIGIN not in response.headers
AssertionError

allow_headers argument probably not working

i have tried to use allow_headers argument. And it seem that it doesn't work.

'*': aiohttp_cors.ResourceOptions(
        expose_headers="*",
        allow_headers=("X-Requested-With", "Content-Type"),
    )

my options request is:

http OPTIONS http://localhost:9001/api/v1/hello_world Origin:'http://localhost:9001' Access-Control-Request-Method:GET X-Requested-With12:httpie

and response is:

HTTP/1.1 200 OK
Access-Control-Allow-Methods: GET
Access-Control-Allow-Origin: http://localhost:9001
Content-Length: 0
Content-Type: application/octet-stream
Date: Wed, 25 Oct 2017 10:39:40 GMT
Server: Python/3.6 aiohttp/2.2.3

I believe that response should be 403 with  not allowed header.
Am i right?

Python 3.8 test_static_resource failure: TypeError: issubclass() arg 1 must be a class

Hello. I wish to run tests on Python 3.8, but I needed to fight some problems. This is how I was able to run them on current master:

diff --git a/pytest.ini b/pytest.ini
deleted file mode 100644
index e62899b..0000000
--- a/pytest.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[pytest]
-filterwarnings=
-    error
diff --git a/tox.ini b/tox.ini
index 9668d37..bb10832 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,6 +5,7 @@ envlist = py34, py35, check
 commands = {envpython} setup.py test
 deps =
     pytest
+    pytest-aiohttp
 
 [testenv:check]
 deps =
$ tox -e py38

And this is the error I get:

============================= test session starts ==============================
platform linux -- Python 3.8.0, pytest-5.2.2, py-1.8.0, pluggy-0.13.0
cachedir: .tox/py38/.pytest_cache
rootdir: /home/churchyard/Dokumenty/aiohttp-cors, inifile: tox.ini, testpaths: aiohttp_cors, tests
plugins: cov-2.8.1, pylint-0.14.1, aiohttp-0.3.0
collected 99 items

tests/doc/test_basic_usage.py ..                                         [  2%]
tests/integration/test_main.py ......................................... [ 43%]
.............................                                            [ 72%]
tests/integration/test_real_browser.py ssssss                            [ 78%]
tests/unit/test___about__.py .                                           [ 79%]
tests/unit/test_cors_config.py ....F...                                  [ 87%]
tests/unit/test_mixin.py .....                                           [ 92%]
tests/unit/test_preflight_handler.py .                                   [ 93%]
tests/unit/test_resource_options.py ...                                  [ 96%]
tests/unit/test_urldispatcher_router_adapter.py ...                      [100%]

=================================== FAILURES ===================================
_____________________________ test_static_resource _____________________________

app = <Application 0x7f735d3d6100>
cors = <aiohttp_cors.cors_config.CorsConfig object at 0x7f735d35c400>

    def test_static_resource(app, cors):
        """Test adding static resource."""
        assert len(app.router.keys()) == 0
        app.router.add_static(
            "/file", "/", name="dynamic_named_route")
        assert len(app.router.keys()) == 1
        for resource in list(app.router.resources()):
>           if issubclass(resource, web.StaticResource):

tests/unit/test_cors_config.py:106: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'aiohttp.web_urldispatcher.StaticResource'>
subclass = <StaticResource 'dynamic_named_route' /file -> PosixPath('/')>

    def __subclasscheck__(cls, subclass):
        """Override for issubclass(subclass, cls)."""
>       return _abc_subclasscheck(cls, subclass)
E       TypeError: issubclass() arg 1 must be a class

.tox/py38/lib64/python3.8/abc.py:102: TypeError
=============================== warnings summary ===============================
tests/unit/test_cors_config.py:33
  /home/churchyard/Dokumenty/aiohttp-cors/tests/unit/test_cors_config.py:33: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def get(self):

tests/integration/test_main.py::test_dummy_setup[pyloop]
  /home/churchyard/Dokumenty/aiohttp-cors/.tox/py38/lib/python3.8/site-packages/aiohttp/web_server.py:53: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    await asyncio.gather(*coros, loop=self._loop)

-- Docs: https://docs.pytest.org/en/latest/warnings.html
============= 1 failed, 92 passed, 6 skipped, 2 warnings in 5.91s ==============

method '*' treated differently from others

I'm trying to use aiohttp-cors with aiohttp-graphl, which registers a route with the '*' method. I get

ValueError: <PlainResource 'graphql' /graphql> already has a '*' handler for all methods

Here is a small reproducer showing that '*' is different:

import asyncio
import aiohttp
import aiohttp_cors
from aiohttp.web_runner import GracefulExit


async def handler(request):
    return aiohttp.web.Response(text="Hello!")

app = aiohttp.web.Application()

cors = aiohttp_cors.setup(app)

routes = [{
    'method': 'GET',
    'path': '/test',
    'handler': handler,
    'name': 'test-good'
    }, {
    'method': '*',  # ValueError: <PlainResource 'another-route'  /another-route> already has a '*' handler for all methods                                           
    'path': '/another-route',
    'handler': handler,
    'name': 'another-route'
    }, ]

for route in routes:
    print('creating a route for method', route['method'])
    cors.add(
        app.router.add_route(
            method=route['method'],
            path=route['path'],
            handler=route['handler'],
            name=route['name']
        )
    )

web.run_app(app, host='localhost', port=8081)

which prints

creating a route for method GET
creating a route for method *
Traceback (most recent call last):
...
ValueError: <PlainResource 'another-route'  /another-route> already has a '*' handler for all methods

Reading the aiohttp-cors code I can see that _is_web_view() is False and then causes this confusing message? But only for method '*' and not GET.

I hacked the aiohttp-cors code to create separate routes for all the methods other than OPTIONS, and my program now runs successfully. But obviously that's not a fix.

Class based views are not supported

When using web.View as the handler if the route being added for '*' then cors doesn't work because the server seems not to be able to find the OPTIONS. Not sure why that is, I didn't take the time to investigate but my guess is that the * end up taking priority and the routing goes to the * handler instead of the CORS handler.

GPG signatures for source validation

As we all know, today more than ever before, it is crucial to be able to trust our computing environments. One of the main difficulties that package maintainers of GNU/Linux distributions face, is the difficulty to verify the authenticity and the integrity of the source code. With GPG signatures it is possible for packagers to verify source code releases quickly and easily.

In order to securely package your software I am kindly requesting GPG signatures for the source tarballs. If you are not yet familiar with secure source code signing I recommend using GPGit which automates the process of secure source code signing and also has a quick start guide on GPG for learning how to use it manually.

Thanks in advance.

CORS on static files

One of our applications requires CORS headers for static files. I'm currently using the default setup to add all of our APIs as being CORS enabled. The static route is added as add_static.

Am I missing something?

TypeError: issubclass() arg 1 must be a class when initialize my app with new relic

I am using aiohttp and initializing new relic in my __main__ file this way:

import newrelic.agent

if config.NEW_RELIC_SETTED:
    newrelic.agent.initialize()

# some initializations ...
web.run_app(app)

Api is the class that I am using aiohttp and initializing my session, web.Application, cors setup and so on

However, when I run my application the following error ocours:

Traceback (most recent call last):
  File "/home/gabrielly.silva/sieve/api_portal/app/__main__.py", line 12, in <module>
    Api().start()
  File "/home/gabrielly.silva/sieve/api_portal/app/api.py", line 41, in __init__
    self.register_routes()
  File "/home/gabrielly.silva/sieve/api_portal/app/api.py", line 90, in register_routes
    self.cors.add(route)
  File "/home/gabrielly.silva/sieve/api_portal/.venv/lib/python3.7/site-packages/aiohttp_cors/cors_config.py", line 263, in add
    return self._cors_impl.add(routing_entity, config)
  File "/home/gabrielly.silva/sieve/api_portal/.venv/lib/python3.7/site-packages/aiohttp_cors/cors_config.py", line 135, in add
    routing_entity, self._preflight_handler)
  File "/home/gabrielly.silva/sieve/api_portal/.venv/lib/python3.7/site-packages/aiohttp_cors/urldispatcher_router_adapter.py", line 195, in add_preflight_handler
    self.add_preflight_handler(route.resource, handler)
  File "/home/gabrielly.silva/sieve/api_portal/.venv/lib/python3.7/site-packages/aiohttp_cors/urldispatcher_router_adapter.py", line 165, in add_preflight_handler
    if _is_web_view(route_obj):
  File "/home/gabrielly.silva/sieve/api_portal/.venv/lib/python3.7/site-packages/aiohttp_cors/urldispatcher_router_adapter.py", line 95, in _is_web_view
    if isinstance(handler, type) and issubclass(handler, web.View):
  File "/usr/lib/python3.7/abc.py", line 143, in __subclasscheck__
    return _abc_subclasscheck(cls, subclass)
TypeError: issubclass() arg 1 must be a class

In the point that the function __is_web_view_ compares if handler is a subclasse of web.View, the handler is wrapped by new relic

$ type(handler)
<class 'newrelic.common.object_wrapper.FunctionWrapper'>

Is there a way that I can work around and solve this?

Thanks

No CORS header on 404 responses ?

I've registered my routes that way:

    cors = aiohttp_cors.setup(app, defaults={
        "*": aiohttp_cors.ResourceOptions(
            allow_credentials=True,
            expose_headers="*",
            allow_headers="*",
            allow_methods="*"
        )
    })
    for route in app.router.routes():
        cors.add(route)

However, the JS fetch API raises a CORS exception if I access a unkown URL.

const resp = await fetch('https://myapi.example.com/unknown/url') // raises a TypeError exception

// This code is never reached, but it would if resp had the CORS headers
resp.status === 404
resp.ok === false

What would be the recommended way to add CORS headers to aiohttp built-in 404 response ?

Cors and IOS Safari

Hello!
Cors wont working on Apple devices - cors is not working. It works only if i manually disable CORS Tracking Protection in device settings.
What's wrong with apple?

403 Error when aiohttp resources are not fully merged

Aiohttp merges resources, when they are added in the correct order:
screenshot from 2019-02-22 15-40-04

When using aiohttp-cors, resources have to be fully merged, or you end up having 2 resources for the same path, one for the GET method one for the POST, but the latter will not be considered when aiohttp-cors answers the OPTIONS request. It seems that aiohttp-cors supports only a single resource per path.

It would be nice if a hint about this could be added to the docs, if the support of multiple resources for the same path is too complex.

Requirements:

aiohttp==3.0.6
aiohttp_cors==0.7.0

Replicate:

def handler():
    pass

app.router.add_route('POST', '/a', handler)
app.router.add_route('GET', '/b', handler)
app.router.add_route('PUT', '/a', handler)

# Configure default CORS settings.
cors = aiohttp_cors.setup(app, defaults={
    "*": aiohttp_cors.ResourceOptions(
    allow_credentials=True,
    expose_headers="*",
    allow_headers="*")
})

# Configure CORS on all routes.
for route in list(app.router.routes()):
    cors.add(route)

can not use when aiohttp version > v1.0.5

[E 2016-11-18_11:45:00 server:208]
Traceback (most recent call last):
File "/Users/yihong/virtualenvs/py35/lib/python3.5/site-packages/aiohttp/server.py", line 265, in start
yield from self.handle_request(message, payload)
File "/Users/yihong/virtualenvs/py35/lib/python3.5/site-packages/aiohttp/web.py", line 96, in handle_request
resp = yield from handler(request)
File "/Users/yihong/virtualenvs/py35/lib/python3.5/site-packages/aiohttp_cors/cors_config.py", line 253, in _preflight_handler
request, origin, request_method)
File "/Users/yihong/virtualenvs/py35/lib/python3.5/site-packages/aiohttp_cors/urldispatcher_router_adapter.py", line 400, in get_preflight_request_config
requested_method, path)
TypeError: resolve() takes 2 positional arguments but 3 were given

can somebody update it?

cant import aiohttp_cors

Hi,
i installed aiohttp_cors, but i cant import it. when i open the python interactive shell with python3 it works, but when i open it with pyrthon3.7 it does not. in my project i use python3.7, is there any way to make it work?

DeprecationWarnings with Python 3.8: "@coroutine" decorator

=============================== warnings summary ===============================
tests/unit/test_cors_config.py:33
  .../tests/unit/test_cors_config.py:33: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def get(self):

tests/integration/test_main.py::test_dummy_setup[pyloop]
  /usr/lib64/python3.8/site-packages/aiohttp/web_server.py:53: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    await asyncio.gather(*coros, loop=self._loop)

-- Docs: https://docs.pytest.org/en/latest/warnings.html
==================== 93 passed, 2 warnings in 2.61 seconds =====================

With current default tests config:

============================= test session starts ==============================
platform linux -- Python 3.8.0, pytest-5.2.2, py-1.8.0, pluggy-0.13.0
cachedir: .tox/py38/.pytest_cache
rootdir: /...aiohttp-cors, inifile: pytest.ini
plugins: cov-2.8.1, pylint-0.14.1, aiohttp-0.3.0
collected 91 items / 1 errors / 90 selected

==================================== ERRORS ====================================
_______________ ERROR collecting tests/unit/test_cors_config.py ________________
tests/unit/test_cors_config.py:30: in <module>
    class _View(web.View, CorsViewMixin):
tests/unit/test_cors_config.py:33: in _View
    def get(self):
/usr/lib64/python3.8/asyncio/coroutines.py:111: in coroutine
    warnings.warn('"@coroutine" decorator is deprecated since Python 3.8, use "async def" instead',
E   DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!
=============================== 1 error in 0.27s ===============================

Problems with aiohttp 3.0.1

This code works fine with aiohttp 2.3.10:

def setup_routes(app):
    app.router.add_routes(routes)
    setup_swagger(app,
                  api_base_url='/',
                  swagger_url='/api/doc',
                  description='API testing interface',
                  title='API',
                  api_version='2.0.0')

    cors = aiohttp_cors.setup(app, defaults={
        "*": aiohttp_cors.ResourceOptions(
            allow_credentials=True,
            expose_headers="*",
            allow_headers="*",
        )
    })

    for route in list(app.router.routes()):
        if not isinstance(route.resource, StaticResource):  # <<< WORKAROUND
            cors.add(route)

curl -H "Origin: http://example.com" -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: X-Requested-With" -X OPTIONS --verbose localhost:8080/api/users

*   Trying ::1...
* TCP_NODELAY set
* connect to ::1 port 8080 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> OPTIONS /api/users HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: */*
> Origin: http://example.com
> Access-Control-Request-Method: POST
> Access-Control-Request-Headers: X-Requested-With
> 
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: http://example.com
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: POST
< Access-Control-Allow-Headers: X-REQUESTED-WITH
< Content-Length: 0
< Content-Type: application/octet-stream
< Date: Wed, 14 Feb 2018 23:19:55 GMT
< Server: Python/3.6 aiohttp/2.3.10
<

But following (fixed OPTIONS error, adding routes differently) code fails for aiohttp 3.0.1:

    # This code fails with:
    # RuntimeError: Added route will never be executed, method OPTIONS is already registered
    # for route in list(app.router.routes()):
    #     if not isinstance(route.resource, StaticResource):  # <<< WORKAROUND
    #         cors.add(route)

    for resource in app.router.resources():
        cors.add(resource)

Same cURL request results in:

*   Trying ::1...
* TCP_NODELAY set
* connect to ::1 port 8080 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> OPTIONS /api/users HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: */*
> Origin: http://example.com
> Access-Control-Request-Method: POST
> Access-Control-Request-Headers: X-Requested-With
> 
< HTTP/1.1 403 Forbidden
< Content-Type: text/plain; charset=utf-8
< Content-Length: 99
< Date: Wed, 14 Feb 2018 23:21:01 GMT
< Server: Python/3.6 aiohttp/3.0.1
< 
* Connection #0 to host localhost left intact
CORS preflight request failed: request method 'POST' is not allowed for 'http://example.com' origin

What is the best way to grant CORS for all subdomains dynamically?

I need to provide CORS permissions for many subdomains (i do not know all), that are located on known list of domains. E.g. *.example.com, *.corp-example.com, etc.

To allow CORS for any origin, i can do the following:

import aiohttp_cors
from aiohttp.web_app import Application

app = Application()
aiohttp_cors.setup(app, defaults={
    "*": aiohttp_cors.ResourceOptions(
        allow_credentials=True,
        expose_headers="*",
        allow_headers="*",
    )
})

for handler in handlers:
    # Register handler
    route = app.router.add_route('*', handler.URL_PATH, handler)
    
    # Handler should be handled with CORS
    app['aiohttp_cors'].add(route)

It works, but is not secure. What is simplest way to check if client origin meets some requirements (e.g. that origin matches ^[a-z0-9_-]+.example.com$) and if does not - to deny request?

I supposed that it is possible to extend some basic method to add such checks, or to provide lambda to aiohttp_cors.setup defaults, that would accept origin as input parameter and return appropriate config.

Consider making methods coroutine

Currently all public API can be expressed in the following example:

class RouterAdapter(AbstractRouterAdapter):
    def route_methods(self, route):
        """Returns list of HTTP methods that route handles"""

    def add_options_method_handler(self, route, handler):
        """Add OPTIONS method request handler that will be issued at the same
        paths as provided `route`.

        :return: Newly added route.
        """

router_adapter = RouterAdapter()
cors = aiohttp_cors.setup(app, router_adapter=router_adapter, ...)
cors.add(route, ...)

Is any of these methods may be required to be coroutines?

E.g. is it reasonable to assume that router.add_route() will never be coroutine for custom routers?

Use of type annotations

@asvetlov:

Say, I doubt using types in annotations make sense.
They are neither checked in runtime as I see nor pep 484 compatible.

@asvetlov, can you explain why they are not PEP 484 compatible?

E.g. this looks like a proper type annotation:

from aiohttp import web
...
class CorsConfig:
...
def setup(app: web.Application, *, defaults: dict=None) -> CorsConfig:

There should be collections.abc.Mapping instead of dict but other parts looks correct to me.

Type annotations yet not used at runtime, but can be used right now for documentation purposes and as hints for IDEs like PyCharm (PyCharm right now has limited support of them).

ValueError: Duplicate 'handler', already handled by <DynamicRoute 'handler' [GET] /v1/handler/ -> <function handler at 0x103c85378>

I get a ValueError when passing a name to router.add_route

        cors.add(
            app.router.add_route(method, full_url, handler, name=name),
            {
                '*': aiohttp_cors.ResourceOptions(
                    allow_credentials=True, expose_headers='*', allow_headers='*'
                )
            }

        )

Traceback:

    app = create_app(TestConfig)
api/app.py:27: in create_app
    routes.setup(app)
api/routes.py:26: in setup
    allow_credentials=True, expose_headers='*', allow_headers='*'
../../../miniconda/envs/rero/lib/python3.5/site-packages/aiohttp_cors/__init__.py:193: in add
    route, self._preflight_handler)
../../../miniconda/envs/rero/lib/python3.5/site-packages/aiohttp_cors/urldispatcher_router_adapter.py:45: in add_options_method_handler
    self._router.register_route(new_route)
../../../miniconda/envs/rero/lib/python3.5/site-packages/aiohttp/web_urldispatcher.py:459: in register_route
    .format(name, self._routes[name]))
E   ValueError: Duplicate 'should_i_release', already handled by <DynamicRoute 'should_i_release' [GET] /v1/should_i_release/{username}/{repo}/ -> <function should_i_release at 0x103c85378>

Removing name=name from the call to app.router.add_route removes the error.

aiohttp 2.0+ support?

Just wanted to know if there is a particular issue why it was restricted to aiohttp <2.0. Was it simply not passing the unittests or someone just needs to thoroughly check unittests and compatibility?

0.7.0 + master: pytest is failing

I'm trying to package your module as an rpm package. So I'm using the typical build, install and test cycle used on building packages from non-root account.

  • "setup.py build"
  • "setup.py install --root </install/prefix>"
  • "pytest with PYTHONPATH pointing to sitearch and sitelib inside </install/prefix>

May I ask for help because few units are failing:

+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-aiohttp-cors-0.7.0-15.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-aiohttp-cors-0.7.0-15.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra --import-mode=importlib
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.12, pytest-6.2.5, py-1.10.0, pluggy-0.13.1
benchmark: 3.4.1 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
Using --randomly-seed=1568170391
rootdir: /home/tkloczko/rpmbuild/BUILD/aiohttp-cors-0.7.0, configfile: pytest.ini
plugins: forked-1.3.0, shutil-1.7.0, virtualenv-1.7.0, expect-1.1.0, flake8-1.0.7, timeout-1.4.2, betamax-0.8.1, freezegun-0.4.2, aspectlib-1.5.2, toolbox-0.5, rerunfailures-9.1.1, requests-mock-1.9.3, cov-2.12.1, flaky-3.7.0, benchmark-3.4.1, xdist-2.3.0, pylama-7.7.1, datadir-1.3.1, regressions-2.2.0, cases-3.6.3, xprocess-0.18.1, black-0.3.12, anyio-3.3.0, asyncio-0.15.1, subtests-0.5.0, isort-2.0.0, hypothesis-6.14.6, mock-3.6.1, profiling-1.7.0, randomly-3.8.0, Faker-8.12.1, nose2pytest-1.0.8, pyfakefs-4.5.1, tornado-0.8.1, twisted-1.13.3, aiohttp-0.3.0
collected 85 items / 2 errors / 83 selected

================================================================================== ERRORS ==================================================================================
_________________________________________________________ ERROR collecting tests/integration/test_real_browser.py __________________________________________________________
tests/integration/test_real_browser.py:30: in <module>
    from selenium import webdriver
/usr/lib/python3.8/site-packages/selenium/webdriver/__init__.py:18: in <module>
    from .firefox.webdriver import WebDriver as Firefox  # noqa
/usr/lib/python3.8/site-packages/selenium/webdriver/firefox/webdriver.py:28: in <module>
    from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
/usr/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py:26: in <module>
    from .webelement import WebElement
/usr/lib/python3.8/site-packages/selenium/webdriver/remote/webelement.py:43: in <module>
    getAttribute_js = pkgutil.get_data(_pkg, 'getAttribute.js').decode('utf8')
/usr/lib64/python3.8/pkgutil.py:638: in get_data
    return loader.get_data(resource_name)
<frozen importlib._bootstrap_external>:1032: in get_data
    ???
E   FileNotFoundError: [Errno 2] No such file or directory: '/usr/lib/python3.8/site-packages/selenium/webdriver/remote/getAttribute.js'
_____________________________________________________________ ERROR collecting tests/unit/test_cors_config.py ______________________________________________________________
tests/unit/test_cors_config.py:30: in <module>
    class _View(web.View, CorsViewMixin):
tests/unit/test_cors_config.py:33: in _View
    def get(self):
/usr/lib64/python3.8/asyncio/coroutines.py:111: in coroutine
    warnings.warn('"@coroutine" decorator is deprecated since Python 3.8, use "async def" instead',
E   DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
========================================================================= short test summary info ==========================================================================
ERROR tests/integration/test_real_browser.py - FileNotFoundError: [Errno 2] No such file or directory: '/usr/lib/python3.8/site-packages/selenium/webdriver/remote/getAtt...
ERROR tests/unit/test_cors_config.py - DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 2 errors during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
============================================================================ 2 errors in 0.64s =============================================================================
pytest-xprocess reminder::Be sure to terminate the started process by running 'pytest --xkill' if you have not explicitly done so in your fixture with 'xprocess.getinfo(<process_name>).terminate()'.

Use semantic versioning

I want to use semantic versioning for versioning aiohttp_cors.

E.g. release not yet stable version as 0.1.0, 0.2.0, ..., and subsequent stable versions as 1.0.0, 2.0.0, ..., according to semantic versioning rules.

Any objections?

/cc @asvetlov

SyntaxError: aiohttp import does not work

While trying to use asyncio and aiohttp , I am getting this error.
Environment: Python 3, Mac

Traceback (most recent call last):
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydev_run_in_console.py", line 52, in run_file
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/Users/divyanshushekhar/ScrapeAMAZON/Scraping/scrape-consistency.py", line 10, in <module>
    import aiohttp
  File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_bundle/pydev_import_hook.py", line 19, in do_import
    module = self._system_import(name, *args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/aiohttp/__init__.py", line 6, in <module>
    from .client import *  # noqa
  File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_bundle/pydev_import_hook.py", line 19, in do_import
    module = self._system_import(name, *args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/aiohttp/client.py", line 15, in <module>
    from . import connector as connector_mod
  File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_bundle/pydev_import_hook.py", line 19, in do_import
    module = self._system_import(name, *args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/aiohttp/connector.py", line 11, in <module>
    from . import hdrs, helpers
  File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_bundle/pydev_import_hook.py", line 19, in do_import
    module = self._system_import(name, *args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/aiohttp/helpers.py", line 29
    ensure_future = asyncio.async
                                ^
SyntaxError: invalid syntax

Routing hierarchy confusion with CORs

We just encountered a strange CORS bug that that we found a solution to. Our solution is this git commit:

diff --git a/prism_orchestrator_server/router.py b/prism_orchestrator_server/router.py
index 58dfd83..34262f7 100644
--- a/prism_orchestrator_server/router.py
+++ b/prism_orchestrator_server/router.py
@@ -106,6 +106,15 @@ def register_routes(container):
     visual_classes = router.add_resource("/visual-classes/")
     visual_classes.add_route("GET", resources["visual_classes"].index)
 
+    visual_class_merge = router.add_resource("/visual-classes/merge")
+    visual_class_merge.add_route("PUT", resources["visual_classes"].merge)
+
+    visual_class_move = router.add_resource("/visual-classes/move")
+    visual_class_move.add_route("PUT", resources["visual_classes"].move)
+
+    visual_class_extract = router.add_resource("/visual-classes/extract")
+    visual_class_extract.add_route("PUT", resources["visual_classes"].extract)
+
     visual_class = router.add_resource("/visual-classes/{visual_class_id}")
     visual_class.add_route("GET", resources["visual_classes"].get)
 
@@ -119,15 +128,6 @@ def register_routes(container):
     )
     visual_class_history.add_route("GET", resources["visual_classes"].get_history)
 
-    visual_class_merge = router.add_resource("/visual-classes/merge")
-    visual_class_merge.add_route("PUT", resources["visual_classes"].merge)
-
-    visual_class_move = router.add_resource("/visual-classes/move")
-    visual_class_move.add_route("PUT", resources["visual_classes"].move)
-
-    visual_class_extract = router.add_resource("/visual-classes/extract")
-    visual_class_extract.add_route("PUT", resources["visual_classes"].extract)
-
     router.add_route("GET", "/instances", redirect_perm("/instances/"))
     router.add_route("POST", "/instances", redirect_perm("/instances/"))
     instances = router.add_resource("/instances/")

Basically the problem was that /visual-classes/merge, /visual-classes/move and /visual-classes/extract routes were defined after /visual-classes/{visual_class_id}, and while aiohttp was able to handle this routing order with no problems, it appears that aiohttp-cors cannot.

That is aiohttp-cors fixes the GET for /visual-classes/{visual_class_id} and later when I try to enable CORS for /visual-classes/merge for PUT, it doesn't work, because it thinks that GET cors options have already been set for /visual-classes/{visual_class_id}.

After some debugging. The only solution was to move the more specific routes on top of the generic route. And then CORS worked beautifully.

I think this is a bug in aiohttp-cors, and it's particularly nasty because it's very silent, and when you think aiohttp routing works fine, but cors doesn't, it can be difficult to realize this.

Is it possible to return `Access-Control-Allow-Origin: *` in response to preflight request instead of existing `Origin` header?

I'm developing figma plugin and figma uploads plugins html from local filesystem, in this case, all requests will have 'Origin: null' header and thus Access-Control-Allow-Origin will have null, which, in turn, leads to CORS error in browser. I've checked preflight_handler.py source and did not found any way to change usage of existing Origin header value to externally defined one(*). Am I missing something?

Yum broken when RPM from this requirement with underscores.

Please never use underscores in python-packages names.

PEP-008 conatins

Python packages should also have short, all-lowercase names, although the use of underscores is discouraged.

We have a really problem. When our build system convert all dependencies to RPM (using FPM) yum is broken. We are can't use this package in our projects.

aiohttp cors breaks with new aiohttp 3.0 release

aiohttp cors setup breaks with

 File "/home/idwaker/Code/webapp/app/api/common/utils/loaders.py", line 43, in init_cors
    cors = aiohttp_cors.setup(app, defaults=CORS_DEFAULTS)
  File "/home/idwaker/.local/share/virtualenvs/webapp-69lhAx_Z/lib/python3.6/site-packages/aiohttp_cors/__init__.py", line 65, in setup
    cors = CorsConfig(app, defaults=defaults)
  File "/home/idwaker/.local/share/virtualenvs/webapp-69lhAx_Z/lib/python3.6/site-packages/aiohttp_cors/cors_config.py", line 247, in __init__
    self._resources_router_adapter)
  File "/home/idwaker/.local/share/virtualenvs/webapp-69lhAx_Z/lib/python3.6/site-packages/aiohttp_cors/cors_config.py", line 117, in __init__
    self._app.on_response_prepare.append(self._on_response_prepare)
  File "aiohttp/_frozenlist.pyx", line 97, in aiohttp._frozenlist.FrozenList.append
  File "aiohttp/_frozenlist.pyx", line 19, in aiohttp._frozenlist.FrozenList._check_frozen
RuntimeError: Cannot modify frozen list.

on latest 3.0 release of aiohttp

my init_cors is something like this

async def init_cors(app: web.Application):
    logger = get_logger(app['name'])
    cors = aiohttp_cors.setup(app, defaults=CORS_DEFAULTS)

    # add resources to cors
    for resource in list(app.router.resources()):
        if isinstance(resource, web.Resource):
            cors.add(resource)
    logger.info("Initialized CORS for {}".format(app['name']))

and i am loading init_cors on app.on_startup

The issue seems to occur when i defer cors setup to on_startup signal like

app.on_startup.append(init_cors)

But it works normally if i put the cors init code on app setup like

 # register routes here
    register_routes(app)

    cors = aiohttp_cors.setup(app, defaults=CORS_DEFAULTS)

    # add resources to cors
    for resource in list(app.router.resources()):
        if isinstance(resource, web.Resource):
            cors.add(resource)

TypeError: Use async with instead - error while using aiohttp 3.3.2

While using asyncio and aiohttp, I am getting this error on environment: Python 3, mac

Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 26 2018, 23:26:24) 
[Clang 6.0 (clang-600.0.57)] on darwin
Traceback (most recent call last):
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydev_run_in_console.py", line 52, in run_file
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/Users/divyanshushekhar/ScrapeFOLDER/Scraping/scrape-parallel.py", line 173, in <module>
    with aiohttp.ClientSession(loop=loop) as session:
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/aiohttp/client.py", line 806, in __enter__
    raise TypeError("Use async with instead")
TypeError: Use async with instead
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x111337b38>

Searching google gives the result https://stackoverflow.com/questions/46066629/proxy-broker-error-use-async-with-instead which says to install aiohttp version 2.0.0 which I did but got another issue #183

Wildcard ports for localhost

I'm wondering if there is a way for me to use a regex to allow any localhost ports to be allowed?

Something like:

"http://localhost:[0-9]*": aiohttp_cors.ResourceOptions(
    expose_headers="*",
    allow_headers="*",
)

router_adapter.py

I believe it worth to be renamed into abc.py -- nice to have all ABC in the same place, router_adapter.py is not good name for all possible abcs.

RouterAdapter should be renamed into AbstractRouterAdapter or something like this.

We can do renaming without making a harm: the library is very young and most likely users don't use RouterAdapter and router_adapter.py explicitly.

Let's make renaming right now, later it will be more painful.

Only 1 is allowed

Hello,
I am trying to work on a project, and I am facing an issue.
The problem is that when I am requesting a resource from the server, google chrome is saying that the "Access-Control-Allow-Origin' header contains multiple values"
This is my code

@asyncio.coroutine
def handler(request):
    return web.Response(
        text="Hello!",
        headers={
            "X-Custom-Server-Header": "Custom data",
        })


app = web.Application()

cors = aiohttp_cors.setup(app)

resource = cors.add(app.router.add_resource("/"))

route = cors.add(
    resource.add_route("POST", handler), {
        "*": aiohttp_cors.ResourceOptions(allow_methods="*",
                                          allow_headers=("X-Requested-With", "Content-Type", "AUTHORIZATION"),
                                          expose_headers="*",
                                          allow_credentials=False
                                          )
    })
if __name__ == '__main__':
    web.run_app(app, port=os.getenv("PORT"), host='0.0.0.0')

i am serving aiohttp over nginx

OOP view

Can I create views in OOP style? How in tornado or in Django CBV.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.