diff --git a/mywebdav/main.py b/mywebdav/main.py index 9f3b211..c2648fc 100644 --- a/mywebdav/main.py +++ b/mywebdav/main.py @@ -2,9 +2,9 @@ import argparse import uvicorn import logging from contextlib import asynccontextmanager -from fastapi import FastAPI, Request, HTTPException +from fastapi import FastAPI, Request, HTTPException, status from fastapi.staticfiles import StaticFiles -from fastapi.responses import HTMLResponse, JSONResponse +from fastapi.responses import HTMLResponse, JSONResponse, Response from tortoise.contrib.fastapi import register_tortoise from .settings import settings from .routers import ( @@ -131,9 +131,22 @@ async def http_exception_handler(request: Request, exc: HTTPException): logger.error( f"HTTPException: {exc.status_code} - {exc.detail} for URL: {request.url}" ) + + headers = exc.headers + + # For WebDAV authentication challenges, we must return the headers + # from the exception and an empty body. A JSON body will confuse WebDAV clients. + if request.url.path.startswith("/webdav") and exc.status_code == status.HTTP_401_UNAUTHORIZED: + return Response(status_code=exc.status_code, headers=headers) + + # For other WebDAV errors, it's better to return a text body than JSON + if request.url.path.startswith("/webdav"): + return Response(content=exc.detail, status_code=exc.status_code, headers=headers) + return JSONResponse( status_code=exc.status_code, content=ErrorResponse(code=exc.status_code, message=exc.detail).model_dump(), + headers=headers, ) diff --git a/tests/test_webdav.py b/tests/test_webdav.py index 2e0fab1..1260b86 100644 --- a/tests/test_webdav.py +++ b/tests/test_webdav.py @@ -76,6 +76,8 @@ async def test_webdav_propfind_root_unauthorized(): ) assert response.status_code == status.HTTP_401_UNAUTHORIZED + assert "WWW-Authenticate" in response.headers + assert 'Basic realm="MyWebdav WebDAV"' in response.headers["WWW-Authenticate"] @pytest.mark.asyncio