diff --git a/HISTORY.md b/HISTORY.md index 3e8f29b7..dca1beab 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,17 @@ **How to upgrade**: remove your `$GOPATH/src/github.com/kataras` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`. +## 5.0.1 -> 5.0.2 + +- [geekypanda/httpcache](https://github.com/geekypanda/httpcache) has been re-written, + by me, got rid of the mutex locks and use individual statcks instead, + gain even more performance boost + +- `InvalidateCache` has been removed, + it wasn't working well for big apps, let cache work with + its automation, is better. + +- Add tests for the `iris.Cache` ## v3 -> [v4](https://github.com/kataras/iris/tree/4.0.0) (fasthttp-based) long term support @@ -371,7 +382,7 @@ type WebsocketConfiguration struct { ``` -- **REMOVE**: `github.com/kataras/iris/context/context.go` , this is no needed anymore. Its only usage was inside `sessions` and `websockets`, a month ago I did improvements to the sessions as a standalone package, the IContext interface is not being used there. With the today's changes, the iris-contrib/websocket doesn't needs the IContext interface too, so the whole folder `./context` is useless and removed now. Users developers don't have any side-affects from this change. +- **REMOVE**: `github.com/kataras/iris/context/context.go` , this is no needed anymore. Its only usage was inside `sessions` and `websockets`, a month ago I did improvements to the sessions as a standalone package, the IContext interface is not being used there. With the today's changes, the iris-contrib/websocket doesn't needs the IContext interface too, so the whole folder `./context` is useless and removed now. Users developers don't have any side-affects from this change. [Examples](https://github.com/iris-contrib/examples), [Book](https://github.com/iris-contrib/gitbook) are up-to-date, just new configuration fields. @@ -835,7 +846,7 @@ OptionServerVScheme(val string) // You're free to change it, but I will trust you to don't, this is the only setting whose somebody, like me, can see if iris web framework is used OptionServerName(val string) -``` +``` View all configuration fields and options by navigating to the [kataras/iris/configuration.go source file](https://github.com/kataras/iris/blob/master/configuration.go) diff --git a/README.md b/README.md index 547fc309..87d29290 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@
-Releases +Releases Examples @@ -825,7 +825,7 @@ I recommend writing your API tests using this new library, [httpexpect](https:// Versioning ------------ -Current: **v5.0.1** +Current: **v5.0.2** Stable: **[v4 LTS](https://github.com/kataras/iris/tree/4.0.0#versioning)** diff --git a/context.go b/context.go index 4edf792c..bf12bcd7 100644 --- a/context.go +++ b/context.go @@ -561,7 +561,6 @@ func (ctx *Context) renderSerialized(contentType string, obj interface{}, option if err != nil { return err } - gzipEnabled := ctx.framework.Config.Gzip charset := ctx.framework.Config.Charset if len(options) > 0 { @@ -1131,11 +1130,6 @@ func (ctx *Context) MaxAge() int64 { return -1 } -// InvalidateCache clears the cache manually for this request uri context's handler's route -func (ctx *Context) InvalidateCache() { - ctx.framework.InvalidateCache(ctx) -} - // Log logs to the iris defined logger func (ctx *Context) Log(format string, a ...interface{}) { ctx.framework.Logger.Printf(format, a...) diff --git a/http.go b/http.go index bc9a1934..34960be3 100644 --- a/http.go +++ b/http.go @@ -13,6 +13,7 @@ import ( "sync" "time" + "github.com/geekypanda/httpcache" "github.com/iris-contrib/letsencrypt" "github.com/kataras/go-errors" "github.com/valyala/fasthttp" @@ -696,9 +697,29 @@ func (e *muxEntry) precedenceTo(index int) int { return newindex } -// -// -// +// cachedMuxEntry is just a wrapper for the Cache functionality +// it seems useless but I prefer to keep the cached handler on its own memory stack, +// reason: no clojures hell in the Cache function +type cachedMuxEntry struct { + cachedHandler fasthttp.RequestHandler +} + +func newCachedMuxEntry(f *Framework, bodyHandler HandlerFunc, expiration time.Duration) *cachedMuxEntry { + fhandler := func(reqCtx *fasthttp.RequestCtx) { + ctx := f.AcquireCtx(reqCtx) + bodyHandler.Serve(ctx) + f.ReleaseCtx(ctx) + } + + cachedHandler := httpcache.CacheFasthttp(fhandler, expiration) + return &cachedMuxEntry{ + cachedHandler: cachedHandler, + } +} + +func (c *cachedMuxEntry) Serve(ctx *Context) { + c.cachedHandler(ctx.RequestCtx) +} type ( // Route contains some useful information about a route diff --git a/http_test.go b/http_test.go index 751f2612..97586e11 100644 --- a/http_test.go +++ b/http_test.go @@ -11,10 +11,12 @@ import ( "fmt" "io/ioutil" "os" + "sync/atomic" "testing" "time" "github.com/gavv/httpexpect" + "github.com/kataras/go-errors" "github.com/kataras/iris" "github.com/kataras/iris/httptest" "github.com/valyala/fasthttp" @@ -689,3 +691,63 @@ func TestMuxFireMethodNotAllowed(t *testing.T) { e.POST("/mypath").Expect().Status(iris.StatusMethodNotAllowed).Body().Equal("Hello from my custom 405 page") iris.Close() } + +var ( + cacheDuration = 5 * time.Second + errCacheTestFailed = errors.New("Expected the main handler to be executed %d times instead of %d.") +) + +// ~14secs +func runCacheTest(e *httpexpect.Expect, path string, counterPtr *uint32, expectedBodyStr, expectedContentType string) error { + e.GET(path).Expect().Status(iris.StatusOK).Body().Equal(expectedBodyStr) + time.Sleep(cacheDuration / 5) // lets wait for a while, cache should be saved and ready + e.GET(path).Expect().Status(iris.StatusOK).Body().Equal(expectedBodyStr) + counter := atomic.LoadUint32(counterPtr) + if counter > 1 { + // n should be 1 because it doesn't changed after the first call + return errCacheTestFailed.Format(1, counter) + } + time.Sleep(cacheDuration) + + // cache should be cleared now + e.GET(path).Expect().Status(iris.StatusOK).ContentType(expectedContentType, "utf-8").Body().Equal(expectedBodyStr) + time.Sleep(cacheDuration / 5) + // let's call again , the cache should be saved + e.GET(path).Expect().Status(iris.StatusOK).ContentType(expectedContentType, "utf-8").Body().Equal(expectedBodyStr) + counter = atomic.LoadUint32(counterPtr) + if counter != 2 { + return errCacheTestFailed.Format(2, counter) + } + + return nil +} + +func TestCache(t *testing.T) { + + iris.ResetDefault() + + expectedBodyStr := "Imagine it as a big message to achieve x20 response performance!" + var textCounter, htmlCounter uint32 + + iris.Get("/text", iris.Cache(func(ctx *iris.Context) { + atomic.AddUint32(&textCounter, 1) + ctx.Text(iris.StatusOK, expectedBodyStr) + }, cacheDuration)) + + iris.Get("/html", iris.Cache(func(ctx *iris.Context) { + atomic.AddUint32(&htmlCounter, 1) + ctx.HTML(iris.StatusOK, expectedBodyStr) + }, cacheDuration)) + + e := httptest.New(iris.Default, t) + + // test cache on text/plain + if err := runCacheTest(e, "/text", &textCounter, expectedBodyStr, "text/plain"); err != nil { + t.Fatal(err) + } + + // text cache on text/html + if err := runCacheTest(e, "/html", &htmlCounter, expectedBodyStr, "text/html"); err != nil { + t.Fatal(err) + } +} diff --git a/iris.go b/iris.go index 08fd6412..15ed7a51 100644 --- a/iris.go +++ b/iris.go @@ -52,6 +52,7 @@ visit https://www.gitbook.com/book/kataras/iris/details package iris // import "github.com/kataras/iris" import ( + "bytes" "fmt" "log" "net" @@ -65,8 +66,6 @@ import ( "sync" "time" - "bytes" - "github.com/geekypanda/httpcache" "github.com/kataras/go-errors" "github.com/kataras/go-fs" "github.com/kataras/go-serializer" @@ -81,7 +80,7 @@ const ( // IsLongTermSupport flag is true when the below version number is a long-term-support version IsLongTermSupport = false // Version is the current version number of the Iris web framework - Version = "5.0.1" + Version = "5.0.2" banner = ` _____ _ |_ _| (_) @@ -167,7 +166,6 @@ type ( TemplateSourceString(string, interface{}) string SerializeToString(string, interface{}, ...map[string]interface{}) string Cache(HandlerFunc, time.Duration) HandlerFunc - InvalidateCache(*Context) } // Framework is our God |\| Google.Search('Greek mythology Iris') @@ -1127,7 +1125,7 @@ func (s *Framework) SerializeToString(keyOrContentType string, obj interface{}, // Usage: iris.Get("/", iris.Cache(func(ctx *iris.Context){ // ctx.WriteString("Hello, world!") // or a template or anything else // }, time.Duration(10*time.Second))) // duration of expiration -// if <=time.Second then it tries to find it though request header's "cache-control" maxage value +// if <=2 seconds then it tries to find it though request header's "cache-control" maxage value // // Note that it depends on a station instance's cache service. // Do not try to call it from default' station if you use the form of app := iris.New(), @@ -1146,33 +1144,8 @@ func Cache(bodyHandler HandlerFunc, expiration time.Duration) HandlerFunc { // Do not try to call it from default' station if you use the form of app := iris.New(), // use the app.Cache instead of iris.Cache func (s *Framework) Cache(bodyHandler HandlerFunc, expiration time.Duration) HandlerFunc { - fh := httpcache.Fasthttp.Cache(func(reqCtx *fasthttp.RequestCtx) { - ctx := s.AcquireCtx(reqCtx) - bodyHandler.Serve(ctx) - s.ReleaseCtx(ctx) - }, expiration) - - return func(ctx *Context) { - fh(ctx.RequestCtx) - } -} - -// InvalidateCache clears the cache body for a specific context's url path(cache unique key) -// -// Note that it depends on a station instance's cache service. -// Do not try to call it from default' station if you use the form of app := iris.New(), -// use the app.InvalidateCache instead of iris.InvalidateCache -func InvalidateCache(ctx *Context) { - Default.InvalidateCache(ctx) -} - -// InvalidateCache clears the cache body for a specific context's url path(cache unique key) -// -// Note that it depends on a station instance's cache service. -// Do not try to call it from default' station if you use the form of app := iris.New(), -// use the app.InvalidateCache instead of iris.InvalidateCache -func (s *Framework) InvalidateCache(ctx *Context) { - httpcache.Fasthttp.Invalidate(ctx.RequestCtx) + ce := newCachedMuxEntry(s, bodyHandler, expiration) + return ce.Serve } // -------------------------------------------------------------------------------------