Commit b74ab99e authored by Oliver Falk's avatar Oliver Falk

Merge branch 'devel' into 'master'

Caching of response objects

See merge request !167
parents ec5ded2d b463f9f9
Pipeline #1965 passed with stages
in 8 minutes and 32 seconds
......@@ -185,9 +185,16 @@ CACHES = {
'LOCATION': [
'127.0.0.1:11211',
],
},
'filesystem': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/ivatar_cache',
'TIMEOUT': 300,
}
}
# This is 5 minutes caching for generated/resized images,
# so the sites don't hit ivatar so much
# so the sites don't hit ivatar so much - it's what's set in the HTTP header
CACHE_IMAGES_MAX_AGE = 5 * 60
CACHE_RESPONSE = False
......@@ -10,6 +10,7 @@ from ssl import SSLError
from django.views.generic.base import TemplateView, View
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound
from django.core.exceptions import ObjectDoesNotExist
from django.core.cache import cache, caches
from django.utils.translation import ugettext_lazy as _
from django.urls import reverse_lazy
from django.db.models import Q
......@@ -22,7 +23,7 @@ from pydenticon5 import Pydenticon5
import pagan
from robohash import Robohash
from ivatar.settings import AVATAR_MAX_SIZE, JPEG_QUALITY, DEFAULT_AVATAR_SIZE
from ivatar.settings import AVATAR_MAX_SIZE, JPEG_QUALITY, DEFAULT_AVATAR_SIZE, CACHE_RESPONSE
from ivatar.settings import CACHE_IMAGES_MAX_AGE
from . ivataraccount.models import ConfirmedEmail, ConfirmedOpenId
from . ivataraccount.models import pil_format, file_format
......@@ -54,6 +55,18 @@ def get_size(request, size=DEFAULT_AVATAR_SIZE):
return size
class CachingHttpResponse(HttpResponse):
def __init__(self, uri, content=b'', content_type=None, status=200, reason=None, charset=None):
if CACHE_RESPONSE:
caches['filesystem'].set(uri, {
'content': content,
'content_type': content_type,
'status': status,
'reason': reason,
'charset': charset
})
return super().__init__(content, content_type, status, reason, charset)
class AvatarImageView(TemplateView):
'''
View to return (binary) image, based on OpenID/Email (both by digest)
......@@ -77,6 +90,19 @@ class AvatarImageView(TemplateView):
forcedefault = False
gravatarredirect = False
gravatarproxy = True
uri = request.build_absolute_uri()
# Check the cache first
if CACHE_RESPONSE:
centry = caches['filesystem'].get(uri)
if centry:
# For DEBUG purpose only print('Cached entry for %s' % uri)
return HttpResponse(
centry['content'],
content_type=centry['content_type'],
status=centry['status'],
reason = centry['reason'],
charset = centry['charset'])
# In case no digest at all is provided, return to home page
if not 'digest' in kwargs:
......@@ -147,8 +173,6 @@ class AvatarImageView(TemplateView):
+ '?s=%i' % size + '&default=%s&f=y' % default
return HttpResponseRedirect(url)
if str(default) == str(404):
return HttpResponseNotFound(_('<h1>Image not found</h1>'))
......@@ -157,7 +181,8 @@ class AvatarImageView(TemplateView):
data = BytesIO()
monsterdata.save(data, 'PNG', quality=JPEG_QUALITY)
data.seek(0)
response = HttpResponse(
response = CachingHttpResponse(
uri,
data,
content_type='image/png')
response['Cache-Control'] = 'max-age=%i' % CACHE_IMAGES_MAX_AGE
......@@ -172,7 +197,8 @@ class AvatarImageView(TemplateView):
data = BytesIO()
robohash.img.save(data, format='png')
data.seek(0)
response = HttpResponse(
response = CachingHttpResponse(
uri,
data,
content_type='image/png')
response['Cache-Control'] = 'max-age=%i' % CACHE_IMAGES_MAX_AGE
......@@ -185,7 +211,8 @@ class AvatarImageView(TemplateView):
img = img.resize((size, size), Image.ANTIALIAS)
img.save(data, 'PNG', quality=JPEG_QUALITY)
data.seek(0)
response = HttpResponse(
response = CachingHttpResponse(
uri,
data,
content_type='image/png')
response['Cache-Control'] = 'max-age=%i' % CACHE_IMAGES_MAX_AGE
......@@ -197,7 +224,8 @@ class AvatarImageView(TemplateView):
img = paganobj.img.resize((size, size), Image.ANTIALIAS)
img.save(data, 'PNG', quality=JPEG_QUALITY)
data.seek(0)
response = HttpResponse(
response = CachingHttpResponse(
uri,
data,
content_type='image/png')
response['Cache-Control'] = 'max-age=%i' % CACHE_IMAGES_MAX_AGE
......@@ -211,7 +239,8 @@ class AvatarImageView(TemplateView):
data = BytesIO()
img.save(data, 'PNG', quality=JPEG_QUALITY)
data.seek(0)
response = HttpResponse(
response = CachingHttpResponse(
uri,
data,
content_type='image/png')
response['Cache-Control'] = 'max-age=%i' % CACHE_IMAGES_MAX_AGE
......@@ -251,7 +280,8 @@ class AvatarImageView(TemplateView):
obj.save()
if imgformat == 'jpg':
imgformat = 'jpeg'
response = HttpResponse(
response = CachingHttpResponse(
uri,
data,
content_type='image/%s' % imgformat)
response['Cache-Control'] = 'max-age=%i' % CACHE_IMAGES_MAX_AGE
......@@ -291,10 +321,14 @@ class GravatarProxyView(View):
# redirect to our default instead.
gravatar_test_url = 'https://secure.gravatar.com/avatar/' + kwargs['digest'] \
+ '?s=%i' % 50
if cache.get(gravatar_test_url) == 'default':
print("Cached Gravatar response: Default.")
return redir_default(default)
try:
testdata = urlopen(gravatar_test_url, timeout=URL_TIMEOUT)
data = BytesIO(testdata.read())
if hashlib.md5(data.read()).hexdigest() == '71bc262d627971d13fe6f3180b93062a':
cache.set(gravatar_test_url, 'default', 60)
return redir_default(default)
except Exception as exc:
print('Gravatar test url fetch failed: %s' % exc)
......@@ -303,28 +337,36 @@ class GravatarProxyView(View):
+ '?s=%i' % size + '&d=%s' % default
try:
if cache.get(gravatar_url) == 'err':
print('Cached Gravatar fetch failed with URL error')
return redir_default(default)
gravatarimagedata = urlopen(gravatar_url, timeout=URL_TIMEOUT)
except HTTPError as exc:
if exc.code != 404 and exc.code != 503:
print(
'Gravatar fetch failed with an unexpected %s HTTP error' %
exc.code)
cache.set(gravatar_url, 'err', 30)
return redir_default(default)
except URLError as exc:
print(
'Gravatar fetch failed with URL error: %s' %
exc.reason)
cache.set(gravatar_url, 'err', 30)
return redir_default(default)
except SSLError as exc:
print(
'Gravatar fetch failed with SSL error: %s' %
exc.reason)
cache.set(gravatar_url, 'err', 30)
return redir_default(default)
try:
data = BytesIO(gravatarimagedata.read())
img = Image.open(data)
data.seek(0)
response = HttpResponse(
response = CachingHttpResponse(
uri,
data.read(),
content_type='image/%s' % file_format(img.format))
response['Cache-Control'] = 'max-age=%i' % CACHE_IMAGES_MAX_AGE
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment