示例#1
0
def callback_app(simple_resource):
    app = App()
    app.add_route('/', simple_resource)
    app.add_route('/sse', simple_resource, suffix='sse')
    app.add_route('/stream', simple_resource, suffix='stream')

    return app
示例#2
0
def test_startup_only():
    class Foo:
        async def process_startup(self, scope, event):
            self._called = True

    foo = Foo()

    app = App()
    app.add_middleware(foo)
    client = testing.TestClient(app)

    client.simulate_get()

    assert foo._called
示例#3
0
def _call_with_scope(scope):
    app = App()

    resource = testing.SimpleTestResourceAsync()

    app.add_route('/', resource)

    req_event_emitter = testing.ASGIRequestEventEmitter()
    resp_event_collector = testing.ASGIResponseEventCollector()

    falcon.invoke_coroutine_sync(app.__call__, scope, req_event_emitter,
                                 resp_event_collector)

    assert resource.called
    return resource
示例#4
0
def test_lifespan_scope_version(spec_version, supported):
    app = App()

    shutting_down = asyncio.Condition()
    req_event_emitter = testing.ASGILifespanEventEmitter(shutting_down)
    resp_event_collector = testing.ASGIResponseEventCollector()

    scope = {
        'type': 'lifespan',
        'asgi': {'spec_version': spec_version, 'version': '3.0'}
    }

    if not supported:
        with pytest.raises(UnsupportedScopeError):
            falcon.async_to_sync(
                app.__call__, scope, req_event_emitter, resp_event_collector
            )

        return

    async def t():
        t = asyncio.get_event_loop().create_task(
            app(scope, req_event_emitter, resp_event_collector)
        )

        # NOTE(kgriffs): Yield to the lifespan task above
        await asyncio.sleep(0.001)

        async with shutting_down:
            shutting_down.notify()

        await t

    falcon.async_to_sync(t)
示例#5
0
    def client(self):
        class SomeResource:
            async def on_get(self, req, resp):
                async def emitter():
                    yield SSEvent(json={'foo': 'bar'})
                    yield SSEvent(json={'bar': 'baz'})

                resp.sse = emitter()

        resource = SomeResource()

        app = App()
        app.add_route('/', resource)

        client = testing.TestClient(app)
        return client
示例#6
0
def test_multiple_events():
    class SomeResource:
        async def on_get(self, req, resp):
            async def emitter():
                yield SSEvent(data=b'ketchup')
                yield SSEvent(data=b'mustard', event='condiment')
                yield SSEvent(data=b'mayo', event='condiment', event_id='1234')
                yield SSEvent(data=b'onions',
                              event='topping',
                              event_id='5678',
                              retry=100)
                yield SSEvent(text='guacamole \u1F951',
                              retry=100,
                              comment='Serve with chips.')
                yield SSEvent(json={'condiment': 'salsa'}, retry=100)

            resp.sse = emitter()

    resource = SomeResource()

    app = App()
    app.add_route('/', resource)

    client = testing.TestClient(app)

    result = client.simulate_get()
    assert result.text == ('data: ketchup\n'
                           '\n'
                           'event: condiment\n'
                           'data: mustard\n'
                           '\n'
                           'event: condiment\n'
                           'id: 1234\n'
                           'data: mayo\n'
                           '\n'
                           'event: topping\n'
                           'id: 5678\n'
                           'retry: 100\n'
                           'data: onions\n'
                           '\n'
                           ': Serve with chips.\n'
                           'retry: 100\n'
                           'data: guacamole \u1F951\n'
                           '\n'
                           'retry: 100\n'
                           'data: {"condiment": "salsa"}\n'
                           '\n')
示例#7
0
def test_non_iterable():
    class SomeResource:
        async def on_get(self, req, resp):
            async def emitter():
                yield

            resp.sse = emitter

    resource = SomeResource()

    app = App()
    app.add_route('/', resource)

    client = testing.TestClient(app)

    with pytest.raises(TypeError):
        client.simulate_get()
示例#8
0
文件: test_ws.py 项目: vmdhhh/falcon
def test_mw_methods_must_be_coroutines():
    class MiddlewareA:
        def process_resource_ws(self, req, ws, resource, params):
            pass

    class MiddlewareB:
        def process_request_ws(self, req, ws):
            pass

    app = App()

    for mw in [MiddlewareA(), MiddlewareB()]:
        with pytest.raises(falcon.CompatibilityError):
            app.add_middleware(mw)

        with pytest.raises(falcon.CompatibilityError):
            App(middleware=mw)
示例#9
0
def create_app(asgi, **app_kwargs):
    if asgi:
        skipif_asgi_unsupported()
        from falcon.asgi import App
    else:
        from falcon import App

    app = App(**app_kwargs)
    return app
示例#10
0
文件: test_scope.py 项目: wsz/falcon
def test_supported_asgi_version(version, supported):
    scope = {
        'type': 'lifespan',
        'asgi': {
            'spec_version': '2.0',
            'version': version
        },
    }
    if version is None:
        del scope['asgi']['version']

    app = App()

    resource = testing.SimpleTestResourceAsync()
    app.add_route('/', resource)

    shutting_down = asyncio.Condition()
    req_event_emitter = testing.ASGILifespanEventEmitter(shutting_down)
    resp_event_collector = testing.ASGIResponseEventCollector()

    async def task():
        coro = asyncio.get_event_loop().create_task(
            app(scope, req_event_emitter, resp_event_collector))

        # NOTE(vytas): Yield to the lifespan task above.
        await asyncio.sleep(0)

        assert len(resp_event_collector.events) == 1
        event = resp_event_collector.events[0]
        if supported:
            assert event['type'] == 'lifespan.startup.complete'
        else:
            assert event['type'] == 'lifespan.startup.failed'
            assert event['message'].startswith(
                'Falcon requires ASGI version 3.x')

        async with shutting_down:
            shutting_down.notify()

        await coro

    falcon.async_to_sync(task)
示例#11
0
文件: test_ws.py 项目: vmdhhh/falcon
async def test_missing_spec_version():
    app = App()

    scope = testing.create_scope_ws()
    del scope['asgi']['spec_version']

    ws = testing.ASGIWebSocketSimulator()

    # NOTE(kgriffs): As long as this does not raise, we know the spec
    #   version defaulted OK.
    await app(scope, ws._emit, ws._collect)
示例#12
0
文件: test_sse.py 项目: vytas7/falcon
def test_multiple_events_early_disconnect():
    class SomeResource:
        async def on_get(self, req, resp):
            async def emitter():
                while True:
                    yield SSEvent(data=b'whassup')
                    await asyncio.sleep(0.01)

            resp.sse = emitter()

    resource = SomeResource()

    app = App()
    app.add_route('/', resource)

    async def _test():
        conductor = testing.ASGIConductor(app)
        result = await conductor.simulate_get()
        assert 'data: whassup' in result.text

        async with testing.ASGIConductor(app) as conductor:
            # NOTE(vytas): Using the get_stream() alias.
            async with conductor.get_stream() as sr:

                event_count = 0

                result_text = ''

                while event_count < 5:
                    chunk = (await sr.stream.read()).decode()
                    if not chunk:
                        continue

                    result_text += chunk
                    event_count += len(chunk.strip().split('\n\n'))

                assert result_text.startswith('data: whassup\n\n' * 5)
                assert event_count == 5

    falcon.async_to_sync(_test)
示例#13
0
def test_startup_raises():
    class Foo:
        def __init__(self):
            self._shutdown_called = False

        async def process_startup(self, scope, event):
            raise Exception('testing 123')

        async def process_shutdown(self, scope, event):
            self._shutdown_called = True

    class Bar:
        def __init__(self):
            self._startup_called = False
            self._shutdown_called = False

        async def process_startup(self, scope, event):
            self._startup_called = True

        async def process_shutdown(self, scope, event):
            self._shutdown_called = True

    foo = Foo()
    bar = Bar()

    app = App()
    app.add_middleware([foo, bar])
    client = testing.TestClient(app)

    with pytest.raises(RuntimeError) as excinfo:
        client.simulate_get()

    message = str(excinfo.value)

    assert message.startswith('ASGI app returned lifespan.startup.failed.')
    assert 'testing 123' in message

    assert not foo._shutdown_called
    assert not bar._startup_called
    assert not bar._shutdown_called
