Skip to content

Commit d2e2fee

Browse files
committed
issue2551147 - Enable compression of http responses in roundup.
gzip, (brotli/zstd with optional packages) on the fly compression/content-encoding enabled by default. Can serve pre-compressed static assets as well if the client can accept it. Docs updated. Also added example nginx config to installation.txt. The config allows nginx to compress data on the fly. If the config is used, dynamic compression in roundup can be disabled. Dedicating this checkin to my father Paul Hector Rouillard 1930-2021. I did much of the development in this changeset while sitting with him as he slept/transitioned. Without his encouragement and example, my desire to learn would not be what it is and I wouldn't be half the person I am.
1 parent 3880baf commit d2e2fee

File tree

8 files changed

+1038
-25
lines changed

8 files changed

+1038
-25
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ install:
120120
- if [[ $TRAVIS_PYTHON_VERSION != "3.4"* ]]; then pip install mistune; fi
121121
- if [[ $TRAVIS_PYTHON_VERSION != "3.4"* && $TRAVIS_PYTHON_VERSION != "2."* ]]; then pip install Markdown; fi
122122
- pip install markdown2
123+
- pip install brotli zstd
123124

124125
before_script:
125126
# set up mysql database

CHANGES.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ Python v2.5 and v2.6. Starting with the v1.6 releases of Roundup
1111
v2.7.2 or later are required to run newer releases of Roundup. From v2.0
1212
onwards Python 3.4 and later are also supported.
1313

14+
20xx-yy-zz 2.2.0
15+
16+
Fixed:
17+
18+
Features:
19+
20+
- issue2551147 - Enable compression of http responses in roundup.
21+
Allow roundup to return gzip, (br or zstd with added modules)
22+
Content-Encoded replies. Compression could be done in upstream
23+
proxies/wsgi server but this allows it to occur natively. (John
24+
Rouillard)
25+
1426
2021-07-13 2.1.0
1527

1628
Fixed:

doc/admin_guide.txt

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,84 @@ overwrite an existing file) from the roundup-server command line use::
191191
roundup_server -p 8017 -u roundup --save-config demo=/trackers/demo \
192192
sysadmin=/trackers/sysadmin
193193

