From 966d524dca3cbc7e16b38a6d74d3d8fc4caba74d Mon Sep 17 00:00:00 2001 From: kqjy Date: Sun, 22 Mar 2026 00:04:55 +0800 Subject: [PATCH] Fix 0-byte uploads caused by Granian stripping Expect header and missing CONTENT_LENGTH for chunked transfers --- app/__init__.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/__init__.py b/app/__init__.py index e9e7b71..50f85f4 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -18,6 +18,8 @@ from flask_cors import CORS from flask_wtf.csrf import CSRFError from werkzeug.middleware.proxy_fix import ProxyFix +import io + from .access_logging import AccessLoggingService from .operation_metrics import OperationMetricsCollector, classify_endpoint from .compression import GzipMiddleware @@ -44,6 +46,20 @@ from .website_domains import WebsiteDomainStore _request_counter = itertools.count(1) +class _ChunkedTransferMiddleware: + def __init__(self, app): + self.app = app + + def __call__(self, environ, start_response): + transfer_encoding = environ.get("HTTP_TRANSFER_ENCODING", "") + if "chunked" in transfer_encoding.lower() and "CONTENT_LENGTH" not in environ: + raw = environ["wsgi.input"] + body = raw.read() + environ["wsgi.input"] = io.BytesIO(body) + environ["CONTENT_LENGTH"] = str(len(body)) + return self.app(environ, start_response) + + def _migrate_config_file(active_path: Path, legacy_paths: List[Path]) -> Path: """Migrate config file from legacy locations to the active path. @@ -110,6 +126,8 @@ def create_app( if app.config.get("ENABLE_GZIP", True): app.wsgi_app = GzipMiddleware(app.wsgi_app, compression_level=6) + app.wsgi_app = _ChunkedTransferMiddleware(app.wsgi_app) + _configure_cors(app) _configure_logging(app)