mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 18:51:03 +01:00
Add one more browser (and 304 server) cache method using ETag and If-None-Match headers
And replace the 'ctx.WriteWithExpiration' with simple 'ctx.Write' at 'StaticEmbeddedHandler' of core/router/fs.go, now that we have plenty of options for client cache give the end-dev the oportunity to use them or not on static embedded handlers Former-commit-id: 9c9e2f3de3c5ad8c9e14e453b67e6b649b02bde8
This commit is contained in:
parent
fae3906587
commit
8e9deec4ab
43
cache/browser.go
vendored
43
cache/browser.go
vendored
|
@ -68,6 +68,46 @@ var StaticCache = func(cacheDur time.Duration) context.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ifNoneMatchHeaderKey = "If-None-Match"
|
||||||
|
|
||||||
|
// ETag is another browser & server cache request-response feature.
|
||||||
|
// It can be used side by side with the `StaticCache`, usually `StaticCache` middleware should go first.
|
||||||
|
// This should be used on routes that serves static files only.
|
||||||
|
// The key of the `ETag` is the `ctx.Request().URL.Path`, invalidation of the not modified cache method
|
||||||
|
// can be made by other request handler as well.
|
||||||
|
//
|
||||||
|
// In typical usage, when a URL is retrieved, the web server will return the resource's current
|
||||||
|
// representation along with its corresponding ETag value,
|
||||||
|
// which is placed in an HTTP response header "ETag" field:
|
||||||
|
//
|
||||||
|
// ETag: "/mypath"
|
||||||
|
//
|
||||||
|
// The client may then decide to cache the representation, along with its ETag.
|
||||||
|
// Later, if the client wants to retrieve the same URL resource again,
|
||||||
|
// it will first determine whether the local cached version of the URL has expired
|
||||||
|
// (through the Cache-Control (`StaticCache` method) and the Expire headers).
|
||||||
|
// If the URL has not expired, it will retrieve the local cached resource.
|
||||||
|
// If it determined that the URL has expired (is stale), then the client will contact the server
|
||||||
|
// and send its previously saved copy of the ETag along with the request in a "If-None-Match" field.
|
||||||
|
//
|
||||||
|
// Usage with combination of `StaticCache`:
|
||||||
|
// assets := app.Party("/assets", cache.StaticCache(24 * time.Hour), ETag)
|
||||||
|
// assets.StaticWeb("/", "./assets") or StaticEmbedded("/", "./assets") or StaticEmbeddedGzip("/", "./assets").
|
||||||
|
//
|
||||||
|
// Similar to `Cache304` but it doesn't depends on any "modified date", it uses just the ETag and If-None-Match headers.
|
||||||
|
//
|
||||||
|
// Read more at: https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching and
|
||||||
|
// https://en.wikipedia.org/wiki/HTTP_ETag
|
||||||
|
var ETag = func(ctx context.Context) {
|
||||||
|
key := ctx.Request().URL.Path
|
||||||
|
ctx.Header(context.ETagHeaderKey, key)
|
||||||
|
if match := ctx.GetHeader(ifNoneMatchHeaderKey); match == key {
|
||||||
|
ctx.WriteNotModified()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Next()
|
||||||
|
}
|
||||||
|
|
||||||
// Cache304 sends a `StatusNotModified` (304) whenever
|
// Cache304 sends a `StatusNotModified` (304) whenever
|
||||||
// the "If-Modified-Since" request header (time) is before the
|
// the "If-Modified-Since" request header (time) is before the
|
||||||
// time.Now() + expiresEvery (always compared to their UTC values).
|
// time.Now() + expiresEvery (always compared to their UTC values).
|
||||||
|
@ -83,7 +123,8 @@ var StaticCache = func(cacheDur time.Duration) context.Handler {
|
||||||
// Developers are free to extend this method's behavior
|
// Developers are free to extend this method's behavior
|
||||||
// by watching system directories changes manually and use of the `ctx.WriteWithExpiration`
|
// by watching system directories changes manually and use of the `ctx.WriteWithExpiration`
|
||||||
// with a "modtime" based on the file modified date,
|
// with a "modtime" based on the file modified date,
|
||||||
// simillary to the `Party#StaticWeb` (which sends status OK(200) and browser disk caching instead of 304).
|
// can be used on Party's that contains a static handler,
|
||||||
|
// i.e `StaticWeb`, `StaticEmbedded` or even `StaticEmbeddedGzip`.
|
||||||
var Cache304 = func(expiresEvery time.Duration) context.Handler {
|
var Cache304 = func(expiresEvery time.Duration) context.Handler {
|
||||||
return func(ctx context.Context) {
|
return func(ctx context.Context) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
25
cache/browser_test.go
vendored
25
cache/browser_test.go
vendored
|
@ -76,3 +76,28 @@ func TestCache304(t *testing.T) {
|
||||||
r = e.GET("/").Expect().Status(httptest.StatusOK)
|
r = e.GET("/").Expect().Status(httptest.StatusOK)
|
||||||
r.Body().Equal("send")
|
r.Body().Equal("send")
|
||||||
}
|
}
|
||||||
|
func TestETag(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
app := iris.New()
|
||||||
|
n := "_"
|
||||||
|
app.Get("/", cache.ETag, func(ctx iris.Context) {
|
||||||
|
ctx.WriteString(n)
|
||||||
|
n += "_"
|
||||||
|
})
|
||||||
|
|
||||||
|
// the first and last test writes the content with status OK without cache,
|
||||||
|
// the rest tests the cache headers and status 304 and return, so body should be "".
|
||||||
|
e := httptest.New(t, app)
|
||||||
|
|
||||||
|
r := e.GET("/").Expect().Status(httptest.StatusOK)
|
||||||
|
r.Header("ETag").Equal("/") // test if header setted.
|
||||||
|
r.Body().Equal("_")
|
||||||
|
|
||||||
|
e.GET("/").WithHeader("ETag", "/").WithHeader("If-None-Match", "/").Expect().
|
||||||
|
Status(httptest.StatusNotModified).Body().Equal("") // browser is responsible, no the test engine.
|
||||||
|
|
||||||
|
r = e.GET("/").Expect().Status(httptest.StatusOK)
|
||||||
|
r.Header("ETag").Equal("/") // test if header setted.
|
||||||
|
r.Body().Equal("__")
|
||||||
|
}
|
||||||
|
|
|
@ -2177,6 +2177,8 @@ const (
|
||||||
IfModifiedSinceHeaderKey = "If-Modified-Since"
|
IfModifiedSinceHeaderKey = "If-Modified-Since"
|
||||||
// CacheControlHeaderKey is the header key of "Cache-Control".
|
// CacheControlHeaderKey is the header key of "Cache-Control".
|
||||||
CacheControlHeaderKey = "Cache-Control"
|
CacheControlHeaderKey = "Cache-Control"
|
||||||
|
// ETagHeaderKey is the header key of "ETag".
|
||||||
|
ETagHeaderKey = "ETag"
|
||||||
|
|
||||||
// ContentDispositionHeaderKey is the header key of "Content-Disposition".
|
// ContentDispositionHeaderKey is the header key of "Content-Disposition".
|
||||||
ContentDispositionHeaderKey = "Content-Disposition"
|
ContentDispositionHeaderKey = "Content-Disposition"
|
||||||
|
@ -2282,7 +2284,7 @@ func (ctx *context) WriteNotModified() {
|
||||||
h := ctx.ResponseWriter().Header()
|
h := ctx.ResponseWriter().Header()
|
||||||
delete(h, ContentTypeHeaderKey)
|
delete(h, ContentTypeHeaderKey)
|
||||||
delete(h, ContentLengthHeaderKey)
|
delete(h, ContentLengthHeaderKey)
|
||||||
if h.Get("Etag") != "" {
|
if h.Get(ETagHeaderKey) != "" {
|
||||||
delete(h, LastModifiedHeaderKey)
|
delete(h, LastModifiedHeaderKey)
|
||||||
}
|
}
|
||||||
ctx.StatusCode(http.StatusNotModified)
|
ctx.StatusCode(http.StatusNotModified)
|
||||||
|
|
|
@ -68,7 +68,7 @@ func StaticEmbeddedHandler(vdir string, assetFn func(name string) ([]byte, error
|
||||||
names = append(names, path)
|
names = append(names, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
modtime := time.Now()
|
// modtime := time.Now()
|
||||||
h := func(ctx context.Context) {
|
h := func(ctx context.Context) {
|
||||||
|
|
||||||
reqPath := strings.TrimPrefix(ctx.Request().URL.Path, "/"+vdir)
|
reqPath := strings.TrimPrefix(ctx.Request().URL.Path, "/"+vdir)
|
||||||
|
@ -100,7 +100,7 @@ func StaticEmbeddedHandler(vdir string, assetFn func(name string) ([]byte, error
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.ContentType(cType)
|
ctx.ContentType(cType)
|
||||||
if _, err := ctx.WriteWithExpiration(buf, modtime); err != nil {
|
if _, err := ctx.Write(buf); err != nil {
|
||||||
ctx.StatusCode(http.StatusInternalServerError)
|
ctx.StatusCode(http.StatusInternalServerError)
|
||||||
ctx.StopExecution()
|
ctx.StopExecution()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user