Former-commit-id: 4c71a275f3e10dd0ce77bf5723c370be765663ab
This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-07-03 13:48:34 +03:00
parent beb3f730a0
commit 8c3e43df7f
3 changed files with 77 additions and 6 deletions

View File

@ -40,6 +40,7 @@
* [Dynamic Path](routing/dynamic-path/main.go) * [Dynamic Path](routing/dynamic-path/main.go)
* [Root Wildcard](routing/dynamic-path/root-wildcard/main.go) * [Root Wildcard](routing/dynamic-path/root-wildcard/main.go)
* [Implement a Parameter Type](routing/macros/main.go) * [Implement a Parameter Type](routing/macros/main.go)
* [Same Path Pattern but Func](routing/dynamic-path/same-pattern-different-func/main.go)
* Middleware * Middleware
* [Per Route](routing/writing-a-middleware/per-route/main.go) * [Per Route](routing/writing-a-middleware/per-route/main.go)
* [Globally](routing/writing-a-middleware/globally/main.go) * [Globally](routing/writing-a-middleware/globally/main.go)

View File

@ -21,6 +21,7 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"sync/atomic"
"time" "time"
"unsafe" "unsafe"
@ -1350,7 +1351,7 @@ func (ctx *context) EndRequest() {
func (ctx *context) IsCanceled() bool { func (ctx *context) IsCanceled() bool {
if reqCtx := ctx.request.Context(); reqCtx != nil { if reqCtx := ctx.request.Context(); reqCtx != nil {
err := reqCtx.Err() err := reqCtx.Err()
if errors.Is(err, stdContext.Canceled) { if err != nil && errors.Is(err, stdContext.Canceled) {
return true return true
} }
} }
@ -1415,18 +1416,74 @@ func (ctx *context) OnClose(cb Handler) {
return return
} }
ctx.OnConnectionClose(cb) // Note(@kataras):
// - on normal request-response lifecycle
// the `SetBeforeFlush` will be called first
// and then `OnConnectionClose`,
// - when request was canceled before handler finish its job
// then the `OnConnectionClose` will be called first instead,
// and when the handler function completed then `SetBeforeFlush` is fired.
// These are synchronized, they cannot be executed the same exact time,
// below we just make sure the "cb" is executed once
// by simple boolean check or an atomic one.
var executed uint32
fn := func() { callback := func(ctx Context) {
if !ctx.IsCanceled() { if atomic.CompareAndSwapUint32(&executed, 0, 1) {
// If the callback not fired by OnConnectionClose already.
cb(ctx) cb(ctx)
} }
} }
ctx.writer.SetBeforeFlush(fn) ctx.OnConnectionClose(callback)
onFlush := func() {
callback(ctx)
}
ctx.writer.SetBeforeFlush(onFlush)
} }
/* Note(@kataras): just leave end-developer decide.
const goroutinesContextKey = "iris.goroutines"
type goroutines struct {
wg *sync.WaitGroup
length int
mu sync.RWMutex
}
var acquireGoroutines = func() interface{} {
return &goroutines{wg: new(sync.WaitGroup)}
}
func (ctx *context) Go(fn func(cancelCtx stdContext.Context)) (running int) {
g := ctx.Values().GetOrSet(goroutinesContextKey, acquireGoroutines).(*goroutines)
if fn != nil {
g.wg.Add(1)
g.mu.Lock()
g.length++
g.mu.Unlock()
ctx.waitFunc = g.wg.Wait
go func(reqCtx stdContext.Context) {
fn(reqCtx)
g.wg.Done()
g.mu.Lock()
g.length--
g.mu.Unlock()
}(ctx.request.Context())
}
g.mu.RLock()
running = g.length
g.mu.RUnlock()
return
}
*/
// ResponseWriter returns an http.ResponseWriter compatible response writer, as expected. // ResponseWriter returns an http.ResponseWriter compatible response writer, as expected.
func (ctx *context) ResponseWriter() ResponseWriter { func (ctx *context) ResponseWriter() ResponseWriter {
return ctx.writer return ctx.writer

View File

@ -815,6 +815,19 @@ func (r *Store) Get(key string) interface{} {
return r.GetDefault(key, nil) return r.GetDefault(key, nil)
} }
// GetOrSet is like `GetDefault` but it accepts a function which is
// fired and its result is used to `Set` if
// the "key" was not found or its value is nil.
func (r *Store) GetOrSet(key string, setFunc func() interface{}) interface{} {
if v, ok := r.GetEntry(key); ok && v.ValueRaw != nil {
return v.Value()
}
value := setFunc()
r.Set(key, value)
return value
}
// Visit accepts a visitor which will be filled // Visit accepts a visitor which will be filled
// by the key-value objects. // by the key-value objects.
func (r *Store) Visit(visitor func(key string, value interface{})) { func (r *Store) Visit(visitor func(key string, value interface{})) {