diff --git a/HISTORY.md b/HISTORY.md
index ab79cfcf..766c8612 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -23,7 +23,7 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
Changes apply to `main` branch.
-- A new way to customize the handler's parameter among with the `hero` and `mvc` packages. New `iris.NewContextWrapper` method and `iris.DefaultContextPool` struct were added to wrap a handler and use a custom context instead of the iris.Context directly.
+- A new way to customize the handler's parameter among with the `hero` and `mvc` packages. New `iris.NewContextWrapper` and `iris.NewContextPool` methods were added to wrap a handler and use a custom context instead of the iris.Context directly. Example at: https://github.com/kataras/iris/tree/main/_examples/routing/custom-context.
- The `cache` sub-package has an update, after 4 years:
diff --git a/_examples/routing/custom-context/main.go b/_examples/routing/custom-context/main.go
index 89b9c207..6148325d 100644
--- a/_examples/routing/custom-context/main.go
+++ b/_examples/routing/custom-context/main.go
@@ -1,6 +1,8 @@
package main
import (
+ "sync"
+
"github.com/kataras/iris/v12"
)
@@ -8,22 +10,12 @@ func main() {
// 1. Create the iris app instance.
app := iris.New()
- /*
- w := iris.NewContextWrapper(&iris.DefaultContextPool[*myCustomContext]{
- AcquireFunc: func(ctx iris.Context) *myCustomContext {
- return &myCustomContext{
- Context: ctx,
- // custom fields here...
- }
- },
- ReleaseFunc: func(t *myCustomContext) {
- // do nothing
- },
- })
- OR: */
// 2. Create the Context Wrapper which will be used to wrap the handlers
// that expect a *myCustomContext instead of iris.Context.
w := iris.NewContextWrapper(&myCustomContextPool{})
+ // OR:
+ // w := iris.NewContextWrapper(iris.NewContextPool[myCustomContext, *myCustomContext]())
+ // The example custom context pool operates exactly the same as the result of iris.NewContextPool.
// 3. Register the handler(s) which expects a *myCustomContext instead of iris.Context.
// The `w.Handler` will wrap the handler and will call the `Acquire` and `Release`
@@ -38,6 +30,38 @@ func index(ctx *myCustomContext) {
ctx.HTML("
Hello, World!
")
}
+/*
+ Custom Context Pool
+*/
+// Create the context sync pool for our custom context,
+// the pool must implement Acquire() T and Release(T) methods to satisfy the iris.ContextPool interface.
+type myCustomContextPool struct {
+ pool sync.Pool
+}
+
+// Acquire returns a new custom context from the pool.
+func (p *myCustomContextPool) Acquire(ctx iris.Context) *myCustomContext {
+ v := p.pool.Get()
+ if v == nil {
+ v = &myCustomContext{
+ Context: ctx,
+ // custom fields here...
+ }
+ }
+
+ return v.(*myCustomContext)
+}
+
+// Release puts a custom context back to the pool.
+func (p *myCustomContextPool) Release(t *myCustomContext) {
+ // You can take advantage of this method to clear the context
+ // and re-use it on the Acquire method, use the sync.Pool.
+ p.pool.Put(t)
+}
+
+/*
+ Custom Context
+*/
// Create a custom context.
type myCustomContext struct {
// It's just an embedded field which is set on AcquireFunc,
@@ -47,28 +71,16 @@ type myCustomContext struct {
iris.Context
}
+// SetContext sets the original iris.Context,
+// should be implemented by custom context type(s) when
+// the ContextWrapper uses a context Pool through the iris.NewContextPool function.
+// Comment line 15, uncomment line 17 and the method below.
+func (c *myCustomContext) SetContext(ctx iris.Context) {
+ c.Context = ctx
+}
+
func (c *myCustomContext) HTML(format string, args ...interface{}) (int, error) {
c.Application().Logger().Info("HTML was called from custom Context")
return c.Context.HTML(format, args...)
}
-
-// Create the context memory pool for your custom context,
-// the pool must contain Acquire() T and Release(T) methods.
-type myCustomContextPool struct{}
-
-// Acquire returns a new custom context from the pool.
-func (p *myCustomContextPool) Acquire(ctx iris.Context) *myCustomContext {
- return &myCustomContext{
- Context: ctx,
- // custom fields here...
- }
-}
-
-// Release puts a custom context back to the pool.
-func (p *myCustomContextPool) Release(t *myCustomContext) {
- // You can take advantage of this method to clear the context
- // and re-use it on the Acquire method, use the sync.Pool.
- //
- // We do nothing for the shake of the exampel.
-}
diff --git a/context_wrapper.go b/context_wrapper.go
index 67b718aa..6315ab61 100644
--- a/context_wrapper.go
+++ b/context_wrapper.go
@@ -1,51 +1,101 @@
package iris
import (
+ "sync"
+
"github.com/kataras/iris/v12/context"
)
-// ContextPool is a pool of T.
+type (
+ // ContextSetter is an interface which can be implemented by a struct
+ // to set the iris.Context to the struct.
+ // The receiver must be a pointer of the struct.
+ ContextSetter interface {
+ // SetContext sets the iris.Context to the struct.
+ SetContext(Context)
+ }
+
+ // ContextSetterPtr is a pointer of T which implements the `ContextSetter` interface.
+ // The T must be a struct.
+ ContextSetterPtr[T any] interface {
+ *T
+ ContextSetter
+ }
+
+ // emptyContextSetter is an empty struct which implements the `ContextSetter` interface.
+ emptyContextSetter struct{}
+)
+
+// SetContext method implements `ContextSetter` interface.
+func (*emptyContextSetter) SetContext(Context) {}
+
+// ContextPool is a pool of T. It's used to acquire and release custom context.
+// Use of custom implementation or `NewContextPool`.
//
-// See `NewContextWrapper` and `ContextPool` for more.
-type ContextPool[T any] interface {
- Acquire(ctx Context) T
- Release(T)
-}
-
-// DefaultContextPool is a pool of T.
-// It's used to acquire and release T.
-// The T is acquired from the pool and released back to the pool after the handler's execution.
-// The T is passed to the handler as an argument.
-// The T is not shared between requests.
-type DefaultContextPool[T any] struct {
- AcquireFunc func(Context) T
- ReleaseFunc func(T)
-}
-
-// Ensure that DefaultContextPool[T] implements ContextPool[T].
-var _ ContextPool[any] = (*DefaultContextPool[any])(nil)
-
-// Acquire returns a new T from the pool's AcquireFunc.
-func (p *DefaultContextPool[T]) Acquire(ctx Context) T {
- acquire := p.AcquireFunc
- if p.AcquireFunc == nil {
- acquire = func(ctx Context) T {
- var t T
- return t
- }
+// See `NewContextWrapper` and `NewContextPool` for more.
+type (
+ ContextPool[T any] interface {
+ // Acquire must return a new T from a pool.
+ Acquire(ctx Context) T
+ // Release must put the T back to the pool.
+ Release(T)
}
- return acquire(ctx)
+ // syncContextPool is a sync pool implementation of T.
+ // It's used to acquire and release T.
+ // The contextPtr is acquired from the sync pool and released back to the sync pool after the handler's execution.
+ // The contextPtr is passed to the handler as an argument.
+ // ThecontextPtr is not shared between requests.
+ // The contextPtr must implement the `ContextSetter` interface.
+ // The T must be a struct.
+ // The contextPtr must be a pointer of T.
+ syncContextPool[T any, contextPtr ContextSetterPtr[T]] struct {
+ pool *sync.Pool
+ }
+)
+
+// Ensure that syncContextPool implements ContextPool.
+var _ ContextPool[*emptyContextSetter] = (*syncContextPool[emptyContextSetter, *emptyContextSetter])(nil)
+
+// NewContextPool returns a new ContextPool default implementation which
+// uses sync.Pool to implement its Acquire and Release methods.
+// The contextPtr is acquired from the sync pool and released back to the sync pool after the handler's execution.
+// The contextPtr is passed to the handler as an argument.
+// ThecontextPtr is not shared between requests.
+// The contextPtr must implement the `ContextSetter` interface.
+// The T must be a struct.
+// The contextPtr must be a pointer of T.
+//
+// Example:
+// w := iris.NewContextWrapper(iris.NewContextPool[myCustomContext, *myCustomContext]())
+func NewContextPool[T any, contextPtr ContextSetterPtr[T]]() ContextPool[contextPtr] {
+ return &syncContextPool[T, contextPtr]{
+ pool: &sync.Pool{
+ New: func() interface{} {
+ var t contextPtr = new(T)
+ return t
+ },
+ },
+ }
}
-// Release does nothing if the pool's ReleaseFunc is nil.
-func (p *DefaultContextPool[T]) Release(t T) {
- release := p.ReleaseFunc
- if p.ReleaseFunc == nil {
- release = func(t T) {}
- }
+// Acquire returns a new T from the sync pool.
+func (p *syncContextPool[T, contextPtr]) Acquire(ctx Context) contextPtr {
+ // var t contextPtr
+ // if v := p.pool.Get(); v == nil {
+ // t = new(T)
+ // } else {
+ // t = v.(contextPtr)
+ // }
- release(t)
+ t := p.pool.Get().(contextPtr)
+ t.SetContext(ctx)
+ return t
+}
+
+// Release puts the T back to the sync pool.
+func (p *syncContextPool[T, contextPtr]) Release(t contextPtr) {
+ p.pool.Put(t)
}
// ContextWrapper is a wrapper for handlers which expect a T instead of iris.Context.
@@ -60,19 +110,13 @@ type ContextWrapper[T any] struct {
// The default pool's AcquireFunc returns a zero value of T.
// The default pool's ReleaseFunc does nothing.
// The default pool is used when the pool is nil.
-// Use the `&iris.DefaultContextPool{...}` to pass a simple context pool.
+// Use the `iris.NewContextPool[T, *T]()` to pass a simple context pool.
+// Then, use the `Handler` method to wrap custom handlers to iris ones.
//
-// See the `Handler` method for more.
// Example: https://github.com/kataras/iris/tree/main/_examples/routing/custom-context
func NewContextWrapper[T any](pool ContextPool[T]) *ContextWrapper[T] {
if pool == nil {
- pool = &DefaultContextPool[T]{
- AcquireFunc: func(ctx Context) T {
- var t T
- return t
- },
- ReleaseFunc: func(t T) {},
- }
+ panic("pool cannot be nil")
}
return &ContextWrapper[T]{
diff --git a/go.mod b/go.mod
index dbff4767..7089e814 100644
--- a/go.mod
+++ b/go.mod
@@ -35,7 +35,7 @@ require (
github.com/redis/go-redis/v9 v9.3.0
github.com/schollz/closestmatch v2.1.0+incompatible
github.com/shirou/gopsutil/v3 v3.23.10
- github.com/tdewolff/minify/v2 v2.20.5
+ github.com/tdewolff/minify/v2 v2.20.6
github.com/vmihailenco/msgpack/v5 v5.4.1
github.com/yosssi/ace v0.0.5
go.etcd.io/bbolt v1.3.8
@@ -95,7 +95,7 @@ require (
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/stretchr/testify v1.8.4 // indirect
- github.com/tdewolff/parse/v2 v2.7.3-0.20231031132452-e7c20a5d77ab // indirect
+ github.com/tdewolff/parse/v2 v2.7.4 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
diff --git a/go.sum b/go.sum
index 80ff493e..862bd486 100644
--- a/go.sum
+++ b/go.sum
@@ -224,12 +224,12 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
-github.com/tdewolff/minify/v2 v2.20.5 h1:IbJpmpAFESnuJPdsvFBJWsDcXE5qHsmaVQrRqhOI9sI=
-github.com/tdewolff/minify/v2 v2.20.5/go.mod h1:N78HtaitkDYAWXFbqhWX/LzgwylwudK0JvybGDVQ+Mw=
-github.com/tdewolff/parse/v2 v2.7.3-0.20231031132452-e7c20a5d77ab h1:4zj+h84OrVW4pljmp+LABknN7VS1IMAbeHj+eckO6Ao=
-github.com/tdewolff/parse/v2 v2.7.3-0.20231031132452-e7c20a5d77ab/go.mod h1:9p2qMIHpjRSTr1qnFxQr+igogyTUTlwvf9awHSm84h8=
-github.com/tdewolff/test v1.0.10 h1:uWiheaLgLcNFqHcdWveum7PQfMnIUTf9Kl3bFxrIoew=
-github.com/tdewolff/test v1.0.10/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
+github.com/tdewolff/minify/v2 v2.20.6 h1:R4+Iw1ZqJxrqH52WWHtCpukMuhmO/EasY8YlDiSxphw=
+github.com/tdewolff/minify/v2 v2.20.6/go.mod h1:9t0EY9xySGt1vrP8iscmJfywQwDCQyQBYN6ge+9GwP0=
+github.com/tdewolff/parse/v2 v2.7.4 h1:zrUn2CFg9+5llbUZcsycctFlNRyV1D5gFBZRxuGzdzk=
+github.com/tdewolff/parse/v2 v2.7.4/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
+github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52 h1:gAQliwn+zJrkjAHVcBEYW/RFvd2St4yYimisvozAYlA=
+github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=