translate query timeouts into proper HTTP responses

Need to use a 503 here because a 408 (Request timeout) will motivate
browsers to immediately resent the request.
This commit is contained in:
Sarah Hoffmann
2023-08-24 15:55:05 +02:00
parent 06a974df36
commit 5a2ebfcd4a
2 changed files with 26 additions and 2 deletions

View File

@@ -37,6 +37,17 @@ async def nominatim_error_handler(req: Request, resp: Response, #pylint: disable
resp.content_type = exception.content_type resp.content_type = exception.content_type
async def timeout_error_handler(req: Request, resp: Response, #pylint: disable=unused-argument
exception: TimeoutError, #pylint: disable=unused-argument
_: Any) -> None:
""" Special error handler that passes message and content type as
per exception info.
"""
resp.status = 503
resp.text = "Query took too long to process."
resp.content_type = 'text/plain; charset=utf-8'
class ParamWrapper(api_impl.ASGIAdaptor): class ParamWrapper(api_impl.ASGIAdaptor):
""" Adaptor class for server glue to Falcon framework. """ Adaptor class for server glue to Falcon framework.
""" """
@@ -139,6 +150,7 @@ def get_application(project_dir: Path,
app = App(cors_enable=api.config.get_bool('CORS_NOACCESSCONTROL'), app = App(cors_enable=api.config.get_bool('CORS_NOACCESSCONTROL'),
middleware=middleware) middleware=middleware)
app.add_error_handler(HTTPNominatimError, nominatim_error_handler) app.add_error_handler(HTTPNominatimError, nominatim_error_handler)
app.add_error_handler(TimeoutError, timeout_error_handler)
legacy_urls = api.config.get_bool('SERVE_LEGACY_URLS') legacy_urls = api.config.get_bool('SERVE_LEGACY_URLS')
for name, func in api_impl.ROUTES: for name, func in api_impl.ROUTES:

View File

@@ -7,14 +7,14 @@
""" """
Server implementation using the starlette webserver framework. Server implementation using the starlette webserver framework.
""" """
from typing import Any, Optional, Mapping, Callable, cast, Coroutine from typing import Any, Optional, Mapping, Callable, cast, Coroutine, Dict, Awaitable
from pathlib import Path from pathlib import Path
import datetime as dt import datetime as dt
from starlette.applications import Starlette from starlette.applications import Starlette
from starlette.routing import Route from starlette.routing import Route
from starlette.exceptions import HTTPException from starlette.exceptions import HTTPException
from starlette.responses import Response from starlette.responses import Response, PlainTextResponse
from starlette.requests import Request from starlette.requests import Request
from starlette.middleware import Middleware from starlette.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
@@ -110,6 +110,13 @@ class FileLoggingMiddleware(BaseHTTPMiddleware):
return response return response
async def timeout_error(request: Request, #pylint: disable=unused-argument
_: Exception) -> Response:
""" Error handler for query timeouts.
"""
return PlainTextResponse("Query took too long to process.", status_code=503)
def get_application(project_dir: Path, def get_application(project_dir: Path,
environ: Optional[Mapping[str, str]] = None, environ: Optional[Mapping[str, str]] = None,
debug: bool = True) -> Starlette: debug: bool = True) -> Starlette:
@@ -136,10 +143,15 @@ def get_application(project_dir: Path,
if log_file: if log_file:
middleware.append(Middleware(FileLoggingMiddleware, file_name=log_file)) middleware.append(Middleware(FileLoggingMiddleware, file_name=log_file))
exceptions: Dict[Any, Callable[[Request, Exception], Awaitable[Response]]] = {
TimeoutError: timeout_error
}
async def _shutdown() -> None: async def _shutdown() -> None:
await app.state.API.close() await app.state.API.close()
app = Starlette(debug=debug, routes=routes, middleware=middleware, app = Starlette(debug=debug, routes=routes, middleware=middleware,
exception_handlers=exceptions,
on_shutdown=[_shutdown]) on_shutdown=[_shutdown])
app.state.API = NominatimAPIAsync(project_dir, environ) app.state.API = NominatimAPIAsync(project_dir, environ)