Handles returning 500 responses when a server error occurs. If 'debug' is set, then traceback responses will be returned, otherwise the designated 'handler' will be called. This middleware class should generally be used to wrap *everything* else up, so that unhandled exception
| 125 | |
| 126 | |
| 127 | class ServerErrorMiddleware: |
| 128 | """ |
| 129 | Handles returning 500 responses when a server error occurs. |
| 130 | |
| 131 | If 'debug' is set, then traceback responses will be returned, |
| 132 | otherwise the designated 'handler' will be called. |
| 133 | |
| 134 | This middleware class should generally be used to wrap *everything* |
| 135 | else up, so that unhandled exceptions anywhere in the stack |
| 136 | always result in an appropriate 500 response. |
| 137 | """ |
| 138 | |
| 139 | def __init__( |
| 140 | self, |
| 141 | app: ASGIApp, |
| 142 | handler: ExceptionHandler | None = None, |
| 143 | debug: bool = False, |
| 144 | ) -> None: |
| 145 | self.app = app |
| 146 | self.handler = handler |
| 147 | self.debug = debug |
| 148 | |
| 149 | async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: |
| 150 | if scope["type"] != "http": |
| 151 | await self.app(scope, receive, send) |
| 152 | return |
| 153 | |
| 154 | response_started = False |
| 155 | |
| 156 | async def _send(message: Message) -> None: |
| 157 | nonlocal response_started, send |
| 158 | |
| 159 | if message["type"] == "http.response.start": |
| 160 | response_started = True |
| 161 | await send(message) |
| 162 | |
| 163 | try: |
| 164 | await self.app(scope, receive, _send) |
| 165 | except Exception as exc: |
| 166 | request = Request(scope) |
| 167 | if self.debug: |
| 168 | # In debug mode, return traceback responses. |
| 169 | response = await run_in_threadpool(self.debug_response, request, exc) |
| 170 | elif self.handler is None: |
| 171 | # Use our default 500 error handler. |
| 172 | response = self.error_response(request, exc) |
| 173 | else: |
| 174 | # Use an installed 500 error handler. |
| 175 | if is_async_callable(self.handler): |
| 176 | response = await self.handler(request, exc) # type: ignore[assignment, arg-type] |
| 177 | else: |
| 178 | response = await run_in_threadpool(self.handler, request, exc) # type: ignore[arg-type] |
| 179 | |
| 180 | if not response_started: |
| 181 | await response(scope, receive, send) |
| 182 | |
| 183 | # We always continue to raise the exception. |
| 184 | # This allows servers to log the error, or allows test clients |
no outgoing calls