Gerasimos (Makis) Maropoulos 2020-09-10 11:24:23 +03:00
parent 333be428c4
commit cf0338d342
No known key found for this signature in database
GPG Key ID: 5DBE766BD26A54E7
4 changed files with 110 additions and 40 deletions

View File

@ -364,6 +364,8 @@ Response:
Other Improvements:
- Fix [#1621](https://github.com/kataras/iris/issues/1621) and add a new `cache.WithKey` to customize the cached entry key.
- Add a `Response() *http.Response` to the Response Recorder.
- Fix Response Recorder `Flush` when transfer-encoding is `chunked`.
- Fix Response Recorder `Clone` concurrent access afterwards.

View File

@ -4,16 +4,14 @@ import (
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/cache"
"github.com/kataras/iris/v12/middleware/basicauth"
)
var markdownContents = []byte(`## Hello Markdown
This is a sample of Markdown contents
Features
--------
@ -23,36 +21,7 @@ All features of Sundown are supported, including:
the --tidy option. Without --tidy, the differences are
mostly in whitespace and entity escaping, where blackfriday is
more consistent and cleaner.
* **Common extensions**, including table support, fenced code
blocks, autolinks, strikethroughs, non-strict emphasis, etc.
* **Safety**. Blackfriday is paranoid when parsing, making it safe
to feed untrusted user input without fear of bad things
happening. The test suite stress tests this and there are no
known inputs that make it crash. If you find one, please let me
know and send me the input that does it.
NOTE: "safety" in this context means *runtime safety only*. In order to
protect yourself against JavaScript injection in untrusted content, see
[this example](https://github.com/russross/blackfriday#sanitize-untrusted-content).
* **Fast processing**. It is fast enough to render on-demand in
most web applications without having to cache the output.
* **Routine safety**. You can run multiple parsers in different
goroutines without ill effect. There is no dependence on global
shared state.
* **Minimal dependencies**. Blackfriday only depends on standard
library packages in Go. The source code is pretty
self-contained, so it is easy to add to any project, including
Google App Engine projects.
* **Standards compliant**. Output successfully validates using the
W3C validation tool for HTML 4.01 and XHTML 1.0 Transitional.
[this is a link](https://github.com/kataras/iris) `)
`)
// Cache should not be used on handlers that contain dynamic data.
// Cache is a good and a must-feature on static content, i.e "about page" or for a whole blog site.
@ -63,6 +32,30 @@ func main() {
// saves its content on the first request and serves it instead of re-calculating the content.
// After 10 seconds it will be cleared and reset.
pages := app.Party("/pages")
pages.Use(cache.Handler(10 * time.Second)) // Per Party.
pages.Get("/", pagesIndex)
pages.Post("/", pagesIndexPost)
// Note: on authenticated requests
// the cache middleare does not run at all (see iris/cache/ruleset).
auth := basicauth.Default(map[string]string{
"admin": "admin",
})
app.Get("/protected", auth, cache.Handler(5*time.Second), protected)
// Set custom cache key/identifier,
// for the sake of the example
// we will SHARE the keys on both GET and POST routes
// so the first one is executed that's the body
// for both of the routes. Please don't do that
// on production, this is just an example.
custom := app.Party("/custom")
custom.Use(cache.WithKey("shared"))
custom.Use(cache.Handler(10 * time.Second))
custom.Get("/", customIndex)
custom.Post("/", customIndexPost)
app.Listen(":8080")
}
@ -74,7 +67,32 @@ func writeMarkdown(ctx iris.Context) {
ctx.Markdown(markdownContents)
}
func pagesIndex(ctx iris.Context) {
println("Handler executed. Content refreshed.")
ctx.WriteString("GET: hello")
}
func pagesIndexPost(ctx iris.Context) {
println("Handler executed. Content refreshed.")
ctx.WriteString("POST: hello")
}
func protected(ctx iris.Context) {
username, _, _ := ctx.Request().BasicAuth()
ctx.Writef("Hello, %s!", username)
}
func customIndex(ctx iris.Context) {
ctx.WriteString("Contents from GET custom index")
}
func customIndexPost(ctx iris.Context) {
ctx.WriteString("Contents from POST custom index")
}
/* Note that `HandleDir` does use the browser's disk caching by-default
therefore, register the cache handler AFTER any HandleDir calls,
for a faster solution that server doesn't need to keep track of the response
navigate to https://github.com/kataras/iris/blob/master/_examples/cache/client-side/main.go */
navigate to https://github.com/kataras/iris/blob/master/_examples/cache/client-side/main.go.
The `HandleDir` has its own cache mechanism, read the 'file-server' examples. */

12
cache/cache.go vendored
View File

@ -34,6 +34,18 @@ import (
"github.com/kataras/iris/v12/context"
)
// WithKey sets a custom entry key for cached pages.
// Should be prepended to the cache handler.
//
// Usage:
// app.Get("/", cache.WithKey("custom-key"), cache.Handler(time.Minute), mainHandler)
func WithKey(key string) context.Handler {
return func(ctx *context.Context) {
client.SetKey(ctx, key)
ctx.Next()
}
}
// Cache accepts the cache expiration duration.
// If the "expiration" input argument is invalid, <=2 seconds,
// then expiration is taken by the "cache-control's maxage" header.

View File

@ -1,6 +1,7 @@
package client
import (
"strings"
"sync"
"time"
@ -73,6 +74,48 @@ func parseLifeChanger(ctx *context.Context) entry.LifeChanger {
}
}
const entryKeyContextKey = "iris.cache.server.entry.key"
// SetKey sets a custom entry key for cached pages.
// See root package-level `WithKey` instead.
func SetKey(ctx *context.Context, key string) {
ctx.Values().Set(entryKeyContextKey, key)
}
// GetKey returns the entry key for the current page.
func GetKey(ctx *context.Context) string {
return ctx.Values().GetString(entryKeyContextKey)
}
func getOrSetKey(ctx *context.Context) string {
if key := GetKey(ctx); key != "" {
return key
}
// Note: by-default the rules(ruleset pkg)
// explictly ignores the cache handler
// execution on authenticated requests
// and immediately runs the next handler:
// if !h.rule.Claim(ctx) ...see `Handler` method.
// So the below two lines are useless,
// however we add it for cases
// that the end-developer messedup with the rules
// and by accident allow authenticated cached results.
username, password, _ := ctx.Request().BasicAuth()
authPart := username + strings.Repeat("*", len(password))
key := ctx.Method() + authPart
u := ctx.Request().URL
if !u.IsAbs() {
key += ctx.Scheme() + ctx.Host()
}
key += u.String()
SetKey(ctx, key)
return key
}
func (h *Handler) ServeHTTP(ctx *context.Context) {
// check for pre-cache validators, if at least one of them return false
// for this specific request, then skip the whole cache
@ -90,16 +133,11 @@ func (h *Handler) ServeHTTP(ctx *context.Context) {
return
}
scheme := "http"
if ctx.Request().TLS != nil {
scheme = "https"
}
var (
response *entry.Response
valid = false
// unique per subdomains and paths with different url query.
key = scheme + ctx.Host() + ctx.Request().URL.RequestURI()
key = getOrSetKey(ctx)
)
h.mu.RLock()