Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deflate compression algorithm #8

Merged
merged 2 commits into from
Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![Coverage](https://coveralls.io/repos/libwilliam/flask-compress/badge.svg)](https://coveralls.io/github/libwilliam/flask-compress)
[![License](https://img.shields.io/pypi/l/flask-compress.svg)](https://github.com/libwilliam/flask-compress/blob/master/LICENSE.txt)

Flask-Compress allows you to easily compress your [Flask](http://flask.pocoo.org/) application's responses with gzip.
Flask-Compress allows you to easily compress your [Flask](http://flask.pocoo.org/) application's responses with gzip, deflate or brotli.

The preferred solution is to have a server (like [Nginx](http://wiki.nginx.org/Main)) automatically compress the static files for you. If you don't have that option Flask-Compress will solve the problem for you.

Expand Down Expand Up @@ -49,7 +49,7 @@ $ easy_install flask-compress

## Using Flask-Compress

Flask-Compress is incredibly simple to use. In order to start gzip'ing your Flask application's assets, the first thing to do is let Flask-Compress know about your [`flask.Flask`](http://flask.pocoo.org/docs/latest/api/#flask.Flask) application object.
Flask-Compress is incredibly simple to use. In order to start compressing your Flask application's assets, the first thing to do is let Flask-Compress know about your [`flask.Flask`](http://flask.pocoo.org/docs/latest/api/#flask.Flask) application object.

```python
from flask import Flask
Expand All @@ -73,7 +73,7 @@ def start_app():
return app
```

In terms of automatically compressing your assets using gzip, passing your [`flask.Flask`](http://flask.pocoo.org/docs/latest/api/#flask.Flask) object to the `flask_compress.Compress` object is all that needs to be done.
In terms of automatically compressing your assets, passing your [`flask.Flask`](http://flask.pocoo.org/docs/latest/api/#flask.Flask) object to the `flask_compress.Compress` object is all that needs to be done.


## Options
Expand All @@ -84,11 +84,12 @@ Within your Flask application's settings you can provide the following settings
| ------ | ----------- | ------- |
| `COMPRESS_MIMETYPES` | Set the list of mimetypes to compress here. | `[`<br>`'text/html',`<br>`'text/css',`<br>`'text/xml',`<br>`'application/json',`<br>`'application/javascript'`<br>`]` |
| `COMPRESS_LEVEL` | Specifies the gzip compression level. | `6` |
| `COMPRESS_DEFLATE_LEVEL` | Specifies the deflate compression level. | `-1` |
| `COMPRESS_MIN_SIZE` | Specifies the minimum file size threshold for compressing files. | `500` |
| `COMPRESS_CACHE_KEY` | Specifies the cache key method for lookup/storage of response data. | `None` |
| `COMPRESS_CACHE_BACKEND` | Specified the backend for storing the cached response data. | `None` |
| `COMPRESS_REGISTER` | Specifies if compression should be automatically registered. | `True` |
| `COMPRESS_ALGORITHM` | Supported compression algorithms. | `['br', 'gzip']` |
| `COMPRESS_ALGORITHM` | Supported compression algorithms. | `['br', 'gzip', 'deflate']` |
| `COMPRESS_BR_MODE` | For Brotli, the compression mode. The options are 0, 1, or 2. These correspond to "generic", "text" (for UTF-8 input), and "font" (for WOFF 2.0). | `0` |
| `COMPRESS_BR_QUALITY` | For Brotli, the desired compression level. Proivdes control over the speed/compression density tradeoff. Higher values provide better compression at the cost of compression time. Ranges from 0 to 11. | `4` |
| `COMPRESS_BR_WINDOW` | For Brotli, this specifies the base-2 logarithm of the sliding window size. Ranges from 10 to 24. | `22` |
Expand Down
9 changes: 7 additions & 2 deletions flask_compress.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import sys
from gzip import GzipFile
import zlib
from io import BytesIO

from collections import defaultdict
Expand Down Expand Up @@ -70,11 +71,12 @@ def init_app(self, app):
('COMPRESS_BR_QUALITY', 4),
('COMPRESS_BR_WINDOW', 22),
('COMPRESS_BR_BLOCK', 0),
('COMPRESS_DEFLATE_LEVEL', -1),
('COMPRESS_MIN_SIZE', 500),
('COMPRESS_CACHE_KEY', None),
('COMPRESS_CACHE_BACKEND', None),
('COMPRESS_REGISTER', True),
('COMPRESS_ALGORITHM', ['br', 'gzip']),
('COMPRESS_ALGORITHM', ['br', 'gzip', 'deflate']),
]

for k, v in defaults:
Expand Down Expand Up @@ -102,7 +104,7 @@ def _choose_compress_algorithm(self, accept_encoding_header):
means the client prefers that algorithm more).

:param accept_encoding_header: Content of the `Accept-Encoding` header
:return: Name of a compression algorithm (e.g. `gzip` or `br`) or `None` if
:return: name of a compression algorithm (`gzip`, `deflate`, `br`) or `None` if
the client and server don't agree on any.
"""
# Map quality factors to requested algorithm names.
Expand Down Expand Up @@ -202,6 +204,9 @@ def compress(self, app, response, algorithm):
fileobj=gzip_buffer) as gzip_file:
gzip_file.write(response.get_data())
return gzip_buffer.getvalue()
elif algorithm == 'deflate':
return zlib.compress(response.get_data(),
app.config['COMPRESS_DEFLATE_LEVEL'])
elif algorithm == 'br':
return brotli.compress(response.get_data(),
mode=app.config['COMPRESS_BR_MODE'],
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
license='MIT',
author='Thomas Mansencal',
author_email='[email protected]',
description='Compress responses in your Flask app with gzip or brotli.',
description='Compress responses in your Flask app with gzip, deflate or brotli.',
long_description=LONG_DESCRIPTION,
long_description_content_type='text/markdown',
py_modules=['flask_compress'],
Expand Down
21 changes: 20 additions & 1 deletion tests/test_flask_compress.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ def test_min_size_default(self):

def test_algorithm_default(self):
""" Tests COMPRESS_ALGORITHM default value is correctly set. """
self.assertEqual(self.app.config['COMPRESS_ALGORITHM'], ['br', 'gzip'])
self.assertEqual(self.app.config['COMPRESS_ALGORITHM'], ['br', 'gzip', 'deflate'])

def test_default_deflate_settings(self):
""" Tests COMPRESS_DELATE_LEVEL default value is correctly set. """
self.assertEqual(self.app.config['COMPRESS_DEFLATE_LEVEL'], -1)

def test_mode_default(self):
""" Tests COMPRESS_BR_MODE default value is correctly set. """
Expand Down Expand Up @@ -145,6 +149,21 @@ def test_quality_level(self):

self.assertNotEqual(response4_size, response11_size)

def test_deflate_compression_level(self):
""" Tests COMPRESS_DELATE_LEVEL correctly affects response data. """
self.app.config['COMPRESS_DEFLATE_LEVEL'] = -1
client = self.app.test_client()
response = client.get('/large/', headers=[('Accept-Encoding', 'deflate')])
response_size = len(response.data)

self.app.config['COMPRESS_DEFLATE_LEVEL'] = 1
client = self.app.test_client()
response = client.get('/large/', headers=[('Accept-Encoding', 'deflate')])
response1_size = len(response.data)

self.assertNotEqual(response_size, response1_size)


class CompressionAlgoTests(unittest.TestCase):
"""
Test different scenarios for compression algorithm negotiation between
Expand Down