Skip to content

Commit

Permalink
Merge pull request #5 from bloodearnest/cache-headers
Browse files Browse the repository at this point in the history
Add support for arbitrary header configuration
  • Loading branch information
rmohr committed May 10, 2016
2 parents e4448c9 + 011739b commit 309ddc3
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 0 deletions.
14 changes: 14 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ Serving compressed files
If a gzip compressed file with the ´gz´ postfix is present, it is served, along with the corresponding headers.
So if the file 'index.html' and the file 'index.html.gz' are present, the file 'index.html.gz' is served, if the the client indicated that it supports gzipped content.

Additionally, you can configure arbitrary headers. You can match files by mime
type, file extension, or prefix. For example, the following would add
Cache-Control headers to paths with a css mime type for 10s, no-cache for all
paths ending in .js for 100s, and add CORS header to all paths under the /imgs/
dir::

headers = [
{'type': 'text/css', 'Cache-Control': 'max-age=10'},
{'ext': '.js', 'Cache-Control': 'no-cache'},
{'prefix': '/imgs/', 'Access-Control-Allow-Origin': '*'},
]
Cling("/my/directory", headers=headers)


Shock
^^^^^

Expand Down
13 changes: 13 additions & 0 deletions static.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ def __call__(self, environ, start_response):
if prezipped:
headers.extend([('Content-Encoding', 'gzip'),
('Vary', 'Accept-Encoding')])
self._add_headers(headers, path_info, content_type)
start_response("200 OK", headers)
if environ['REQUEST_METHOD'] == 'GET':
return self._body(full_path, environ, file_like)
Expand Down Expand Up @@ -213,6 +214,18 @@ def _conditions(self, full_path, environ):
mtime = stat(full_path).st_mtime
return str(mtime), rfc822.formatdate(mtime)

def _add_headers(self, headers, path, content_type):
DEFAULT = '__static_no_match__'
CONFIG_ITEMS = ['prefix', 'type', 'ext']
for config in getattr(self, 'headers', []):
if path.startswith(config.get('prefix', DEFAULT)) or \
content_type == config.get('type', DEFAULT) or \
path.endswith(config.get('ext', DEFAULT)):

for key, value in config.items():
if key not in CONFIG_ITEMS:
headers.append((key, value))

def _file_like(self, full_path):
"""Return the appropriate file object."""
return open(full_path, 'rb')
Expand Down
29 changes: 29 additions & 0 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,35 @@ def test_gzip_cling(cling):
assert response.headers['Vary'] == 'Accept-Encoding'


def test_headers_cling():
from static import Cling

def get(headers, url='/index.html'):
app = webtest.TestApp(Cling(root="testdata/pub", headers=headers))
return app.get(url)

response = get([{'type': 'text/html', 'Cache-Control': 'max-age=10'}])
assert response.headers['Cache-Control'] == 'max-age=10'

response = get(
[{'type': 'text/html', 'Cache-Control': 'max-age=10'}], '/test.xml')
assert 'Cache-Control' not in response.headers.keys()

response = get([{'ext': 'html', 'Cache-Control': 'max-age=10'}])
assert response.headers['Cache-Control'] == 'max-age=10'

response = get(
[{'ext': 'html', 'Cache-Control': 'max-age=10'}], '/test.xml')
assert 'Cache-Control' not in response.headers.keys()

response = get([{'prefix': '/index', 'Cache-Control': 'max-age=10'}])
assert response.headers['Cache-Control'] == 'max-age=10'

response = get(
[{'prefix': '/index', 'Cache-Control': 'max-age=10'}], '/test.xml')
assert 'Cache-Control' not in response.headers.keys()


def test_static_shock(shock):
response = shock.get("/index.html")
assert "Mixed Content" in response
Expand Down

0 comments on commit 309ddc3

Please sign in to comment.