tangled
alpha
login
or
join now
back
round
0
view raw
appview,knotserver: use ETag based caching for blobs
#511
merged
opened by
oppi.li
4 months ago
targeting
master
from
push-pwqwlvnsqtqr
this avoids stale raw content from being sent to clients.
Signed-off-by: oppiliappan
me@oppi.li
options
unified
split
Changed files
+34
-9
appview
repo
repo.go
knotserver
routes.go
+22
-2
appview/repo/repo.go
···
612
612
if !rp.config.Core.Dev {
613
613
protocol = "https"
614
614
}
615
615
+
615
616
blobURL := fmt.Sprintf("%s://%s/%s/%s/raw/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath)
616
616
-
resp, err := http.Get(blobURL)
617
617
+
618
618
+
req, err := http.NewRequest("GET", blobURL, nil)
619
619
+
if err != nil {
620
620
+
log.Println("failed to create request", err)
621
621
+
return
622
622
+
}
623
623
+
624
624
+
// forward the If-None-Match header
625
625
+
if clientETag := r.Header.Get("If-None-Match"); clientETag != "" {
626
626
+
req.Header.Set("If-None-Match", clientETag)
627
627
+
}
628
628
+
629
629
+
client := &http.Client{}
630
630
+
resp, err := client.Do(req)
617
631
if err != nil {
618
618
-
log.Println("failed to reach knotserver:", err)
632
632
+
log.Println("failed to reach knotserver", err)
619
633
rp.pages.Error503(w)
620
634
return
621
635
}
622
636
defer resp.Body.Close()
623
637
638
638
+
// forward 304 not modified
639
639
+
if resp.StatusCode == http.StatusNotModified {
640
640
+
w.WriteHeader(http.StatusNotModified)
641
641
+
return
642
642
+
}
643
643
+
624
644
if resp.StatusCode != http.StatusOK {
625
645
log.Printf("knotserver returned non-OK status for raw blob %s: %d", blobURL, resp.StatusCode)
626
646
w.WriteHeader(resp.StatusCode)
+12
-7
knotserver/routes.go
···
286
286
mimeType = "image/svg+xml"
287
287
}
288
288
289
289
+
contentHash := sha256.Sum256(contents)
290
290
+
eTag := fmt.Sprintf("\"%x\"", contentHash)
291
291
+
289
292
// allow image, video, and text/plain files to be served directly
290
293
switch {
291
291
-
case strings.HasPrefix(mimeType, "image/"):
292
292
-
// allowed
293
293
-
case strings.HasPrefix(mimeType, "video/"):
294
294
-
// allowed
294
294
+
case strings.HasPrefix(mimeType, "image/"), strings.HasPrefix(mimeType, "video/"):
295
295
+
if clientETag := r.Header.Get("If-None-Match"); clientETag == eTag {
296
296
+
w.WriteHeader(http.StatusNotModified)
297
297
+
return
298
298
+
}
299
299
+
w.Header().Set("ETag", eTag)
300
300
+
295
301
case strings.HasPrefix(mimeType, "text/plain"):
296
296
-
// allowed
302
302
+
w.Header().Set("Cache-Control", "public, no-cache")
303
303
+
297
304
default:
298
305
l.Error("attempted to serve disallowed file type", "mimetype", mimeType)
299
306
writeError(w, "only image, video, and text files can be accessed directly", http.StatusForbidden)
300
307
return
301
308
}
302
309
303
303
-
w.Header().Set("Cache-Control", "public, max-age=86400") // cache for 24 hours
304
304
-
w.Header().Set("ETag", fmt.Sprintf("%x", sha256.Sum256(contents)))
305
310
w.Header().Set("Content-Type", mimeType)
306
311
w.Write(contents)
307
312
}