PySimpleSocial/util/exception_handlers.py

76 lines
2.8 KiB
Python
Raw Normal View History

2022-10-04 21:13:26 +02:00
from config import LOGGER, NotAuthenticated
from fastapi import Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from fastapi.exceptions import HTTPException, StarletteHTTPException
from slowapi.errors import RateLimitExceeded
async def rate_limited(request: Request, error: RateLimitExceeded) -> JSONResponse:
"""
Handles the equivalent of a 429 Too Many Requests error
"""
2022-10-06 17:08:08 +02:00
2022-10-04 21:13:26 +02:00
n = 0
while True:
if error.detail[n].isnumeric():
n += 1
else:
break
error.detail = error.detail[:n] + " requests" + error.detail[n:]
2022-10-06 17:08:08 +02:00
LOGGER.info(f"{request.client.host} got rate-limited at {str(request.url)} " f"(exceeded {error.detail})")
return JSONResponse(
status_code=200,
content=dict(
status_code=429,
msg=f"Too many requests, retry after {error.detail[error.detail.find('per') + 4:]}",
),
)
2022-10-04 21:13:26 +02:00
def not_authenticated(request: Request, _: NotAuthenticated) -> JSONResponse:
"""
Handles the equivalent of a 401 Unauthorized exception
"""
2022-10-04 21:13:26 +02:00
LOGGER.info(f"{request.client.host} failed to authenticate at {str(request.url)}")
2022-10-06 17:08:08 +02:00
return JSONResponse(status_code=200, content=dict(status_code=401, msg="Authentication is required"))
2022-10-04 21:13:26 +02:00
def request_invalid(request: Request, exc: RequestValidationError) -> JSONResponse:
"""
Handles Bad Request exceptions from FastAPI
"""
2022-10-06 17:08:08 +02:00
LOGGER.info(f"{request.client.host} sent an invalid request at {request.url!r}: {type(exc).__name__}: {exc}")
return JSONResponse(
status_code=200,
content=dict(status_code=400, msg=f"Bad request: {type(exc).__name__}: {exc}"),
)
2022-10-04 21:13:26 +02:00
2022-10-06 17:08:08 +02:00
def http_exception(request: Request, exc: HTTPException | StarletteHTTPException) -> JSONResponse:
"""
Handles HTTP-specific exceptions raised explicitly by
path operations
"""
2022-10-04 21:13:26 +02:00
if exc.status_code >= 500:
LOGGER.error(
2022-10-06 17:08:08 +02:00
f"{request.client.host} raised a {exc.status_code} error at {request.url!r}:" f"{type(exc).__name__}: {exc}"
)
2022-10-06 17:08:08 +02:00
return JSONResponse(status_code=200, content=dict(status_code=500, msg="Internal Server Error"))
2022-10-04 21:13:26 +02:00
else:
2022-10-06 17:08:08 +02:00
LOGGER.info(f"{request.client.host} raised an HTTP error ({exc.status_code}) at {str(request.url)}")
return JSONResponse(status_code=200, content=dict(status_code=exc.status_code, msg=exc.detail))
async def generic_error(request: Request, exc: Exception) -> JSONResponse:
"""
Handles generic, unexpected errors in the ASGI application
"""
2022-10-06 17:08:08 +02:00
LOGGER.info(f"{request.client.host} raised an unexpected error ({type(exc).__name__}: {exc}) at {str(request.url)}")
# We can't leak anything about the error, it would be too risky
2022-10-06 17:08:08 +02:00
return JSONResponse(status_code=200, content=dict(status_code=500, msg="Internal Server Error"))