Using the Python API with FastAPI and a decorator that protects a resource with an API key, as written in the latest docs, I'm getting exceptions when requests are called concurrently (as usual for an API).
I'd expect the application not to crash and the verification decorator not to fail. Instead, when using the proposed decorator, there should be an internal handling of request concurrency errors and some kind of cache and retry logic to handle such request bursts.
I have seen the on_exc callback handler, but I assume it does not handle this scenario, right?
Error:
INFO: 172.17.0.17924 - "GET / HTTP/1.1" 500 Internal Server Error ERROR: Exception in ASGI application Traceback (most recent call last): File "/o/installpath/lib/python3.12/site-packages-datapipe-api-env/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 419, in run_asgio result = await app( # type: ignore[func-returns-value]t ^^^^^^^^^/installpath/lib/python3.12/site-packages^^^^^^^^^^^^^^^^^^^^^^^^ File "/installpath/lib/python3.12/site-packages/uvicorn/middapi-env/lib/python3.12/site-packagesheaders.py", line 84, in __call__o return await self.app(scope, receive, send)api-env/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
return await self.app(scope, receive, send)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__
await super().__call__(scope, receive, send)
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/starlette/applications.py", line 123, in __call__
await self.middleware_stack(scope, receive, send)
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__
raise exc
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__
await self.app(scope, receive, _send)
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/starlette/routing.py", line 758, in __call__
await self.middleware_stack(scope, receive, send)
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/starlette/routing.py", line 778, in app
await route.handle(scope, receive, send)
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/starlette/routing.py", line 299, in handle
await self.app(scope, receive, send)
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/starlette/routing.py", line 79, in app
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/starlette/routing.py", line 74, in app
response = await func(request)
^^^^^^^^^^^^^^^^^^^
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/fastapi/routing.py", line 278, in app
raw_response = await run_endpoint_function(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/fastapi/routing.py", line 191, in run_endpoint_function
return await dependant.call(**values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/unkey/decorators.py", line 135, in inner
return _on_exc(exc)
^^^^^^^^^^^^
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/unkey/decorators.py", line 99, in _on_exc
raise exc
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/unkey/decorators.py", line 112, in inner
result = await _client.keys.verify_key(key, api_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/unkey/services/keys.py", line 120, in verify_key
data = await self._http.fetch(route, payload=payload)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/unkey/services/http.py", line 146, in fetch
return await self._request( # type: ignore[no-any-return]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/unkey/services/http.py", line 70, in _request
response = await req(url, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/aiohttp/client.py", line 605, in _request
await resp.start(conn)
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/aiohttp/client_reqrep.py", line 966, in start
message, payload = await protocol.read() # type: ignore[union-attr]
^^^^^^^^^^^^^^^^^^^^^
File "/installpath/lib/python3.12/site-packages/lib/python3.12/site-packages/aiohttp/streams.py", line 622, in read
await self._waiter
aiohttp.client_exceptions.ServerDisconnectedError: Server disconnected
protect a route with a decorator
launch say 10 parallel curl commands that request the same resource
sample (python) FastAPI code:
import typing
import unkey
import fastapi
UNKEY_API_ID='your_api_key'
def api_key_extractor(*args: typing.Any, **kwargs: typing.Any) -> typing.Optional[str]:
"""Extract the API key from the Bearer token header in request, used by unkey.protected() decorator."""
if isinstance(auth := kwargs.get("authorization"), str):
api_key = auth.split(" ")[-1]
return api_key
return None
@app.get("/")
@unkey.protected(UNKEY_API_ID, api_key_extractor)
async def reroute_broker(
*,
authorization: str = fastapi.Header(None),
unkey_verification: typing.Any = None,
) -> fastapi.Response:
"""Sample route"""
assert isinstance(unkey_verification, unkey.ApiKeyVerification)
assert unkey_verification.valid
print(unkey_verification.owner_id)
return {"message": "protected!"}
sample (sh) request code:
BASE_URL='your_url'
AUTH_TOKEN='api_token'
for i in $(seq 1 10); do
curl -H "Authorization: Bearer $AUTH_TOKEN" "$BASE_URL" &
done
wait
Using the Python API with FastAPI and a decorator that protects a resource with an API key, as written in the latest docs, I'm getting exceptions when requests are called concurrently (as usual for an API).
I'd expect the application not to crash and the verification decorator not to fail. Instead, when using the proposed decorator, there should be an internal handling of request concurrency errors and some kind of cache and retry logic to handle such request bursts.
I have seen the
on_exc
callback handler, but I assume it does not handle this scenario, right?Error:
sample (python) FastAPI code:
sample (sh) request code:
Python 3.12.2 using the following packages: