123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- import logging
- import sys
- from functools import wraps
- from asgiref.sync import iscoroutinefunction, sync_to_async
- from django.conf import settings
- from django.core import signals
- from django.core.exceptions import (
- BadRequest,
- PermissionDenied,
- RequestDataTooBig,
- SuspiciousOperation,
- TooManyFieldsSent,
- TooManyFilesSent,
- )
- from django.http import Http404
- from django.http.multipartparser import MultiPartParserError
- from django.urls import get_resolver, get_urlconf
- from django.utils.log import log_response
- from django.views import debug
- def convert_exception_to_response(get_response):
- """
- Wrap the given get_response callable in exception-to-response conversion.
- All exceptions will be converted. All known 4xx exceptions (Http404,
- PermissionDenied, MultiPartParserError, SuspiciousOperation) will be
- converted to the appropriate response, and all other exceptions will be
- converted to 500 responses.
- This decorator is automatically applied to all middleware to ensure that
- no middleware leaks an exception and that the next middleware in the stack
- can rely on getting a response instead of an exception.
- """
- if iscoroutinefunction(get_response):
- @wraps(get_response)
- async def inner(request):
- try:
- response = await get_response(request)
- except Exception as exc:
- response = await sync_to_async(
- response_for_exception, thread_sensitive=False
- )(request, exc)
- return response
- return inner
- else:
- @wraps(get_response)
- def inner(request):
- try:
- response = get_response(request)
- except Exception as exc:
- response = response_for_exception(request, exc)
- return response
- return inner
- def response_for_exception(request, exc):
- if isinstance(exc, Http404):
- if settings.DEBUG:
- response = debug.technical_404_response(request, exc)
- else:
- response = get_exception_response(
- request, get_resolver(get_urlconf()), 404, exc
- )
- elif isinstance(exc, PermissionDenied):
- response = get_exception_response(
- request, get_resolver(get_urlconf()), 403, exc
- )
- log_response(
- "Forbidden (Permission denied): %s",
- request.path,
- response=response,
- request=request,
- exception=exc,
- )
- elif isinstance(exc, MultiPartParserError):
- response = get_exception_response(
- request, get_resolver(get_urlconf()), 400, exc
- )
- log_response(
- "Bad request (Unable to parse request body): %s",
- request.path,
- response=response,
- request=request,
- exception=exc,
- )
- elif isinstance(exc, BadRequest):
- if settings.DEBUG:
- response = debug.technical_500_response(
- request, *sys.exc_info(), status_code=400
- )
- else:
- response = get_exception_response(
- request, get_resolver(get_urlconf()), 400, exc
- )
- log_response(
- "%s: %s",
- str(exc),
- request.path,
- response=response,
- request=request,
- exception=exc,
- )
- elif isinstance(exc, SuspiciousOperation):
- if isinstance(exc, (RequestDataTooBig, TooManyFieldsSent, TooManyFilesSent)):
-
-
- request._mark_post_parse_error()
-
-
- security_logger = logging.getLogger(
- "django.security.%s" % exc.__class__.__name__
- )
- security_logger.error(
- str(exc),
- exc_info=exc,
- extra={"status_code": 400, "request": request},
- )
- if settings.DEBUG:
- response = debug.technical_500_response(
- request, *sys.exc_info(), status_code=400
- )
- else:
- response = get_exception_response(
- request, get_resolver(get_urlconf()), 400, exc
- )
- else:
- signals.got_request_exception.send(sender=None, request=request)
- response = handle_uncaught_exception(
- request, get_resolver(get_urlconf()), sys.exc_info()
- )
- log_response(
- "%s: %s",
- response.reason_phrase,
- request.path,
- response=response,
- request=request,
- exception=exc,
- )
-
- if not getattr(response, "is_rendered", True) and callable(
- getattr(response, "render", None)
- ):
- response = response.render()
- return response
- def get_exception_response(request, resolver, status_code, exception):
- try:
- callback = resolver.resolve_error_handler(status_code)
- response = callback(request, exception=exception)
- except Exception:
- signals.got_request_exception.send(sender=None, request=request)
- response = handle_uncaught_exception(request, resolver, sys.exc_info())
- return response
- def handle_uncaught_exception(request, resolver, exc_info):
- """
- Processing for any otherwise uncaught exceptions (those that will
- generate HTTP 500 responses).
- """
- if settings.DEBUG_PROPAGATE_EXCEPTIONS:
- raise
- if settings.DEBUG:
- return debug.technical_500_response(request, *exc_info)
-
- callback = resolver.resolve_error_handler(500)
- return callback(request)
|