Skip to content

Commit

Permalink
updates based on feedback (#5)
Browse files Browse the repository at this point in the history
* use renamed http recorder

* use renamed http recorder

* updates based on feedback

* grammar
  • Loading branch information
bparli authored Jun 26, 2017
1 parent 31c5410 commit d8ade2d
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 18 deletions.
8 changes: 6 additions & 2 deletions docs/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ Here is an example of backends and servers definition:
- a circuit breaker is added on `backend1` using the expression `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window

## Custom Error pages
Custom error pages can be returned, in lieu of the default, according frontend-configured ranges of HTTP Status codes. In the example below, if a 503 status is returned from the frontend "website", the custom error page at http://2.3.4.5/500.html is returned with the actual status code set in the HTTP header. Note, the 500.html page itself is not hosted on traefik, but some other infrastructure. The configured status code ranges are inclusive; that is, in the below example, the 500.html page will be returned for status codes 500 through, and including, 599.
Custom error pages can be returned, in lieu of the default, according to frontend-configured ranges of HTTP Status codes. In the example below, if a 503 status is returned from the frontend "website", the custom error page at http://2.3.4.5/503.html is returned with the actual status code set in the HTTP header. Note, the 503.html page itself is not hosted on traefik, but some other infrastructure.

```toml
[frontends]
Expand All @@ -407,7 +407,7 @@ Custom error pages can be returned, in lieu of the default, according frontend-c
[error.network]
status = ["500-599"]
backend = "error"
query = "/500.html"
query = "/{status}.html"
[frontends.website.routes.website]
rule = "Host: website.mydomain.com"

Expand All @@ -420,6 +420,10 @@ Custom error pages can be returned, in lieu of the default, according frontend-c
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"```

Now the 500s.html error page is returned for the configured code range. The configured status code ranges are inclusive; that is, in the above example, the 500s.html page will be returned for status codes 500 through, and including, 599.

# Configuration

Træfik's configuration has two parts:
Expand Down
10 changes: 5 additions & 5 deletions integration/error_pages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ func (ep *ErrorPagesSuite) SetUpSuite(c *check.C) {

func (ep *ErrorPagesSuite) TestSimpleConfiguration(c *check.C) {

errorPageHost := ep.composeProject.Container(c, "apache").NetworkSettings.IPAddress
backendHost := ep.composeProject.Container(c, "nginx").NetworkSettings.IPAddress
errorPageHost := ep.composeProject.Container(c, "nginx2").NetworkSettings.IPAddress
backendHost := ep.composeProject.Container(c, "nginx1").NetworkSettings.IPAddress

file := ep.adaptFile(c, "fixtures/error_pages/simple.toml", struct {
Server1 string
Expand All @@ -45,8 +45,8 @@ func (ep *ErrorPagesSuite) TestSimpleConfiguration(c *check.C) {

func (ep *ErrorPagesSuite) TestErrorPage(c *check.C) {

errorPageHost := ep.composeProject.Container(c, "apache").NetworkSettings.IPAddress
backendHost := ep.composeProject.Container(c, "nginx").NetworkSettings.IPAddress
errorPageHost := ep.composeProject.Container(c, "nginx2").NetworkSettings.IPAddress
backendHost := ep.composeProject.Container(c, "nginx1").NetworkSettings.IPAddress

//error.toml contains a mis-configuration of the backend host
file := ep.adaptFile(c, "fixtures/error_pages/error.toml", struct {
Expand All @@ -64,6 +64,6 @@ func (ep *ErrorPagesSuite) TestErrorPage(c *check.C) {
c.Assert(err, checker.IsNil)
frontendReq.Host = "test.local"

err = try.Request(frontendReq, 2*time.Second, try.BodyContains("It works!"))
err = try.Request(frontendReq, 2*time.Second, try.BodyContains("An error occurred."))
c.Assert(err, checker.IsNil)
}
2 changes: 1 addition & 1 deletion integration/fixtures/error_pages/error.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ logLevel = "DEBUG"
[frontends.frontend1.errors.networks]
status = ["500-502", "503-599"]
backend = "error"
query = ""
query = "/50x.html"
2 changes: 1 addition & 1 deletion integration/fixtures/error_pages/simple.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ logLevel = "DEBUG"
[frontends.frontend1.errors.networks]
status = ["500-502", "503-599"]
backend = "error"
query = ""
query = "/50x.html"
6 changes: 3 additions & 3 deletions integration/resources/compose/error_pages.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
nginx:
nginx1:
image: nginx:alpine
nginx2:
image: nginx:alpine
apache:
image: httpd:2.4
10 changes: 6 additions & 4 deletions middlewares/error_pages.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package middlewares

import (
"net/http"
"regexp"
"strconv"
"strings"

Expand Down Expand Up @@ -52,12 +53,14 @@ func (ep *ErrorPagesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request,
recorder.responseWriter = w
next.ServeHTTP(recorder, req)

w.WriteHeader(recorder.Code)
//check the recorder code against the configured http status code ranges
for _, block := range ep.HTTPCodeRanges {
if recorder.Code >= block[0] && recorder.Code <= block[1] {
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 {
log.Errorf("Caught HTTP Status Code %d, returning error page", recorder.Code)
re := regexp.MustCompile("{status}")
finalURL := re.ReplaceAllString(ep.BackendURL, strconv.Itoa(recorder.Code))
if newReq, err := http.NewRequest(http.MethodGet, finalURL, nil); err != nil {
w.Write([]byte(http.StatusText(recorder.Code)))
} else {
ep.errorPageForwarder.ServeHTTP(w, newReq)
Expand All @@ -68,6 +71,5 @@ func (ep *ErrorPagesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request,

//did not catch a configured status code so proceed with the request
utils.CopyHeaders(w.Header(), recorder.Header())
w.WriteHeader(recorder.Code)
w.Write(recorder.Body.Bytes())
}
39 changes: 37 additions & 2 deletions middlewares/error_pages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"strconv"
"testing"

"github.com/codegangsta/negroni"
Expand Down Expand Up @@ -64,8 +65,42 @@ func TestErrorPage(t *testing.T) {

n502.ServeHTTP(recorder502, req)

assert.Equal(t, http.StatusBadGateway, recorder502.Code, "HTTP status Internal Server Error")
assert.Equal(t, http.StatusBadGateway, recorder502.Code, "HTTP status Bad Gateway")
assert.Contains(t, recorder502.Body.String(), "oops")
assert.NotContains(t, recorder502.Body.String(), "Test Server", "Should return the oops page")
assert.NotContains(t, recorder502.Body.String(), "Test Server", "Should return the oops page since we have not configured the 502 code")

}

func TestErrorPageQuery(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.RequestURI() == "/"+strconv.Itoa(503) {
fmt.Fprintln(w, "503 Test Server")
} else {
fmt.Fprintln(w, "Failed")
}

}))
defer ts.Close()

testErrorPage := &types.ErrorPage{Backend: "error", Query: "/{status}", Status: []string{"503-503"}}
testHandler, err := NewErrorPagesHandler(*testErrorPage, ts.URL)
assert.Equal(t, nil, err, "Should be no error")
assert.Equal(t, testHandler.BackendURL, ts.URL+"/{status}", "Should be equal")

handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(503)
fmt.Fprintln(w, "oops")
})
recorder := httptest.NewRecorder()
req, err := http.NewRequest("GET", ts.URL+"/test", nil)
n := negroni.New()
n.Use(testHandler)
n.UseHandler(handler)

n.ServeHTTP(recorder, req)

assert.Equal(t, http.StatusServiceUnavailable, recorder.Code, "HTTP status Service Unavailable")
assert.Contains(t, recorder.Body.String(), "503 Test Server")
assert.NotContains(t, recorder.Body.String(), "oops", "Should not return the oops page")

}

0 comments on commit d8ade2d

Please sign in to comment.