Skip to content

refactor: asgimiddleware everywhere if possible #4055

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 131 commits into
base: v3.0
Choose a base branch
from

Conversation

euri10
Copy link
Contributor

@euri10 euri10 commented Mar 15, 2025

WIP

The middlewares are currently mostly based on either MiddlewareProtocol, AbstractMiddleware or AbstractAuthenticationMiddleware .

Their usage is spread around a few places:

  1. using some kwags on the Litestar object, for instance: Litestar(allowed_hosts=allowed_hosts_config)
  2. using the app_init kwargs on the Litestar object like in Litestar(on_app_init=[jwt_auth.on_app_init])
  3. using a plugin through the plugins kwarg on the Litestar object.
  4. using the middleware kwarg on the Litestar object trough the middleware property of the config, like for instance Litestar(middleware=[LoggingMiddlewareConfig().middleware]))
  5. Using the handler decorator via middleware like in @get("/", middleware=[jwt_auth.middleware])

The idea of this PR is to:

  1. switch them all to the shiny ASGIMiddleware (and BaseAuthenticationMiddleware which subclasses it)
  2. allow their usage through either point 3-4-5 above

I think a logical subsequent step would be to remove entirely point 1 and 2 from the Litestar object, but that may go too far idk.

Current state:

Class Parent Class Usage
CSRFMiddleware MiddlewareProtocol 1 > csrf_config
OpenTelemetryInstrumentationMiddleware AbstractMiddleware 3 and 4
CORSMiddleware AbstractMiddleware 1 > cors_config
AllowedHostsMiddleware AbstractMiddleware 1 > allowed_hosts
CompressionMiddleware AbstractMiddleware 1 > compression_config
LoggingMiddleware AbstractMiddleware 4
RateLimitMiddleware AbstractMiddleware 4
ResponseCacheMiddleware AbstractMiddleware 1 > response_cache_config and 4
SessionMiddleware AbstractMiddleware, Generic[BaseSessionBackendT] 4
PrometheusMiddleware AbstractMiddleware 4
JWTAuthenticationMiddleware AbstractAuthenticationMiddleware 2 and 5
SessionAuthMiddleware AbstractAuthenticationMiddleware 4

Also, xxxMiddleware is often tied to xxxConfig, but there are some inconsistencies I didn't touch like for instance:
LoggingMiddlewareConfig is in litestar/middleware/logging.py along with the LoggingMiddleware

BUT for others there is a litestar/config folder that contains the CSRFConfig for instance ie config and middleware are seperated,
I can totally understand the split but since now xxxConfig is just a config for xxxMiddleware then I'd feel it would be better all are on the same middleware folder in their respective middleware

@github-actions github-actions bot added area/asgi area/background-tasks area/channels area/ci This PR involves changes to the CI/Infra area/connection area/constants This PR involves changes to the constants area/contrib This PR involves changes to the contrib (Deprecated) area/controller area/datastructures area/dependencies This PR involves changes to the dependencies area/di area/docs This PR involves changes to the documentation area/dto This PR involves changes to the DTOs area/enums This PR involves changes to the enums area/events area/handlers This PR involves changes to the handlers area/kwargs area/layers area/logging area/middleware This PR involves changes to the middleware area/multipart area/openapi This PR involves changes to the OpenAPI schema area/params This PR involves changes to the params area/plugins This PR involves changes to the plugins area/private-api This PR involves changes to the privatized API area/response area/router area/security area/serialization area/signature labels Mar 15, 2025
Copy link
Member

@provinzkraut provinzkraut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I went through everything and it's looking quite good so far!

Comment on lines 205 to 215
if app.csrf_config:
asgi_handler = CSRFMiddleware(app=asgi_handler, config=app.csrf_config)

if app.compression_config:
asgi_handler = CompressionMiddleware(app=asgi_handler, config=app.compression_config)

if has_cached_route:
asgi_handler = ResponseCacheMiddleware(app=asgi_handler, config=app.response_cache_config)

if app.allowed_hosts:
asgi_handler = AllowedHostsMiddleware(app=asgi_handler, config=app.allowed_hosts)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want the change to be backwards compatible, this would have to stay in until we deprecate the configs entirely. You think that's feasible, or too much work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be doable, Im gonna work on that asap

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok this is interesing because putting this back made me found 3 tests that are still using csrf_config in test_openai/test_plugins

do we agree all tests should use the "new" way of setting this up and the "old" way is just tested by the deprecation messages ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's fun, those tests I missed revealed some regression with just using middleware, trying to fix that before the above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done @provinzkraut ;)

Comment on lines +92 to +94
# middleware=[SessionMiddleware(ServerSideSessionBackend(session_backend_config_memory)),
# SessionAuthMiddleware(session_auth)],
plugins=[SessionPlugin(session_auth)],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kept this now I remember to "showcase" the plugin is the same essentially as stacking the middlewares, now should you change their order and you're f**,

for middleware in handler_middleware:
if not isinstance(middleware, CORSMiddleware) and not isinstance(
middleware, OpenTelemetryInstrumentationMiddleware
):
asgi_handler = middleware(asgi_handler)

# we keep the 2.x behaviour but add a check to see if the middleware is already in the stack, and raise a deprecation warning
if app.csrf_config and not any(isinstance(middleware, CSRFMiddleware) for middleware in handler_middleware):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we move the deprecation warnings to Litestar.__init__? Feels a bit odd in here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although on second thought, maybe it's okay to have them here, where they're actually being used

@@ -262,7 +262,7 @@ def create_request_interceptor(csrf_config: CSRFConfig) -> str:
"""

if request.app.csrf_config:
c = request.app.csrf_config
c = request.app.csrf_config # pragma: no cover
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no coverage for these?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we dont use Litestar(csrf_config=...) anymore in the tests but I can tweak them so that it's using that branch

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mhm. As long as it's just deprecated, it should still be supported and therefore tested

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok modified

@@ -182,38 +183,61 @@ def build_route_middleware_stack(
Returns:
An ASGIApp that is composed of a "stack" of middlewares.
"""

from litestar.contrib.opentelemetry import OpenTelemetryInstrumentationMiddleware
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to base my hotreload plugin of this branch and I get:

    try:
        import opentelemetry  # noqa: F401
    except ImportError as e:
>       raise MissingDependencyException("opentelemetry") from e
E       litestar.exceptions.base_exceptions.MissingDependencyException: Package 'opentelemetry' is not installed but required. You can install it by running 'pip install litestar[opentelemetry]' to install litestar with the required extra or 'pip install opentelemetry' to install the package separately

so I start to think the private _patch_opentelemetry_middleware is designed to handle the case where opentelemetry isnt installed ?

Copy link

github-actions bot commented Apr 3, 2025

Documentation preview will be available shortly at https://litestar-org.github.io/litestar-docs-preview/4055

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/docs This PR involves changes to the documentation area/middleware This PR involves changes to the middleware area/plugins This PR involves changes to the plugins area/private-api This PR involves changes to the privatized API area/security pr/external pr/internal size: large Triage Required 🏥 This requires triage
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants