mirror of
https://github.com/kataras/iris.git
synced 2025-03-21 11:16:28 +01:00
new Timeout, TimeoutMessage configuration fields and apps.OnApplicationRegistered listener
This commit is contained in:
parent
968a9ec06c
commit
d6cfe3fe5b
|
@ -28,6 +28,10 @@ The codebase for Dependency Injection, Internationalization and localization and
|
||||||
|
|
||||||
## Fixes and Improvements
|
## Fixes and Improvements
|
||||||
|
|
||||||
|
- New `Configuration.Timeout` and `Configuration.TimeoutMessage` fields. Use it to set HTTP timeouts. Note that your http server's (`Application.ConfigureHost`) Read/Write timeouts should be a bit higher than the `Configuration.Timeout` in order to give some time to http timeout handler to kick in and be able to send the `Configuration.TimeoutMessage` properly.
|
||||||
|
|
||||||
|
- New `apps.OnApplicationRegistered` method which listens on new Iris applications hosted under the same binary. Use it on your `init` functions to configure Iris applications by any spot in your project's files.
|
||||||
|
|
||||||
- `Context.JSON` respects any object implements the `easyjson.Marshaler` interface and renders the result using the [easyjon](https://github.com/mailru/easyjson)'s writer.
|
- `Context.JSON` respects any object implements the `easyjson.Marshaler` interface and renders the result using the [easyjon](https://github.com/mailru/easyjson)'s writer.
|
||||||
|
|
||||||
- minor: `Context` structure implements the standard go Context interface now (includes: Deadline, Done, Err and Value methods). Handlers can now just pass the `ctx iris.Context` as a shortcut of `ctx.Request().Context()` when needed.
|
- minor: `Context` structure implements the standard go Context interface now (includes: Deadline, Done, Err and Value methods). Handlers can now just pass the `ctx iris.Context` as a shortcut of `ctx.Request().Context()` when needed.
|
||||||
|
|
12
apps/apps.go
12
apps/apps.go
|
@ -48,3 +48,15 @@ func GetAll() []*iris.Application {
|
||||||
|
|
||||||
return apps
|
return apps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnApplicationRegistered adds a function which fires when a new application
|
||||||
|
// is registered.
|
||||||
|
func OnApplicationRegistered(listeners ...func(app *iris.Application)) {
|
||||||
|
appListeners := make([]func(context.Application), 0, len(listeners))
|
||||||
|
for i := range listeners {
|
||||||
|
appListeners = append(appListeners, func(ctxApp context.Application) {
|
||||||
|
listeners[i](ctxApp.(*iris.Application))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
context.OnApplicationRegistered(appListeners...)
|
||||||
|
}
|
||||||
|
|
|
@ -208,6 +208,14 @@ func WithKeepAlive(keepAliveDur time.Duration) Configurator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithTimeout sets the `Configuration.Timeout` field to the given duration.
|
||||||
|
func WithTimeout(timeoutDur time.Duration, htmlBody string) Configurator {
|
||||||
|
return func(app *Application) {
|
||||||
|
app.config.Timeout = timeoutDur
|
||||||
|
app.config.TimeoutMessage = htmlBody
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithoutServerError will cause to ignore the matched "errors"
|
// WithoutServerError will cause to ignore the matched "errors"
|
||||||
// from the main application's `Run/Listen` function.
|
// from the main application's `Run/Listen` function.
|
||||||
//
|
//
|
||||||
|
@ -498,7 +506,6 @@ func WithSitemap(startURL string) Configurator {
|
||||||
} else {
|
} else {
|
||||||
href = "/" + langPath + loc
|
href = "/" + langPath + loc
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if app.I18n.Subdomain {
|
} else if app.I18n.Subdomain {
|
||||||
// then use the subdomain.
|
// then use the subdomain.
|
||||||
// e.g. http://el.domain.com/path
|
// e.g. http://el.domain.com/path
|
||||||
|
@ -627,6 +634,17 @@ type Configuration struct {
|
||||||
//
|
//
|
||||||
// Defaults to 0.
|
// Defaults to 0.
|
||||||
KeepAlive time.Duration `ini:"keepalive" json:"keepAlive" yaml:"KeepAlive" toml:"KeepAlive" env:"KEEP_ALIVE"`
|
KeepAlive time.Duration `ini:"keepalive" json:"keepAlive" yaml:"KeepAlive" toml:"KeepAlive" env:"KEEP_ALIVE"`
|
||||||
|
// Timeout wraps the application's router with an http timeout handler
|
||||||
|
// if the value is greater than zero.
|
||||||
|
//
|
||||||
|
// The underline response writer supports the Pusher interface but does not support
|
||||||
|
// the Hijacker or Flusher interfaces when Timeout handler is registered.
|
||||||
|
//
|
||||||
|
// Read more at: https://pkg.go.dev/net/http#TimeoutHandler.
|
||||||
|
Timeout time.Duration `ini:"timeout" json:"timeout" yaml:"Timeout" toml:"Timeout"`
|
||||||
|
// TimeoutMessage specifies the HTML body when a handler hits its life time based
|
||||||
|
// on the Timeout configuration field.
|
||||||
|
TimeoutMessage string `ini:"timeout_message" json:"timeoutMessage" yaml:"TimeoutMessage" toml:"TimeoutMessage"`
|
||||||
// Tunneling can be optionally set to enable ngrok http(s) tunneling for this Iris app instance.
|
// Tunneling can be optionally set to enable ngrok http(s) tunneling for this Iris app instance.
|
||||||
// See the `WithTunneling` Configurator too.
|
// See the `WithTunneling` Configurator too.
|
||||||
Tunneling TunnelingConfiguration `ini:"tunneling" json:"tunneling,omitempty" yaml:"Tunneling" toml:"Tunneling"`
|
Tunneling TunnelingConfiguration `ini:"tunneling" json:"tunneling,omitempty" yaml:"Tunneling" toml:"Tunneling"`
|
||||||
|
@ -914,6 +932,16 @@ func (c Configuration) GetKeepAlive() time.Duration {
|
||||||
return c.KeepAlive
|
return c.KeepAlive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetKeepAlive returns the Timeout field.
|
||||||
|
func (c Configuration) GetTimeout() time.Duration {
|
||||||
|
return c.Timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKeepAlive returns the TimeoutMessage field.
|
||||||
|
func (c Configuration) GetTimeoutMessage() string {
|
||||||
|
return c.TimeoutMessage
|
||||||
|
}
|
||||||
|
|
||||||
// GetDisablePathCorrection returns the DisablePathCorrection field.
|
// GetDisablePathCorrection returns the DisablePathCorrection field.
|
||||||
func (c Configuration) GetDisablePathCorrection() bool {
|
func (c Configuration) GetDisablePathCorrection() bool {
|
||||||
return c.DisablePathCorrection
|
return c.DisablePathCorrection
|
||||||
|
@ -1088,6 +1116,14 @@ func WithConfiguration(c Configuration) Configurator {
|
||||||
main.KeepAlive = v
|
main.KeepAlive = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v := c.Timeout; v > 0 {
|
||||||
|
main.Timeout = v
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := c.TimeoutMessage; v != "" {
|
||||||
|
main.TimeoutMessage = v
|
||||||
|
}
|
||||||
|
|
||||||
if len(c.Tunneling.Tunnels) > 0 {
|
if len(c.Tunneling.Tunnels) > 0 {
|
||||||
main.Tunneling = c.Tunneling
|
main.Tunneling = c.Tunneling
|
||||||
}
|
}
|
||||||
|
@ -1234,12 +1270,18 @@ func WithConfiguration(c Configuration) Configurator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultTimeoutMessage is the default timeout message which is rendered
|
||||||
|
// on expired handlers when timeout handler is registered (see Timeout configuration field).
|
||||||
|
var DefaultTimeoutMessage = `<html><head><title>Timeout</title></head><body><h1>Timeout</h1>Looks like the server is taking too long to respond, this can be caused by either poor connectivity or an error with our servers. Please try again in a while.</body></html>`
|
||||||
|
|
||||||
// DefaultConfiguration returns the default configuration for an iris station, fills the main Configuration
|
// DefaultConfiguration returns the default configuration for an iris station, fills the main Configuration
|
||||||
func DefaultConfiguration() Configuration {
|
func DefaultConfiguration() Configuration {
|
||||||
return Configuration{
|
return Configuration{
|
||||||
LogLevel: "info",
|
LogLevel: "info",
|
||||||
SocketSharding: false,
|
SocketSharding: false,
|
||||||
KeepAlive: 0,
|
KeepAlive: 0,
|
||||||
|
Timeout: 0,
|
||||||
|
TimeoutMessage: DefaultTimeoutMessage,
|
||||||
DisableStartupLog: false,
|
DisableStartupLog: false,
|
||||||
DisableInterruptHandler: false,
|
DisableInterruptHandler: false,
|
||||||
DisablePathCorrection: false,
|
DisablePathCorrection: false,
|
||||||
|
|
|
@ -113,8 +113,9 @@ var (
|
||||||
// It's slice instead of map because if IRIS_APP_NAME env var exists,
|
// It's slice instead of map because if IRIS_APP_NAME env var exists,
|
||||||
// by-default all applications running on the same machine
|
// by-default all applications running on the same machine
|
||||||
// will have the same name unless `Application.SetName` is called.
|
// will have the same name unless `Application.SetName` is called.
|
||||||
registeredApps []Application
|
registeredApps []Application
|
||||||
mu sync.RWMutex
|
onApplicationRegisteredListeners []func(Application)
|
||||||
|
mu sync.RWMutex
|
||||||
)
|
)
|
||||||
|
|
||||||
// RegisterApplication registers an application to the global shared storage.
|
// RegisterApplication registers an application to the global shared storage.
|
||||||
|
@ -126,6 +127,20 @@ func RegisterApplication(app Application) {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
registeredApps = append(registeredApps, app)
|
registeredApps = append(registeredApps, app)
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
|
|
||||||
|
mu.RLock()
|
||||||
|
for _, listener := range onApplicationRegisteredListeners {
|
||||||
|
listener(app)
|
||||||
|
}
|
||||||
|
mu.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnApplicationRegistered adds a function which fires when a new application
|
||||||
|
// is registered.
|
||||||
|
func OnApplicationRegistered(listeners ...func(app Application)) {
|
||||||
|
mu.Lock()
|
||||||
|
onApplicationRegisteredListeners = append(onApplicationRegisteredListeners, listeners...)
|
||||||
|
mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetApplications returns a slice of all the registered Applications.
|
// GetApplications returns a slice of all the registered Applications.
|
||||||
|
|
|
@ -20,6 +20,10 @@ type ConfigurationReadOnly interface {
|
||||||
GetSocketSharding() bool
|
GetSocketSharding() bool
|
||||||
// GetKeepAlive returns the KeepAlive field.
|
// GetKeepAlive returns the KeepAlive field.
|
||||||
GetKeepAlive() time.Duration
|
GetKeepAlive() time.Duration
|
||||||
|
// GetKeepAlive returns the Timeout field.
|
||||||
|
GetTimeout() time.Duration
|
||||||
|
// GetKeepAlive returns the TimeoutMessage field.
|
||||||
|
GetTimeoutMessage() string
|
||||||
// GetDisablePathCorrection returns the DisablePathCorrection field
|
// GetDisablePathCorrection returns the DisablePathCorrection field
|
||||||
GetDisablePathCorrection() bool
|
GetDisablePathCorrection() bool
|
||||||
// GetDisablePathCorrectionRedirection returns the DisablePathCorrectionRedirection field.
|
// GetDisablePathCorrectionRedirection returns the DisablePathCorrectionRedirection field.
|
||||||
|
|
|
@ -708,7 +708,6 @@ func (ctx *Context) StopExecution() {
|
||||||
// And stop.
|
// And stop.
|
||||||
ctx.currentHandlerIndex = stopExecutionIndex
|
ctx.currentHandlerIndex = stopExecutionIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsStopped reports whether the current position of the context's handlers is -1,
|
// IsStopped reports whether the current position of the context's handlers is -1,
|
||||||
|
@ -2697,7 +2696,6 @@ func (ctx *Context) ReadMsgPack(ptr interface{}) error {
|
||||||
// As a special case if the "ptr" was a pointer to string or []byte
|
// As a special case if the "ptr" was a pointer to string or []byte
|
||||||
// then it will bind it to the request body as it is.
|
// then it will bind it to the request body as it is.
|
||||||
func (ctx *Context) ReadBody(ptr interface{}) error {
|
func (ctx *Context) ReadBody(ptr interface{}) error {
|
||||||
|
|
||||||
// If the ptr is string or byte, read the body as it's.
|
// If the ptr is string or byte, read the body as it's.
|
||||||
switch v := ptr.(type) {
|
switch v := ptr.(type) {
|
||||||
case *string:
|
case *string:
|
||||||
|
@ -4871,7 +4869,6 @@ func CookieAllowReclaim(cookieNames ...string) CookieOption {
|
||||||
header.Del("Cookie")
|
header.Del("Cookie")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CookieAllowSubdomains set to the Cookie Options
|
// CookieAllowSubdomains set to the Cookie Options
|
||||||
|
@ -5888,5 +5885,12 @@ func (ctx *Context) GetID() interface{} {
|
||||||
// It returns the Context's ID given by a `SetID`call,
|
// It returns the Context's ID given by a `SetID`call,
|
||||||
// followed by the client's IP and the method:uri.
|
// followed by the client's IP and the method:uri.
|
||||||
func (ctx *Context) String() string {
|
func (ctx *Context) String() string {
|
||||||
return fmt.Sprintf("[%s] %s ▶ %s:%s", ctx.GetID(), ctx.RemoteAddr(), ctx.Method(), ctx.Request().RequestURI)
|
id := ctx.GetID()
|
||||||
|
if id != nil {
|
||||||
|
if stringer, ok := id.(fmt.Stringer); ok {
|
||||||
|
id = stringer.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("[%v] %s ▶ %s:%s", id, ctx.RemoteAddr(), ctx.Method(), ctx.Request().RequestURI)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12/context"
|
"github.com/kataras/iris/v12/context"
|
||||||
|
|
||||||
|
@ -271,6 +272,25 @@ func (router *Router) Downgraded() bool {
|
||||||
return router.mainHandler != nil && router.requestHandler == nil
|
return router.mainHandler != nil && router.requestHandler == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetTimeoutHandler overrides the main handler with a timeout handler.
|
||||||
|
//
|
||||||
|
// TimeoutHandler supports the Pusher interface but does not support
|
||||||
|
// the Hijacker or Flusher interfaces.
|
||||||
|
//
|
||||||
|
// All previous registered wrappers and middlewares are still executed as expected.
|
||||||
|
func (router *Router) SetTimeoutHandler(timeout time.Duration, msg string) {
|
||||||
|
if timeout <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mainHandler := router.mainHandler
|
||||||
|
h := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
mainHandler(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
router.mainHandler = http.TimeoutHandler(http.HandlerFunc(h), timeout, msg).ServeHTTP
|
||||||
|
}
|
||||||
|
|
||||||
// WrapRouter adds a wrapper on the top of the main router.
|
// WrapRouter adds a wrapper on the top of the main router.
|
||||||
// Usually it's useful for third-party middleware
|
// Usually it's useful for third-party middleware
|
||||||
// when need to wrap the entire application with a middleware like CORS.
|
// when need to wrap the entire application with a middleware like CORS.
|
||||||
|
|
5
iris.go
5
iris.go
|
@ -640,6 +640,11 @@ func (app *Application) Build() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
app.HTTPErrorHandler = routerHandler
|
app.HTTPErrorHandler = routerHandler
|
||||||
|
|
||||||
|
if app.config.Timeout > 0 {
|
||||||
|
app.Router.SetTimeoutHandler(app.config.Timeout, app.config.TimeoutMessage)
|
||||||
|
}
|
||||||
|
|
||||||
// re-build of the router from outside can be done with
|
// re-build of the router from outside can be done with
|
||||||
// app.RefreshRouter()
|
// app.RefreshRouter()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,17 @@ import (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
context.SetHandlerName("iris/middleware/pprof.*", "iris.profiling")
|
context.SetHandlerName("iris/middleware/pprof.*", "iris.profiling")
|
||||||
|
|
||||||
|
/* for our readers:
|
||||||
|
apps.OnApplicationRegistered(func(app *iris.Application) {
|
||||||
|
app.Any("/debug/pprof/cmdline", iris.FromStd(pprof.Cmdline))
|
||||||
|
app.Any("/debug/pprof/profile", iris.FromStd(pprof.Profile))
|
||||||
|
app.Any("/debug/pprof/symbol", iris.FromStd(pprof.Symbol))
|
||||||
|
app.Any("/debug/pprof/trace", iris.FromStd(pprof.Trace))
|
||||||
|
|
||||||
|
app.Any("/debug/pprof /debug/pprof/{action:string}", New())
|
||||||
|
})
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// net/http/pprof copy:
|
// net/http/pprof copy:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user