示例#14
0
def test_no_events():
    class Emitter:
        def __aiter__(self):
            return self

        async def __anext__(self):
            raise StopAsyncIteration

    class SomeResource:
        async def on_get(self, req, resp):
            self._called = True
            resp.sse = Emitter()

    resource = SomeResource()

    app = App()
    app.add_route('/', resource)

    client = testing.TestClient(app)
    client.simulate_get()

    assert resource._called
示例#15
0
def test_shutdown_raises():
    class HandlerA:
        def __init__(self):
            self._startup_called = False

        async def process_startup(self, scope, event):
            self._startup_called = True

        async def process_shutdown(self, scope, event):
            raise Exception('testing 321')

    class HandlerB:
        def __init__(self):
            self._startup_called = False
            self._shutdown_called = False

        async def process_startup(self, scope, event):
            self._startup_called = True

        async def process_shutdown(self, scope, event):
            self._shutdown_called = True

    a = HandlerA()
    b1 = HandlerB()
    b2 = HandlerB()

    app = App()
    app.add_middleware(b1)
    app.add_middleware([a, b2])
    client = testing.TestClient(app)

    with pytest.raises(RuntimeError) as excinfo:
        client.simulate_get()

    message = str(excinfo.value)

    assert message.startswith('ASGI app returned lifespan.shutdown.failed.')
    assert 'testing 321' in message

    assert a._startup_called
    assert b1._startup_called
    assert not b1._shutdown_called
    assert b2._startup_called
    assert b2._shutdown_called
示例#16
0
文件: test_ws.py 项目: vmdhhh/falcon
async def test_bad_first_event():
    app = App()

    scope = testing.create_scope_ws()
    del scope['asgi']['spec_version']

    ws = testing.ASGIWebSocketSimulator()
    wrapped_emit = ws._emit

    async def _emit():
        if ws._state == ClientWebSocketState.CONNECT:
            ws._state = ClientWebSocketState.HANDSHAKE
            return {'type': EventType.WS_SEND}

        return await wrapped_emit(ws)

    ws._emit = _emit

    # NOTE(kgriffs): If there is a bad first event, the framework should
    #   just bail out early and close the request...
    await app(scope, ws._emit, ws._collect)

    assert ws.closed
    assert ws.close_code == CloseCode.SERVER_ERROR
示例#17
0
文件: api.py 项目: onecrayon/redspoon
"""Configures the main Falcon app and routes

`app` is hoisted to the main application; e.g.:

    from application import app
"""
from falcon.asgi import App

from . import views
from .environment import settings
from .middleware.sqlalchemy import SQLAlchemySessionManager
from .middleware.firebase import FirebaseSessionManager


# Create our main application
app = App(middleware=[
    SQLAlchemySessionManager(),
    FirebaseSessionManager(),
])
# Allow HTTP cookies for local development
app.resp_options.secure_cookies_by_default = settings.debug

# Define our application routes
app.add_route('/health-check', views.HealthCheck())
app.add_route('/session', views.FirebaseAuth())
示例#18
0
def test_multiple():
    class SomeResource:
        def __init__(self):
            self.counter = Counter()

        async def on_get(self, req, resp):
            async def background_job_async():
                self.counter['backround:on_get:async'] += 1

            def background_job_sync():
                self.counter['backround:on_get:sync'] += 20

            with pytest.raises(TypeError):
                resp.schedule(background_job_sync)

            resp.schedule_sync(background_job_sync)
            resp.schedule(background_job_async)
            resp.schedule_sync(background_job_sync)
            resp.schedule(background_job_async)

        async def on_post(self, req, resp):
            async def background_job_async():
                self.counter['backround:on_get:async'] += 1000

            def background_job_sync():
                self.counter['backround:on_get:sync'] += 2000

            resp.schedule(background_job_async)
            resp.schedule(background_job_async)
            resp.schedule_sync(background_job_sync)
            resp.schedule_sync(background_job_sync)

        async def on_put(self, req, resp):
            async def background_job_async():
                self.counter['backround:on_get:async'] += 1000

            c = background_job_async()

            try:
                resp.schedule(c)
            finally:
                await c

    resource = SomeResource()

    app = App()
    app.add_route('/', resource)

    client = testing.TestClient(app)

    client.simulate_get()
    client.simulate_post()

    time.sleep(0.5)

    assert resource.counter['backround:on_get:async'] == 2002
    assert resource.counter['backround:on_get:sync'] == 4040

    result = client.simulate_put()
    assert result.status_code == 500

    # NOTE(kgriffs): Remove default handlers so that we can check the raised
    #   exception is what we expecte.
    app._error_handlers.clear()
    with pytest.raises(TypeError) as exinfo:
        client.simulate_put()

    assert 'coroutine' in str(exinfo.value)
示例#19
0
def test_multiple_handlers():
    counter = 0

    class HandlerA:
        async def process_startup(self, scope, event):
            nonlocal counter
            self._called_startup = counter
            counter += 1

    class HandlerB:
        async def process_startup(self, scope, event):
            nonlocal counter
            self._called_startup = counter
            counter += 1

        async def process_shutdown(self, scope, event):
            nonlocal counter
            self._called_shutdown = counter
            counter += 1

    class HandlerC:
        async def process_shutdown(self, scope, event):
            nonlocal counter
            self._called_shutdown = counter
            counter += 1

    class HandlerD:
        async def process_startup(self, scope, event):
            nonlocal counter
            self._called_startup = counter
            counter += 1

    class HandlerE:
        async def process_startup(self, scope, event):
            nonlocal counter
            self._called_startup = counter
            counter += 1

        async def process_shutdown(self, scope, event):
            nonlocal counter
            self._called_shutdown = counter
            counter += 1

        async def process_request(self, req, resp):
            self._called_request = True

    app = App()

    a = HandlerA()
    b = HandlerB()
    c = HandlerC()
    d = HandlerD()
    e = HandlerE()

    app.add_middleware([a, b, c, d, e])

    client = testing.TestClient(app)
    client.simulate_get()

    assert a._called_startup == 0
    assert b._called_startup == 1
    assert d._called_startup == 2
    assert e._called_startup == 3

    assert e._called_shutdown == 4
    assert c._called_shutdown == 5
    assert b._called_shutdown == 6

    assert e._called_request
