Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problems with aiohttp 3.0.1 #155

Open
kamikaze opened this issue Feb 14, 2018 · 29 comments
Open

Problems with aiohttp 3.0.1 #155

kamikaze opened this issue Feb 14, 2018 · 29 comments

Comments

@kamikaze
Copy link

kamikaze commented Feb 14, 2018

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
@kamikaze
Copy link
Author

kamikaze commented Feb 14, 2018

Actually with the following code CORS doesn't work even for 2.3.10:

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

@kamikaze
Copy link
Author

kamikaze commented Feb 14, 2018

Route definition:

@routes.post('/api/users')
async def create_user(request: Request):
    return web.json_response(status=web.HTTPCreated.status_code)

@asyd
Copy link

asyd commented Feb 23, 2018

We have the issue here, have you found a workaround?

@asvetlov
Copy link
Member

Guys, the library has several technical debts.
I've converted all tests to pytest fixtures usage and replaced yield from with async/await syntax.
Next thing is getting rid of special flag for aiohttp.web.View support in favor of isinstance check.
After getting this done the code will be ready to adopting to aiohttp 3.
I expect to find a time for it in a week.

@asyd
Copy link

asyd commented Feb 23, 2018

Ok thanks for your answer!

@asvetlov
Copy link
Member

asvetlov commented Mar 5, 2018

Fixed by #160

@asvetlov asvetlov closed this as completed Mar 5, 2018
@mheppner
Copy link

mheppner commented Mar 6, 2018

I hate to be that guy, but will there be a new release to PyPI? I see there was a commit to bump the version, but I don't see it published yet. Thanks :)

@asvetlov
Copy link
Member

asvetlov commented Mar 6, 2018

Thanks for remember.
Yesterday travis was overwhelmed, I forgot to initiate a new release building procedure.
Done

@kamikaze
Copy link
Author

Having following error:

CORS preflight request failed: request method 'POST' is not allowed for 'http://example.com' origin

with:
aiohttp==3.0.9
aiohttp-cors==0.7.0

same code works for aiohttp 2 and aiohttp-cors 0.6.0. Is there real example of adding routes to cors that do work and was tested?

@kamikaze
Copy link
Author

https://github.com/aio-libs/aiohttp-cors/blob/master/aiohttp_cors/urldispatcher_router_adapter.py#L296

Here resource_config.method_config is an empty dict. So if not setting default allow_methods to '*' globally - requests do not work with CORS

@kamikaze
Copy link
Author

btw. Adding to cors with both methods doesn't help:

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

@valentinmk
Copy link

The same for me. Rolled back to aiohttp 2 and aiohttp_cors 0.6.0

@valentinradu
Copy link

valentinradu commented Apr 18, 2018

Same here. aiohttp 2.0 + aiohttp-cors 0.6.0 seems to be a good solution for now.

@asvetlov asvetlov reopened this Apr 18, 2018
@asvetlov
Copy link
Member

Guys, if somebody wants to provide a PR -- you are welcome.
I'm convinced that the problem exists but have no estimation for the fix.

@valentinradu
Copy link

@asvetlov I'll try to look into it over the weekend. Btw, for me, this only happens on localhost, all other places work fine.

@asvetlov
Copy link
Member

Cool!

@valentinmk
Copy link

I could make some progress to solve this problem.
This problem happens when you tried to name all routes as it recommended by Swagger notation.
And I do not find any fast solution to fix it.
Script to reproduce this problem:

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


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

app = aiohttp.web.Application()

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

routes = [{
    'method': 'GET',
    'path': '/test',
    'handler': handler,
    'name': 'test-good'
    }, {
    'method': 'POST',
    'path': '/test',
    'handler': handler,
    'name': 'test-good'
    }, {
    'method': 'GET',
    'path': '/test-bad',
    'handler': handler,
    'name': 'test-bad-get'
    }, {
    'method': 'POST',
    'path': '/test-bad',
    'handler': handler,
    'name': 'test-bad-post'
    }, ]
for route in routes:
    cors.add(
        app.router.add_route(
            method=route['method'],
            path=route['path'],
            handler=route['handler'],
            name=route['name']
        )
    )
loop = asyncio.get_event_loop()
handler = app._make_handler()
server = loop.run_until_complete(
    loop.create_server(
        handler,
        host='localhost',
        port='8081'
    )
)

try:
    loop.run_forever()
except (GracefulExit, KeyboardInterrupt):
    pass
finally:
    loop.stop()

GET and POST for /test works perfect.
GET and POST for /test-bad isn't work as expected.

For self._resource_config at

resource_config = self._resource_config[resource]

we will get some thing like that:

{
  <PlainResource 'test-bad-get'  /test-bad>: instance(_ResourceConfig):
    default_config: {
    },
    method_config: {
      'GET': {
      },
    }
  <PlainResource 'test-bad-post'  /test-bad>: instance(_ResourceConfig):
    default_config: {
    },
    method_config: {
      'POST': {
      },
    }
  <PlainResource 'test-good'  /test>: instance(_ResourceConfig):
    default_config: {
    },
    method_config: {
      'GET': {
      },
      'POST': {
      },
    }
}

But for OPTION request to url /test-bad resource nether with Access-Control-Request-Method set to 'GET' or 'POST' at

resource = self._request_resource(preflight_request)

will return every time:

<PlainResource 'test-bad-get'  /test-bad>

So we try to find method POST in <PlainResource 'test-bad-get' /test-bad> node of self._resource_config and couldn't as it designed.

@asvetlov
Copy link
Member

That's interesting.
Technically test-bad-get and test-bad-post are the same resource which always returns the same URL in url_for().
Perhaps aiohttp should raise a warning on trying to add the same URL pattern under different names.

Would be nice to fix aiohttp-cors to correctly process this case as well maybe but I not sure how complex the fix is.

@jersobh
Copy link

jersobh commented Jun 1, 2018

I have this problem also. I'm keeping with aiohttp 2.3.10. Thanks.

@valentinradu
Copy link

aiohttp-3.3.2 + aiohttp-cors-0.7.0 works fine too! <3

@TTRh
Copy link

TTRh commented May 9, 2019

Hey guys ! I think i have the same issue. @valentinmk did you find a workaround ?

@valentinradu
Copy link

@TTRh I used the above mentioned versions and it worked. Unfortunately I was not able to make it work with the latest version.

@jersobh
Copy link

jersobh commented May 9, 2019 via email

@TTRh
Copy link

TTRh commented May 9, 2019

Hum only rollbacking to aiohttp 2 worked for me :/

@atalaybaysal
Copy link

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)

This code worked for me with aiohttp 3.5.4, aiohttp-cors 0.7.0

@makusudi
Copy link

up)
same problems with aiohttp 3.6.2 and aiohttp-cors 0.7.0

@asvetlov
Copy link
Member

Pull Request for aiohttp-cors is welcome!

@kirzharov
Copy link

kirzharov commented Jul 17, 2020

I'm faced with this issue with POST requests and CORS also, but everything works fine with this code:

from aiohttp_cors import setup as cors_setup, ResourceOptions

...

routes = [
    web.get("/", handle),
    web.post("/something", another_handle),
]

app.router.add_routes(routes)

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

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

Versions: aiohttp 3.6.2 and aiohttp-cors 0.7.0

Maybe this will help someone else with this issue.

@leiyugithub
Copy link

from aiohttp_cors import setup as cors_setup, ResourceOptions

thanks it's work for me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests