populate middleware lists from settings middleware must be called after the environment is fixed see call in subclasses adapt handler if needed the exceptionhandling stack is still always synchronous for now so adapt that way adapt the top of the stack if needed we only assign to this when initialization is complete as it is used as a flag for initialization being complete adapt a method to be in the correct mode if isasync is false synchronous methods are left alone asynchronous methods are wrapped with asynctosync if isasync is true synchronous methods are wrapped with synctoasync asynchronous methods are left alone return an httpresponse object for the given httprequest setup default url resolver for this thread seturlconfsettings rooturlconf response self middlewarechainrequest response resourceclosers appendrequest close if response statuscode 400 logresponse s s response reasonphrase request path responseresponse requestrequest return response async def getresponseasyncself request setup default url resolver for this thread seturlconfsettings rooturlconf response await self middlewarechainrequest response resourceclosers appendrequest close if response statuscode 400 await synctoasynclogresponse threadsensitivefalse s s response reasonphrase request path responseresponse requestrequest return response def getresponseself request response none callback callbackargs callbackkwargs self resolverequestrequest apply view middleware for middlewaremethod in self viewmiddleware response middlewaremethod request callback callbackargs callbackkwargs if response break if response is none wrappedcallback self makeviewatomiccallback if it is an asynchronous view run it in a subthread if iscoroutinefunctionwrappedcallback wrappedcallback asynctosyncwrappedcallback try response wrappedcallbackrequest callbackargs callbackkwargs except exception as e response self processexceptionbymiddlewaree request if response is none raise complain if the view returned none a common error self checkresponseresponse callback if the response supports deferred rendering apply template response middleware and then render the response if hasattrresponse render and callableresponse render for middlewaremethod in self templateresponsemiddleware response middlewaremethodrequest response complain if the template response middleware returned none a common error self checkresponse response middlewaremethod names processtemplateresponse middlewaremethod self class name try response response render except exception as e response self processexceptionbymiddlewaree request if response is none raise return response async def getresponseasyncself request response none callback callbackargs callbackkwargs self resolverequestrequest apply view middleware for middlewaremethod in self viewmiddleware response await middlewaremethod request callback callbackargs callbackkwargs if response break if response is none wrappedcallback self makeviewatomiccallback if it is a synchronous view run it in a subthread if not iscoroutinefunctionwrappedcallback wrappedcallback synctoasync wrappedcallback threadsensitivetrue try response await wrappedcallback request callbackargs callbackkwargs except exception as e response await synctoasync self processexceptionbymiddleware threadsensitivetrue e request if response is none raise complain if the view returned none or an uncalled coroutine self checkresponseresponse callback if the response supports deferred rendering apply template response middleware and then render the response if hasattrresponse render and callableresponse render for middlewaremethod in self templateresponsemiddleware response await middlewaremethodrequest response complain if the template response middleware returned none or an uncalled coroutine self checkresponse response middlewaremethod names processtemplateresponse middlewaremethod self class name try if iscoroutinefunctionresponse render response await response render else response await synctoasync response render threadsensitivetrue except exception as e response await synctoasync self processexceptionbymiddleware threadsensitivetrue e request if response is none raise make sure the response is not a coroutine if asyncio iscoroutineresponse raise runtimeerrorresponse is still a coroutine return response def resolverequestself request work out the resolver if hasattrrequest urlconf urlconf request urlconf seturlconfurlconf resolver getresolverurlconf else resolver getresolver resolve the view and assign the match object back to the request resolvermatch resolver resolverequest pathinfo request resolvermatch resolvermatch return resolvermatch def checkresponseself response callback namenone if not response is none or asyncio iscoroutineresponse return if not name if isinstancecallback types functiontype fbv name the view s s callback module callback name else cbv name the view s s call callback module callback class name if response is none raise valueerror s didn t return an httpresponse object it returned none instead name elif asyncio iscoroutineresponse raise valueerror s didn t return an httpresponse object it returned an unawaited coroutine instead you may need to add an await into your view name other utility methods def makeviewatomicself view nonatomicrequests getattrview nonatomicrequests set for alias settingsdict in connections settings items if settingsdictatomicrequests and alias not in nonatomicrequests if iscoroutinefunctionview raise runtimeerror you cannot use atomicrequests with async views view transaction atomicusingaliasview return view def processexceptionbymiddlewareself exception request for middlewaremethod in self exceptionmiddleware response middlewaremethodrequest exception if response return response return none def reseturlconfsender kwargs populate middleware lists from settings middleware must be called after the environment is fixed see __call__ in subclasses adapt handler if needed the exception handling stack is still always synchronous for now so adapt that way adapt the top of the stack if needed we only assign to this when initialization is complete as it is used as a flag for initialization being complete adapt a method to be in the correct mode if is_async is false synchronous methods are left alone asynchronous methods are wrapped with async_to_sync if is_async is true synchronous methods are wrapped with sync_to_async asynchronous methods are left alone return an httpresponse object for the given httprequest setup default url resolver for this thread asynchronous version of get_response funneling everything including wsgi into a single async get_response is too slow avoid the context switch by using a separate async response path setup default url resolver for this thread resolve and call the view then apply view exception and template_response middleware this method is everything that happens inside the request response middleware apply view middleware if it is an asynchronous view run it in a subthread complain if the view returned none a common error if the response supports deferred rendering apply template response middleware and then render the response complain if the template response middleware returned none a common error resolve and call the view then apply view exception and template_response middleware this method is everything that happens inside the request response middleware apply view middleware if it is a synchronous view run it in a subthread complain if the view returned none or an uncalled coroutine if the response supports deferred rendering apply template response middleware and then render the response complain if the template response middleware returned none or an uncalled coroutine make sure the response is not a coroutine retrieve set the urlconf for the request return the view resolved with its args and kwargs work out the resolver resolve the view and assign the match object back to the request raise an error if the view returned none or an uncalled coroutine fbv cbv other utility methods pass the exception to the exception middleware if no middleware return a response for this exception return none reset the urlconf after each request is finished
import asyncio
import logging
import types

from asgiref.sync import async_to_sync, iscoroutinefunction, sync_to_async

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, MiddlewareNotUsed
from django.core.signals import request_finished
from django.db import connections, transaction
from django.urls import get_resolver, set_urlconf
from django.utils.log import log_response
from django.utils.module_loading import import_string

from .exception import convert_exception_to_response

logger = logging.getLogger("django.request")


class BaseHandler:
    _view_middleware = None
    _template_response_middleware = None
    _exception_middleware = None
    _middleware_chain = None

    def load_middleware(self, is_async=False):
        
        self._view_middleware = []
        self._template_response_middleware = []
        self._exception_middleware = []

        get_response = self._get_response_async if is_async else self._get_response
        handler = convert_exception_to_response(get_response)
        handler_is_async = is_async
        for middleware_path in reversed(settings.MIDDLEWARE):
            middleware = import_string(middleware_path)
            middleware_can_sync = getattr(middleware, "sync_capable", True)
            middleware_can_async = getattr(middleware, "async_capable", False)
            if not middleware_can_sync and not middleware_can_async:
                raise RuntimeError(
                    "Middleware %s must have at least one of "
                    "sync_capable/async_capable set to True." % middleware_path
                )
            elif not handler_is_async and middleware_can_sync:
                middleware_is_async = False
            else:
                middleware_is_async = middleware_can_async
            try:
                
                adapted_handler = self.adapt_method_mode(
                    middleware_is_async,
                    handler,
                    handler_is_async,
                    debug=settings.DEBUG,
                    name="middleware %s" % middleware_path,
                )
                mw_instance = middleware(adapted_handler)
            except MiddlewareNotUsed as exc:
                if settings.DEBUG:
                    if str(exc):
                        logger.debug("MiddlewareNotUsed(%r): %s", middleware_path, exc)
                    else:
                        logger.debug("MiddlewareNotUsed: %r", middleware_path)
                continue
            else:
                handler = adapted_handler

            if mw_instance is None:
                raise ImproperlyConfigured(
                    "Middleware factory %s returned None." % middleware_path
                )

            if hasattr(mw_instance, "process_view"):
                self._view_middleware.insert(
                    0,
                    self.adapt_method_mode(is_async, mw_instance.process_view),
                )
            if hasattr(mw_instance, "process_template_response"):
                self._template_response_middleware.append(
                    self.adapt_method_mode(
                        is_async, mw_instance.process_template_response
                    ),
                )
            if hasattr(mw_instance, "process_exception"):
                
                
                self._exception_middleware.append(
                    self.adapt_method_mode(False, mw_instance.process_exception),
                )

            handler = convert_exception_to_response(mw_instance)
            handler_is_async = middleware_is_async

        
        handler = self.adapt_method_mode(is_async, handler, handler_is_async)
        
        
        self._middleware_chain = handler

    def adapt_method_mode(
        self,
        is_async,
        method,
        method_is_async=None,
        debug=False,
        name=None,
    ):
        
        if method_is_async is None:
            method_is_async = iscoroutinefunction(method)
        if debug and not name:
            name = name or "method %s()" % method.__qualname__
        if is_async:
            if not method_is_async:
                if debug:
                    logger.debug("Synchronous handler adapted for %s.", name)
                return sync_to_async(method, thread_sensitive=True)
        elif method_is_async:
            if debug:
                logger.debug("Asynchronous handler adapted for %s.", name)
            return async_to_sync(method)
        return method

    def get_response(self, request):
        
        
        set_urlconf(settings.ROOT_URLCONF)
        response = self._middleware_chain(request)
        response._resource_closers.append(request.close)
        if response.status_code >= 400:
            log_response(
                "%s: %s",
                response.reason_phrase,
                request.path,
                response=response,
                request=request,
            )
        return response

    async def get_response_async(self, request):
        
        
        set_urlconf(settings.ROOT_URLCONF)
        response = await self._middleware_chain(request)
        response._resource_closers.append(request.close)
        if response.status_code >= 400:
            await sync_to_async(log_response, thread_sensitive=False)(
                "%s: %s",
                response.reason_phrase,
                request.path,
                response=response,
                request=request,
            )
        return response

    def _get_response(self, request):
        
        response = None
        callback, callback_args, callback_kwargs = self.resolve_request(request)

        
        for middleware_method in self._view_middleware:
            response = middleware_method(
                request, callback, callback_args, callback_kwargs
            )
            if response:
                break

        if response is None:
            wrapped_callback = self.make_view_atomic(callback)
            
            if iscoroutinefunction(wrapped_callback):
                wrapped_callback = async_to_sync(wrapped_callback)
            try:
                response = wrapped_callback(request, *callback_args, **callback_kwargs)
            except Exception as e:
                response = self.process_exception_by_middleware(e, request)
                if response is None:
                    raise

        
        self.check_response(response, callback)

        
        
        if hasattr(response, "render") and callable(response.render):
            for middleware_method in self._template_response_middleware:
                response = middleware_method(request, response)
                
                
                self.check_response(
                    response,
                    middleware_method,
                    name="%s.process_template_response"
                    % (middleware_method.__self__.__class__.__name__,),
                )
            try:
                response = response.render()
            except Exception as e:
                response = self.process_exception_by_middleware(e, request)
                if response is None:
                    raise

        return response

    async def _get_response_async(self, request):
        
        response = None
        callback, callback_args, callback_kwargs = self.resolve_request(request)

        
        for middleware_method in self._view_middleware:
            response = await middleware_method(
                request, callback, callback_args, callback_kwargs
            )
            if response:
                break

        if response is None:
            wrapped_callback = self.make_view_atomic(callback)
            
            if not iscoroutinefunction(wrapped_callback):
                wrapped_callback = sync_to_async(
                    wrapped_callback, thread_sensitive=True
                )
            try:
                response = await wrapped_callback(
                    request, *callback_args, **callback_kwargs
                )
            except Exception as e:
                response = await sync_to_async(
                    self.process_exception_by_middleware,
                    thread_sensitive=True,
                )(e, request)
                if response is None:
                    raise

        
        self.check_response(response, callback)

        
        
        if hasattr(response, "render") and callable(response.render):
            for middleware_method in self._template_response_middleware:
                response = await middleware_method(request, response)
                
                
                self.check_response(
                    response,
                    middleware_method,
                    name="%s.process_template_response"
                    % (middleware_method.__self__.__class__.__name__,),
                )
            try:
                if iscoroutinefunction(response.render):
                    response = await response.render()
                else:
                    response = await sync_to_async(
                        response.render, thread_sensitive=True
                    )()
            except Exception as e:
                response = await sync_to_async(
                    self.process_exception_by_middleware,
                    thread_sensitive=True,
                )(e, request)
                if response is None:
                    raise

        
        if asyncio.iscoroutine(response):
            raise RuntimeError("Response is still a coroutine.")
        return response

    def resolve_request(self, request):
        
        
        if hasattr(request, "urlconf"):
            urlconf = request.urlconf
            set_urlconf(urlconf)
            resolver = get_resolver(urlconf)
        else:
            resolver = get_resolver()
        
        resolver_match = resolver.resolve(request.path_info)
        request.resolver_match = resolver_match
        return resolver_match

    def check_response(self, response, callback, name=None):
        
        if not (response is None or asyncio.iscoroutine(response)):
            return
        if not name:
            if isinstance(callback, types.FunctionType):  
                name = "The view %s.%s" % (callback.__module__, callback.__name__)
            else:  
                name = "The view %s.%s.__call__" % (
                    callback.__module__,
                    callback.__class__.__name__,
                )
        if response is None:
            raise ValueError(
                "%s didn't return an HttpResponse object. It returned None "
                "instead." % name
            )
        elif asyncio.iscoroutine(response):
            raise ValueError(
                "%s didn't return an HttpResponse object. It returned an "
                "unawaited coroutine instead. You may need to add an 'await' "
                "into your view." % name
            )

    

    def make_view_atomic(self, view):
        non_atomic_requests = getattr(view, "_non_atomic_requests", set())
        for alias, settings_dict in connections.settings.items():
            if settings_dict["ATOMIC_REQUESTS"] and alias not in non_atomic_requests:
                if iscoroutinefunction(view):
                    raise RuntimeError(
                        "You cannot use ATOMIC_REQUESTS with async views."
                    )
                view = transaction.atomic(using=alias)(view)
        return view

    def process_exception_by_middleware(self, exception, request):
        
        for middleware_method in self._exception_middleware:
            response = middleware_method(request, exception)
            if response:
                return response
        return None


def reset_urlconf(sender, **kwargs):
    
    set_urlconf(None)


request_finished.connect(reset_urlconf)