示例#20
0
文件: test_ws.py 项目: vmdhhh/falcon
async def test_echo():  # noqa: C901
    consumer_sleep = 0.01
    producer_loop = 10
    producer_sleep_factor = consumer_sleep / (producer_loop / 2)

    class Resource:
        def __init__(self):
            self.caught_operation_not_allowed = False

        async def on_websocket(self, req, ws, p1, p2, injected):
            # NOTE(kgriffs): Normally the receiver task is not started
            #   until the websocket is started. But here we start it
            #   first in order to simulate a potential race condition
            #   that ASGIWebSocketSimulator._emit() guards against,
            #   in case it ever arises due to the way the target ASGI
            #   app may be implemented.
            ws._buffered_receiver.start()

            await asyncio.sleep(0)

            if ws.unaccepted:
                await ws.accept()

            try:
                await ws.accept()
            except falcon.OperationNotAllowed:
                self.caught_operation_not_allowed = True

            if ws.ready:
                await ws.send_text(
                    f'{p1}:{p2}:{req.context.message}:{injected}')

            messages = deque()
            sink_task = falcon.create_task(self._sink(ws, messages))

            while not sink_task.done():
                if not messages:
                    await asyncio.sleep(0)
                    continue

                try:
                    await ws.send_text(messages.popleft())
                except falcon.WebSocketDisconnected:
                    break

            sink_task.cancel()
            try:
                await sink_task
            except asyncio.CancelledError:
                pass

        async def _sink(self, ws, messages):
            while True:
                # NOTE(kgriffs): Throttle slightly to allow messages to
                #   fill up the buffer.
                await asyncio.sleep(consumer_sleep)

                try:
                    message = await ws.receive_text()
                except falcon.WebSocketDisconnected:
                    break

                if message != 'ignore':
                    messages.append(message)

    class MiddlewareA:
        async def process_resource_ws(self, req, ws, resource, params):
            assert isinstance(resource, Resource)
            assert isinstance(ws, falcon.asgi.WebSocket)
            params['injected'] = '42'

    class MiddlewareB:
        async def process_request_ws(self, req, ws):
            assert isinstance(ws, falcon.asgi.WebSocket)
            req.context.message = 'hello'

    resource = Resource()

    # NOTE(kgriffs): The two methods are split across different middleware
    #   classes so that we can test code paths that check for the existence
    #   of one WebSocket middleware method vs the other, and also so that
    #   we can make sure both the kwarg and the add_middlware() paths
    #   succeed.
    app = App(middleware=MiddlewareA())
    app.add_middleware(MiddlewareB())

    app.add_route('/{p1}/{p2}', resource)

    async with testing.ASGIConductor(app) as c:
        async with c.simulate_ws('/v1/v2', headers={}) as ws:
            assert (await ws.receive_text()) == 'v1:v2:hello:42'

            for i in range(producer_loop):
                message = str(
                    i
                ) if i else ''  # Test round-tripping the empty string as well

                for i in range(100):
                    await ws.send_text('ignore')

                # NOTE(kgriffs): For part of the time, cause the buffer on the
                #   server side to fill up, and for the remainder of the time
                #   for the buffer to be empty and wait on the client for
                #   additional messages.
                await asyncio.sleep(i * producer_sleep_factor)

                await ws.send_text(message)
                assert (await ws.receive_text()) == message

            await ws.close()

            assert ws.closed
            assert ws.close_code == CloseCode.NORMAL

    assert resource.caught_operation_not_allowed
示例#21
0
def test_sync_helpers():
    safely_values = []
    unsafely_values = []
    shirley_values = []

    class SomeResource:
        async def on_get(self, req, resp):
            safely_coroutine_objects = []
            unsafely_coroutine_objects = []
            shirley_coroutine_objects = []

            def callme_safely(a, b, c=None):
                # NOTE(kgriffs): Sleep to prove that there isn't another
                #   instance running in parallel that is able to race ahead.
                time.sleep(0.001)
                safely_values.append((a, b, c))

            def callme_unsafely(a, b, c=None):
                time.sleep(0.01)

                # NOTE(vytas): Deliberately exaggerate a race condition here
                #   in order to ensure a more deterministic test outcome.
                if a == 137:
                    for _ in range(1000):
                        if len(unsafely_values) > 137:
                            break
                        time.sleep(0.01)

                unsafely_values.append((a, b, c))

            def callme_shirley(a=42, b=None):
                time.sleep(0.01)
                v = (a, b)
                shirley_values.append(v)

                # NOTE(kgriffs): Test that returning values works as expected
                return v

            # NOTE(kgriffs): Test setting threadsafe=True explicitly
            cmus = falcon.util.wrap_sync_to_async(callme_unsafely, threadsafe=True)
            cms = falcon.util.wrap_sync_to_async(callme_safely, threadsafe=False)

            loop = falcon.util.get_running_loop()

            # NOTE(kgriffs): create_task() is used here, so that the coroutines
            #   are scheduled immediately in the order created; under Python
            #   3.6, asyncio.gather() does not seem to always schedule
            #   them in order, so we do it this way to make it predictable.
            for i in range(1000):
                safely_coroutine_objects.append(
                    loop.create_task(cms(i, i + 1, c=i + 2))
                )
                unsafely_coroutine_objects.append(
                    loop.create_task(cmus(i, i + 1, c=i + 2))
                )
                shirley_coroutine_objects.append(
                    loop.create_task(falcon.util.sync_to_async(callme_shirley, 24, b=i))
                )

            await asyncio.gather(
                *(
                    safely_coroutine_objects +
                    unsafely_coroutine_objects +
                    shirley_coroutine_objects
                )
            )

            assert (42, None) == await falcon.util.sync_to_async(callme_shirley)
            assert (1, 2) == await falcon.util.sync_to_async(callme_shirley, 1, 2)
            assert (3, 4) == await falcon.util.sync_to_async(callme_shirley, 3, b=4)

            assert (5, None) == await falcon.util.wrap_sync_to_async(callme_shirley)(5)
            assert (42, 6) == await falcon.util.wrap_sync_to_async(
                callme_shirley, threadsafe=True)(b=6)

            with pytest.raises(TypeError):
                await falcon.util.sync_to_async(callme_shirley, -1, bogus=-1)

    resource = SomeResource()

    app = App()
    app.add_route('/', resource)

    client = testing.TestClient(app)

    result = client.simulate_get()
    assert result.status_code == 200

    assert len(safely_values) == 1000
    for i, val in enumerate(safely_values):
        assert val == (i, i + 1, i + 2)

    assert len(unsafely_values) == 1000
    assert any(
        val != (i, i + 1, i + 2)
        for i, val in enumerate(unsafely_values)
    )

    for i, val in enumerate(shirley_values):
        assert val[0] in {24, 42, 1, 5, 3}
        assert val[1] is None or (0 <= val[1] < 1000)
