parent
2c976227dd
commit
121c057b90
10 changed files with 411 additions and 9 deletions
|
@ -396,6 +396,39 @@ Here is an example of backends and servers definition:
|
||||||
- `backend2` will forward the traffic to two servers: `http://172.17.0.4:80"` with weight `1` and `http://172.17.0.5:80` with weight `2` using `drr` load-balancing strategy.
|
- `backend2` will forward the traffic to two servers: `http://172.17.0.4:80"` with weight `1` and `http://172.17.0.5:80` with weight `2` using `drr` load-balancing strategy.
|
||||||
- a circuit breaker is added on `backend1` using the expression `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window
|
- 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 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]
|
||||||
|
[frontends.website]
|
||||||
|
backend = "website"
|
||||||
|
[errors]
|
||||||
|
[error.network]
|
||||||
|
status = ["500-599"]
|
||||||
|
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"
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
# Configuration
|
||||||
|
|
||||||
Træfik's configuration has two parts:
|
Træfik's configuration has two parts:
|
||||||
|
|
69
integration/error_pages_test.go
Normal file
69
integration/error_pages_test.go
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/integration/try"
|
||||||
|
"github.com/go-check/check"
|
||||||
|
checker "github.com/vdemeester/shakers"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrorPagesSuite test suites (using libcompose)
|
||||||
|
type ErrorPagesSuite struct{ BaseSuite }
|
||||||
|
|
||||||
|
func (ep *ErrorPagesSuite) SetUpSuite(c *check.C) {
|
||||||
|
ep.createComposeProject(c, "error_pages")
|
||||||
|
ep.composeProject.Start(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ep *ErrorPagesSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
|
|
||||||
|
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
|
||||||
|
Server2 string
|
||||||
|
}{backendHost, errorPageHost})
|
||||||
|
defer os.Remove(file)
|
||||||
|
cmd := exec.Command(traefikBinary, "--configFile="+file)
|
||||||
|
|
||||||
|
err := cmd.Start()
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
|
frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:80", nil)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
frontendReq.Host = "test.local"
|
||||||
|
|
||||||
|
err = try.Request(frontendReq, 2*time.Second, try.BodyContains("nginx"))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ep *ErrorPagesSuite) TestErrorPage(c *check.C) {
|
||||||
|
|
||||||
|
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 {
|
||||||
|
Server1 string
|
||||||
|
Server2 string
|
||||||
|
}{backendHost, errorPageHost})
|
||||||
|
defer os.Remove(file)
|
||||||
|
cmd := exec.Command(traefikBinary, "--configFile="+file)
|
||||||
|
|
||||||
|
err := cmd.Start()
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
|
frontendReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:80", nil)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
frontendReq.Host = "test.local"
|
||||||
|
|
||||||
|
err = try.Request(frontendReq, 2*time.Second, try.BodyContains("An error occurred."))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
}
|
27
integration/fixtures/error_pages/error.toml
Normal file
27
integration/fixtures/error_pages/error.toml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
defaultEntryPoints = ["http"]
|
||||||
|
|
||||||
|
logLevel = "DEBUG"
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
|
||||||
|
[file]
|
||||||
|
[backends]
|
||||||
|
[backends.backend1]
|
||||||
|
[backends.backend1.servers.server1]
|
||||||
|
url = "http://{{.Server1}}:8989474"
|
||||||
|
[backends.error]
|
||||||
|
[backends.error.servers.error]
|
||||||
|
url = "http://{{.Server2}}:80"
|
||||||
|
[frontends]
|
||||||
|
[frontends.frontend1]
|
||||||
|
passHostHeader = true
|
||||||
|
backend = "backend1"
|
||||||
|
[frontends.frontend1.routes.test_1]
|
||||||
|
rule = "Host:test.local"
|
||||||
|
[frontends.frontend1.errors]
|
||||||
|
[frontends.frontend1.errors.networks]
|
||||||
|
status = ["500-502", "503-599"]
|
||||||
|
backend = "error"
|
||||||
|
query = "/50x.html"
|
27
integration/fixtures/error_pages/simple.toml
Normal file
27
integration/fixtures/error_pages/simple.toml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
defaultEntryPoints = ["http"]
|
||||||
|
|
||||||
|
logLevel = "DEBUG"
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
|
||||||
|
[file]
|
||||||
|
[backends]
|
||||||
|
[backends.backend1]
|
||||||
|
[backends.backend1.servers.server1]
|
||||||
|
url = "http://{{.Server1}}:80"
|
||||||
|
[backends.error]
|
||||||
|
[backends.error.servers.error]
|
||||||
|
url = "http://{{.Server2}}:80"
|
||||||
|
[frontends]
|
||||||
|
[frontends.frontend1]
|
||||||
|
passHostHeader = true
|
||||||
|
backend = "backend1"
|
||||||
|
[frontends.frontend1.routes.test_1]
|
||||||
|
rule = "Host:test.local"
|
||||||
|
[frontends.frontend1.errors]
|
||||||
|
[frontends.frontend1.errors.networks]
|
||||||
|
status = ["500-502", "503-599"]
|
||||||
|
backend = "error"
|
||||||
|
query = "/50x.html"
|
|
@ -38,6 +38,7 @@ func init() {
|
||||||
check.Suite(&EurekaSuite{})
|
check.Suite(&EurekaSuite{})
|
||||||
check.Suite(&AcmeSuite{})
|
check.Suite(&AcmeSuite{})
|
||||||
check.Suite(&DynamoDBSuite{})
|
check.Suite(&DynamoDBSuite{})
|
||||||
|
check.Suite(&ErrorPagesSuite{})
|
||||||
}
|
}
|
||||||
|
|
||||||
var traefikBinary = "../dist/traefik"
|
var traefikBinary = "../dist/traefik"
|
||||||
|
|
4
integration/resources/compose/error_pages.yml
Normal file
4
integration/resources/compose/error_pages.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
nginx1:
|
||||||
|
image: nginx:alpine
|
||||||
|
nginx2:
|
||||||
|
image: nginx:alpine
|
77
middlewares/error_pages.go
Normal file
77
middlewares/error_pages.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/log"
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
|
"github.com/vulcand/oxy/forward"
|
||||||
|
"github.com/vulcand/oxy/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
//ErrorPagesHandler is a middleware that provides the custom error pages
|
||||||
|
type ErrorPagesHandler struct {
|
||||||
|
HTTPCodeRanges [][2]int
|
||||||
|
BackendURL string
|
||||||
|
errorPageForwarder *forward.Forwarder
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewErrorPagesHandler initializes the utils.ErrorHandler for the custom error pages
|
||||||
|
func NewErrorPagesHandler(errorPage types.ErrorPage, backendURL string) (*ErrorPagesHandler, error) {
|
||||||
|
fwd, err := forward.New()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//Break out the http status code ranges into a low int and high int
|
||||||
|
//for ease of use at runtime
|
||||||
|
var blocks [][2]int
|
||||||
|
for _, block := range errorPage.Status {
|
||||||
|
codes := strings.Split(block, "-")
|
||||||
|
//if only a single HTTP code was configured, assume the best and create the correct configuration on the user's behalf
|
||||||
|
if len(codes) == 1 {
|
||||||
|
codes = append(codes, codes[0])
|
||||||
|
}
|
||||||
|
lowCode, err := strconv.Atoi(codes[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
highCode, err := strconv.Atoi(codes[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
blocks = append(blocks, [2]int{lowCode, highCode})
|
||||||
|
}
|
||||||
|
return &ErrorPagesHandler{
|
||||||
|
HTTPCodeRanges: blocks,
|
||||||
|
BackendURL: backendURL + errorPage.Query,
|
||||||
|
errorPageForwarder: fwd},
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ep *ErrorPagesHandler) ServeHTTP(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
|
||||||
|
recorder := newRetryResponseRecorder()
|
||||||
|
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.Errorf("Caught HTTP Status Code %d, returning error page", recorder.Code)
|
||||||
|
finalURL := strings.Replace(ep.BackendURL, "{status}", strconv.Itoa(recorder.Code), -1)
|
||||||
|
if newReq, err := http.NewRequest(http.MethodGet, finalURL, nil); err != nil {
|
||||||
|
w.Write([]byte(http.StatusText(recorder.Code)))
|
||||||
|
} else {
|
||||||
|
ep.errorPageForwarder.ServeHTTP(w, newReq)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//did not catch a configured status code so proceed with the request
|
||||||
|
utils.CopyHeaders(w.Header(), recorder.Header())
|
||||||
|
w.Write(recorder.Body.Bytes())
|
||||||
|
}
|
140
middlewares/error_pages_test.go
Normal file
140
middlewares/error_pages_test.go
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/codegangsta/negroni"
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestErrorPage(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintln(w, "Test Server")
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
testErrorPage := &types.ErrorPage{Backend: "error", Query: "/test", Status: []string{"500-501", "503-599"}}
|
||||||
|
testHandler, err := NewErrorPagesHandler(*testErrorPage, ts.URL)
|
||||||
|
|
||||||
|
assert.Equal(t, nil, err, "Should be no error")
|
||||||
|
assert.Equal(t, testHandler.BackendURL, ts.URL+"/test", "Should be equal")
|
||||||
|
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
req, err := http.NewRequest("GET", ts.URL+"/test", nil)
|
||||||
|
|
||||||
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintln(w, "traefik")
|
||||||
|
})
|
||||||
|
n := negroni.New()
|
||||||
|
n.Use(testHandler)
|
||||||
|
n.UseHandler(handler)
|
||||||
|
|
||||||
|
n.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, recorder.Code, "HTTP statusOK")
|
||||||
|
assert.Contains(t, recorder.Body.String(), "traefik")
|
||||||
|
|
||||||
|
handler500 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
fmt.Fprintln(w, "oops")
|
||||||
|
})
|
||||||
|
recorder500 := httptest.NewRecorder()
|
||||||
|
n500 := negroni.New()
|
||||||
|
n500.Use(testHandler)
|
||||||
|
n500.UseHandler(handler500)
|
||||||
|
|
||||||
|
n500.ServeHTTP(recorder500, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusInternalServerError, recorder500.Code, "HTTP status Internal Server Error")
|
||||||
|
assert.Contains(t, recorder500.Body.String(), "Test Server")
|
||||||
|
assert.NotContains(t, recorder500.Body.String(), "oops", "Should not return the oops page")
|
||||||
|
|
||||||
|
handler502 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(502)
|
||||||
|
fmt.Fprintln(w, "oops")
|
||||||
|
})
|
||||||
|
recorder502 := httptest.NewRecorder()
|
||||||
|
n502 := negroni.New()
|
||||||
|
n502.Use(testHandler)
|
||||||
|
n502.UseHandler(handler502)
|
||||||
|
|
||||||
|
n502.ServeHTTP(recorder502, req)
|
||||||
|
|
||||||
|
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 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")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorPageSingleCode(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"}}
|
||||||
|
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")
|
||||||
|
|
||||||
|
}
|
|
@ -772,6 +772,22 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
|
||||||
backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(*hcOpts)
|
backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(*hcOpts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(frontend.Errors) > 0 {
|
||||||
|
for _, errorPage := range frontend.Errors {
|
||||||
|
if configuration.Backends[errorPage.Backend] != nil && configuration.Backends[errorPage.Backend].Servers["error"].URL != "" {
|
||||||
|
errorPageHandler, err := middlewares.NewErrorPagesHandler(errorPage, configuration.Backends[errorPage.Backend].Servers["error"].URL)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error creating custom error page middleware, %v", err)
|
||||||
|
} else {
|
||||||
|
negroni.Use(errorPageHandler)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Errorf("Error Page is configured for Frontend %s, but either Backend %s is not set or Backend URL is missing", frontendName, errorPage.Backend)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
maxConns := configuration.Backends[frontend.Backend].MaxConn
|
maxConns := configuration.Backends[frontend.Backend].MaxConn
|
||||||
if maxConns != nil && maxConns.Amount != 0 {
|
if maxConns != nil && maxConns.Amount != 0 {
|
||||||
extractFunc, err := utils.NewExtractor(maxConns.ExtractorFunc)
|
extractFunc, err := utils.NewExtractor(maxConns.ExtractorFunc)
|
||||||
|
|
|
@ -54,6 +54,13 @@ type Route struct {
|
||||||
Rule string `json:"rule,omitempty"`
|
Rule string `json:"rule,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ErrorPage holds custom error page configuration
|
||||||
|
type ErrorPage struct {
|
||||||
|
Status []string `json:"status,omitempty"`
|
||||||
|
Backend string `json:"backend,omitempty"`
|
||||||
|
Query string `json:"query,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Headers holds the custom header configuration
|
// Headers holds the custom header configuration
|
||||||
type Headers struct {
|
type Headers struct {
|
||||||
CustomRequestHeaders map[string]string `json:"customRequestHeaders,omitempty"`
|
CustomRequestHeaders map[string]string `json:"customRequestHeaders,omitempty"`
|
||||||
|
@ -108,15 +115,16 @@ func (h Headers) HasSecureHeadersDefined() bool {
|
||||||
|
|
||||||
// Frontend holds frontend configuration.
|
// Frontend holds frontend configuration.
|
||||||
type Frontend struct {
|
type Frontend struct {
|
||||||
EntryPoints []string `json:"entryPoints,omitempty"`
|
EntryPoints []string `json:"entryPoints,omitempty"`
|
||||||
Backend string `json:"backend,omitempty"`
|
Backend string `json:"backend,omitempty"`
|
||||||
Routes map[string]Route `json:"routes,omitempty"`
|
Routes map[string]Route `json:"routes,omitempty"`
|
||||||
PassHostHeader bool `json:"passHostHeader,omitempty"`
|
PassHostHeader bool `json:"passHostHeader,omitempty"`
|
||||||
PassTLSCert bool `json:"passTLSCert,omitempty"`
|
PassTLSCert bool `json:"passTLSCert,omitempty"`
|
||||||
Priority int `json:"priority"`
|
Priority int `json:"priority"`
|
||||||
BasicAuth []string `json:"basicAuth"`
|
BasicAuth []string `json:"basicAuth"`
|
||||||
WhitelistSourceRange []string `json:"whitelistSourceRange,omitempty"`
|
WhitelistSourceRange []string `json:"whitelistSourceRange,omitempty"`
|
||||||
Headers Headers `json:"headers,omitempty"`
|
Headers Headers `json:"headers,omitempty"`
|
||||||
|
Errors map[string]ErrorPage `json:"errors,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadBalancerMethod holds the method of load balancing to use.
|
// LoadBalancerMethod holds the method of load balancing to use.
|
||||||
|
|
Loading…
Reference in a new issue