Skip to content
This repository has been archived by the owner on Oct 15, 2021. It is now read-only.

Commit

Permalink
Add support for cache control headers (fixes #21)
Browse files Browse the repository at this point in the history
  • Loading branch information
leplatrem committed Jul 1, 2017
1 parent 831c1bd commit b46493a
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 37 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ CHANGELOG
0.4.0 (unreleased)
------------------

- Nothing changed yet.
**New features**

- Add support for cache control headers (``If-None-Match`` and ``If-Modified-Since``) (fixes #21)

0.3.0 (2016-10-27)
------------------
Expand Down
9 changes: 3 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VIRTUALENV = virtualenv
VIRTUALENV = virtualenv --python=python3
VENV := $(shell echo $${VIRTUAL_ENV-.venv})
PYTHON = $(VENV)/bin/python
INSTALL_STAMP = $(VENV)/.install.stamp
Expand All @@ -24,13 +24,10 @@ build-requirements:

virtualenv: $(PYTHON)
$(PYTHON):
virtualenv $(VENV)

moto:
$(VENV)/bin/moto_server s3bucket_path -H 0.0.0.0 -p 5000
$(VIRTUALENV) $(VENV)

tests-once: install
$(VENV)/bin/tox -e py27
$(VENV)/bin/tox -e py35

tests:
$(VENV)/bin/tox
Expand Down
5 changes: 3 additions & 2 deletions kinto_amo/tests/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ class AMOTestCase(BaseWebTest, unittest.TestCase):
entry_point = kinto_main
config = 'config.ini'

def get_app_settings(self, extras=None):
ini_path = os.path.join(HERE, self.config)
@classmethod
def get_app_settings(cls, extras=None):
ini_path = os.path.join(HERE, cls.config)
config = configparser.ConfigParser()
config.read(ini_path)
settings = dict(config.items('app:main'))
Expand Down
68 changes: 42 additions & 26 deletions kinto_amo/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,43 @@


class AMOTest(AMOTestCase):
url = SERVICE_ENDPOINT.format(api_ver="3",
app=constants.FIREFOX_APPID,
app_ver="46.0")

def test_amo_view_returns_xml(self):
url = SERVICE_ENDPOINT.format(api_ver="3",
app=constants.FIREFOX_APPID,
app_ver="46.0")
resp = self.app.get(url)
resp = self.app.get(self.url)
assert resp.content_type == "application/xml"

def test_has_etag_response_header(self):
resp = self.app.get(self.url)
assert "ETag" in resp.headers

def test_has_last_modified_response_header(self):
resp = self.app.get(self.url)
assert "Last-Modified" in resp.headers

def test_returns_304_if_not_modified_since(self):
resp = self.app.get(self.url)
last_etag = resp.headers["ETag"]
last_modified = resp.headers["Last-Modified"]

resp = self.app.get(self.url, headers={"If-None-Match": last_etag})
assert resp.status_code == 304

resp = self.app.get(self.url, headers={"If-Modified-Since": last_modified})
assert resp.status_code == 304

def test_returns_200_if_not_modified_since_is_malformed(self):
# We don't want to break clients.
resp = self.app.get(self.url, headers={"If-Modified-Since": "semaine derniere"})
assert resp.status_code == 200

def test_returns_200_if_none_match_is_malformed(self):
# We don't want to break clients.
resp = self.app.get(self.url, headers={"If-None-Match": "42a"})
assert resp.status_code == 200

def test_amo_view_only_match_numeric_api_ver(self):
url = SERVICE_ENDPOINT.format(api_ver="wrong",
app=constants.FIREFOX_APPID,
Expand All @@ -34,10 +64,7 @@ def test_amo_view_only_match_numeric_api_ver(self):
assert resp.content_type == "application/json"

def test_amo_view_also_match_with_metrics_args(self):
url = SERVICE_ENDPOINT.format(api_ver="3",
app=constants.FIREFOX_APPID,
app_ver="46.0")
url += ('PRODUCT/BUILD_ID/BUILD_TARGET/LOCALE/CHANNEL/'
url = self.url + ('PRODUCT/BUILD_ID/BUILD_TARGET/LOCALE/CHANNEL/'
'OS_VERSION/DISTRIBUTION/DISTRIBUTION_VERSION/'
'PING_COUNT/TOTAL_PING_COUNT/DAYS_SINCE_LAST_PING/')
resp = self.app.get(url)
Expand All @@ -46,38 +73,26 @@ def test_amo_view_also_match_with_metrics_args(self):

def test_amo_views_passes_api_ver_and_app_args_to_addons_exporter(self):
with mock.patch('kinto_amo.views.services.write_addons_items') as wai:
url = SERVICE_ENDPOINT.format(api_ver="3",
app=constants.FIREFOX_APPID,
app_ver="46.0")
self.app.get(url)
self.app.get(self.url)
assert wai.mock_calls[0][2] == {'api_ver': 3,
'app_id': constants.FIREFOX_APPID}

def test_amo_views_passes_api_and_app_ver_args_to_plugin_exporter(self):
with mock.patch('kinto_amo.views.services.write_plugin_items') as wpi:
url = SERVICE_ENDPOINT.format(api_ver="3",
app=constants.FIREFOX_APPID,
app_ver="46.0")
self.app.get(url)
self.app.get(self.url)
assert wpi.mock_calls[0][2] == {'api_ver': 3,
'app_id': constants.FIREFOX_APPID,
'app_ver': "46.0"}

def test_amo_views_passes_api_and_app_args_to_gfx_exporter(self):
with mock.patch('kinto_amo.views.services.write_gfx_items') as wgi:
url = SERVICE_ENDPOINT.format(api_ver="3",
app=constants.FIREFOX_APPID,
app_ver="46.0")
self.app.get(url)
self.app.get(self.url)
assert wgi.mock_calls[0][2] == {'api_ver': 3,
'app_id': constants.FIREFOX_APPID}

def test_amo_views_passes_api_and_app_args_to_certificates_exporter(self):
with mock.patch('kinto_amo.views.services.write_cert_items') as wci:
url = SERVICE_ENDPOINT.format(api_ver="3",
app=constants.FIREFOX_APPID,
app_ver="46.0")
self.app.get(url)
self.app.get(self.url)
assert wci.mock_calls[0][2] == {'api_ver': 3}


Expand Down Expand Up @@ -200,8 +215,9 @@ def test_not_enabled_gfx_are_ignored(self):

class AMOCustomTest(AMOTestCase):

def get_app_settings(self, extras=None):
settings = super(AMOCustomTest, self).get_app_settings(extras)
@classmethod
def get_app_settings(cls, extras=None):
settings = super().get_app_settings(extras)
settings['amo.preview.addons'] = '/buckets/blocklists/collections/addons'
settings['amo.preview.plugins'] = '/buckets/blocklists/collections/plugins'
settings['amo.preview.gfx'] = '/buckets/blocklists/collections/gfx'
Expand Down
16 changes: 15 additions & 1 deletion kinto_amo/views/services.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pyramid.httpexceptions import HTTPNotFound
from pyramid.httpexceptions import HTTPNotFound, HTTPNotModified
from kinto.core import Service, utils
from kinto.core.storage import Filter

Expand Down Expand Up @@ -48,6 +48,20 @@ def get_blocklist(request):
if cert_records:
last_update = max(last_update, cert_records[-1]['last_modified'])

# Expose highest timestamp in response headers.
last_etag = '"{}"'.format(last_update)
request.response.headers['ETag'] = last_etag
request.response.last_modified = last_update / 1000.0

if_none_match = request.headers.get('If-None-Match')
if_modified_since = request.headers.get('If-Modified-Since')
if if_none_match is not None or if_modified_since is not None:
if if_none_match == last_etag or request.if_modified_since == request.response.last_modified:
response = HTTPNotModified()
response.headers['ETag'] = last_etag
response.last_modified = last_update / 1000.0
raise response

xml_tree = etree.Element(
'blocklist',
xmlns="http://www.mozilla.org/2006/addons-blocklist",
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py27,py34,kinto-master,flake8
envlist = py35,kinto-master,flake8

[testenv]
commands =
Expand Down

0 comments on commit b46493a

Please sign in to comment.