From 9cacdb1497dd63eb8707f608eb453498bbdf900b Mon Sep 17 00:00:00 2001 From: Umputun Date: Sat, 12 May 2018 13:03:01 -0500 Subject: [PATCH] client-side caching for avatars --- app/rest/auth/avatar.go | 19 +++++++++++++++++++ app/rest/auth/avatar_test.go | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/rest/auth/avatar.go b/app/rest/auth/avatar.go index fdaf8f35..f78fcb8b 100644 --- a/app/rest/auth/avatar.go +++ b/app/rest/auth/avatar.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "path" + "strconv" "strings" "sync" "time" @@ -87,7 +88,20 @@ func (p *AvatarProxy) Routes() (string, chi.Router) { // GET /123456789.image router.Get("/{avatar}", func(w http.ResponseWriter, r *http.Request) { + avatar := chi.URLParam(r, "avatar") + + // client-side caching + etag := `"` + avatar + `"` + w.Header().Set("Etag", etag) + w.Header().Set("Cache-Control", "max-age=2592000") // 30 days + if match := r.Header.Get("If-None-Match"); match != "" { + if strings.Contains(match, etag) { + w.WriteHeader(http.StatusNotModified) + return + } + } + location := p.location(strings.TrimSuffix(avatar, imgSfx)) avFile := path.Join(location, avatar) fh, err := os.Open(avFile) @@ -103,6 +117,11 @@ func (p *AvatarProxy) Routes() (string, chi.Router) { }() w.Header().Set("Content-Type", "image/*") + if fi, err := fh.Stat(); err == nil { + w.Header().Set("Content-Length", strconv.Itoa(int(fi.Size()))) + } + + // write all headers if status, ok := r.Context().Value(render.StatusCtxKey).(int); ok { w.WriteHeader(status) } diff --git a/app/rest/auth/avatar_test.go b/app/rest/auth/avatar_test.go index 71741cd8..9389aa64 100644 --- a/app/rest/auth/avatar_test.go +++ b/app/rest/auth/avatar_test.go @@ -85,7 +85,7 @@ func TestRoutes(t *testing.T) { handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code) - assert.Equal(t, http.Header{"Content-Type": []string{"image/*"}}, rr.HeaderMap) + assert.EqualValues(t, http.Header{"Content-Type": []string{"image/*"}, "Content-Length": []string{"21"}}, rr.HeaderMap) bb := bytes.Buffer{} sz, err := io.Copy(&bb, rr.Body) assert.NoError(t, err)