From ac66f3632c11d0a0d27571efa52693779a78c2a4 Mon Sep 17 00:00:00 2001 From: Yusur Princeps Date: Sun, 10 Aug 2025 10:48:32 +0200 Subject: [PATCH] bugfix to negotiate(), port to Flask --- src/suou/flask.py | 15 ++++++++++++++- src/suou/quart.py | 9 ++++----- src/suou/sass.py | 4 ++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/suou/flask.py b/src/suou/flask.py index f097c8e..1a2ffef 100644 --- a/src/suou/flask.py +++ b/src/suou/flask.py @@ -16,6 +16,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. from typing import Any from flask import Flask, abort, current_app, g, request + +from suou.http import WantsContentType from .i18n import I18n from .configparse import ConfigOptions from .dorks import SENSITIVE_ENDPOINTS @@ -82,8 +84,19 @@ def harden(app: Flask): return app +def negotiate() -> WantsContentType: + """ + Return an appropriate MIME type for the sake of content negotiation. + """ + if any(request.path.startswith(f'/{p.strip('/')}/') for p in current_app.config.get('REST_PATHS', [])): + return WantsContentType.JSON + elif request.user_agent.string.startswith('Mozilla/'): + return WantsContentType.HTML + else: + return request.accept_mimetypes.best_match([WantsContentType.PLAIN, WantsContentType.JSON, WantsContentType.HTML]) + # Optional dependency: do not import into __init__.py -__all__ = ('add_context_from_config', 'add_i18n', 'get_flask_conf', 'harden') +__all__ = ('add_context_from_config', 'add_i18n', 'get_flask_conf', 'harden', 'negotiate') diff --git a/src/suou/quart.py b/src/suou/quart.py index 6f393bc..36b4a5f 100644 --- a/src/suou/quart.py +++ b/src/suou/quart.py @@ -16,8 +16,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. from __future__ import annotations -from flask import current_app -from quart import Quart, request, g +from quart import current_app, Quart, request, g from quart_schema import QuartSchema from suou.http import WantsContentType @@ -58,14 +57,14 @@ def add_i18n(app: Quart, i18n: I18n, var_name: str = 'T', *, def negotiate() -> WantsContentType: """ - Return an appropriate MIME type for content negotiation. + Return an appropriate MIME type for the sake of content negotiation. """ - if 'application/json' in request.accept_mimetypes or any(request.path.startswith(f'/{p.strip('/')}/') for p in current_app.config.get('REST_PATHS')): + if any(request.path.startswith(f'/{p.strip('/')}/') for p in current_app.config.get('REST_PATHS', [])): return WantsContentType.JSON elif request.user_agent.string.startswith('Mozilla/'): return WantsContentType.HTML else: - return WantsContentType.PLAIN + return request.accept_mimetypes.best_match([WantsContentType.PLAIN, WantsContentType.JSON, WantsContentType.HTML]) def add_rest(app: Quart, *bases: str, **kwargs) -> QuartSchema: diff --git a/src/suou/sass.py b/src/suou/sass.py index 7c2b82d..f727f9d 100644 --- a/src/suou/sass.py +++ b/src/suou/sass.py @@ -87,7 +87,7 @@ class SassAsyncMiddleware(_MiddlewareFactory): 'type': 'http.response.start', 'status': self.error_status, 'headers': [ - 'Content-Type: text/css; charset=utf-8' + ('Content-Type', 'text/css; charset=utf-8'), ] }) await send({ @@ -124,7 +124,7 @@ class SassAsyncMiddleware(_MiddlewareFactory): 'type': 'http.response.start', 'status': 200, 'headers': [ - 'Content-Type: text/css; charset=utf-8' + ('Content-Type', 'text/css; charset=utf-8'), ] }) async for chunk in _read_file(os.path.join(package_dir, result)):