mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 02:31:04 +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
|
||||
// the "If-Modified-Since" request header (time) is before the
|
||||
// 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
|
||||
// by watching system directories changes manually and use of the `ctx.WriteWithExpiration`
|
||||
// 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 {
|
||||
return func(ctx context.Context) {
|
||||
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.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"
|
||||
// CacheControlHeaderKey is the header key of "Cache-Control".
|
||||
CacheControlHeaderKey = "Cache-Control"
|
||||
// ETagHeaderKey is the header key of "ETag".
|
||||
ETagHeaderKey = "ETag"
|
||||
|
||||
// ContentDispositionHeaderKey is the header key of "Content-Disposition".
|
||||
ContentDispositionHeaderKey = "Content-Disposition"
|
||||
|
@ -2282,7 +2284,7 @@ func (ctx *context) WriteNotModified() {
|
|||
h := ctx.ResponseWriter().Header()
|
||||
delete(h, ContentTypeHeaderKey)
|
||||
delete(h, ContentLengthHeaderKey)
|
||||
if h.Get("Etag") != "" {
|
||||
if h.Get(ETagHeaderKey) != "" {
|
||||
delete(h, LastModifiedHeaderKey)
|
||||
}
|
||||
ctx.StatusCode(http.StatusNotModified)
|
||||
|
|
|
@ -68,7 +68,7 @@ func StaticEmbeddedHandler(vdir string, assetFn func(name string) ([]byte, error
|
|||
names = append(names, path)
|
||||
}
|
||||
|
||||
modtime := time.Now()
|
||||
// modtime := time.Now()
|
||||
h := func(ctx context.Context) {
|
||||
|
||||
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)
|
||||
if _, err := ctx.WriteWithExpiration(buf, modtime); err != nil {
|
||||
if _, err := ctx.Write(buf); err != nil {
|
||||
ctx.StatusCode(http.StatusInternalServerError)
|
||||
ctx.StopExecution()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user