| 577 | |
| 578 | |
| 579 | class Router: |
| 580 | def __init__( |
| 581 | self, |
| 582 | routes: Sequence[BaseRoute] | None = None, |
| 583 | redirect_slashes: bool = True, |
| 584 | default: ASGIApp | None = None, |
| 585 | # the generic to Lifespan[AppType] is the type of the top level application |
| 586 | # which the router cannot know statically, so we use Any |
| 587 | lifespan: Lifespan[Any] | None = None, |
| 588 | *, |
| 589 | middleware: Sequence[Middleware] | None = None, |
| 590 | max_body_size: int | None = None, |
| 591 | ) -> None: |
| 592 | self.routes = [] if routes is None else list(routes) |
| 593 | self.redirect_slashes = redirect_slashes |
| 594 | self.default = self.not_found if default is None else default |
| 595 | |
| 596 | if lifespan is None: |
| 597 | self.lifespan_context: Lifespan[Any] = _DefaultLifespan(self) |
| 598 | |
| 599 | elif inspect.isasyncgenfunction(lifespan): |
| 600 | warnings.warn( |
| 601 | "async generator function lifespans are deprecated, " |
| 602 | "use an @contextlib.asynccontextmanager function instead", |
| 603 | StarletteDeprecationWarning, |
| 604 | ) |
| 605 | self.lifespan_context = asynccontextmanager(lifespan) |
| 606 | elif inspect.isgeneratorfunction(lifespan): |
| 607 | warnings.warn( |
| 608 | "generator function lifespans are deprecated, use an @contextlib.asynccontextmanager function instead", |
| 609 | StarletteDeprecationWarning, |
| 610 | ) |
| 611 | self.lifespan_context = _wrap_gen_lifespan_context(lifespan) |
| 612 | else: |
| 613 | self.lifespan_context = lifespan |
| 614 | |
| 615 | self.middleware_stack = self.app |
| 616 | if middleware: |
| 617 | for cls, args, kwargs in reversed(middleware): |
| 618 | self.middleware_stack = cls(self.middleware_stack, *args, **kwargs) |
| 619 | if max_body_size is not None: |
| 620 | self.middleware_stack = RequestBodyLimitMiddleware(self.middleware_stack, max_body_size=max_body_size) |
| 621 | |
| 622 | async def not_found(self, scope: Scope, receive: Receive, send: Send) -> None: |
| 623 | if scope["type"] == "websocket": |
| 624 | websocket_close = WebSocketClose() |
| 625 | await websocket_close(scope, receive, send) |
| 626 | return |
| 627 | |
| 628 | # If we're running inside a starlette application then raise an |
| 629 | # exception, so that the configurable exception handler can deal with |
| 630 | # returning the response. For plain ASGI apps, just return the response. |
| 631 | if "app" in scope: |
| 632 | raise HTTPException(status_code=404) |
| 633 | else: |
| 634 | response = PlainTextResponse("Not Found", status_code=404) |
| 635 | await response(scope, receive, send) |
| 636 |
no outgoing calls