From 81f6b429bc91321e33ad645fe94531604fbd6313 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Sat, 4 Aug 2018 20:50:03 +0200 Subject: [PATCH] allow to cache proxied remote data requests cf #513 cf #510 cf #160 --- docs/changelog.md | 8 +++++++- umap/settings/base.py | 2 +- umap/static/umap/js/umap.forms.js | 15 +++++++++++++-- umap/static/umap/js/umap.js | 4 ++-- umap/static/umap/js/umap.layer.js | 3 ++- umap/tests/test_views.py | 28 ++++++++++++++++++++++++++++ umap/views.py | 13 ++++++++++--- 7 files changed, 63 insertions(+), 10 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index aef8f5cc..ee20f38a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -49,6 +49,13 @@ COMMIT; umap compress +## dev + +- allow to cache proxied remote data requests (#513 #510 #160) +- fixed popup template parsing of url with url as query string (#607) +- naive support for nested variables in templates (#600) +- Removed Map.tilelayer foreignkey + ## 1.0.0.rc-1 - BREAKING: support of python 2 is removed per upgrading to Django 2.0 - WARNING: merge Leaflet-Storage and django-leaflet-storage inside umap to ease @@ -81,7 +88,6 @@ COMMIT; - add `{rank}` as dynamic feature property (to be used in popup or icon symbol) - add an explicit button to attach a owner to an anonyous map (#568) - Add 'TablePanel' popup template (#481) -- Removed Map.tilelayer foreignkey ## 0.8.0 diff --git a/umap/settings/base.py b/umap/settings/base.py index 23a93bc3..3d5d592d 100644 --- a/umap/settings/base.py +++ b/umap/settings/base.py @@ -167,7 +167,7 @@ ENABLE_ACCOUNT_LOGIN = False UMAP_ALLOW_ANONYMOUS = False UMAP_EXTRA_URLS = { 'routing': 'http://www.openstreetmap.org/directions?engine=osrm_car&route={lat},{lng}&locale={locale}#map={zoom}/{lat}/{lng}', # noqa - 'ajax_proxy': '/ajax-proxy/?url={url}' + 'ajax_proxy': '/ajax-proxy/?url={url}&ttl={ttl}' } UMAP_KEEP_VERSIONS = 10 SITE_URL = "http://umap.org" diff --git a/umap/static/umap/js/umap.forms.js b/umap/static/umap/js/umap.forms.js index aa112191..dde70bce 100644 --- a/umap/static/umap/js/umap.forms.js +++ b/umap/static/umap/js/umap.forms.js @@ -70,8 +70,8 @@ L.FormBuilder.Select.include({ }, getDefault: function () { - if (this.options.inheritable) return undefined - else return L.FormBuilder.Select.prototype.getDefault.call(this) + if (this.options.inheritable) return undefined; + return this.getOptions()[0][0]; } }); @@ -201,6 +201,17 @@ L.FormBuilder.IconClassSwitcher = L.FormBuilder.Select.extend({ }); +L.FormBuilder.ProxyTTLSelect = L.FormBuilder.Select.extend({ + + selectOptions: [ + [undefined, L._('No cache')], + ['300', L._('5 min')], + ['3600', L._('1 hour')], + ['86400', L._('1 day')] + ] + +}); + L.FormBuilder.PopupTemplate = L.FormBuilder.Select.extend({ selectOptions: [ diff --git a/umap/static/umap/js/umap.js b/umap/static/umap/js/umap.js index 21da48d6..5880afd6 100644 --- a/umap/static/umap/js/umap.js +++ b/umap/static/umap/js/umap.js @@ -1624,9 +1624,9 @@ L.U.Map.include({ return L.Util.greedyTemplate(url, this.getGeoContext(), true); }, - proxyUrl: function (url) { + proxyUrl: function (url, ttl) { if (this.options.urls.ajax_proxy) { - url = L.Util.template(this.options.urls.ajax_proxy, {url: encodeURIComponent(url)}); + url = L.Util.greedyTemplate(this.options.urls.ajax_proxy, {url: encodeURIComponent(url), ttl: ttl}); } return url; }, diff --git a/umap/static/umap/js/umap.layer.js b/umap/static/umap/js/umap.layer.js index b9dd679e..1bc67bd0 100644 --- a/umap/static/umap/js/umap.layer.js +++ b/umap/static/umap/js/umap.layer.js @@ -339,7 +339,7 @@ L.U.DataLayer = L.Class.extend({ if (!this.isVisible()) return; var self = this, url = this.map.localizeUrl(this.options.remoteData.url); - if (this.options.remoteData.proxy) url = this.map.proxyUrl(url); + if (this.options.remoteData.proxy) url = this.map.proxyUrl(url, this.options.remoteData.ttl); this.map.ajax({ uri: url, verb: 'GET', @@ -804,6 +804,7 @@ L.U.DataLayer = L.Class.extend({ ]; if (this.map.options.urls.ajax_proxy) { remoteDataFields.push(['options.remoteData.proxy', {handler: 'Switch', label: L._('Proxy request'), helpEntries: 'proxyRemoteData'}]); + remoteDataFields.push(['options.remoteData.ttl', {handler: 'ProxyTTLSelect', label: L._('Cache proxied request')}]); } var remoteDataContainer = L.DomUtil.createFieldset(container, L._('Remote data')); diff --git a/umap/tests/test_views.py b/umap/tests/test_views.py index b53360f4..41232fa2 100644 --- a/umap/tests/test_views.py +++ b/umap/tests/test_views.py @@ -82,6 +82,34 @@ def test_valid_proxy_request(client): assert 'Cookie' not in response['Vary'] +def test_valid_proxy_request_with_ttl(client): + url = reverse('ajax-proxy') + params = {'url': 'http://example.org', 'ttl': 3600} + headers = { + 'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest', + 'HTTP_REFERER': settings.SITE_URL + } + response = client.get(url, params, **headers) + assert response.status_code == 200 + assert 'Example Domain' in response.content.decode() + assert 'Cookie' not in response['Vary'] + assert response['X-Accel-Expires'] == '3600' + + +def test_valid_proxy_request_with_invalid_ttl(client): + url = reverse('ajax-proxy') + params = {'url': 'http://example.org', 'ttl': 'invalid'} + headers = { + 'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest', + 'HTTP_REFERER': settings.SITE_URL + } + response = client.get(url, params, **headers) + assert response.status_code == 200 + assert 'Example Domain' in response.content.decode() + assert 'Cookie' not in response['Vary'] + assert 'X-Accel-Expires' not in response + + @pytest.mark.django_db def test_login_does_not_contain_form_if_not_enabled(client, settings): settings.ENABLE_ACCOUNT_LOGIN = False diff --git a/umap/views.py b/umap/views.py index b920629f..17879f6b 100644 --- a/umap/views.py +++ b/umap/views.py @@ -267,7 +267,7 @@ class AjaxProxy(View): # You should not use this in production (use Nginx or so) try: url = validate_url(self.request) - except AssertionError as e: + except AssertionError: return HttpResponseBadRequest() headers = { 'User-Agent': 'uMapProxy +http://wiki.openstreetmap.org/wiki/UMap' @@ -285,8 +285,15 @@ class AjaxProxy(View): content = proxied_request.read() # Quick hack to prevent Django from adding a Vary: Cookie header self.request.session.accessed = False - return HttpResponse(content, status=status_code, - content_type=mimetype) + response = HttpResponse(content, status=status_code, + content_type=mimetype) + try: + ttl = int(self.request.GET.get('ttl')) + except (TypeError, ValueError): + pass + else: + response['X-Accel-Expires'] = ttl + return response ajax_proxy = AjaxProxy.as_view()