Merge lp:~gz/charms/trusty/apache2-reverseproxy/gzip_encoded_extensions into lp:~abentley/charms/trusty/apache2-reverseproxy/apache-website-interface

Proposed by Martin Packman
Status: Merged
Merged at revision: 22
Proposed branch: lp:~gz/charms/trusty/apache2-reverseproxy/gzip_encoded_extensions
Merge into: lp:~abentley/charms/trusty/apache2-reverseproxy/apache-website-interface
Diff against target: 130 lines (+52/-6)
3 files modified
README.md (+8/-0)
config.yaml (+4/-0)
scripts/config-changed (+40/-6)
To merge this branch: bzr merge lp:~gz/charms/trusty/apache2-reverseproxy/gzip_encoded_extensions
Reviewer Review Type Date Requested Status
Aaron Bentley Approve
Review via email: mp+261139@code.launchpad.net

Description of the change

Adds a new configuration option to serve gzip encoded files with the correct content-encoding header

Two main things of interest:

* We should arguably set the content encoding in S3. This branch does not preclude doing that as well. Unfortunately s3cmd doesn't make it easy, and we'd have to fix up the previous files. See:

<https://github.com/s3tools/s3cmd/issues/396>

Which also raises the other side, if we have Content-Encoding set in S3, we really want the reverse proxy to *always* set accept gzip, and handle the vary/not vary stuff locally - rather than passing through to S3 and letting them decode and transfer uncompressed data.

* Rather than juse using mod_mime AddEncoding, this branch takes a more convoluted approach so it's possible to limit on multiple extensions. This should address Aaron's previous concern as we can then configure just ".log.gz" to be marked, we we know is safe to be displayed in browser.

In the LocationMatch block, we're first disabling mod_deflate (which otherwise futzes with things), then just setting the Content-Encoding header. No Vary business going on, we'd prefer that browsers and other tools get the encoded version.

It would be nice to add some unit tests, we can maybe refactor a bit later?

To post a comment you must log in.
Revision history for this message
Aaron Bentley (abentley) wrote :

Please add docs to the README. See also inline comments.

review: Needs Fixing
23. By Martin Packman

Address review by abentley

Revision history for this message
Martin Packman (gz) wrote :

Pushed up a change with the things you mentioned.

Revision history for this message
Aaron Bentley (abentley) wrote :

One small change requested inline.

review: Approve
24. By Martin Packman