194+
Configuring Compression
195+
=======================
196+
197+
Roundup will compress HTTP responses to clients on the fly. Dynamic,
198+
on the fly, compression is enabled by default, to disable it set::
199+
200+
[web]
201+
...
202+
dynamic_compression = No
203+
204+
in the tracker's ``config.ini``. You should disable compression if
205+
your proxy (e.g. nginx or apache) or wsgi server (uwsgi) is configured
206+
to compress responses on the fly. The python standard library includes
207+
gzip support. For brotli or zstd you will need to install packges. See
208+
the `installation documentation`_ for details.
209+
210+
Some assets will not be compressed on the fly. Assets with mime types
211+
of "image/png" or "image/jpeg" will not be compressed. You
212+
can add mime types to the list by using ``interfaces.py`` as discussed
213+
in the `customisation documentation`_. As an example adding::
214+
215+
from roundup.cgi.client import Client
216+
217+
Client.precompressed_mime_types.append('application/zip`)
218+
219+
to ``interfaces.py`` will prevent zip files from being compressed.
220+
221+
Any content less than 100 bytes in size will not be compressed (e.g
222+
errors messages, short json responses).
223+
224+
Zstd will be used if the client can understand it, followed by brotli
225+
then gzip encoding. Currently the preference order is hard coded into
226+
the server and not parsed using ``q`` values from the client's
227+
Accept-Encoding header. This is an area for improvement.
228+
229+
In addition to dynamic compression, static files/assets accessed using
230+
``@@file`` can be pre-compressed. This reduces CPU load on the server
231+
and reduces the time required to respond to the client. By default
232+
searching for pre-compressed files is disabled. To enable it set::
233+
234+
[web]
235+
...
236+
use_precompressed_files = Yes
237+
238+
in the tracker's ``config.ini`` file. Then you can create a
239+
precompressed file and it will be served if the client is able to
240+
accept it. For a file ``.../@@file/library.js`` you can create::
241+
242+
tracker_home/html/library.js.gzip
243+
tracker_home/html/library.js.br
244+
tracker_home/html/library.js.zstd
245+
246+
which should be created by using (respectively)::
247+
248+
gzip --keep --suffix .gzip library.js
249+
brotli library.js
250+
zstd library.js && mv library.js.zst library.js.zstd
251+
252+
see the man pages for options that control compression level. Note
253+
that some levels require additional memory on the client side, so you
254+
may not always want to use the highest compression available.
255+
256+
A pre-compressed file will not be used if its modified date is earlier
257+
than the uncompressed file. For example, if ``library.js.gzip`` is
258+
older (has earlier modification date) than ``library.js``,
259+
``library.js.gzip`` will be ignored. ``library.js`` will be
260+
served instead. ``library.js`` will be dynamically compressed on the
261+
fly and a warning message will be logged.
262+
263+
Precompressed files override dynamic compression. For example, assume
264+
the client can accept brotli and gzip. If there are no precompressed
265+
files, the data will be compressed dynamically (on the fly) using
266+
brotli. If there is a precompressed gzip file present the client will
267+
get the gzip version and not a brotli compressed version. This
268+
mechanism allows the admin to allow use of brotli and zstd for
269+
dynamic content, but not for static content.
270+
271+
194272
Users and Security
195273
==================
196274

@@ -525,3 +603,4 @@ Run ``roundup-admin help commands`` for a complete list of subcommands.
525603

526604
.. _`customisation documentation`: customizing.html
527605
.. _`upgrading documentation`: upgrading.html
606+
.. _`installation documentation`: installation.html

doc/installation.txt

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,14 @@ markdown, markdown2 or mistune
129129
To use markdown rendering you need to have the markdown_, markdown2_
130130
or mistune_ (v0.8.4 tested) package installed.
131131

132+
zstd, brotli
133+
To have roundup compress the returned data using one of these
134+
algorithms, you can install one or more of zstd_ or brotli_.
135+
Roundup's responses can always be compressed with gzip from the
136+
Python standard library. Also nginx and various wsgi server can
137+
compress the response from roundup as they transmit/proxy it to the
138+
client.
139+
132140
Windows Service
133141
You can run Roundup as a Windows service if pywin32_ is installed.
134142
Otherwise it must be started manually.
@@ -386,7 +394,8 @@ There are multiple web interfaces to choose from:
386394
4. `Zope product - ZRoundup`_
387395
5. `Apache HTTP Server with mod_wsgi`_
388396
6. `Apache HTTP Server with mod_python`_ (deprecated)
389-
7. `WSGI Variations`_
397+
7. `Nginx HTTP Server`_
398+
8. `WSGI Variations`_
390399

391400
You may need to give the web server user permission to access the tracker home
392401
- see the `UNIX environment steps`_ for information. You may also need to
@@ -818,6 +827,120 @@ The URL for accessing these trackers then become:
818827
Note that in order to use https connections you must set up Apache for secure
819828
serving with SSL.
820829

830+
Nginx HTTP Server
831+
~~~~~~~~~~~~~~~~~
832+
833+
This configuration uses gunicorn to run roundup behind an Nginx proxy.
834+
The proxy also compresses the data using gzip. The url for the tracker
835+
in config.ini should be ``https://tracker.example.org``.
836+
837+
.. code::
838+
839+
user nginx;
840+
worker_processes auto;
841+
worker_rlimit_nofile 10000;
842+
843+
error_log /var/log/nginx/global-error.log;
844+
pid /var/run/nginx.pid;
845+
846+
events {
847+
worker_connections 1024;
848+
}
849+
850+
upstream tracker-tracker {
851+
# gunicorn uses this socket for communication
852+
server unix:/var/run/roundup/tracker.sock fail_timeout=0;
853+
}
854+
855+
http {
856+
include /etc/nginx/mime.types;
857+
default_type application/octet-stream;
858+
859+
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
860+
'$status $body_bytes_sent "$http_referer" '
861+
'"$http_user_agent" "$http_x_forwarded_for"';
862+
863+
access_log /var/log/nginx/global-access.log main;
864+
sendfile on;
865+
tcp_nopush on;
866+
tcp_nodelay on;
867+
server_tokens off;
868+
869+
gzip_http_version 1.1;
870+
gzip_proxied any;
871+
gzip_min_length 500;
872+
# default comp_level is 1
873+
gzip_comp_level 6;
874+
gzip_disable msie6
875+
gzip_types text/plain text/css
876+
text/xml application/xml
877+
text/javascript application/javascript
878+
text/json application/json;
879+
# upstream proxies need to match Accept-Encoding as
880+
# part of their cache check
881+
gzip_vary on
882+
883+
server {
884+
listen 80;
885+
server_name tracker.example.org;
886+
887+
location /.well-known/acme-challenge/ {
888+
alias /etc/lego/.well-known/acme-challenge/;
889+
try_files $uri =404;
890+
}
891+
892+
location / {
893+
return 301 https://$http_host$request_uri;
894+
}
895+
}
896+
897+
server {
898+
listen 443 ssl;
899+
server_name tracker.example.org;
900+
include mime.types;
901+
902+
# By default use the snakeoil certificate...
903+
# change this if you are using a real SSL cert
904+
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
905+
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
906+
907+
# These are useful for @@files where roundup is bypassed.
908+
# but can be set by roundup as well. See:
909+
# https://wiki.roundup-tracker.org/AddingContentSecurityPolicy
910+
# which also sets other security headers.
911+
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
912+
add_header X-Frame-Options "sameorigin";
913+
add_header X-Xss-Protection "1; mode=block";
914+
add_header X-Content-Type-Options "nosniff";
915+
add_header X-Permitted-Cross-Domain-Policies "none";
916+
917+
error_log /var/log/nginx/roundup-tracker.error.log;
918+
access_log /var/log/nginx/roundup-tracker.access.log
919+
920+
root /home/roundup/trackers/tracker/;
921+
922+
# have nginx return files from @@file directly rather than
923+
# going though roundup
924+
location /@@file/ {
925+
rewrite ^/@@file/(.*) /html/$1 break;
926+
# note that you can not use cache control (see customizing doc)
927+
# in roundup to set the expires headers since we are
928+
# bypassing roundup. Consider using a map or different
929+
# location stanzas to vary the expiration times.
930+
expires 1h;
931+
}
932+
933+
location / {
934+
# must define tracker-tracker in upstream stanza
935+
proxy_pass http://tracker-tracker/;
936+
proxy_set_header Host $host;
937+
proxy_set_header X-Real-IP $remote_addr;
938+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
939+
}
940+
}
941+
}
942+
943+
821944
WSGI Variations
822945
~~~~~~~~~~~~~~~
823946

@@ -957,6 +1080,9 @@ Assuming the proxy forwards /tracker, run gunicorn as::
9571080

9581081
this runs roundup at port 8917 on the loopback interface. You should
9591082
configure the reverse proxy to talk to 127.0.0.1 at port 8917.
1083+
If you want you can use a unix domain socket instead. Example:
1084+
``--bind unix:///var/run/roundup/tracker.sock`` would be used for the
1085+
nginx configuration below.
9601086

9611087
.. index:: pair: web interface; uWSGI
9621088
single: wsgi; uWSGI
@@ -1438,6 +1564,7 @@ there are no errors. If there are errors, please let us know!
14381564
.. _`adding MySQL users`:
14391565
https://dev.mysql.com/doc/refman/8.0/en/creating-accounts.html
14401566
.. _apache: https://httpd.apache.org/
1567+
.. _brotli: https://pypi.org/project/Brotli/
14411568
.. _docutils: https://pypi.org/project/docutils/
14421569
.. _flup: https://pypi.org/project/flup/
14431570
.. _gpg: https://www.gnupg.org/software/gpgme/index.html
@@ -1457,3 +1584,4 @@ there are no errors. If there are errors, please let us know!
14571584
.. _pywin32: https://pypi.org/project/pywin32/
14581585
.. _Whoosh: https://whoosh.readthedocs.org/en/latest
14591586
.. _Xapian: https://xapian.org/
1587+
.. _zstd: https://pypi.org/project/zstd/

doc/upgrading.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,25 @@ Contents:
2626

2727
.. index:: Upgrading; 2.0.0 to 2.1.0
2828

29+
Migrating from 2.1.0 to 2.x.y
30+
=============================
31+
32+
Check Compression Settings
33+
--------------------------
34+
35+
Read the `administration guide`_ section on 'Configuring Compression'.
36+
37+
Upgrade tracker's config.ini file. Use::
38+
39+
roundup-admin -i /path/to/tracker updateconfig newconfig.ini
40+
41+
to generate a new ini file preserving all your settings. You can then
42+
merge any local comments from the tracker's ``config.ini`` into
43+
``newconfig.ini``. Compare the old and new files and configure new
44+
compression settings as you want. Then replace ``config.ini`` with the
45+
``newconfig.ini`` file.
46+
47+
2948
Migrating from 2.0.0 to 2.1.0
3049
=============================
3150

0 commit comments

Comments
 (0)