ashleysommer / sanic-cors

A Sanic extension for handling Cross Origin Resource Sharing (CORS), making cross-origin AJAX possible. Based on flask-cors by Cory Dolphin.
MIT License
160 stars 22 forks source link

SPF error when using Asyncio server instance #49

Open WoodyFleurant opened 4 years ago

WoodyFleurant commented 4 years ago

Following official documentation from Sanic , it is not possible to use CORS plugin with AsyncIO server instanciation.

Doing the following will provoke a crash, and server won't be able to process requests : "Error is SPF processing a request before App server is started."

import asyncio
import socket
import os

from sanic import Sanic
from sanic.response import json
from sanic_cors import CORS

app = Sanic(__name__)
CORS(app)

@app.route("/")
async def test(request):
    return json({"hello": "world"})

server_socket = '/tmp/sanic.sock'

sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

try:
    os.remove(server_socket)
finally:
    sock.bind(server_socket)

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    srv_coro = app.create_server(
        sock=sock,
        return_asyncio_server=True,
        asyncio_server_kwargs=dict(
            start_serving=False
        )
    )
    srv = loop.run_until_complete(srv_coro)
    try:
        assert srv.is_serving() is False
        loop.run_until_complete(srv.start_serving())
        assert srv.is_serving() is True
        loop.run_until_complete(srv.serve_forever())
    except KeyboardInterrupt:
        srv.close()
        loop.close()
ashleysommer commented 4 years ago

This is very similar to this bug: https://github.com/ashleysommer/sanicpluginsframework/issues/12 This is a SanicPluginsFramework issue, not specifically caused by Sanic-CORS.

This bug is triggered because starting Sanic in non-standard ways does not trigger the before_server_start or after_server_start events. So any listeners waiting for those events never get triggered. Another similar problem is mentioned in #47.

You can manually trigger the on_server_start listener from Sanic-Plugins-Framework, you can use the same workaround that is mentioned in https://github.com/ashleysommer/sanicpluginsframework/issues/12#issuecomment-523238446

import asyncio
import socket
import os

from sanic import Sanic
from sanic.response import json
from sanic_cors import CORS
from spf import SanicPluginsFramework #<- this is added

app = Sanic(__name__)
spf = SanicPluginsFramework(app) #<- this is added
cors = spf.register_plugin(CORS) #<- this is changed

@app.route("/")
async def test(request):
    return json({"hello": "world"})

server_socket = '/tmp/sanic.sock'

sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

try:
    os.remove(server_socket)
finally:
    sock.bind(server_socket)

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    srv_coro = app.create_server(
        sock=sock,
        return_asyncio_server=True,
        asyncio_server_kwargs=dict(
            start_serving=False
        )
    )
    srv = loop.run_until_complete(srv_coro)
    try:
        assert srv.is_serving() is False
        spf._on_server_start(app, loop) #<- this is added
        loop.run_until_complete(srv.start_serving())
        assert srv.is_serving() is True
        loop.run_until_complete(srv.serve_forever())
    except KeyboardInterrupt:
        srv.close()
        loop.close()
WoodyFleurant commented 3 years ago

Thanks for your quick answer, really appreciated. Would you suggest that I add documentation about this use case in this project or in Sanic ?

ashleysommer commented 3 years ago

Documentation about this really should go in the Sanic Plugins Framework project. The issue is caused by spf not receiving signal that the app started up, and the solution is to manually trigger _on_server_start() of the spf object. However most people probably wouldn't immediately go to the SPF docs to read about about it if they're seeing this problem. It might be a documentation problem with Sanic, because the docs don't currently mention that this way of starting sanic might break plugins which expect Sanic to behave in a conventional way. See this recent issue which is another side-effect of using the create_server() method: https://github.com/huge-success/sanic/issues/1925