mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-09-17 07:07:58 +00:00
Fix: correct text/stream compression workaround (#14064)
This commit is contained in:
@@ -1,8 +1,23 @@
|
||||
from compression_middleware.middleware import CompressionMiddleware
|
||||
from django.conf import settings
|
||||
|
||||
from paperless import version
|
||||
|
||||
|
||||
class StreamAwareCompressionMiddleware(CompressionMiddleware):
|
||||
"""
|
||||
Bypasses compression for server-sent streams (text/event-stream).
|
||||
|
||||
See https://github.com/friedelwolff/django-compression-middleware/pull/7
|
||||
"""
|
||||
|
||||
def process_response(self, request, response):
|
||||
content_type = response.headers.get("Content-Type", "")
|
||||
if content_type.startswith("text/event-stream"):
|
||||
return response
|
||||
return super().process_response(request, response)
|
||||
|
||||
|
||||
class ApiVersionMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
@@ -10,7 +10,6 @@ from pathlib import Path
|
||||
from typing import Final
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from compression_middleware.middleware import CompressionMiddleware
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from dotenv import load_dotenv
|
||||
@@ -201,22 +200,10 @@ MIDDLEWARE = [
|
||||
"allauth.account.middleware.AccountMiddleware",
|
||||
]
|
||||
|
||||
# Optional to enable compression
|
||||
# Optional to enable compression. The subclass leaves server-sent events
|
||||
# uncompressed; see paperless.middleware.StreamAwareCompressionMiddleware.
|
||||
if get_bool_from_env("PAPERLESS_ENABLE_COMPRESSION", "yes"): # pragma: no cover
|
||||
MIDDLEWARE.insert(0, "compression_middleware.middleware.CompressionMiddleware")
|
||||
|
||||
# Workaround to not compress streaming responses (e.g. chat).
|
||||
# See https://github.com/friedelwolff/django-compression-middleware/pull/7
|
||||
original_process_response = CompressionMiddleware.process_response
|
||||
|
||||
|
||||
def patched_process_response(self, request, response):
|
||||
if getattr(request, "compress_exempt", False):
|
||||
return response
|
||||
return original_process_response(self, request, response)
|
||||
|
||||
|
||||
CompressionMiddleware.process_response = patched_process_response
|
||||
MIDDLEWARE.insert(0, "paperless.middleware.StreamAwareCompressionMiddleware")
|
||||
|
||||
ROOT_URLCONF = "paperless.urls"
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
from django.http import HttpResponse
|
||||
from django.http import StreamingHttpResponse
|
||||
from django.test import RequestFactory
|
||||
from django.test import TestCase
|
||||
|
||||
from paperless.middleware import StreamAwareCompressionMiddleware
|
||||
|
||||
|
||||
class TestStreamAwareCompressionMiddleware(TestCase):
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
self.factory = RequestFactory()
|
||||
self.middleware = StreamAwareCompressionMiddleware(lambda request: None)
|
||||
|
||||
def _request(self):
|
||||
return self.factory.get(
|
||||
"/api/documents/chat/",
|
||||
HTTP_ACCEPT_ENCODING="gzip, deflate, br, zstd",
|
||||
)
|
||||
|
||||
def test_event_stream_is_not_compressed(self) -> None:
|
||||
"""
|
||||
GIVEN:
|
||||
- A server-sent event response produced chunk by chunk
|
||||
WHEN:
|
||||
- The compression middleware processes it
|
||||
THEN:
|
||||
- It is passed through unencoded, one wire chunk per source chunk
|
||||
"""
|
||||
chunks = [f"token{i} ".encode() for i in range(40)]
|
||||
response = StreamingHttpResponse(
|
||||
iter(chunks),
|
||||
content_type="text/event-stream",
|
||||
)
|
||||
|
||||
response = self.middleware.process_response(self._request(), response)
|
||||
|
||||
assert not response.has_header("Content-Encoding")
|
||||
assert list(response.streaming_content) == chunks
|
||||
|
||||
def test_regular_response_is_still_compressed(self) -> None:
|
||||
"""
|
||||
GIVEN:
|
||||
- An ordinary response large enough to be worth compressing
|
||||
WHEN:
|
||||
- The compression middleware processes it
|
||||
THEN:
|
||||
- It is compressed as before
|
||||
"""
|
||||
response = HttpResponse(b"a" * 5000, content_type="application/json")
|
||||
|
||||
response = self.middleware.process_response(self._request(), response)
|
||||
|
||||
assert response.has_header("Content-Encoding")
|
||||
Reference in New Issue
Block a user