diff --git a/httpbin/core.py b/httpbin/core.py index 6b6e5c0..3f83c39 100644 --- a/httpbin/core.py +++ b/httpbin/core.py @@ -204,6 +204,15 @@ def view_deflate_encoded_content(): 'origin', 'headers', method=request.method, deflated=True)) +@app.route('/brotli') +@filters.brotli +def view_brotli_encoded_content(): + """Returns Brotli-Encoded Data.""" + + return jsonify(get_dict( + 'origin', 'headers', method=request.method, brotli=True)) + + @app.route('/redirect/') def redirect_n_times(n): """302 Redirects n times.""" diff --git a/httpbin/filters.py b/httpbin/filters.py index e18382c..c6268b6 100644 --- a/httpbin/filters.py +++ b/httpbin/filters.py @@ -10,6 +10,8 @@ This module provides response filter decorators. import gzip as gzip2 import zlib +import brotli as _brotli + from six import BytesIO from decimal import Decimal from time import time as now @@ -88,3 +90,26 @@ def deflate(f, *args, **kwargs): return data return deflated_data + + +@decorator +def brotli(f, *args, **kwargs): + """Brotli Flask Response Decorator""" + + data = f(*args, **kwargs) + + if isinstance(data, Response): + content = data.data + else: + content = data + + deflated_data = _brotli.compress(content) + + if isinstance(data, Response): + data.data = deflated_data + data.headers['Content-Encoding'] = 'br' + data.headers['Content-Length'] = str(len(data.data)) + + return data + + return deflated_data diff --git a/setup.py b/setup.py index 5542ad8..76f4ecf 100644 --- a/setup.py +++ b/setup.py @@ -32,5 +32,7 @@ setup( ], packages=find_packages(), include_package_data = True, # include files listed in MANIFEST.in - install_requires=['Flask','MarkupSafe','decorator','itsdangerous','six'], + install_requires=[ + 'Flask','MarkupSafe','decorator','itsdangerous','six','brotlipy' + ], ) diff --git a/test_httpbin.py b/test_httpbin.py index 0e9a210..99c352c 100755 --- a/test_httpbin.py +++ b/test_httpbin.py @@ -236,6 +236,10 @@ class HttpbinTestCase(unittest.TestCase): response = self.app.get('/gzip') self.assertEqual(response.status_code, 200) + def test_brotli(self): + response = self.app.get('/brotli') + self.assertEqual(response.status_code, 200) + def test_digest_auth_with_wrong_password(self): auth_header = 'Digest username="user",realm="wrong",nonce="wrong",uri="/digest-auth/user/passwd/MD5",response="wrong",opaque="wrong"' response = self.app.get(