Use custom InvalidConfig as suggested by abentley in review

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'README.md'
--- README.md 2014-09-23 19:44:39 +0000
+++ README.md 2015-07-03 19:36:09 +0000
@@ -41,3 +41,11 @@
41 juju deploy cs:~abentley/trusty/apache2-reverseproxy data-proxy41 juju deploy cs:~abentley/trusty/apache2-reverseproxy data-proxy
42 juju set data-proxy domain_name=data.org external_host=data.example.org:8042 juju set data-proxy domain_name=data.org external_host=data.example.org:80
43 juju add-relation data-proxy:extension apache243 juju add-relation data-proxy:extension apache2
44
45# Marking certain extensions as gzip encoded
46
47When proxying a site, the 'gzip_encoded_extensions' key can be set to add a
48Content-Encoding header to files with the given extensions. This can make it
49possible for user agents to display the content inline. For example:
50
51 juju set data-proxy gzip_encoded_extensions=".log.gz .txt.gz"
4452
=== modified file 'config.yaml'
--- config.yaml 2014-10-14 13:04:52 +0000
+++ config.yaml 2015-07-03 19:36:09 +0000
@@ -29,3 +29,7 @@
29 type: string29 type: string
30 default: "80"30 default: "80"
31 description: "Space-separated list of ports to proxy http on."31 description: "Space-separated list of ports to proxy http on."
32 gzip_encoded_extensions:
33 type: string
34 default: ""
35 description: "Space-separated list of extensions that are gzip encoded."
3236
=== modified file 'scripts/config-changed'
--- scripts/config-changed 2015-05-11 20:36:31 +0000
+++ scripts/config-changed 2015-07-03 19:36:09 +0000
@@ -2,6 +2,7 @@
2# Copyright 2014 Aaron Bentley2# Copyright 2014 Aaron Bentley
3import errno3import errno
4import os4import os
5import re
5import urllib26import urllib2
67
7import hookenv8import hookenv
@@ -36,6 +37,27 @@
36 SSLCertificateFile /etc/ssl/certs/{ssl_certlocation}37 SSLCertificateFile /etc/ssl/certs/{ssl_certlocation}
37 SSLCertificateKeyFile /etc/ssl/private/{ssl_keylocation}38 SSLCertificateKeyFile /etc/ssl/private/{ssl_keylocation}
38"""39"""
40extra_add_gzip_encoding_template = """\
41 <LocationMatch "{extension_re}">
42 SetEnv no-gzip
43 Header set Content-Encoding gzip
44 </LocationMatch>
45"""
46
47
48class InvalidConfig(Exception):
49 """Raise to indicate the given charm config was invalid."""
50
51 def __init__(self, key, value):
52 self.key = key
53 self.value = value
54
55 def __str__(self):
56 return "Invalid {} config value: {!r}".format(self.key, self.value)
57
58 def __repr__(self):
59 return "{}({!r}, {!r})".format(
60 self.__class__.__name, self.key, self.value)
3961
4062
41def get_hosts(config):63def get_hosts(config):
@@ -57,7 +79,7 @@
5779
5880
59def generate_vhost_config(domain, proxy_port, hosts, preserve_host, doc_root,81def generate_vhost_config(domain, proxy_port, hosts, preserve_host, doc_root,
60 key='', cert='', handle_503=False):82 key='', cert='', handle_503=False, gzip_exts=''):
61 if preserve_host:83 if preserve_host:
62 preserve_host = 'On'84 preserve_host = 'On'
63 else:85 else:
@@ -73,6 +95,12 @@
73 proxy_proto = 'https'95 proxy_proto = 'https'
74 extra += https_extra_template.format(ssl_keylocation=key,96 extra += https_extra_template.format(ssl_keylocation=key,
75 ssl_certlocation=cert)97 ssl_certlocation=cert)
98 if gzip_exts:
99 if re.match(r'(?:\.\w+ ?)+$', gzip_exts) is None:
100 raise InvalidConfig("gzip_encoded_extensions", gzip_exts)
101 exts = gzip_exts.rstrip().split()
102 match = '.*({})'.format('|'.join(re.escape(s) for s in exts))
103 extra += extra_add_gzip_encoding_template.format(extension_re=match)
76 return template.format(104 return template.format(
77 domain_name=domain, host=hosts[0][0], port=hosts[0][1],105 domain_name=domain, host=hosts[0][0], port=hosts[0][1],
78 proxy_proto=proxy_proto, proxy_port=proxy_port,106 proxy_proto=proxy_proto, proxy_port=proxy_port,
@@ -80,7 +108,7 @@
80108
81109
82def generate_config(config, domain, hosts, http_ports, doc_root, handle_503,110def generate_config(config, domain, hosts, http_ports, doc_root, handle_503,
83 key, cert, use_ssl):111 key, cert, use_ssl, gzip_exts):
84 if len(hosts) == 0:112 if len(hosts) == 0:
85 return ''113 return ''
86 preserve_host = bool(config.get('preserve_host'))114 preserve_host = bool(config.get('preserve_host'))
@@ -88,7 +116,7 @@
88 for port in http_ports:116 for port in http_ports:
89 site_configs.append(117 site_configs.append(
90 generate_vhost_config(domain, port, hosts, preserve_host, doc_root,118 generate_vhost_config(domain, port, hosts, preserve_host, doc_root,
91 handle_503=handle_503)119 handle_503=handle_503, gzip_exts=gzip_exts)
92 )120 )
93 if use_ssl:121 if use_ssl:
94 site_configs.append(generate_vhost_config(122 site_configs.append(generate_vhost_config(
@@ -131,13 +159,19 @@
131 key = config.get('ssl_keylocation', '')159 key = config.get('ssl_keylocation', '')
132 cert = config.get('ssl_certlocation', '')160 cert = config.get('ssl_certlocation', '')
133 use_ssl = bool('' not in (key, cert))161 use_ssl = bool('' not in (key, cert))
162 gzip_exts = config.get('gzip_encoded_extensions', '')
134 site_modules = ['proxy', 'proxy_http', 'headers']163 site_modules = ['proxy', 'proxy_http', 'headers']
135 if use_ssl:164 if use_ssl:
136 site_modules.append('ssl')165 site_modules.append('ssl')
137 ports.append(443)166 ports.append(443)
138 relation_settings['site_config'] = generate_config(167 try:
139 config, domain, hosts, http_ports, doc_root, handle_503, key, cert,168 relation_settings['site_config'] = generate_config(
140 use_ssl)169 config, domain, hosts, http_ports, doc_root, handle_503, key, cert,
170 use_ssl, gzip_exts)
171 except InvalidConfig as e:
172 hookenv.log('Failed to generate site config: {}'.format(e),
173 hookenv.ERROR)
174 relation_settings['enabled'] = False
141 relation_settings['ports'] = ' '.join(str(p) for p in ports)175 relation_settings['ports'] = ' '.join(str(p) for p in ports)
142 relation_settings['site_modules'] = ' '.join(site_modules)176 relation_settings['site_modules'] = ' '.join(site_modules)
143 for rid in hookenv.relation_ids("apache-website"):177 for rid in hookenv.relation_ids("apache-website"):

Subscribers

People subscribed via source and target branches

to all changes: