mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
fix #1531 and introduce the 'Configuration.ResetOnFireErrorCode' (read HISTORY.md)
Former-commit-id: 84f1e894378a6dfd94e0bf057f4037e35aee0c4f
This commit is contained in:
parent
34d0d98130
commit
7bb2223226
|
@ -371,6 +371,8 @@ Other Improvements:
|
||||||
|
|
||||||
![DBUG routes](https://iris-go.com/images/v12.2.0-dbug2.png?v=0)
|
![DBUG routes](https://iris-go.com/images/v12.2.0-dbug2.png?v=0)
|
||||||
|
|
||||||
|
- Fixed handler's error response not be respected when response recorder or gzip writer was used instead of the common writer. Fixes [#1531](https://github.com/kataras/iris/issues/1531). It contains a **BREAKING CHANGE** of: the new `Configuration.ResetOnFireErrorCode` field should be set **to true** in order to behave as it used before this update (to reset the contents on recorder or gzip writer).
|
||||||
|
|
||||||
- New builtin [requestid](https://github.com/kataras/iris/tree/master/middleware/requestid) middleware.
|
- New builtin [requestid](https://github.com/kataras/iris/tree/master/middleware/requestid) middleware.
|
||||||
|
|
||||||
- New builtin [JWT](https://github.com/kataras/iris/tree/master/middleware/jwt) middleware based on [square/go-jose](https://github.com/square/go-jose) featured with optional encryption to set claims with sensitive data when necessary.
|
- New builtin [JWT](https://github.com/kataras/iris/tree/master/middleware/jwt) middleware based on [square/go-jose](https://github.com/square/go-jose) featured with optional encryption to set claims with sensitive data when necessary.
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
* [Route State](routing/route-state/main.go)
|
* [Route State](routing/route-state/main.go)
|
||||||
* [Reverse Routing](routing/reverse/main.go)
|
* [Reverse Routing](routing/reverse/main.go)
|
||||||
* [Router Wrapper](routing/custom-wrapper/main.go)
|
* [Router Wrapper](routing/custom-wrapper/main.go)
|
||||||
* [Custom Router](routing/custom-high-level-router/main.go)
|
* [Custom Router](routing/custom-router/main.go)
|
||||||
* Custom Context
|
* Custom Context
|
||||||
* [Method Overriding](routing/custom-context/method-overriding/main.go)
|
* [Method Overriding](routing/custom-context/method-overriding/main.go)
|
||||||
* [New Implementation](routing/custom-context/new-implementation/main.go)
|
* [New Implementation](routing/custom-context/new-implementation/main.go)
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (b *Bootstrapper) SetupWebsockets(endpoint string, handler websocket.ConnHa
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupErrorHandlers prepares the http error handlers
|
// SetupErrorHandlers prepares the http error handlers
|
||||||
// `(context.StatusCodeNotSuccessful`, which defaults to < 200 || >= 400 but you can change it).
|
// `(context.StatusCodeNotSuccessful`, which defaults to >=400 (but you can change it).
|
||||||
func (b *Bootstrapper) SetupErrorHandlers() {
|
func (b *Bootstrapper) SetupErrorHandlers() {
|
||||||
b.OnAnyErrorCode(func(ctx iris.Context) {
|
b.OnAnyErrorCode(func(ctx iris.Context) {
|
||||||
err := iris.Map{
|
err := iris.Map{
|
||||||
|
|
|
@ -72,7 +72,7 @@ func (r *customRouter) RouteExists(ctx iris.Context, method, path string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *customRouter) FireErrorCode(ctx iris.Context) {
|
func (r *customRouter) FireErrorCode(ctx iris.Context, reset bool) {
|
||||||
// responseStatusCode := ctx.GetStatusCode() // set by prior ctx.StatusCode calls
|
// responseStatusCode := ctx.GetStatusCode() // set by prior ctx.StatusCode calls
|
||||||
// [...]
|
// [...]
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12/_examples/response-writer/herotemplate/template"
|
"github.com/kataras/iris/v12/_examples/view/herotemplate/template"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12"
|
"github.com/kataras/iris/v12"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kataras/iris/v12/_examples/response-writer/quicktemplate/templates"
|
"github.com/kataras/iris/v12/_examples/view/quicktemplate/templates"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12"
|
"github.com/kataras/iris/v12"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kataras/iris/v12/_examples/response-writer/quicktemplate/templates"
|
"github.com/kataras/iris/v12/_examples/view/quicktemplate/templates"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12"
|
"github.com/kataras/iris/v12"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kataras/iris/v12/_examples/response-writer/quicktemplate/templates"
|
"github.com/kataras/iris/v12/_examples/view/quicktemplate/templates"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12"
|
"github.com/kataras/iris/v12"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kataras/iris/v12/_examples/response-writer/quicktemplate/controllers"
|
"github.com/kataras/iris/v12/_examples/view/quicktemplate/controllers"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12"
|
"github.com/kataras/iris/v12"
|
||||||
)
|
)
|
||||||
|
|
|
@ -269,13 +269,6 @@ var WithEmptyFormError = func(app *Application) {
|
||||||
app.config.FireEmptyFormError = true
|
app.config.FireEmptyFormError = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithoutAutoFireStatusCode disables the AutoFireStatusCode setting.
|
|
||||||
//
|
|
||||||
// See `Configuration`.
|
|
||||||
var WithoutAutoFireStatusCode = func(app *Application) {
|
|
||||||
app.config.DisableAutoFireStatusCode = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithPathEscape sets the EnablePathEscape setting to true.
|
// WithPathEscape sets the EnablePathEscape setting to true.
|
||||||
//
|
//
|
||||||
// See `Configuration`.
|
// See `Configuration`.
|
||||||
|
@ -305,6 +298,20 @@ var WithFireMethodNotAllowed = func(app *Application) {
|
||||||
app.config.FireMethodNotAllowed = true
|
app.config.FireMethodNotAllowed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithoutAutoFireStatusCode sets the DisableAutoFireStatusCode setting to true.
|
||||||
|
//
|
||||||
|
// See `Configuration`.
|
||||||
|
var WithoutAutoFireStatusCode = func(app *Application) {
|
||||||
|
app.config.DisableAutoFireStatusCode = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithResetOnFireErrorCode sets the ResetOnFireErrorCode setting to true.
|
||||||
|
//
|
||||||
|
// See `Configuration`.
|
||||||
|
var WithResetOnFireErrorCode = func(app *Application) {
|
||||||
|
app.config.ResetOnFireErrorCode = true
|
||||||
|
}
|
||||||
|
|
||||||
// WithTimeFormat sets the TimeFormat setting.
|
// WithTimeFormat sets the TimeFormat setting.
|
||||||
//
|
//
|
||||||
// See `Configuration`.
|
// See `Configuration`.
|
||||||
|
@ -829,6 +836,21 @@ type Configuration struct {
|
||||||
// fires the 405 error instead of 404
|
// fires the 405 error instead of 404
|
||||||
// Defaults to false.
|
// Defaults to false.
|
||||||
FireMethodNotAllowed bool `json:"fireMethodNotAllowed,omitempty" yaml:"FireMethodNotAllowed" toml:"FireMethodNotAllowed"`
|
FireMethodNotAllowed bool `json:"fireMethodNotAllowed,omitempty" yaml:"FireMethodNotAllowed" toml:"FireMethodNotAllowed"`
|
||||||
|
// DisableAutoFireStatusCode if true then it turns off the http error status code
|
||||||
|
// handler automatic execution on error code from a `Context.StatusCode` call.
|
||||||
|
// By-default a custom http error handler will be fired when "Context.StatusCode(errorCode)" called.
|
||||||
|
//
|
||||||
|
// Defaults to false.
|
||||||
|
DisableAutoFireStatusCode bool `json:"disableAutoFireStatusCode,omitempty" yaml:"DisableAutoFireStatusCode" toml:"DisableAutoFireStatusCode"`
|
||||||
|
// ResetOnFireErrorCode if true then any previously response body or headers through
|
||||||
|
// response recorder or gzip writer will be ignored and the router
|
||||||
|
// will fire the registered (or default) HTTP error handler instead.
|
||||||
|
// See `core/router/handler#FireErrorCode` and `Context.EndRequest` for more details.
|
||||||
|
//
|
||||||
|
// Read more at: https://github.com/kataras/iris/issues/1531
|
||||||
|
//
|
||||||
|
// Defaults to false.
|
||||||
|
ResetOnFireErrorCode bool `json:"resetOnFireErrorCode,omitempty" yaml:"ResetOnFireErrorCode" toml:"ResetOnFireErrorCode"`
|
||||||
|
|
||||||
// EnableOptimization when this field is true
|
// EnableOptimization when this field is true
|
||||||
// then the application tries to optimize for the best performance where is possible.
|
// then the application tries to optimize for the best performance where is possible.
|
||||||
|
@ -848,20 +870,6 @@ type Configuration struct {
|
||||||
// will return an `iris.ErrEmptyForm` on empty request form data.
|
// will return an `iris.ErrEmptyForm` on empty request form data.
|
||||||
FireEmptyFormError bool `json:"fireEmptyFormError,omitempty" yaml:"FireEmptyFormError" yaml:"FireEmptyFormError"`
|
FireEmptyFormError bool `json:"fireEmptyFormError,omitempty" yaml:"FireEmptyFormError" yaml:"FireEmptyFormError"`
|
||||||
|
|
||||||
// DisableAutoFireStatusCode if true then it turns off the http error status code handler automatic execution
|
|
||||||
// from (`context.StatusCodeNotSuccessful`, defaults to < 200 || >= 400).
|
|
||||||
// If that is false then for a direct error firing, then call the "context#FireStatusCode(statusCode)" manually.
|
|
||||||
//
|
|
||||||
// By-default a custom http error handler will be fired when "context.StatusCode(code)" called,
|
|
||||||
// code should be equal with the result of the the `context.StatusCodeNotSuccessful` in order to be received as an "http error handler".
|
|
||||||
//
|
|
||||||
// Developer may want this option to set as true in order to manually call the
|
|
||||||
// error handlers when needed via "context#FireStatusCode(< 200 || >= 400)".
|
|
||||||
// HTTP Custom error handlers are being registered via app.OnErrorCode(code, handler)".
|
|
||||||
//
|
|
||||||
// Defaults to false.
|
|
||||||
DisableAutoFireStatusCode bool `json:"disableAutoFireStatusCode,omitempty" yaml:"DisableAutoFireStatusCode" toml:"DisableAutoFireStatusCode"`
|
|
||||||
|
|
||||||
// TimeFormat time format for any kind of datetime parsing
|
// TimeFormat time format for any kind of datetime parsing
|
||||||
// Defaults to "Mon, 02 Jan 2006 15:04:05 GMT".
|
// Defaults to "Mon, 02 Jan 2006 15:04:05 GMT".
|
||||||
TimeFormat string `json:"timeFormat,omitempty" yaml:"TimeFormat" toml:"TimeFormat"`
|
TimeFormat string `json:"timeFormat,omitempty" yaml:"TimeFormat" toml:"TimeFormat"`
|
||||||
|
@ -1038,19 +1046,28 @@ func (c Configuration) GetFireEmptyFormError() bool {
|
||||||
return c.DisableBodyConsumptionOnUnmarshal
|
return c.DisableBodyConsumptionOnUnmarshal
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDisableAutoFireStatusCode returns the Configuration#DisableAutoFireStatusCode.
|
// GetDisableAutoFireStatusCode returns the Configuration.DisableAutoFireStatusCode.
|
||||||
// Returns true when the http error status code handler automatic execution turned off.
|
// Returns true when the http error status code handler automatic execution turned off.
|
||||||
func (c Configuration) GetDisableAutoFireStatusCode() bool {
|
func (c Configuration) GetDisableAutoFireStatusCode() bool {
|
||||||
return c.DisableAutoFireStatusCode
|
return c.DisableAutoFireStatusCode
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTimeFormat returns the Configuration#TimeFormat,
|
// GetResetOnFireErrorCode returns the Configuration.ResetOnFireErrorCode.
|
||||||
|
// Returns true when the router should not respect the handler's error response and
|
||||||
|
// fire the registered error handler instead.
|
||||||
|
//
|
||||||
|
// See https://github.com/kataras/iris/issues/1531
|
||||||
|
func (c Configuration) GetResetOnFireErrorCode() bool {
|
||||||
|
return c.ResetOnFireErrorCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTimeFormat returns the Configuration.TimeFormat,
|
||||||
// format for any kind of datetime parsing.
|
// format for any kind of datetime parsing.
|
||||||
func (c Configuration) GetTimeFormat() string {
|
func (c Configuration) GetTimeFormat() string {
|
||||||
return c.TimeFormat
|
return c.TimeFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCharset returns the Configuration#Charset,
|
// GetCharset returns the Configuration.Charset,
|
||||||
// the character encoding for various rendering
|
// the character encoding for various rendering
|
||||||
// used for templates and the rest of the responses.
|
// used for templates and the rest of the responses.
|
||||||
func (c Configuration) GetCharset() string {
|
func (c Configuration) GetCharset() string {
|
||||||
|
@ -1203,6 +1220,14 @@ func WithConfiguration(c Configuration) Configurator {
|
||||||
main.FireMethodNotAllowed = v
|
main.FireMethodNotAllowed = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v := c.DisableAutoFireStatusCode; v {
|
||||||
|
main.DisableAutoFireStatusCode = v
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := c.ResetOnFireErrorCode; v {
|
||||||
|
main.ResetOnFireErrorCode = v
|
||||||
|
}
|
||||||
|
|
||||||
if v := c.DisableBodyConsumptionOnUnmarshal; v {
|
if v := c.DisableBodyConsumptionOnUnmarshal; v {
|
||||||
main.DisableBodyConsumptionOnUnmarshal = v
|
main.DisableBodyConsumptionOnUnmarshal = v
|
||||||
}
|
}
|
||||||
|
@ -1211,10 +1236,6 @@ func WithConfiguration(c Configuration) Configurator {
|
||||||
main.FireEmptyFormError = v
|
main.FireEmptyFormError = v
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := c.DisableAutoFireStatusCode; v {
|
|
||||||
main.DisableAutoFireStatusCode = v
|
|
||||||
}
|
|
||||||
|
|
||||||
if v := c.TimeFormat; v != "" {
|
if v := c.TimeFormat; v != "" {
|
||||||
main.TimeFormat = v
|
main.TimeFormat = v
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,8 +59,12 @@ type Application interface {
|
||||||
// Look core/router/APIBuilder#GetRoutes for more.
|
// Look core/router/APIBuilder#GetRoutes for more.
|
||||||
GetRoutesReadOnly() []RouteReadOnly
|
GetRoutesReadOnly() []RouteReadOnly
|
||||||
|
|
||||||
// FireErrorCode executes an error http status code handler
|
// FireErrorCode handles the response's error response.
|
||||||
// based on the context's status code.
|
// If `Configuration.ResetOnFireErrorCode()` is true
|
||||||
|
// and the response writer was a recorder or a gzip writer one
|
||||||
|
// then it will try to reset the headers and the body before calling the
|
||||||
|
// registered (or default) error handler for that error code set by
|
||||||
|
// `ctx.StatusCode` method.
|
||||||
FireErrorCode(ctx Context)
|
FireErrorCode(ctx Context)
|
||||||
|
|
||||||
// RouteExists reports whether a particular route exists
|
// RouteExists reports whether a particular route exists
|
||||||
|
|
|
@ -39,6 +39,18 @@ type ConfigurationReadOnly interface {
|
||||||
GetForceLowercaseRouting() bool
|
GetForceLowercaseRouting() bool
|
||||||
// GetFireMethodNotAllowed returns the configuration.FireMethodNotAllowed.
|
// GetFireMethodNotAllowed returns the configuration.FireMethodNotAllowed.
|
||||||
GetFireMethodNotAllowed() bool
|
GetFireMethodNotAllowed() bool
|
||||||
|
// GetDisableAutoFireStatusCode returns the configuration.DisableAutoFireStatusCode.
|
||||||
|
// Returns true when the http error status code handler automatic execution turned off.
|
||||||
|
GetDisableAutoFireStatusCode() bool
|
||||||
|
// ResetOnFireErrorCode if true then any previously response body or headers through
|
||||||
|
// response recorder or gzip writer will be ignored and the router
|
||||||
|
// will fire the registered (or default) HTTP error handler instead.
|
||||||
|
// See `core/router/handler#FireErrorCode` and `Context.EndRequest` for more details.
|
||||||
|
//
|
||||||
|
// Read more at: https://github.com/kataras/iris/issues/1531
|
||||||
|
//
|
||||||
|
// Defaults to false.
|
||||||
|
GetResetOnFireErrorCode() bool
|
||||||
|
|
||||||
// GetEnableOptimizations returns whether
|
// GetEnableOptimizations returns whether
|
||||||
// the application has performance optimizations enabled.
|
// the application has performance optimizations enabled.
|
||||||
|
@ -57,9 +69,6 @@ type ConfigurationReadOnly interface {
|
||||||
// If true then the `context.ReadBody/ReadForm` will return an `iris.ErrEmptyForm`
|
// If true then the `context.ReadBody/ReadForm` will return an `iris.ErrEmptyForm`
|
||||||
// on empty request form data.
|
// on empty request form data.
|
||||||
GetFireEmptyFormError() bool
|
GetFireEmptyFormError() bool
|
||||||
// GetDisableAutoFireStatusCode returns the configuration.DisableAutoFireStatusCode.
|
|
||||||
// Returns true when the http error status code handler automatic execution turned off.
|
|
||||||
GetDisableAutoFireStatusCode() bool
|
|
||||||
|
|
||||||
// GetTimeFormat returns the configuration.TimeFormat,
|
// GetTimeFormat returns the configuration.TimeFormat,
|
||||||
// format for any kind of datetime parsing.
|
// format for any kind of datetime parsing.
|
||||||
|
|
|
@ -1287,21 +1287,6 @@ func (ctx *context) BeginRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx.writer.BeginResponse(w)
|
ctx.writer.BeginResponse(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatusCodeNotSuccessful defines if a specific "statusCode" is not
|
|
||||||
// a valid status code for a successful response.
|
|
||||||
// It defaults to < 200 || >= 400
|
|
||||||
//
|
|
||||||
// Read more at `iris#DisableAutoFireStatusCode`, `iris/core/router#ErrorCodeHandler`
|
|
||||||
// and `iris/core/router#OnAnyErrorCode` for relative information.
|
|
||||||
//
|
|
||||||
// Do NOT change it.
|
|
||||||
//
|
|
||||||
// It's exported for extreme situations--special needs only, when the Iris server and the client
|
|
||||||
// is not following the RFC: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
|
||||||
var StatusCodeNotSuccessful = func(statusCode int) bool {
|
|
||||||
return statusCode < 200 || statusCode >= 400
|
|
||||||
}
|
|
||||||
|
|
||||||
// EndRequest is executing once after a response to the request was sent and this context is useless or released.
|
// EndRequest is executing once after a response to the request was sent and this context is useless or released.
|
||||||
// Do NOT call it manually. Framework calls it automatically.
|
// Do NOT call it manually. Framework calls it automatically.
|
||||||
//
|
//
|
||||||
|
@ -1313,29 +1298,9 @@ func (ctx *context) EndRequest() {
|
||||||
ctx.deferFunc(ctx)
|
ctx.deferFunc(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ctx.Application().ConfigurationReadOnly().GetDisableAutoFireStatusCode() &&
|
if !ctx.app.ConfigurationReadOnly().GetDisableAutoFireStatusCode() &&
|
||||||
StatusCodeNotSuccessful(ctx.GetStatusCode()) {
|
StatusCodeNotSuccessful(ctx.GetStatusCode()) {
|
||||||
// author's note:
|
ctx.app.FireErrorCode(ctx)
|
||||||
// if recording, the error handler can handle
|
|
||||||
// the rollback and remove any response written before,
|
|
||||||
// we don't have to do anything here, written is <=0 (-1 for default empty, even no status code)
|
|
||||||
// when Recording
|
|
||||||
// because we didn't flush the response yet
|
|
||||||
// if !recording then check if the previous handler didn't send something
|
|
||||||
// to the client.
|
|
||||||
if ctx.writer.Written() <= 0 {
|
|
||||||
// Author's notes:
|
|
||||||
// previously: == -1,
|
|
||||||
// <=0 means even if empty write called which has meaning;
|
|
||||||
// rel: core/router/status.go#Fire-else
|
|
||||||
// mvc/activator/funcmethod/func_result_dispatcher.go#DispatchCommon-write
|
|
||||||
// mvc/response.go#defaultFailureResponse - no text given but
|
|
||||||
// status code should be fired, but it couldn't because of the .Write
|
|
||||||
// action, the .Written() was 0 even on empty response, this 0 means that
|
|
||||||
// a status code given, the previous check of the "== -1" didn't make check for that,
|
|
||||||
// we do now.
|
|
||||||
ctx.Application().FireErrorCode(ctx)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.writer.FlushResponse()
|
ctx.writer.FlushResponse()
|
||||||
|
@ -1779,7 +1744,7 @@ func (ctx *context) Method() string {
|
||||||
// Path returns the full request path,
|
// Path returns the full request path,
|
||||||
// escaped if EnablePathEscape config field is true.
|
// escaped if EnablePathEscape config field is true.
|
||||||
func (ctx *context) Path() string {
|
func (ctx *context) Path() string {
|
||||||
return ctx.RequestPath(ctx.Application().ConfigurationReadOnly().GetEnablePathEscape())
|
return ctx.RequestPath(ctx.app.ConfigurationReadOnly().GetEnablePathEscape())
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeQuery returns the uri parameter as url (string)
|
// DecodeQuery returns the uri parameter as url (string)
|
||||||
|
@ -1861,7 +1826,7 @@ func (ctx *context) Subdomain() (subdomain string) {
|
||||||
|
|
||||||
// listening on mydomain.com:80
|
// listening on mydomain.com:80
|
||||||
// subdomain = mydomain, but it's wrong, it should return ""
|
// subdomain = mydomain, but it's wrong, it should return ""
|
||||||
vhost := ctx.Application().ConfigurationReadOnly().GetVHost()
|
vhost := ctx.app.ConfigurationReadOnly().GetVHost()
|
||||||
if strings.Contains(vhost, subdomain) { // then it's not subdomain
|
if strings.Contains(vhost, subdomain) { // then it's not subdomain
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -1875,7 +1840,7 @@ func (ctx *context) Subdomain() (subdomain string) {
|
||||||
// Order may change.
|
// Order may change.
|
||||||
// Example: https://github.com/kataras/iris/tree/master/_examples/routing/intelligence/manual
|
// Example: https://github.com/kataras/iris/tree/master/_examples/routing/intelligence/manual
|
||||||
func (ctx *context) FindClosest(n int) []string {
|
func (ctx *context) FindClosest(n int) []string {
|
||||||
return ctx.Application().FindClosestPaths(ctx.Subdomain(), ctx.Path(), n)
|
return ctx.app.FindClosestPaths(ctx.Subdomain(), ctx.Path(), n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsWWW returns true if the current subdomain (if any) is www.
|
// IsWWW returns true if the current subdomain (if any) is www.
|
||||||
|
@ -1883,7 +1848,7 @@ func (ctx *context) IsWWW() bool {
|
||||||
host := ctx.Host()
|
host := ctx.Host()
|
||||||
if index := strings.IndexByte(host, '.'); index > 0 {
|
if index := strings.IndexByte(host, '.'); index > 0 {
|
||||||
// if it has a subdomain and it's www then return true.
|
// if it has a subdomain and it's www then return true.
|
||||||
if subdomain := host[0:index]; !strings.Contains(ctx.Application().ConfigurationReadOnly().GetVHost(), subdomain) {
|
if subdomain := host[0:index]; !strings.Contains(ctx.app.ConfigurationReadOnly().GetVHost(), subdomain) {
|
||||||
return subdomain == "www"
|
return subdomain == "www"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1910,8 +1875,8 @@ const xForwardedForHeaderKey = "X-Forwarded-For"
|
||||||
// `Configuration.WithoutRemoteAddrHeader(...)` and
|
// `Configuration.WithoutRemoteAddrHeader(...)` and
|
||||||
// `Configuration.RemoteAddrPrivateSubnets` for more.
|
// `Configuration.RemoteAddrPrivateSubnets` for more.
|
||||||
func (ctx *context) RemoteAddr() string {
|
func (ctx *context) RemoteAddr() string {
|
||||||
remoteHeaders := ctx.Application().ConfigurationReadOnly().GetRemoteAddrHeaders()
|
remoteHeaders := ctx.app.ConfigurationReadOnly().GetRemoteAddrHeaders()
|
||||||
privateSubnets := ctx.Application().ConfigurationReadOnly().GetRemoteAddrPrivateSubnets()
|
privateSubnets := ctx.app.ConfigurationReadOnly().GetRemoteAddrPrivateSubnets()
|
||||||
|
|
||||||
for headerName, enabled := range remoteHeaders {
|
for headerName, enabled := range remoteHeaders {
|
||||||
if !enabled {
|
if !enabled {
|
||||||
|
@ -2102,7 +2067,7 @@ func (ctx *context) GetLocale() Locale {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if locale := ctx.Application().I18nReadOnly().GetLocale(ctx); locale != nil {
|
if locale := ctx.app.I18nReadOnly().GetLocale(ctx); locale != nil {
|
||||||
ctx.values.Set(contextKey, locale)
|
ctx.values.Set(contextKey, locale)
|
||||||
return locale
|
return locale
|
||||||
}
|
}
|
||||||
|
@ -2125,7 +2090,7 @@ func (ctx *context) Tr(format string, args ...interface{}) string { // other nam
|
||||||
// SetVersion force-sets the API Version integrated with the "iris/versioning" subpackage.
|
// SetVersion force-sets the API Version integrated with the "iris/versioning" subpackage.
|
||||||
// It can be used inside a middleare.
|
// It can be used inside a middleare.
|
||||||
func (ctx *context) SetVersion(constraint string) {
|
func (ctx *context) SetVersion(constraint string) {
|
||||||
ctx.values.Set(ctx.Application().ConfigurationReadOnly().GetVersionContextKey(), constraint)
|
ctx.values.Set(ctx.app.ConfigurationReadOnly().GetVersionContextKey(), constraint)
|
||||||
}
|
}
|
||||||
|
|
||||||
// +------------------------------------------------------------+
|
// +------------------------------------------------------------+
|
||||||
|
@ -2150,7 +2115,7 @@ func shouldAppendCharset(cType string) bool {
|
||||||
|
|
||||||
func (ctx *context) contentTypeOnce(cType string, charset string) {
|
func (ctx *context) contentTypeOnce(cType string, charset string) {
|
||||||
if charset == "" {
|
if charset == "" {
|
||||||
charset = ctx.Application().ConfigurationReadOnly().GetCharset()
|
charset = ctx.app.ConfigurationReadOnly().GetCharset()
|
||||||
}
|
}
|
||||||
|
|
||||||
if shouldAppendCharset(cType) {
|
if shouldAppendCharset(cType) {
|
||||||
|
@ -2180,7 +2145,7 @@ func (ctx *context) ContentType(cType string) {
|
||||||
// if doesn't contain a charset already then append it
|
// if doesn't contain a charset already then append it
|
||||||
if !strings.Contains(cType, "charset") {
|
if !strings.Contains(cType, "charset") {
|
||||||
if shouldAppendCharset(cType) {
|
if shouldAppendCharset(cType) {
|
||||||
cType += "; charset=" + ctx.Application().ConfigurationReadOnly().GetCharset()
|
cType += "; charset=" + ctx.app.ConfigurationReadOnly().GetCharset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2457,7 +2422,7 @@ func (ctx *context) FormValues() map[string][]string {
|
||||||
// Form contains the parsed form data, including both the URL
|
// Form contains the parsed form data, including both the URL
|
||||||
// field's query parameters and the POST or PUT form data.
|
// field's query parameters and the POST or PUT form data.
|
||||||
func (ctx *context) form() (form map[string][]string, found bool) {
|
func (ctx *context) form() (form map[string][]string, found bool) {
|
||||||
return GetForm(ctx.request, ctx.Application().ConfigurationReadOnly().GetPostMaxMemory(), ctx.Application().ConfigurationReadOnly().GetDisableBodyConsumptionOnUnmarshal())
|
return GetForm(ctx.request, ctx.app.ConfigurationReadOnly().GetPostMaxMemory(), ctx.app.ConfigurationReadOnly().GetDisableBodyConsumptionOnUnmarshal())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetForm returns the request form (url queries, post or multipart) values.
|
// GetForm returns the request form (url queries, post or multipart) values.
|
||||||
|
@ -2661,7 +2626,7 @@ func (ctx *context) FormFile(key string) (multipart.File, *multipart.FileHeader,
|
||||||
// here but do it in order to apply the post limit,
|
// here but do it in order to apply the post limit,
|
||||||
// the internal request.FormFile will not do it if that's filled
|
// the internal request.FormFile will not do it if that's filled
|
||||||
// and it's not a stream body.
|
// and it's not a stream body.
|
||||||
if err := ctx.request.ParseMultipartForm(ctx.Application().ConfigurationReadOnly().GetPostMaxMemory()); err != nil {
|
if err := ctx.request.ParseMultipartForm(ctx.app.ConfigurationReadOnly().GetPostMaxMemory()); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2694,7 +2659,7 @@ func (ctx *context) FormFile(key string) (multipart.File, *multipart.FileHeader,
|
||||||
//
|
//
|
||||||
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/upload-files
|
// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/upload-files
|
||||||
func (ctx *context) UploadFormFiles(destDirectory string, before ...func(Context, *multipart.FileHeader)) (n int64, err error) {
|
func (ctx *context) UploadFormFiles(destDirectory string, before ...func(Context, *multipart.FileHeader)) (n int64, err error) {
|
||||||
err = ctx.request.ParseMultipartForm(ctx.Application().ConfigurationReadOnly().GetPostMaxMemory())
|
err = ctx.request.ParseMultipartForm(ctx.app.ConfigurationReadOnly().GetPostMaxMemory())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -2840,7 +2805,7 @@ func (ctx *context) SetMaxRequestBodySize(limitOverBytes int64) {
|
||||||
//
|
//
|
||||||
// However, whenever you can use the `ctx.Request().Body` instead.
|
// However, whenever you can use the `ctx.Request().Body` instead.
|
||||||
func (ctx *context) GetBody() ([]byte, error) {
|
func (ctx *context) GetBody() ([]byte, error) {
|
||||||
return GetBody(ctx.request, ctx.Application().ConfigurationReadOnly().GetDisableBodyConsumptionOnUnmarshal())
|
return GetBody(ctx.request, ctx.app.ConfigurationReadOnly().GetDisableBodyConsumptionOnUnmarshal())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBody reads and returns the request body.
|
// GetBody reads and returns the request body.
|
||||||
|
@ -2907,11 +2872,11 @@ func (ctx *context) UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.Application().Validate(outPtr)
|
return ctx.app.Validate(outPtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *context) shouldOptimize() bool {
|
func (ctx *context) shouldOptimize() bool {
|
||||||
return ctx.Application().ConfigurationReadOnly().GetEnableOptimizations()
|
return ctx.app.ConfigurationReadOnly().GetEnableOptimizations()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadJSON reads JSON from request's body and binds it to a value of any json-valid type.
|
// ReadJSON reads JSON from request's body and binds it to a value of any json-valid type.
|
||||||
|
@ -2962,7 +2927,7 @@ var ErrEmptyForm = errors.New("empty form")
|
||||||
func (ctx *context) ReadForm(formObject interface{}) error {
|
func (ctx *context) ReadForm(formObject interface{}) error {
|
||||||
values := ctx.FormValues()
|
values := ctx.FormValues()
|
||||||
if len(values) == 0 {
|
if len(values) == 0 {
|
||||||
if ctx.Application().ConfigurationReadOnly().GetFireEmptyFormError() {
|
if ctx.app.ConfigurationReadOnly().GetFireEmptyFormError() {
|
||||||
return ErrEmptyForm
|
return ErrEmptyForm
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -2973,7 +2938,7 @@ func (ctx *context) ReadForm(formObject interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.Application().Validate(formObject)
|
return ctx.app.Validate(formObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadQuery binds url query to "ptr". The struct field tag is "url".
|
// ReadQuery binds url query to "ptr". The struct field tag is "url".
|
||||||
|
@ -2990,7 +2955,7 @@ func (ctx *context) ReadQuery(ptr interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.Application().Validate(ptr)
|
return ctx.app.Validate(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadProtobuf binds the body to the "ptr" of a proto Message and returns any error.
|
// ReadProtobuf binds the body to the "ptr" of a proto Message and returns any error.
|
||||||
|
@ -3015,7 +2980,7 @@ func (ctx *context) ReadMsgPack(ptr interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.Application().Validate(ptr)
|
return ctx.app.Validate(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadBody binds the request body to the "ptr" depending on the HTTP Method and the Request's Content-Type.
|
// ReadBody binds the request body to the "ptr" depending on the HTTP Method and the Request's Content-Type.
|
||||||
|
@ -3434,7 +3399,7 @@ const (
|
||||||
//
|
//
|
||||||
// Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
|
// Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
|
||||||
func (ctx *context) ViewLayout(layoutTmplFile string) {
|
func (ctx *context) ViewLayout(layoutTmplFile string) {
|
||||||
ctx.values.Set(ctx.Application().ConfigurationReadOnly().GetViewLayoutContextKey(), layoutTmplFile)
|
ctx.values.Set(ctx.app.ConfigurationReadOnly().GetViewLayoutContextKey(), layoutTmplFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ViewData saves one or more key-value pair in order to be passed if and when .View
|
// ViewData saves one or more key-value pair in order to be passed if and when .View
|
||||||
|
@ -3456,7 +3421,7 @@ func (ctx *context) ViewLayout(layoutTmplFile string) {
|
||||||
//
|
//
|
||||||
// Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
|
// Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
|
||||||
func (ctx *context) ViewData(key string, value interface{}) {
|
func (ctx *context) ViewData(key string, value interface{}) {
|
||||||
viewDataContextKey := ctx.Application().ConfigurationReadOnly().GetViewDataContextKey()
|
viewDataContextKey := ctx.app.ConfigurationReadOnly().GetViewDataContextKey()
|
||||||
if key == "" {
|
if key == "" {
|
||||||
ctx.values.Set(viewDataContextKey, value)
|
ctx.values.Set(viewDataContextKey, value)
|
||||||
return
|
return
|
||||||
|
@ -3485,7 +3450,7 @@ func (ctx *context) ViewData(key string, value interface{}) {
|
||||||
// Similarly to `viewData := ctx.Values().Get("iris.viewData")` or
|
// Similarly to `viewData := ctx.Values().Get("iris.viewData")` or
|
||||||
// `viewData := ctx.Values().Get(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey())`.
|
// `viewData := ctx.Values().Get(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey())`.
|
||||||
func (ctx *context) GetViewData() map[string]interface{} {
|
func (ctx *context) GetViewData() map[string]interface{} {
|
||||||
viewDataContextKey := ctx.Application().ConfigurationReadOnly().GetViewDataContextKey()
|
viewDataContextKey := ctx.app.ConfigurationReadOnly().GetViewDataContextKey()
|
||||||
v := ctx.values.Get(viewDataContextKey)
|
v := ctx.values.Get(viewDataContextKey)
|
||||||
|
|
||||||
// if no values found, then return nil
|
// if no values found, then return nil
|
||||||
|
@ -3527,7 +3492,7 @@ func (ctx *context) GetViewData() map[string]interface{} {
|
||||||
// Examples: https://github.com/kataras/iris/tree/master/_examples/view
|
// Examples: https://github.com/kataras/iris/tree/master/_examples/view
|
||||||
func (ctx *context) View(filename string, optionalViewModel ...interface{}) error {
|
func (ctx *context) View(filename string, optionalViewModel ...interface{}) error {
|
||||||
ctx.ContentType(ContentHTMLHeaderValue)
|
ctx.ContentType(ContentHTMLHeaderValue)
|
||||||
cfg := ctx.Application().ConfigurationReadOnly()
|
cfg := ctx.app.ConfigurationReadOnly()
|
||||||
|
|
||||||
layout := ctx.values.GetString(cfg.GetViewLayoutContextKey())
|
layout := ctx.values.GetString(cfg.GetViewLayoutContextKey())
|
||||||
|
|
||||||
|
@ -3539,7 +3504,7 @@ func (ctx *context) View(filename string, optionalViewModel ...interface{}) erro
|
||||||
bindingData = ctx.values.Get(cfg.GetViewDataContextKey())
|
bindingData = ctx.values.Get(cfg.GetViewDataContextKey())
|
||||||
}
|
}
|
||||||
|
|
||||||
err := ctx.Application().View(ctx, filename, layout, bindingData)
|
err := ctx.app.View(ctx, filename, layout, bindingData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.StatusCode(http.StatusInternalServerError)
|
ctx.StatusCode(http.StatusInternalServerError)
|
||||||
ctx.StopExecution()
|
ctx.StopExecution()
|
||||||
|
@ -3763,7 +3728,7 @@ func (ctx *context) JSON(v interface{}, opts ...JSON) (n int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Application().Logger().Debugf("JSON: %v", err)
|
ctx.app.Logger().Debugf("JSON: %v", err)
|
||||||
ctx.StatusCode(http.StatusInternalServerError) // it handles the fallback to normal mode here which also removes the gzip headers.
|
ctx.StatusCode(http.StatusInternalServerError) // it handles the fallback to normal mode here which also removes the gzip headers.
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -3772,7 +3737,7 @@ func (ctx *context) JSON(v interface{}, opts ...JSON) (n int, err error) {
|
||||||
|
|
||||||
n, err = WriteJSON(ctx.writer, v, options, ctx.shouldOptimize())
|
n, err = WriteJSON(ctx.writer, v, options, ctx.shouldOptimize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Application().Logger().Debugf("JSON: %v", err)
|
ctx.app.Logger().Debugf("JSON: %v", err)
|
||||||
ctx.StatusCode(http.StatusInternalServerError)
|
ctx.StatusCode(http.StatusInternalServerError)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -3838,7 +3803,7 @@ func (ctx *context) JSONP(v interface{}, opts ...JSONP) (int, error) {
|
||||||
|
|
||||||
n, err := WriteJSONP(ctx.writer, v, options, ctx.shouldOptimize())
|
n, err := WriteJSONP(ctx.writer, v, options, ctx.shouldOptimize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Application().Logger().Debugf("JSONP: %v", err)
|
ctx.app.Logger().Debugf("JSONP: %v", err)
|
||||||
ctx.StatusCode(http.StatusInternalServerError)
|
ctx.StatusCode(http.StatusInternalServerError)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -3935,7 +3900,7 @@ func (ctx *context) XML(v interface{}, opts ...XML) (int, error) {
|
||||||
|
|
||||||
n, err := WriteXML(ctx.writer, v, options, ctx.shouldOptimize())
|
n, err := WriteXML(ctx.writer, v, options, ctx.shouldOptimize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Application().Logger().Debugf("XML: %v", err)
|
ctx.app.Logger().Debugf("XML: %v", err)
|
||||||
ctx.StatusCode(http.StatusInternalServerError)
|
ctx.StatusCode(http.StatusInternalServerError)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -4015,7 +3980,7 @@ func (ctx *context) Markdown(markdownB []byte, opts ...Markdown) (int, error) {
|
||||||
|
|
||||||
n, err := WriteMarkdown(ctx.writer, markdownB, options)
|
n, err := WriteMarkdown(ctx.writer, markdownB, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Application().Logger().Debugf("Markdown: %v", err)
|
ctx.app.Logger().Debugf("Markdown: %v", err)
|
||||||
ctx.StatusCode(http.StatusInternalServerError)
|
ctx.StatusCode(http.StatusInternalServerError)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -4027,7 +3992,7 @@ func (ctx *context) Markdown(markdownB []byte, opts ...Markdown) (int, error) {
|
||||||
func (ctx *context) YAML(v interface{}) (int, error) {
|
func (ctx *context) YAML(v interface{}) (int, error) {
|
||||||
out, err := yaml.Marshal(v)
|
out, err := yaml.Marshal(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Application().Logger().Debugf("YAML: %v", err)
|
ctx.app.Logger().Debugf("YAML: %v", err)
|
||||||
ctx.StatusCode(http.StatusInternalServerError)
|
ctx.StatusCode(http.StatusInternalServerError)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -4225,7 +4190,7 @@ func (ctx *context) Negotiate(v interface{}) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if charset == "" {
|
if charset == "" {
|
||||||
charset = ctx.Application().ConfigurationReadOnly().GetCharset()
|
charset = ctx.app.ConfigurationReadOnly().GetCharset()
|
||||||
}
|
}
|
||||||
|
|
||||||
if encoding == "gzip" {
|
if encoding == "gzip" {
|
||||||
|
@ -5410,7 +5375,7 @@ func (ctx *context) BeginTransaction(pipe func(t *Transaction)) {
|
||||||
t := newTransaction(ctx) // it calls this *context, so the overriding with a new pool's New of context.Context wil not work here.
|
t := newTransaction(ctx) // it calls this *context, so the overriding with a new pool's New of context.Context wil not work here.
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
ctx.Application().Logger().Warn(fmt.Errorf("recovery from panic: %w", ErrTransactionInterrupt))
|
ctx.app.Logger().Warn(fmt.Errorf("recovery from panic: %w", ErrTransactionInterrupt))
|
||||||
// complete (again or not , doesn't matters) the scope without loud
|
// complete (again or not , doesn't matters) the scope without loud
|
||||||
t.Complete(nil)
|
t.Complete(nil)
|
||||||
// we continue as normal, no need to return here*
|
// we continue as normal, no need to return here*
|
||||||
|
@ -5498,7 +5463,7 @@ func (ctx *context) Exec(method string, path string) {
|
||||||
|
|
||||||
// execute the route from the (internal) context router
|
// execute the route from the (internal) context router
|
||||||
// this way we keep the sessions and the values
|
// this way we keep the sessions and the values
|
||||||
ctx.Application().ServeHTTPC(ctx)
|
ctx.app.ServeHTTPC(ctx)
|
||||||
|
|
||||||
// set the request back to its previous state
|
// set the request back to its previous state
|
||||||
req.RequestURI = backupPath
|
req.RequestURI = backupPath
|
||||||
|
@ -5513,7 +5478,7 @@ func (ctx *context) Exec(method string, path string) {
|
||||||
// RouteExists reports whether a particular route exists
|
// RouteExists reports whether a particular route exists
|
||||||
// It will search from the current subdomain of context's host, if not inside the root domain.
|
// It will search from the current subdomain of context's host, if not inside the root domain.
|
||||||
func (ctx *context) RouteExists(method, path string) bool {
|
func (ctx *context) RouteExists(method, path string) bool {
|
||||||
return ctx.Application().RouteExists(ctx, method, path)
|
return ctx.app.RouteExists(ctx, method, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -117,8 +117,9 @@ func (w *GzipResponseWriter) Write(contents []byte) (int, error) {
|
||||||
func (w *GzipResponseWriter) Writef(format string, a ...interface{}) (n int, err error) {
|
func (w *GzipResponseWriter) Writef(format string, a ...interface{}) (n int, err error) {
|
||||||
n, err = fmt.Fprintf(w, format, a...)
|
n, err = fmt.Fprintf(w, format, a...)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if w.ResponseWriter.Header()[ContentTypeHeaderKey] == nil {
|
h := w.ResponseWriter.Header()
|
||||||
w.ResponseWriter.Header().Set(ContentTypeHeaderKey, ContentTextHeaderValue)
|
if h[ContentTypeHeaderKey] == nil {
|
||||||
|
h.Set(ContentTypeHeaderKey, ContentTextHeaderValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,8 +131,9 @@ func (w *GzipResponseWriter) Writef(format string, a ...interface{}) (n int, err
|
||||||
func (w *GzipResponseWriter) WriteString(s string) (n int, err error) {
|
func (w *GzipResponseWriter) WriteString(s string) (n int, err error) {
|
||||||
n, err = w.Write([]byte(s))
|
n, err = w.Write([]byte(s))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if w.ResponseWriter.Header()[ContentTypeHeaderKey] == nil {
|
h := w.ResponseWriter.Header()
|
||||||
w.ResponseWriter.Header().Set(ContentTypeHeaderKey, ContentTextHeaderValue)
|
if h[ContentTypeHeaderKey] == nil {
|
||||||
|
h.Set(ContentTypeHeaderKey, ContentTextHeaderValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -183,11 +185,10 @@ func AddGzipHeaders(w ResponseWriter) {
|
||||||
w.Header().Add(ContentEncodingHeaderKey, GzipHeaderValue)
|
w.Header().Add(ContentEncodingHeaderKey, GzipHeaderValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlushResponse validates the response headers in order to be compatible with the gzip written data
|
// Body returns the body tracked from the writer so far,
|
||||||
// and writes the data to the underline ResponseWriter.
|
// do not use this for edit.
|
||||||
func (w *GzipResponseWriter) FlushResponse() {
|
func (w *GzipResponseWriter) Body() []byte {
|
||||||
_, _ = w.WriteNow(w.chunks)
|
return w.chunks
|
||||||
w.ResponseWriter.FlushResponse()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetBody resets the response body.
|
// ResetBody resets the response body.
|
||||||
|
@ -200,3 +201,10 @@ func (w *GzipResponseWriter) ResetBody() {
|
||||||
func (w *GzipResponseWriter) Disable() {
|
func (w *GzipResponseWriter) Disable() {
|
||||||
w.disabled = true
|
w.disabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FlushResponse validates the response headers in order to be compatible with the gzip written data
|
||||||
|
// and writes the data to the underline ResponseWriter.
|
||||||
|
func (w *GzipResponseWriter) FlushResponse() {
|
||||||
|
_, _ = w.WriteNow(w.chunks)
|
||||||
|
w.ResponseWriter.FlushResponse()
|
||||||
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ func (w *ResponseRecorder) SetBodyString(s string) {
|
||||||
w.SetBody([]byte(s))
|
w.SetBody([]byte(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Body returns the body tracked from the writer so far
|
// Body returns the body tracked from the writer so far,
|
||||||
// do not use this for edit.
|
// do not use this for edit.
|
||||||
func (w *ResponseRecorder) Body() []byte {
|
func (w *ResponseRecorder) Body() []byte {
|
||||||
return w.chunks
|
return w.chunks
|
||||||
|
@ -190,7 +190,7 @@ func (w *ResponseRecorder) Clone() ResponseWriter {
|
||||||
func (w *ResponseRecorder) WriteTo(res ResponseWriter) {
|
func (w *ResponseRecorder) WriteTo(res ResponseWriter) {
|
||||||
if to, ok := res.(*ResponseRecorder); ok {
|
if to, ok := res.(*ResponseRecorder); ok {
|
||||||
|
|
||||||
// set the status code, to is first ( probably an error? (context.StatusCodeNotSuccessful, defaults to < 200 || >= 400).
|
// set the status code, to is first ( probably an error? (context.StatusCodeNotSuccessful, defaults to >=400).
|
||||||
if statusCode := w.ResponseWriter.StatusCode(); statusCode == defaultStatusCode {
|
if statusCode := w.ResponseWriter.StatusCode(); statusCode == defaultStatusCode {
|
||||||
to.WriteHeader(statusCode)
|
to.WriteHeader(statusCode)
|
||||||
}
|
}
|
||||||
|
|
117
context/status.go
Normal file
117
context/status.go
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
package context
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// ClientErrorCodes holds the 4xx Client errors.
|
||||||
|
var (
|
||||||
|
ClientErrorCodes = []int{
|
||||||
|
http.StatusBadRequest,
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
http.StatusPaymentRequired,
|
||||||
|
http.StatusForbidden,
|
||||||
|
http.StatusNotFound,
|
||||||
|
http.StatusMethodNotAllowed,
|
||||||
|
http.StatusNotAcceptable,
|
||||||
|
http.StatusProxyAuthRequired,
|
||||||
|
http.StatusRequestTimeout,
|
||||||
|
http.StatusConflict,
|
||||||
|
http.StatusGone,
|
||||||
|
http.StatusLengthRequired,
|
||||||
|
http.StatusPreconditionFailed,
|
||||||
|
http.StatusRequestEntityTooLarge,
|
||||||
|
http.StatusRequestURITooLong,
|
||||||
|
http.StatusUnsupportedMediaType,
|
||||||
|
http.StatusRequestedRangeNotSatisfiable,
|
||||||
|
http.StatusExpectationFailed,
|
||||||
|
http.StatusTeapot,
|
||||||
|
http.StatusMisdirectedRequest,
|
||||||
|
http.StatusUnprocessableEntity,
|
||||||
|
http.StatusLocked,
|
||||||
|
http.StatusFailedDependency,
|
||||||
|
http.StatusTooEarly,
|
||||||
|
http.StatusUpgradeRequired,
|
||||||
|
http.StatusPreconditionRequired,
|
||||||
|
http.StatusTooManyRequests,
|
||||||
|
http.StatusRequestHeaderFieldsTooLarge,
|
||||||
|
http.StatusUnavailableForLegalReasons,
|
||||||
|
// Unofficial.
|
||||||
|
StatusPageExpired,
|
||||||
|
StatusBlockedByWindowsParentalControls,
|
||||||
|
StatusInvalidToken,
|
||||||
|
StatusTokenRequired,
|
||||||
|
}
|
||||||
|
// ServerErrorCodes holds the 5xx Server errors.
|
||||||
|
ServerErrorCodes = []int{
|
||||||
|
http.StatusInternalServerError,
|
||||||
|
http.StatusNotImplemented,
|
||||||
|
http.StatusBadGateway,
|
||||||
|
http.StatusServiceUnavailable,
|
||||||
|
http.StatusGatewayTimeout,
|
||||||
|
http.StatusHTTPVersionNotSupported,
|
||||||
|
http.StatusVariantAlsoNegotiates,
|
||||||
|
http.StatusInsufficientStorage,
|
||||||
|
http.StatusLoopDetected,
|
||||||
|
http.StatusNotExtended,
|
||||||
|
http.StatusNetworkAuthenticationRequired,
|
||||||
|
// Unofficial.
|
||||||
|
StatusBandwidthLimitExceeded,
|
||||||
|
StatusInvalidSSLCertificate,
|
||||||
|
StatusSiteOverloaded,
|
||||||
|
StatusSiteFrozen,
|
||||||
|
StatusNetworkReadTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientAndServerErrorCodes is the static list of all client and server error codes.
|
||||||
|
ClientAndServerErrorCodes = append(ClientErrorCodes, ServerErrorCodes...)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Unofficial status error codes.
|
||||||
|
const (
|
||||||
|
// 4xx
|
||||||
|
StatusPageExpired = 419
|
||||||
|
StatusBlockedByWindowsParentalControls = 450
|
||||||
|
StatusInvalidToken = 498
|
||||||
|
StatusTokenRequired = 499
|
||||||
|
// 5xx
|
||||||
|
StatusBandwidthLimitExceeded = 509
|
||||||
|
StatusInvalidSSLCertificate = 526
|
||||||
|
StatusSiteOverloaded = 529
|
||||||
|
StatusSiteFrozen = 530
|
||||||
|
StatusNetworkReadTimeout = 598
|
||||||
|
)
|
||||||
|
|
||||||
|
var unofficialStatusText = map[int]string{
|
||||||
|
StatusPageExpired: "Page Expired",
|
||||||
|
StatusBlockedByWindowsParentalControls: "Blocked by Windows Parental Controls",
|
||||||
|
StatusInvalidToken: "Invalid Token",
|
||||||
|
StatusTokenRequired: "Token Required",
|
||||||
|
StatusBandwidthLimitExceeded: "Bandwidth Limit Exceeded",
|
||||||
|
StatusInvalidSSLCertificate: "Invalid SSL Certificate",
|
||||||
|
StatusSiteOverloaded: "Site is overloaded",
|
||||||
|
StatusSiteFrozen: "Site is frozen",
|
||||||
|
StatusNetworkReadTimeout: "Network read timeout error",
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusText returns a text for the HTTP status code. It returns the empty
|
||||||
|
// string if the code is unknown.
|
||||||
|
func StatusText(code int) string {
|
||||||
|
text := http.StatusText(code)
|
||||||
|
if text == "" {
|
||||||
|
text = unofficialStatusText[code]
|
||||||
|
}
|
||||||
|
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusCodeNotSuccessful defines if a specific "statusCode" is not
|
||||||
|
// a valid status code for a successful response.
|
||||||
|
// By default if the status code is lower than 400 then it is not a failure one,
|
||||||
|
// otherwise it is considered as an error code.
|
||||||
|
//
|
||||||
|
// Read more at `iris/Configuration#DisableAutoFireStatusCode` and
|
||||||
|
// `iris/core/router/Party#OnAnyErrorCode` for relative information.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Modify this variable when your Iris server or/and client
|
||||||
|
// not follows the RFC: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||||
|
var StatusCodeNotSuccessful = func(statusCode int) bool { return statusCode >= 400 }
|
|
@ -984,109 +984,11 @@ func (api *APIBuilder) OnErrorCode(statusCode int, handlers ...context.Handler)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientErrorCodes holds the 4xx Client errors.
|
|
||||||
var (
|
|
||||||
ClientErrorCodes = []int{
|
|
||||||
http.StatusBadRequest,
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
http.StatusPaymentRequired,
|
|
||||||
http.StatusForbidden,
|
|
||||||
http.StatusNotFound,
|
|
||||||
http.StatusMethodNotAllowed,
|
|
||||||
http.StatusNotAcceptable,
|
|
||||||
http.StatusProxyAuthRequired,
|
|
||||||
http.StatusRequestTimeout,
|
|
||||||
http.StatusConflict,
|
|
||||||
http.StatusGone,
|
|
||||||
http.StatusLengthRequired,
|
|
||||||
http.StatusPreconditionFailed,
|
|
||||||
http.StatusRequestEntityTooLarge,
|
|
||||||
http.StatusRequestURITooLong,
|
|
||||||
http.StatusUnsupportedMediaType,
|
|
||||||
http.StatusRequestedRangeNotSatisfiable,
|
|
||||||
http.StatusExpectationFailed,
|
|
||||||
http.StatusTeapot,
|
|
||||||
http.StatusMisdirectedRequest,
|
|
||||||
http.StatusUnprocessableEntity,
|
|
||||||
http.StatusLocked,
|
|
||||||
http.StatusFailedDependency,
|
|
||||||
http.StatusTooEarly,
|
|
||||||
http.StatusUpgradeRequired,
|
|
||||||
http.StatusPreconditionRequired,
|
|
||||||
http.StatusTooManyRequests,
|
|
||||||
http.StatusRequestHeaderFieldsTooLarge,
|
|
||||||
http.StatusUnavailableForLegalReasons,
|
|
||||||
// Unofficial.
|
|
||||||
StatusPageExpired,
|
|
||||||
StatusBlockedByWindowsParentalControls,
|
|
||||||
StatusInvalidToken,
|
|
||||||
StatusTokenRequired,
|
|
||||||
}
|
|
||||||
// ServerErrorCodes holds the 5xx Server errors.
|
|
||||||
ServerErrorCodes = []int{
|
|
||||||
http.StatusInternalServerError,
|
|
||||||
http.StatusNotImplemented,
|
|
||||||
http.StatusBadGateway,
|
|
||||||
http.StatusServiceUnavailable,
|
|
||||||
http.StatusGatewayTimeout,
|
|
||||||
http.StatusHTTPVersionNotSupported,
|
|
||||||
http.StatusVariantAlsoNegotiates,
|
|
||||||
http.StatusInsufficientStorage,
|
|
||||||
http.StatusLoopDetected,
|
|
||||||
http.StatusNotExtended,
|
|
||||||
http.StatusNetworkAuthenticationRequired,
|
|
||||||
// Unofficial.
|
|
||||||
StatusBandwidthLimitExceeded,
|
|
||||||
StatusInvalidSSLCertificate,
|
|
||||||
StatusSiteOverloaded,
|
|
||||||
StatusSiteFrozen,
|
|
||||||
StatusNetworkReadTimeout,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Unofficial status error codes.
|
|
||||||
const (
|
|
||||||
// 4xx
|
|
||||||
StatusPageExpired = 419
|
|
||||||
StatusBlockedByWindowsParentalControls = 450
|
|
||||||
StatusInvalidToken = 498
|
|
||||||
StatusTokenRequired = 499
|
|
||||||
// 5xx
|
|
||||||
StatusBandwidthLimitExceeded = 509
|
|
||||||
StatusInvalidSSLCertificate = 526
|
|
||||||
StatusSiteOverloaded = 529
|
|
||||||
StatusSiteFrozen = 530
|
|
||||||
StatusNetworkReadTimeout = 598
|
|
||||||
)
|
|
||||||
|
|
||||||
var unofficialStatusText = map[int]string{
|
|
||||||
StatusPageExpired: "Page Expired",
|
|
||||||
StatusBlockedByWindowsParentalControls: "Blocked by Windows Parental Controls",
|
|
||||||
StatusInvalidToken: "Invalid Token",
|
|
||||||
StatusTokenRequired: "Token Required",
|
|
||||||
StatusBandwidthLimitExceeded: "Bandwidth Limit Exceeded",
|
|
||||||
StatusInvalidSSLCertificate: "Invalid SSL Certificate",
|
|
||||||
StatusSiteOverloaded: "Site is overloaded",
|
|
||||||
StatusSiteFrozen: "Site is frozen",
|
|
||||||
StatusNetworkReadTimeout: "Network read timeout error",
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatusText returns a text for the HTTP status code. It returns the empty
|
|
||||||
// string if the code is unknown.
|
|
||||||
func StatusText(code int) string {
|
|
||||||
text := http.StatusText(code)
|
|
||||||
if text == "" {
|
|
||||||
text = unofficialStatusText[code]
|
|
||||||
}
|
|
||||||
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnAnyErrorCode registers a handlers chain for all error codes
|
// OnAnyErrorCode registers a handlers chain for all error codes
|
||||||
// (4xxx and 5xxx, change the `ClientErrorCodes` and `ServerErrorCodes` variables to modify those)
|
// (4xxx and 5xxx, change the `context.ClientErrorCodes` and `context.ServerErrorCodes` variables to modify those)
|
||||||
// Look `OnErrorCode` too.
|
// Look `OnErrorCode` too.
|
||||||
func (api *APIBuilder) OnAnyErrorCode(handlers ...context.Handler) (routes []*Route) {
|
func (api *APIBuilder) OnAnyErrorCode(handlers ...context.Handler) (routes []*Route) {
|
||||||
for _, statusCode := range append(ClientErrorCodes, ServerErrorCodes...) {
|
for _, statusCode := range context.ClientAndServerErrorCodes {
|
||||||
routes = append(routes, api.OnErrorCode(statusCode, handlers...)...)
|
routes = append(routes, api.OnErrorCode(statusCode, handlers...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,8 @@ type (
|
||||||
// HTTPErrorHandler should contain a method `FireErrorCode` which
|
// HTTPErrorHandler should contain a method `FireErrorCode` which
|
||||||
// handles http unsuccessful status codes.
|
// handles http unsuccessful status codes.
|
||||||
HTTPErrorHandler interface {
|
HTTPErrorHandler interface {
|
||||||
|
// FireErrorCode should send an error response to the client based
|
||||||
|
// on the given context's response status code.
|
||||||
FireErrorCode(ctx context.Context)
|
FireErrorCode(ctx context.Context)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -437,27 +439,55 @@ func (h *routerHandler) HandleRequest(ctx context.Context) {
|
||||||
ctx.StatusCode(http.StatusNotFound)
|
ctx.StatusCode(http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func statusCodeSuccessful(statusCode int) bool {
|
||||||
|
return !context.StatusCodeNotSuccessful(statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FireErrorCode handles the response's error response.
|
||||||
|
// If `Configuration.ResetOnFireErrorCode()` is true
|
||||||
|
// and the response writer was a recorder or a gzip writer one
|
||||||
|
// then it will try to reset the headers and the body before calling the
|
||||||
|
// registered (or default) error handler for that error code set by
|
||||||
|
// `ctx.StatusCode` method.
|
||||||
func (h *routerHandler) FireErrorCode(ctx context.Context) {
|
func (h *routerHandler) FireErrorCode(ctx context.Context) {
|
||||||
|
// On common response writer, always check
|
||||||
|
// if we can't reset the body and the body has been filled
|
||||||
|
// which means that the status code already sent,
|
||||||
|
// then do not fire this custom error code,
|
||||||
|
// rel: context/context.go#EndRequest.
|
||||||
|
//
|
||||||
|
// Note that, this is set to 0 on recorder and gzip writer because they cache the response,
|
||||||
|
// so we check their len(Body()) instead, look below.
|
||||||
|
if ctx.ResponseWriter().Written() > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
statusCode := ctx.GetStatusCode() // the response's cached one.
|
statusCode := ctx.GetStatusCode() // the response's cached one.
|
||||||
|
|
||||||
// if we can reset the body
|
if ctx.Application().ConfigurationReadOnly().GetResetOnFireErrorCode() /* could be an argument too but we must not break the method */ {
|
||||||
if w, ok := ctx.IsRecording(); ok {
|
// if we can reset the body, probably manual call of `Application.FireErrorCode`.
|
||||||
if statusCodeSuccessful(w.StatusCode()) { // if not an error status code
|
if w, ok := ctx.IsRecording(); ok {
|
||||||
w.WriteHeader(statusCode) // then set it manually here, otherwise it should be set via ctx.StatusCode(...)
|
if statusCodeSuccessful(w.StatusCode()) { // if not an error status code
|
||||||
|
w.WriteHeader(statusCode) // then set it manually here, otherwise it should be set via ctx.StatusCode(...)
|
||||||
|
}
|
||||||
|
// reset if previous content and it's recorder, keep the status code.
|
||||||
|
w.ClearHeaders()
|
||||||
|
w.ResetBody()
|
||||||
|
} else if w, ok := ctx.ResponseWriter().(*context.GzipResponseWriter); ok {
|
||||||
|
// reset and disable the gzip in order to be an expected form of http error result
|
||||||
|
w.ResetBody()
|
||||||
|
w.Disable()
|
||||||
}
|
}
|
||||||
// reset if previous content and it's recorder, keep the status code.
|
|
||||||
w.ClearHeaders()
|
|
||||||
w.ResetBody()
|
|
||||||
} else if w, ok := ctx.ResponseWriter().(*context.GzipResponseWriter); ok {
|
|
||||||
// reset and disable the gzip in order to be an expected form of http error result
|
|
||||||
w.ResetBody()
|
|
||||||
w.Disable()
|
|
||||||
} else {
|
} else {
|
||||||
// if we can't reset the body and the body has been filled
|
// check if a body already set (the error response is handled by the handler itself, see `Context.EndRequest`)
|
||||||
// which means that the status code already sent,
|
if w, ok := ctx.IsRecording(); ok {
|
||||||
// then do not fire this custom error code.
|
if len(w.Body()) > 0 {
|
||||||
if ctx.ResponseWriter().Written() > 0 { // != -1, rel: context/context.go#EndRequest
|
return
|
||||||
return
|
}
|
||||||
|
} else if w, ok := ctx.ResponseWriter().(*context.GzipResponseWriter); ok {
|
||||||
|
if len(w.Body()) > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,11 +553,7 @@ func (h *routerHandler) FireErrorCode(ctx context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// not error handler found, write a default message.
|
// not error handler found, write a default message.
|
||||||
ctx.WriteString(StatusText(statusCode))
|
ctx.WriteString(context.StatusText(statusCode))
|
||||||
}
|
|
||||||
|
|
||||||
func statusCodeSuccessful(statusCode int) bool {
|
|
||||||
return !context.StatusCodeNotSuccessful(statusCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *routerHandler) subdomainAndPathAndMethodExists(ctx context.Context, t *trie, method, path string) bool {
|
func (h *routerHandler) subdomainAndPathAndMethodExists(ctx context.Context, t *trie, method, path string) bool {
|
||||||
|
|
|
@ -35,11 +35,11 @@ func TestOnAnyErrorCode(t *testing.T) {
|
||||||
ctx.WriteString(expectedFoundResponse)
|
ctx.WriteString(expectedFoundResponse)
|
||||||
})
|
})
|
||||||
|
|
||||||
app.Get("/406", func(ctx context.Context) {
|
expected407 := "this should be sent, we manage the response response by ourselves"
|
||||||
|
app.Get("/407", func(ctx context.Context) {
|
||||||
ctx.Record()
|
ctx.Record()
|
||||||
ctx.WriteString("this should not be sent, only status text will be sent")
|
ctx.WriteString(expected407)
|
||||||
ctx.WriteString("the handler can handle 'rollback' of the text when error code fired because of the recorder")
|
ctx.StatusCode(iris.StatusProxyAuthRequired)
|
||||||
ctx.StatusCode(iris.StatusNotAcceptable)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
e := httptest.New(t, app)
|
e := httptest.New(t, app)
|
||||||
|
@ -57,7 +57,26 @@ func TestOnAnyErrorCode(t *testing.T) {
|
||||||
|
|
||||||
checkAndClearBuf(t, buff, expectedPrintBeforeExecuteErr)
|
checkAndClearBuf(t, buff, expectedPrintBeforeExecuteErr)
|
||||||
|
|
||||||
e.GET("/406").Expect().Status(iris.StatusNotAcceptable).
|
e.GET("/407").Expect().Status(iris.StatusProxyAuthRequired).
|
||||||
|
Body().Equal(expected407)
|
||||||
|
|
||||||
|
// Test Configuration.ResetOnFireErrorCode.
|
||||||
|
app2 := iris.New()
|
||||||
|
app2.Configure(iris.WithResetOnFireErrorCode)
|
||||||
|
|
||||||
|
app2.OnAnyErrorCode(func(ctx context.Context) {
|
||||||
|
buff.WriteString(expectedPrintBeforeExecuteErr)
|
||||||
|
ctx.Next()
|
||||||
|
}, defaultErrHandler)
|
||||||
|
|
||||||
|
app2.Get("/406", func(ctx context.Context) {
|
||||||
|
ctx.Record()
|
||||||
|
ctx.WriteString("this should not be sent, only status text will be sent")
|
||||||
|
ctx.WriteString("the handler can handle 'rollback' of the text when error code fired because of the recorder")
|
||||||
|
ctx.StatusCode(iris.StatusNotAcceptable)
|
||||||
|
})
|
||||||
|
|
||||||
|
httptest.New(t, app2).GET("/406").Expect().Status(iris.StatusNotAcceptable).
|
||||||
Body().Equal(http.StatusText(iris.StatusNotAcceptable))
|
Body().Equal(http.StatusText(iris.StatusNotAcceptable))
|
||||||
|
|
||||||
checkAndClearBuf(t, buff, expectedPrintBeforeExecuteErr)
|
checkAndClearBuf(t, buff, expectedPrintBeforeExecuteErr)
|
||||||
|
|
20
iris.go
20
iris.go
|
@ -95,10 +95,10 @@ const (
|
||||||
StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5
|
StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5
|
||||||
StatusUnavailableForLegalReasons = 451 // RFC 7725, 3
|
StatusUnavailableForLegalReasons = 451 // RFC 7725, 3
|
||||||
// Unofficial Client Errors.
|
// Unofficial Client Errors.
|
||||||
StatusPageExpired = router.StatusPageExpired
|
StatusPageExpired = context.StatusPageExpired
|
||||||
StatusBlockedByWindowsParentalControls = router.StatusBlockedByWindowsParentalControls
|
StatusBlockedByWindowsParentalControls = context.StatusBlockedByWindowsParentalControls
|
||||||
StatusInvalidToken = router.StatusInvalidToken
|
StatusInvalidToken = context.StatusInvalidToken
|
||||||
StatusTokenRequired = router.StatusTokenRequired
|
StatusTokenRequired = context.StatusTokenRequired
|
||||||
//
|
//
|
||||||
StatusInternalServerError = 500 // RFC 7231, 6.6.1
|
StatusInternalServerError = 500 // RFC 7231, 6.6.1
|
||||||
StatusNotImplemented = 501 // RFC 7231, 6.6.2
|
StatusNotImplemented = 501 // RFC 7231, 6.6.2
|
||||||
|
@ -112,18 +112,18 @@ const (
|
||||||
StatusNotExtended = 510 // RFC 2774, 7
|
StatusNotExtended = 510 // RFC 2774, 7
|
||||||
StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
|
StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
|
||||||
// Unofficial Server Errors.
|
// Unofficial Server Errors.
|
||||||
StatusBandwidthLimitExceeded = router.StatusBandwidthLimitExceeded
|
StatusBandwidthLimitExceeded = context.StatusBandwidthLimitExceeded
|
||||||
StatusInvalidSSLCertificate = router.StatusInvalidSSLCertificate
|
StatusInvalidSSLCertificate = context.StatusInvalidSSLCertificate
|
||||||
StatusSiteOverloaded = router.StatusSiteOverloaded
|
StatusSiteOverloaded = context.StatusSiteOverloaded
|
||||||
StatusSiteFrozen = router.StatusSiteFrozen
|
StatusSiteFrozen = context.StatusSiteFrozen
|
||||||
StatusNetworkReadTimeout = router.StatusNetworkReadTimeout
|
StatusNetworkReadTimeout = context.StatusNetworkReadTimeout
|
||||||
)
|
)
|
||||||
|
|
||||||
// StatusText returns a text for the HTTP status code. It returns the empty
|
// StatusText returns a text for the HTTP status code. It returns the empty
|
||||||
// string if the code is unknown.
|
// string if the code is unknown.
|
||||||
//
|
//
|
||||||
// Shortcut for core/router#StatusText.
|
// Shortcut for core/router#StatusText.
|
||||||
var StatusText = router.StatusText
|
var StatusText = context.StatusText
|
||||||
|
|
||||||
// HTTP Methods copied from `net/http`.
|
// HTTP Methods copied from `net/http`.
|
||||||
const (
|
const (
|
||||||
|
|
Loading…
Reference in New Issue
Block a user