From dafd75966f1f4bd79fe9179e3fd7f3be756613bd Mon Sep 17 00:00:00 2001 From: Tobias Gruetzmacher Date: Sun, 18 May 2025 22:16:07 +0200 Subject: [PATCH] Add support for a zstandard compressed endpoint --- httpbin/core.py | 17 +++++++++++++++++ httpbin/filters.py | 23 +++++++++++++++++++++++ httpbin/templates/httpbin.1.html | 1 + pyproject.toml | 1 + tests/test_httpbin.py | 4 ++++ 5 files changed, 46 insertions(+) diff --git a/httpbin/core.py b/httpbin/core.py index e23cc86b..67f2b523 100644 --- a/httpbin/core.py +++ b/httpbin/core.py @@ -541,6 +541,23 @@ def view_brotli_encoded_content(): return jsonify(get_dict("origin", "headers", method=request.method, brotli=True)) +@app.route("/zstd") +@filters.zstd +def view_zstandard_encoded_content(): + """Returns zstandard-encoded data. + --- + tags: + - Response formats + produces: + - application/json + responses: + 200: + description: zstandard-encoded data. + """ + + return jsonify(get_dict("origin", "headers", method=request.method, zstandard=True)) + + @app.route("/redirect/") def redirect_n_times(n): """302 Redirects n times. diff --git a/httpbin/filters.py b/httpbin/filters.py index 4deeaaad..cc91e46e 100644 --- a/httpbin/filters.py +++ b/httpbin/filters.py @@ -11,6 +11,7 @@ import zlib import brotlicffi as _brotli +import zstandard from six import BytesIO from decimal import Decimal @@ -113,3 +114,25 @@ def brotli(f, *args, **kwargs): return data return deflated_data + +@decorator +def zstd(f, *args, **kwargs): + """zstandard Flask Response Decorator""" + + data = f(*args, **kwargs) + + if isinstance(data, Response): + content = data.data + else: + content = data + + deflated_data = zstandard.ZstdCompressor().compress(content) + + if isinstance(data, Response): + data.data = deflated_data + data.headers['Content-Encoding'] = 'zstd' + data.headers['Content-Length'] = str(len(data.data)) + + return data + + return deflated_data diff --git a/httpbin/templates/httpbin.1.html b/httpbin/templates/httpbin.1.html index ceafd973..84b6c5e0 100644 --- a/httpbin/templates/httpbin.1.html +++ b/httpbin/templates/httpbin.1.html @@ -21,6 +21,7 @@

ENDPOINTS

  • /gzip Returns gzip-encoded data.
  • /deflate Returns deflate-encoded data.
  • /brotli Returns brotli-encoded data.
  • +
  • /zstd Returns zstandard-encoded data.
  • /status/:code Returns given HTTP Status code.
  • /response-headers?key=val Returns given response headers.
  • /redirect/:n 302 Redirects n times.
  • diff --git a/pyproject.toml b/pyproject.toml index 894c5509..368c1790 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ dependencies = [ 'importlib-metadata; python_version<"3.8"', "six", "werkzeug >= 2.2.2", + "zstandard >= 0.20.0", ] [project.optional-dependencies] diff --git a/tests/test_httpbin.py b/tests/test_httpbin.py index 6b751245..2cc0b703 100755 --- a/tests/test_httpbin.py +++ b/tests/test_httpbin.py @@ -303,6 +303,10 @@ def test_brotli(self): response = self.app.get('/brotli') self.assertEqual(response.status_code, 200) + def test_zstandard(self): + response = self.app.get('/zstd') + self.assertEqual(response.status_code, 200) + def test_bearer_auth(self): token = 'abcd1234' response = self.app.get(