示例#22
0
        else:
            response.text = dumps(
                {
                    'params': {
                        'user': user,
                        'record': record
                    },
                    'query': request.params,
                    'data': await request.get_media(),
                },
                ensure_ascii=False,
                separators=(',', ':'))


app = App()


# first add ten more routes to load routing system
# ------------------------------------------------
class req_ok:
    async def on_get(self, *args, **kwargs):
        pass


for n in range(5):
    app.add_route(f"/route-{n}", req_ok())
    app.add_route(f"/route-dyn-{n}/{{part}}", req_ok())

# then prepare endpoints for the benchmark
# ----------------------------------------
示例#23
0
def test_multiple_events():
    expected_result_text = ('data: ketchup\n'
                            '\n'
                            'event: condiment\n'
                            'data: mustard\n'
                            '\n'
                            'event: condiment\n'
                            'id: 1234\n'
                            'data: mayo\n'
                            '\n'
                            'event: topping\n'
                            'id: 5678\n'
                            'retry: 100\n'
                            'data: onions\n'
                            '\n'
                            ': Serve with chips.\n'
                            'retry: 100\n'
                            'data: guacamole \u1F951\n'
                            '\n'
                            'retry: 100\n'
                            'data: {"condiment": "salsa"}\n'
                            '\n')

    class SomeResource:
        async def on_get(self, req, resp):
            async def emitter():
                for event in [
                        SSEvent(data=b'ketchup'),
                        SSEvent(data=b'mustard', event='condiment'),
                        SSEvent(data=b'mayo',
                                event='condiment',
                                event_id='1234'),
                        SSEvent(data=b'onions',
                                event='topping',
                                event_id='5678',
                                retry=100),
                        SSEvent(text='guacamole \u1F951',
                                retry=100,
                                comment='Serve with chips.'),
                        SSEvent(json={'condiment': 'salsa'}, retry=100),
                ]:
                    yield event
                    await asyncio.sleep(0.001)

            resp.sse = emitter()

    resource = SomeResource()

    app = App()
    app.add_route('/', resource)

    client = testing.TestClient(app)

    async def _test():
        async with client as conductor:
            # NOTE(kgriffs): Single-shot test will only allow the first
            #   one or two events since a client disconnect will be emitted
            #   into the app immediately.
            result = await conductor.simulate_get()
            assert expected_result_text.startswith(result.text)

            async with conductor.simulate_get_stream() as sr:
                event_count = 0

                result_text = ''

                while True:
                    chunk = (await sr.stream.read()).decode()

                    if not chunk:
                        continue

                    result_text += chunk
                    event_count += len(chunk.strip().split('\n\n'))

                    if 'salsa' in chunk:
                        break

                assert not (await sr.stream.read())

                assert event_count == 6
                assert result_text == expected_result_text

    falcon.async_to_sync(_test)