diff --git a/src/webserver.py b/src/webserver.py index f5d9ab1..985e528 100644 --- a/src/webserver.py +++ b/src/webserver.py @@ -274,9 +274,17 @@ class SnapshotsWebServer: with open(requested_file, "rb") as f: content = f.read() + # Set charset for text files + charset = None + if content_type.startswith( + ("text/", "application/json", "application/javascript") + ): + charset = "utf-8" + return web.Response( body=content, content_type=content_type, + charset=charset, headers={ "Cache-Control": "public, max-age=3600", "Last-Modified": datetime.fromtimestamp( @@ -320,9 +328,17 @@ class SnapshotsWebServer: with open(requested_file, "rb") as f: content = f.read() + # Set charset for text files + charset = None + if content_type.startswith( + ("text/", "application/json", "application/javascript") + ): + charset = "utf-8" + return web.Response( body=content, content_type=content_type, + charset=charset, headers={ "Cache-Control": "public, max-age=86400", # Cache assets for 24 hours "Last-Modified": datetime.fromtimestamp( @@ -342,10 +358,10 @@ class SnapshotsWebServer: suffix = file_path.suffix.lower() content_types = { - ".html": "text/html; charset=utf-8", - ".css": "text/css; charset=utf-8", - ".js": "application/javascript; charset=utf-8", - ".json": "application/json; charset=utf-8", + ".html": "text/html", + ".css": "text/css", + ".js": "application/javascript", + ".json": "application/json", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".png": "image/png", @@ -354,8 +370,8 @@ class SnapshotsWebServer: ".svg": "image/svg+xml", ".ico": "image/x-icon", ".pdf": "application/pdf", - ".txt": "text/plain; charset=utf-8", - ".log": "text/plain; charset=utf-8", + ".txt": "text/plain", + ".log": "text/plain", } return content_types.get(suffix, "application/octet-stream") @@ -366,14 +382,14 @@ class SnapshotsWebServer: app.router.add_get("/", self.index_handler) # Serve HTML files directly - app.router.add_get("/{filename:.+\.html}", self.file_handler) + app.router.add_get(r"/{filename:.+\.html}", self.file_handler) # Serve assets (images, CSS, JS, etc.) app.router.add_get("/assets/{path:.+}", self.assets_handler) # Serve other static files (logs, etc.) app.router.add_get( - "/{filename:.+\.(css|js|json|txt|log|ico)}", self.file_handler + r"/{filename:.+\.(css|js|json|txt|log|ico)}", self.file_handler ) async def create_app(self): @@ -383,20 +399,40 @@ class SnapshotsWebServer: # Setup routes self.setup_routes(app) - # Add middleware for logging + # Add error handling middleware + @web.middleware + async def error_middleware(request, handler): + try: + return await handler(request) + except web.HTTPException: + # Re-raise HTTP exceptions (like 404, 403, etc.) + raise + except Exception as e: + # Log unexpected errors and return 500 + self.logger.error( + f"Unexpected error for {request.method} {request.path}: {e}" + ) + return web.Response( + text="
An unexpected error occurred.
", + status=500, + content_type="text/html", + ) + + # Add logging middleware + @web.middleware async def logging_middleware(request, handler): start_time = datetime.now() # Get client IP address def get_client_ip(): - # Check for forwarded header first - forwarded = request.headers.get("X-Forwarded-For") - if forwarded: - return forwarded.split(",")[0].strip() - - # Try to get from transport try: - if request.transport: + # Check for forwarded header first + forwarded = request.headers.get("X-Forwarded-For") + if forwarded: + return forwarded.split(",")[0].strip() + + # Try to get from transport + if hasattr(request, "transport") and request.transport: peername = request.transport.get_extra_info("peername") if peername: return peername[0] @@ -427,6 +463,8 @@ class SnapshotsWebServer: ) raise + # Add middleware in correct order (error handling first, then logging) + app.middlewares.append(error_middleware) app.middlewares.append(logging_middleware) return app