From 5b7f08ed086274161c49fb80c894789ab158ece3 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Mon, 27 Feb 2023 12:04:09 +0100 Subject: [PATCH] Refactor gzip creation --- umap/settings/base.py | 1 + umap/utils.py | 3 +++ umap/views.py | 57 +++++++++++++++++-------------------------- 3 files changed, 27 insertions(+), 34 deletions(-) diff --git a/umap/settings/base.py b/umap/settings/base.py index a5d98cc7..49575758 100644 --- a/umap/settings/base.py +++ b/umap/settings/base.py @@ -224,6 +224,7 @@ DATABASES = { } } UMAP_READONLY = False +UMAP_GZIP = True LOCALE_PATHS = [os.path.join(PROJECT_DIR, 'locale')] # ============================================================================= diff --git a/umap/utils.py b/umap/utils.py index 46da7749..9afe69b4 100644 --- a/umap/utils.py +++ b/umap/utils.py @@ -1,4 +1,5 @@ import gzip +import os from django.urls import get_resolver from django.urls import URLPattern, URLResolver @@ -106,9 +107,11 @@ def decorated_patterns(func, *urls): def gzip_file(from_path, to_path): + stat = os.stat(from_path) with open(from_path, 'rb') as f_in: with gzip.open(to_path, 'wb') as f_out: f_out.writelines(f_in) + os.utime(to_path, (stat.st_mtime, stat.st_mtime)) def is_ajax(request): diff --git a/umap/views.py b/umap/views.py index 537ea226..709b3be9 100644 --- a/umap/views.py +++ b/umap/views.py @@ -1,10 +1,10 @@ -import hashlib import json +import mimetypes import os import re import socket +from pathlib import Path -import mimetypes from django.conf import settings from django.contrib import messages from django.contrib.auth import logout as do_logout @@ -689,38 +689,16 @@ class GZipMixin(object): EXT = ".gz" - def _path(self): + @property + def path(self): return self.object.geojson.path - def path(self): - """ - Serve gzip file if client accept it. - Generate or update the gzip file if needed. - """ - path = self._path() - statobj = os.stat(path) - ae = self.request.META.get('HTTP_ACCEPT_ENCODING', '') - if re_accepts_gzip.search(ae) and getattr(settings, 'UMAP_GZIP', True): - gzip_path = "{path}{ext}".format(path=path, ext=self.EXT) - up_to_date = True - if not os.path.exists(gzip_path): - up_to_date = False - else: - gzip_statobj = os.stat(gzip_path) - if statobj.st_mtime > gzip_statobj.st_mtime: - up_to_date = False - if not up_to_date: - gzip_file(path, gzip_path) - path = gzip_path - return path - def etag(self): - path = self.path() # Align ETag with Nginx one, because when using X-Send-File, If-None-Match # and If-Modified-Since are handled by Nginx. # https://github.com/nginx/nginx/blob/4ace957c4e08bcbf9ef5e9f83b8e43458bead77f/src/http/ngx_http_core_module.c#L1675-L1709 - statobj = os.stat(path) - return 'W/"%x-%x"' % (int(statobj.st_mtime), statobj.st_size) + statobj = os.stat(self.path) + return '"%x-%x"' % (int(statobj.st_mtime), statobj.st_size) class DataLayerView(GZipMixin, BaseDetailView): @@ -728,29 +706,40 @@ class DataLayerView(GZipMixin, BaseDetailView): def render_to_response(self, context, **response_kwargs): response = None - path = self.path() + path = self.path + # Generate gzip if needed + accepts_gzip = re_accepts_gzip.search( + self.request.META.get("HTTP_ACCEPT_ENCODING", "") + ) + if accepts_gzip and settings.UMAP_GZIP: + gzip_path = Path(f"{path}{self.EXT}") + if not gzip_path.exists(): + gzip_file(path, gzip_path) if getattr(settings, "UMAP_XSENDFILE_HEADER", None): response = HttpResponse() path = path.replace(settings.MEDIA_ROOT, "/internal") response[settings.UMAP_XSENDFILE_HEADER] = path else: - # TODO IMS + # Do not use in production + # (no cache-control/If-Modified-Since/If-None-Match) statobj = os.stat(path) with open(path, "rb") as f: # Should not be used in production! - response = HttpResponse(f.read(), content_type="application/json") + response = HttpResponse(f.read(), content_type="application/geo+json") response["Last-Modified"] = http_date(statobj.st_mtime) response["ETag"] = self.etag() response["Content-Length"] = statobj.st_size response["Vary"] = "Accept-Encoding" - if path.endswith(self.EXT): - response["Content-Encoding"] = "gzip" + if accepts_gzip and settings.UMAP_GZIP: + response["Content-Encoding"] = "gzip" return response class DataLayerVersion(DataLayerView): - def _path(self): + + @property + def path(self): return "{root}/{path}".format( root=settings.MEDIA_ROOT, path=self.object.get_version_path(self.kwargs["name"]),