-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom Error Pages #1675
Custom Error Pages #1675
Conversation
@bparli, your PR seems good for me. But, I believe the issue 1634 forgets an Use Case : how to specify an error backend for only one error code? [frontends]
[frontends.website]
backend = "website"
[errors]
[error.network]
status = "404"
backend = "error"
query = "/{status}.html"
[frontends.website.routes.website]
rule = "Host: website.mydomain.com"
[backends]
[backends.website]
[backends.website.servers.website]
url = "https://1.2.3.4"
[backends.error]
[backends.error.servers.error]
url = "http://2.3.4.5" Moreover, the best solution may be to specify a list of single code(s) and range (as printed page into printer windows) and configure it as below : [frontends]
[frontends.website]
backend = "website"
[errors]
[error.network]
status = "401,404,[500-600]"
backend = "error"
query = "/{status}.html"
[frontends.website.routes.website]
rule = "Host: website.mydomain.com"
[backends]
[backends.website]
[backends.website.servers.website]
url = "https://1.2.3.4"
[backends.error]
[backends.error.servers.error]
url = "http://2.3.4.5" I guess the second suggestion is a little bit harder to develop. @emilevauge, @bparli, what is your mind about this? |
Thanks for taking a look @nmengin The concern you raise did actually cross my mind, but I stuck to the original design and made sure the one-off HTTP code was still covered (albeit not very aesthetically as you point out). Also, I felt the one-off HTTP code may not be a common case; I've usually seen, or configured myself, for a range (or even an entire class) of codes. Of course, you all have a good vantage point so I can work on addressing your concern if you feel its helpful or necessary. Also, did you mean to ask @emilevauge above instead of that other person? |
@containous/traefik do you think we should implement the one-off HTTP code on top of ranges? IMHO, ranges only may be OK at first. |
@bparli @emilevauge , I am OK with you. LGTM |
re-gofmt types.go
* use renamed http recorder
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @bparli !
Thanks for this great PR, and sorry for being slow on reviewing.
I made few comments and just wanted to know what you think of adding the possibility to manage variables in query: /{status}.html
(like what we proposed in #1634). It may be too restricting not being able to configure the query. WDYT?
But other than these little things, looks very good to me ❤️
middlewares/error_pages.go
Outdated
log.Debugf("Caught HTTP Status Code %d, returning error page", recorder.Code) | ||
w.WriteHeader(recorder.Code) | ||
if newReq, err := http.NewRequest(http.MethodGet, ep.BackendURL, nil); err != nil { | ||
w.Write([]byte(http.StatusText(recorder.Code))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may log err
here
docs/basics.md
Outdated
[error.network] | ||
status = ["500-599"] | ||
backend = "error" | ||
query = "/500.html" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/{status}.html
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems I originally misunderstood this point. I like it though and will extend to include the {status} query
c.Assert(err, checker.IsNil) | ||
} | ||
|
||
func (ep *ErrorPagesSuite) TestErrorPage(c *check.C) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be even better to test using a non empty query IMHO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point
* use renamed http recorder * use renamed http recorder * updates based on feedback * grammar
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many Thanks for this great PR!
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great PR
Thanks
middlewares/error_pages.go
Outdated
for _, block := range ep.HTTPCodeRanges { | ||
if recorder.Code >= block[0] && recorder.Code <= block[1] { | ||
log.Errorf("Caught HTTP Status Code %d, returning error page", recorder.Code) | ||
re := regexp.MustCompile("{status}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can allocate the compiled regex in the construction of ErrorPagesHandler ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you don't need a regex:
finalURL := strings.Replace(vli, "{status}", "400", -1)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if err != nil { | ||
return nil, err | ||
} | ||
highCode, err := strconv.Atoi(codes[1]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we haven't "-", it panic here with out of range
Can you handle the case when there is no "-" (with a good error)
And add a test for this
thx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there are actually two options here, both simple. 1) catch it and log an error or 2) catch it and assume the user means to serve the error page for just the single code. In this case the single entry can still be used to create the correct array for checking at runtime. So, for example if ["503"] (instead of ["503-503"]) is configured, the array ["503", "503"] is created and we continue on. We essentially create the correct configuration on the user's behalf.
This was actually raised by @nmengin above, I should have just addressed it then
WDYT @juliens ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that handle the "no range status code" would be better, but a log could be enough
|
||
if len(frontend.Errors) > 0 { | ||
for _, errorPage := range frontend.Errors { | ||
if configuration.Backends[errorPage.Backend] != nil && configuration.Backends[errorPage.Backend].Servers["error"].URL != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a log if the backend doesn't exist and/or if the URL is empty
* use renamed http recorder * use renamed http recorder * updates based on feedback * grammar * use string instead of regex, handle single code * add error message for mis-configured error backend *Use string instead of regex, handle single code
Thanks everyone for the reviews and feedback. I think its all addressed to this point |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Thanks
docs/basics.md
Outdated
url = "http://2.3.4.5" | ||
``` | ||
|
||
In the above example, the error page rendered was based on the status code. Instead, the query parameter can also be set to some generic error page like so: ```query = "/500s.html"``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you replace ``` by `
@bparli It's time to squash and rebase 😃 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
[frontends] | ||
[frontends.website] | ||
backend = "website" | ||
[errors] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This bit of documentation looks wrong: the test fixture that resembles this has [frontends.frontend1.errors]
[frontends.frontend1.errors.networks]
, but the doc here just says [errors]
and [errors.networks]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for noticing! Could you please file a new issue or ideally submit a PR? This one has been closed a while ago.
Thank you.
Description
Follow up to PR 1576 and issue 1634.
Following the above design and discussion: