Use cache-busting URLs for proxied files (#294)

Migrates (in a backwards-compatible way) from `/proxy/identity_image/271/` to `/proxy/identity_image/271/f5d8e72f2b/`.
dently).
This commit is contained in:
Corry Haines 2022-12-28 10:39:40 -08:00 committed by GitHub
parent 161b1a8af1
commit 296780d5cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 40 additions and 18 deletions

View File

@ -169,7 +169,9 @@ class Emoji(StatorModel):
if self.file: if self.file:
return AutoAbsoluteUrl(self.file.url) return AutoAbsoluteUrl(self.file.url)
elif self.remote_url: elif self.remote_url:
return AutoAbsoluteUrl(f"/proxy/emoji/{self.pk}/") return AutoAbsoluteUrl(
f"/proxy/emoji/{self.pk}/", hash_tail_input=self.remote_url
)
return StaticAbsoluteUrl("img/blank-emoji-128.png") return StaticAbsoluteUrl("img/blank-emoji-128.png")
def as_html(self): def as_html(self):

View File

@ -81,13 +81,17 @@ class PostAttachment(StatorModel):
elif self.file: elif self.file:
return RelativeAbsoluteUrl(self.file.url) return RelativeAbsoluteUrl(self.file.url)
else: else:
return AutoAbsoluteUrl(f"/proxy/post_attachment/{self.pk}/") return AutoAbsoluteUrl(
f"/proxy/post_attachment/{self.pk}/", hash_tail_input=self.remote_url
)
def full_url(self): def full_url(self):
if self.file: if self.file:
return RelativeAbsoluteUrl(self.file.url) return RelativeAbsoluteUrl(self.file.url)
else: else:
return AutoAbsoluteUrl(f"/proxy/post_attachment/{self.pk}/") return AutoAbsoluteUrl(
f"/proxy/post_attachment/{self.pk}/", hash_tail_input=self.remote_url
)
### ActivityPub ### ### ActivityPub ###

View File

@ -1,3 +1,4 @@
import hashlib
import sys import sys
from urllib.parse import urljoin from urllib.parse import urljoin
@ -27,8 +28,19 @@ class AutoAbsoluteUrl(RelativeAbsoluteUrl):
or a passed identity's URI domain. or a passed identity's URI domain.
""" """
def __init__(self, relative: str, identity=None): def __init__(
self,
relative: str,
identity=None,
hash_tail_input: str | None = None,
hash_tail_length: int = 10,
):
self.relative = relative self.relative = relative
if hash_tail_input:
# When provided, attach a hash of the input (typically the proxied URL)
# SHA1 chosen as it generally has the best performance in modern python, and security is not a concern
# Hash truncation is generally fine, as in the typical use case the hash is scoped to the identity PK
self.relative += f"{hashlib.sha1(hash_tail_input.encode('ascii')).hexdigest()[:hash_tail_length]}/"
if identity: if identity:
absolute_prefix = f"https://{identity.domain.uri_domain}/" absolute_prefix = f"https://{identity.domain.uri_domain}/"
else: else:

View File

@ -104,8 +104,6 @@ http {
proxy_cache_valid 301 307 12h; proxy_cache_valid 301 307 12h;
proxy_cache_valid 500 502 503 504 0s; proxy_cache_valid 500 502 503 504 0s;
proxy_cache_valid any 72h; proxy_cache_valid any 72h;
proxy_hide_header Cache-Control;
add_header Cache-Control "public, max-age=3600";
add_header X-Cache $upstream_cache_status; add_header X-Cache $upstream_cache_status;
} }

View File

@ -31,7 +31,7 @@ class BaseProxyView(View):
headers={ headers={
"X-Accel-Redirect": "/__takahe_accel__/", "X-Accel-Redirect": "/__takahe_accel__/",
"X-Takahe-RealUri": remote_url, "X-Takahe-RealUri": remote_url,
"Cache-Control": "public, max-age=3600", "Cache-Control": "public",
}, },
) )
else: else:
@ -52,7 +52,9 @@ class BaseProxyView(View):
"Content-Type": remote_response.headers.get( "Content-Type": remote_response.headers.get(
"Content-Type", "application/octet-stream" "Content-Type", "application/octet-stream"
), ),
"Cache-Control": "public, max-age=3600", "Cache-Control": remote_response.headers.get(
"Cache-Control", "public, max-age=3600"
),
}, },
) )

View File

@ -211,23 +211,23 @@ urlpatterns = [
path("debug/500/", debug.ServerError.as_view()), path("debug/500/", debug.ServerError.as_view()),
path("debug/oauth_authorize/", debug.OauthAuthorize.as_view()), path("debug/oauth_authorize/", debug.OauthAuthorize.as_view()),
# Media/image proxy # Media/image proxy
path( re_path(
"proxy/identity_icon/<identity_id>/", "^proxy/identity_icon/(?P<identity_id>[^/]+)/((?P<image_hash>[^/]+)/)?$",
mediaproxy.IdentityIconCacheView.as_view(), mediaproxy.IdentityIconCacheView.as_view(),
name="proxy_identity_icon", name="proxy_identity_icon",
), ),
path( re_path(
"proxy/identity_image/<identity_id>/", "^proxy/identity_image/(?P<identity_id>[^/]+)/((?P<image_hash>[^/]+)/)?$",
mediaproxy.IdentityImageCacheView.as_view(), mediaproxy.IdentityImageCacheView.as_view(),
name="proxy_identity_image", name="proxy_identity_image",
), ),
path( re_path(
"proxy/post_attachment/<attachment_id>/", "^proxy/post_attachment/(?P<attachment_id>[^/]+)/((?P<image_hash>[^/]+)/)?$",
mediaproxy.PostAttachmentCacheView.as_view(), mediaproxy.PostAttachmentCacheView.as_view(),
name="proxy_post_attachment", name="proxy_post_attachment",
), ),
path( re_path(
"proxy/emoji/<emoji_id>/", "^proxy/emoji/(?P<emoji_id>[^/]+)/((?P<image_hash>[^/]+)/)?$",
mediaproxy.EmojiCacheView.as_view(), mediaproxy.EmojiCacheView.as_view(),
name="proxy_emoji", name="proxy_emoji",
), ),

View File

@ -268,7 +268,9 @@ class Identity(StatorModel):
if self.icon: if self.icon:
return RelativeAbsoluteUrl(self.icon.url) return RelativeAbsoluteUrl(self.icon.url)
elif self.icon_uri: elif self.icon_uri:
return AutoAbsoluteUrl(f"/proxy/identity_icon/{self.pk}/") return AutoAbsoluteUrl(
f"/proxy/identity_icon/{self.pk}/", hash_tail_input=self.icon_uri
)
else: else:
return StaticAbsoluteUrl("img/unknown-icon-128.png") return StaticAbsoluteUrl("img/unknown-icon-128.png")
@ -279,7 +281,9 @@ class Identity(StatorModel):
if self.image: if self.image:
return AutoAbsoluteUrl(self.image.url) return AutoAbsoluteUrl(self.image.url)
elif self.image_uri: elif self.image_uri:
return AutoAbsoluteUrl(f"/proxy/identity_image/{self.pk}/") return AutoAbsoluteUrl(
f"/proxy/identity_image/{self.pk}/", hash_tail_input=self.image_uri
)
return None return None
@property @property