diff --git a/radicale/app/__init__.py b/radicale/app/__init__.py index 1303dc9f..0a93dc2c 100644 --- a/radicale/app/__init__.py +++ b/radicale/app/__init__.py @@ -36,6 +36,7 @@ import pprint import pstats import random import time +import traceback import zlib from http import client from typing import Iterable, List, Mapping, Tuple, Union @@ -574,6 +575,10 @@ class Application(ApplicationPartDelete, ApplicationPartHead, except PermissionError as e: logger.error("PermissionError: %s", e) status, headers, answer, xml_request = httputils.INTERNAL_SERVER_ERROR + except Exception as e: + logger.error("Exception: %s", e) + logging.error("%s", traceback.format_exc()) + status, headers, answer, xml_request = httputils.INTERNAL_SERVER_ERROR # Profiling if self._profiling_per_request: diff --git a/radicale/app/options.py b/radicale/app/options.py index 9e347de2..bcb3663e 100644 --- a/radicale/app/options.py +++ b/radicale/app/options.py @@ -3,7 +3,7 @@ # Copyright © 2008 Pascal Halter # Copyright © 2008-2017 Guillaume Ayoub # Copyright © 2017-2021 Unrud -# Copyright © 2025-2025 Peter Bieringer +# Copyright © 2025-2026 Peter Bieringer # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,6 +18,7 @@ # You should have received a copy of the GNU General Public License # along with Radicale. If not, see . +import os from http import client from radicale import httputils, types @@ -33,4 +34,7 @@ class ApplicationPartOptions(ApplicationBase): "Allow": ", ".join( name[3:] for name in dir(self) if name.startswith("do_")), "DAV": httputils.DAV_HEADERS} + if 'PYTEST_VERSION' in os.environ and 'PYTEST_RADICALE_RAISE_GENERIC_ERROR' in os.environ: + # trigger special test case + raise ValueError('PYTEST_RADICALE_RAISE_GENERIC_ERROR') return client.OK, headers, None, None diff --git a/radicale/tests/test_base.py b/radicale/tests/test_base.py index d17f3d15..b36497dd 100644 --- a/radicale/tests/test_base.py +++ b/radicale/tests/test_base.py @@ -720,6 +720,24 @@ permissions: RrWw""") _, headers, _ = self.request("OPTIONS", "/", check=200) assert "DAV" in headers + def test_server_error_delay_basic(self) -> None: + """Trigger a server error and check delay.""" + delay = .3 + delay_min = delay * 0.9 # no random jitter during test + delay_max = delay + 0.2 # no random jitter during test + if sys.platform == "darwin": # no reliable sleep times + delay_max = delay_max * 1.5 + + self.configure({"server": {"delay_on_error": delay}}) + os.environ["PYTEST_RADICALE_RAISE_GENERIC_ERROR"] = "1" + time_begin = datetime.datetime.now() + _, headers, answer = self.request("OPTIONS", "/", check=500) + time_end = datetime.datetime.now() + time_delta = (time_end - time_begin).total_seconds() + assert time_delta > delay_min + assert time_delta < delay_max + del os.environ["PYTEST_RADICALE_RAISE_GENERIC_ERROR"] + def test_delete_collection(self) -> None: """Delete a collection.""" self.mkcalendar("/calendar.ics/")