diff --git a/openhands/server/app.py b/openhands/server/app.py index 151e16eb3d56..ea95bcf3e0b4 100644 --- a/openhands/server/app.py +++ b/openhands/server/app.py @@ -12,9 +12,9 @@ from openhands import __version__ from openhands.server.middleware import ( AttachConversationMiddleware, + CacheControlMiddleware, InMemoryRateLimiter, LocalhostCORSMiddleware, - NoCacheMiddleware, RateLimitMiddleware, ) from openhands.server.routes.conversation import app as conversation_api_router @@ -50,7 +50,7 @@ async def _lifespan(app: FastAPI): allow_headers=['*'], ) -app.add_middleware(NoCacheMiddleware) +app.add_middleware(CacheControlMiddleware) app.add_middleware( RateLimitMiddleware, rate_limiter=InMemoryRateLimiter(requests=10, seconds=1) ) diff --git a/openhands/server/middleware.py b/openhands/server/middleware.py index a7b30e479bbc..eb0e4ef3f28b 100644 --- a/openhands/server/middleware.py +++ b/openhands/server/middleware.py @@ -8,6 +8,7 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from starlette.middleware.base import BaseHTTPMiddleware +from starlette.requests import Request as StarletteRequest from starlette.types import ASGIApp from openhands.server.shared import session_manager @@ -36,14 +37,17 @@ def is_allowed_origin(self, origin: str) -> bool: return super().is_allowed_origin(origin) -class NoCacheMiddleware(BaseHTTPMiddleware): +class CacheControlMiddleware(BaseHTTPMiddleware): """ Middleware to disable caching for all routes by adding appropriate headers """ async def dispatch(self, request, call_next): response = await call_next(request) - if not request.url.path.startswith('/assets'): + if request.url.path.startswith('/assets'): + # The content of the assets directory has fingerprinted file names so we cache aggressively + response.headers['Cache-Control'] = 'public, max-age=2592000, immutable' + else: response.headers['Cache-Control'] = ( 'no-cache, no-store, must-revalidate, max-age=0' ) @@ -95,7 +99,9 @@ def __init__(self, app: ASGIApp, rate_limiter: InMemoryRateLimiter): super().__init__(app) self.rate_limiter = rate_limiter - async def dispatch(self, request, call_next): + async def dispatch(self, request: StarletteRequest, call_next): + if not self.is_rate_limited_request(request): + return await call_next(request) ok = await self.rate_limiter(request) if not ok: return JSONResponse( @@ -105,6 +111,12 @@ async def dispatch(self, request, call_next): ) return await call_next(request) + def is_rate_limited_request(self, request: StarletteRequest): + if request.url.path.startswith('/assets'): + return False + # Put Other non rate limited checks here + return True + class AttachConversationMiddleware(SessionMiddlewareInterface): def __init__(self, app):