mirror of https://github.com/jlelse/GoBlog
Refactor cache, just use ETag
This commit is contained in:
parent
0f5ddb2e80
commit
a75fa07f57
46
cache.go
46
cache.go
|
@ -2,15 +2,12 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/araddon/dateparse"
|
|
||||||
"github.com/dgraph-io/ristretto"
|
"github.com/dgraph-io/ristretto"
|
||||||
"go.goblog.app/app/pkgs/bufferpool"
|
"go.goblog.app/app/pkgs/bufferpool"
|
||||||
"golang.org/x/sync/singleflight"
|
"golang.org/x/sync/singleflight"
|
||||||
|
@ -20,7 +17,6 @@ const (
|
||||||
cacheLoggedInKey contextKey = "cacheLoggedIn"
|
cacheLoggedInKey contextKey = "cacheLoggedIn"
|
||||||
cacheExpirationKey contextKey = "cacheExpiration"
|
cacheExpirationKey contextKey = "cacheExpiration"
|
||||||
|
|
||||||
lastModified = "Last-Modified"
|
|
||||||
cacheControl = "Cache-Control"
|
cacheControl = "Cache-Control"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -88,13 +84,6 @@ func (a *goBlog) cacheMiddleware(next http.Handler) http.Handler {
|
||||||
w.WriteHeader(http.StatusNotModified)
|
w.WriteHeader(http.StatusNotModified)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ifModifiedSinceHeader := r.Header.Get("If-Modified-Since"); ifModifiedSinceHeader != "" {
|
|
||||||
if t, err := dateparse.ParseAny(ifModifiedSinceHeader); err == nil && t.After(ci.creationTime) {
|
|
||||||
// send 304
|
|
||||||
w.WriteHeader(http.StatusNotModified)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// set status code
|
// set status code
|
||||||
w.WriteHeader(ci.code)
|
w.WriteHeader(ci.code)
|
||||||
// write cached body
|
// write cached body
|
||||||
|
@ -156,24 +145,15 @@ func (a *goBlog) setCacheHeaders(w http.ResponseWriter, cache *cacheItem) {
|
||||||
}
|
}
|
||||||
// Set cache headers
|
// Set cache headers
|
||||||
w.Header().Set("ETag", cache.eTag)
|
w.Header().Set("ETag", cache.eTag)
|
||||||
w.Header().Set(lastModified, cache.creationTime.UTC().Format(http.TimeFormat))
|
w.Header().Set(cacheControl, "public,no-cache")
|
||||||
if w.Header().Get(cacheControl) == "" {
|
|
||||||
if cache.expiration != 0 {
|
|
||||||
w.Header().Set(cacheControl, fmt.Sprintf("public,max-age=%d,stale-while-revalidate=%d", cache.expiration, cache.expiration))
|
|
||||||
} else {
|
|
||||||
exp := a.cfg.Cache.Expiration
|
|
||||||
w.Header().Set(cacheControl, fmt.Sprintf("public,max-age=%d,s-max-age=%d,stale-while-revalidate=%d", exp, exp/3, exp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type cacheItem struct {
|
type cacheItem struct {
|
||||||
expiration int
|
expiration int
|
||||||
creationTime time.Time
|
eTag string
|
||||||
eTag string
|
code int
|
||||||
code int
|
header http.Header
|
||||||
header http.Header
|
body []byte
|
||||||
body []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate byte size of cache item using size of header, body and etag
|
// Calculate byte size of cache item using size of header, body and etag
|
||||||
|
@ -203,24 +183,12 @@ func (c *cache) getCache(key string, next http.Handler, r *http.Request) *cacheI
|
||||||
rec := newCacheRecorder()
|
rec := newCacheRecorder()
|
||||||
next.ServeHTTP(rec, cr)
|
next.ServeHTTP(rec, cr)
|
||||||
item := rec.finish()
|
item := rec.finish()
|
||||||
// Set eTag
|
|
||||||
item.eTag = item.header.Get("ETag")
|
|
||||||
if item.eTag == "" {
|
|
||||||
item.eTag = fmt.Sprintf("%x", sha256.Sum256(item.body))
|
|
||||||
}
|
|
||||||
// Set creation time
|
|
||||||
item.creationTime = time.Now()
|
|
||||||
if lm := item.header.Get(lastModified); lm != "" {
|
|
||||||
if parsedTime, te := dateparse.ParseLocal(lm); te == nil {
|
|
||||||
item.creationTime = parsedTime
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Set expiration
|
// Set expiration
|
||||||
item.expiration, _ = cr.Context().Value(cacheExpirationKey).(int)
|
item.expiration, _ = cr.Context().Value(cacheExpirationKey).(int)
|
||||||
// Remove problematic headers
|
// Remove problematic headers
|
||||||
item.header.Del("Accept-Ranges")
|
item.header.Del("Accept-Ranges")
|
||||||
item.header.Del("ETag")
|
item.header.Del("ETag")
|
||||||
item.header.Del(lastModified)
|
item.header.Del("Last-Modified")
|
||||||
// Save cache
|
// Save cache
|
||||||
if cch := item.header.Get(cacheControl); !containsStrings(cch, "no-store", "private", "no-cache") {
|
if cch := item.header.Get(cacheControl); !containsStrings(cch, "no-store", "private", "no-cache") {
|
||||||
cost := int64(item.cost())
|
cost := int64(item.cost())
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,6 +23,10 @@ func newCacheRecorder() *cacheRecorder {
|
||||||
|
|
||||||
func (c *cacheRecorder) finish() *cacheItem {
|
func (c *cacheRecorder) finish() *cacheItem {
|
||||||
c.done = true
|
c.done = true
|
||||||
|
c.item.eTag = c.item.header.Get("ETag")
|
||||||
|
if c.item.eTag == "" {
|
||||||
|
c.item.eTag = fmt.Sprintf("%x", sha256.Sum256(c.item.body))
|
||||||
|
}
|
||||||
return &c.item
|
return &c.item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/dgraph-io/ristretto"
|
"github.com/dgraph-io/ristretto"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -14,9 +13,8 @@ import (
|
||||||
|
|
||||||
func Benchmark_cacheItem_cost(b *testing.B) {
|
func Benchmark_cacheItem_cost(b *testing.B) {
|
||||||
ci := &cacheItem{
|
ci := &cacheItem{
|
||||||
creationTime: time.Now(),
|
eTag: "abc",
|
||||||
eTag: "abc",
|
code: 200,
|
||||||
code: 200,
|
|
||||||
header: http.Header{
|
header: http.Header{
|
||||||
"Content-Type": []string{"text/html"},
|
"Content-Type": []string{"text/html"},
|
||||||
},
|
},
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -30,7 +30,7 @@ require (
|
||||||
github.com/gorilla/handlers v1.5.1
|
github.com/gorilla/handlers v1.5.1
|
||||||
github.com/gorilla/sessions v1.2.1
|
github.com/gorilla/sessions v1.2.1
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/hacdias/indieauth/v3 v3.0.1
|
github.com/hacdias/indieauth/v3 v3.1.0
|
||||||
github.com/jlaffaye/ftp v0.1.0
|
github.com/jlaffaye/ftp v0.1.0
|
||||||
// master
|
// master
|
||||||
github.com/jlelse/feeds v1.2.1-0.20210704161900-189f94254ad4
|
github.com/jlelse/feeds v1.2.1-0.20210704161900-189f94254ad4
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -238,8 +238,8 @@ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z
|
||||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/hacdias/indieauth/v3 v3.0.1 h1:b/C2VlqOzijvVJuezFcGhEmMZyCvZgdHgLZIb9BOlxk=
|
github.com/hacdias/indieauth/v3 v3.1.0 h1:aVj9hp9cNCYrlUnSybrG7vNSgJ1/4JIsSa0jsQ3zpHk=
|
||||||
github.com/hacdias/indieauth/v3 v3.0.1/go.mod h1:mVMKFOoVMYgGb06HgXiSevyIJu3/GoRo/EujEXSpAKs=
|
github.com/hacdias/indieauth/v3 v3.1.0/go.mod h1:EmGEAbqGNhCnJR6Uk00t83x7ASQeo0EBNf/5XiFvS9w=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
|
Loading…
Reference in New Issue