diff --git a/py2http/default_configs.py b/py2http/default_configs.py index f0c51f9..baab228 100644 --- a/py2http/default_configs.py +++ b/py2http/default_configs.py @@ -150,7 +150,14 @@ def bottle_error_handler(error: Exception): def flask_error_handler(error: Exception): - raise NotImplementedError() + """The ``(body, status, headers)`` a Flask view returns for an error, mapped like + the bottle handler (``OPAQUE_ERRORS`` hides the text of a 500).""" + message = str(error) + status = error_status(error) + if status == 500 and os.getenv("OPAQUE_ERRORS", None): + message = "Internal server error" + status_line = f"400 {type(error).__name__}" if status == 400 else status + return {"error": message}, status_line, _status_headers(status) def default_error_handler(error: Exception): diff --git a/py2http/tests/test_error_statuses.py b/py2http/tests/test_error_statuses.py index ad43571..e71e9ef 100644 --- a/py2http/tests/test_error_statuses.py +++ b/py2http/tests/test_error_statuses.py @@ -63,3 +63,39 @@ def test_aiohttp_error_handler(error, status): assert caught.value.status == status if status == 500: assert "internal detail" not in caught.value.text + + +@pytest.mark.parametrize("error, status", CASES) +def test_flask_error_handler(error, status, monkeypatch): + flask = pytest.importorskip("flask") + monkeypatch.delenv("PY2HTTP_WWW_AUTHENTICATE", raising=False) + monkeypatch.setenv("PY2HTTP_FRAMEWORK", "flask") + from py2http.default_configs import default_error_handler + + app = flask.Flask(__name__) + + @app.route("/fail") + def fail(): + return default_error_handler(error) + + response = app.test_client().get("/fail") + assert response.status_code == status + assert set(response.get_json()) == {"error"} + if status == 400: # as the bottle handler: the status line names the error + assert response.status == f"400 {type(error).__name__}" + if status == 401: + assert response.headers["WWW-Authenticate"] == "Bearer" + else: + assert "WWW-Authenticate" not in response.headers + if status == 500: + assert "internal detail" in response.get_json()["error"] + + +def test_flask_opaque_errors_hides_500_text(monkeypatch): + flask = pytest.importorskip("flask") + monkeypatch.setenv("OPAQUE_ERRORS", "1") + from py2http.default_configs import flask_error_handler + + app = flask.Flask(__name__) + app.add_url_rule("/f", "f", lambda: flask_error_handler(KeyError("internal detail"))) + assert app.test_client().get("/f").get_json() == {"error": "Internal server error"}