Support rewriting status codes in error page middleware
This commit is contained in:
parent
f0849e8ee6
commit
fa76ed57d3
18 changed files with 195 additions and 8 deletions
|
@ -222,10 +222,15 @@ type ErrorPage struct {
|
|||
// as ranges by separating two codes with a dash (500-599),
|
||||
// or a combination of the two (404,418,500-599).
|
||||
Status []string `json:"status,omitempty" toml:"status,omitempty" yaml:"status,omitempty" export:"true"`
|
||||
// StatusRewrites defines a mapping of status codes that should be returned instead of the original error status codes.
|
||||
// For example: "418": 404 or "410-418": 404
|
||||
StatusRewrites map[string]int `json:"statusRewrites,omitempty" toml:"statusRewrites,omitempty" yaml:"statusRewrites,omitempty" export:"true"`
|
||||
// Service defines the name of the service that will serve the error page.
|
||||
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"`
|
||||
// Query defines the URL for the error page (hosted by service).
|
||||
// The {status} variable can be used in order to insert the status code in the URL.
|
||||
// The {originalStatus} variable can be used in order to insert the upstream status code in the URL.
|
||||
// The {url} variable can be used in order to insert the escaped request URL.
|
||||
Query string `json:"query,omitempty" toml:"query,omitempty" yaml:"query,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
|
|
|
@ -313,6 +313,13 @@ func (in *ErrorPage) DeepCopyInto(out *ErrorPage) {
|
|||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.StatusRewrites != nil {
|
||||
in, out := &in.StatusRewrites, &out.StatusRewrites
|
||||
*out = make(map[string]int, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,12 @@ type customErrors struct {
|
|||
backendHandler http.Handler
|
||||
httpCodeRanges types.HTTPCodeRanges
|
||||
backendQuery string
|
||||
statusRewrites []statusRewrite
|
||||
}
|
||||
|
||||
type statusRewrite struct {
|
||||
fromCodes types.HTTPCodeRanges
|
||||
toCode int
|
||||
}
|
||||
|
||||
// New creates a new custom error pages middleware.
|
||||
|
@ -53,12 +59,27 @@ func New(ctx context.Context, next http.Handler, config dynamic.ErrorPage, servi
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Parse StatusRewrites
|
||||
statusRewrites := make([]statusRewrite, 0, len(config.StatusRewrites))
|
||||
for k, v := range config.StatusRewrites {
|
||||
ranges, err := types.NewHTTPCodeRanges([]string{k})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statusRewrites = append(statusRewrites, statusRewrite{
|
||||
fromCodes: ranges,
|
||||
toCode: v,
|
||||
})
|
||||
}
|
||||
|
||||
return &customErrors{
|
||||
name: name,
|
||||
next: next,
|
||||
backendHandler: backend,
|
||||
httpCodeRanges: httpCodeRanges,
|
||||
backendQuery: config.Query,
|
||||
statusRewrites: statusRewrites,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -84,12 +105,28 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
|
||||
// check the recorder code against the configured http status code ranges
|
||||
code := catcher.getCode()
|
||||
logger.Debug().Msgf("Caught HTTP Status Code %d, returning error page", code)
|
||||
|
||||
originalCode := code
|
||||
|
||||
// Check if we need to rewrite the status code
|
||||
for _, rsc := range c.statusRewrites {
|
||||
if rsc.fromCodes.Contains(code) {
|
||||
code = rsc.toCode
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if code != originalCode {
|
||||
logger.Debug().Msgf("Caught HTTP Status Code %d (rewritten to %d), returning error page", originalCode, code)
|
||||
} else {
|
||||
logger.Debug().Msgf("Caught HTTP Status Code %d, returning error page", code)
|
||||
}
|
||||
|
||||
var query string
|
||||
if len(c.backendQuery) > 0 {
|
||||
query = "/" + strings.TrimPrefix(c.backendQuery, "/")
|
||||
query = strings.ReplaceAll(query, "{status}", strconv.Itoa(code))
|
||||
query = strings.ReplaceAll(query, "{originalStatus}", strconv.Itoa(originalCode))
|
||||
query = strings.ReplaceAll(query, "{url}", url.QueryEscape(req.URL.String()))
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ spec:
|
|||
status:
|
||||
- "404"
|
||||
- "500"
|
||||
statusRewrites:
|
||||
404: 200
|
||||
query: query
|
||||
service:
|
||||
name: whoami
|
||||
|
|
|
@ -737,8 +737,9 @@ func (p *Provider) createErrorPageMiddleware(client Client, namespace string, er
|
|||
}
|
||||
|
||||
errorPageMiddleware := &dynamic.ErrorPage{
|
||||
Status: errorPage.Status,
|
||||
Query: errorPage.Query,
|
||||
Status: errorPage.Status,
|
||||
StatusRewrites: errorPage.StatusRewrites,
|
||||
Query: errorPage.Query,
|
||||
}
|
||||
|
||||
cb := configBuilder{
|
||||
|
|
|
@ -4160,7 +4160,10 @@ func TestLoadIngressRoutes(t *testing.T) {
|
|||
Middlewares: map[string]*dynamic.Middleware{
|
||||
"default-errorpage": {
|
||||
Errors: &dynamic.ErrorPage{
|
||||
Status: []string{"404", "500"},
|
||||
Status: []string{"404", "500"},
|
||||
StatusRewrites: map[string]int{
|
||||
"404": 200,
|
||||
},
|
||||
Service: "default-errorpage-errorpage-service",
|
||||
Query: "query",
|
||||
},
|
||||
|
|
|
@ -68,11 +68,16 @@ type ErrorPage struct {
|
|||
// as ranges by separating two codes with a dash (500-599),
|
||||
// or a combination of the two (404,418,500-599).
|
||||
Status []string `json:"status,omitempty"`
|
||||
// StatusRewrites defines a mapping of status codes that should be returned instead of the original error status codes.
|
||||
// For example: "418": 404 or "410-418": 404
|
||||
StatusRewrites map[string]int `json:"statusRewrites,omitempty"`
|
||||
// Service defines the reference to a Kubernetes Service that will serve the error page.
|
||||
// More info: https://doc.traefik.io/traefik/v3.3/middlewares/http/errorpages/#service
|
||||
Service Service `json:"service,omitempty"`
|
||||
// Query defines the URL for the error page (hosted by service).
|
||||
// The {status} variable can be used in order to insert the status code in the URL.
|
||||
// The {originalStatus} variable can be used in order to insert the upstream status code in the URL.
|
||||
// The {url} variable can be used in order to insert the escaped request URL.
|
||||
Query string `json:"query,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
@ -229,6 +229,13 @@ func (in *ErrorPage) DeepCopyInto(out *ErrorPage) {
|
|||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.StatusRewrites != nil {
|
||||
in, out := &in.StatusRewrites, &out.StatusRewrites
|
||||
*out = make(map[string]int, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
in.Service.DeepCopyInto(&out.Service)
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue