From ae6e84414360a8ac2aac488349cdbceaf2bf12b2 Mon Sep 17 00:00:00 2001 From: Tom Moulard Date: Tue, 10 May 2022 11:00:09 +0200 Subject: [PATCH] Support URL replacement in errors middleware --- docs/content/middlewares/http/errorpages.md | 53 +++++++++++-------- pkg/middlewares/customerrors/custom_errors.go | 1 + .../customerrors/custom_errors_test.go | 20 ++++++- 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/docs/content/middlewares/http/errorpages.md b/docs/content/middlewares/http/errorpages.md index 25d3e508d..81986d667 100644 --- a/docs/content/middlewares/http/errorpages.md +++ b/docs/content/middlewares/http/errorpages.md @@ -1,16 +1,16 @@ --- -title: "Traefik ErrorPage Documentation" -description: "In Traefik Proxy, the ErrorPage middleware returns custom pages according to configured ranges of HTTP Status codes. Read the technical documentation." +title: "Traefik Errors Documentation" +description: "In Traefik Proxy, the Errors middleware returns custom pages according to configured ranges of HTTP Status codes. Read the technical documentation." --- -# ErrorPage +# Errors It Has Never Been Easier to Say That Something Went Wrong {: .subtitle } -![ErrorPages](../../assets/img/middleware/errorpages.png) +![Errors](../../assets/img/middleware/errorpages.png) -The ErrorPage middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. +The Errors middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. !!! important @@ -21,16 +21,16 @@ The ErrorPage middleware returns a custom page in lieu of the default, according ```yaml tab="Docker" # Dynamic Custom Error Page for 5XX Status Code labels: - - "traefik.http.middlewares.test-errorpage.errors.status=500-599" - - "traefik.http.middlewares.test-errorpage.errors.service=serviceError" - - "traefik.http.middlewares.test-errorpage.errors.query=/{status}.html" + - "traefik.http.middlewares.test-errors.errors.status=500-599" + - "traefik.http.middlewares.test-errors.errors.service=serviceError" + - "traefik.http.middlewares.test-errors.errors.query=/{status}.html" ``` ```yaml tab="Kubernetes" apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: - name: test-errorpage + name: test-errors spec: errors: status: @@ -43,32 +43,32 @@ spec: ```yaml tab="Consul Catalog" # Dynamic Custom Error Page for 5XX Status Code -- "traefik.http.middlewares.test-errorpage.errors.status=500-599" -- "traefik.http.middlewares.test-errorpage.errors.service=serviceError" -- "traefik.http.middlewares.test-errorpage.errors.query=/{status}.html" +- "traefik.http.middlewares.test-errors.errors.status=500-599" +- "traefik.http.middlewares.test-errors.errors.service=serviceError" +- "traefik.http.middlewares.test-errors.errors.query=/{status}.html" ``` ```json tab="Marathon" "labels": { - "traefik.http.middlewares.test-errorpage.errors.status": "500-599", - "traefik.http.middlewares.test-errorpage.errors.service": "serviceError", - "traefik.http.middlewares.test-errorpage.errors.query": "/{status}.html" + "traefik.http.middlewares.test-errors.errors.status": "500-599", + "traefik.http.middlewares.test-errors.errors.service": "serviceError", + "traefik.http.middlewares.test-errors.errors.query": "/{status}.html" } ``` ```yaml tab="Rancher" # Dynamic Custom Error Page for 5XX Status Code labels: - - "traefik.http.middlewares.test-errorpage.errors.status=500-599" - - "traefik.http.middlewares.test-errorpage.errors.service=serviceError" - - "traefik.http.middlewares.test-errorpage.errors.query=/{status}.html" + - "traefik.http.middlewares.test-errors.errors.status=500-599" + - "traefik.http.middlewares.test-errors.errors.service=serviceError" + - "traefik.http.middlewares.test-errors.errors.query=/{status}.html" ``` ```yaml tab="File (YAML)" # Custom Error Page for 5XX http: middlewares: - test-errorpage: + test-errors: errors: status: - "500-599" @@ -82,7 +82,7 @@ http: ```toml tab="File (TOML)" # Custom Error Page for 5XX [http.middlewares] - [http.middlewares.test-errorpage.errors] + [http.middlewares.test-errors.errors] status = ["500-599"] service = "serviceError" query = "/{status}.html" @@ -121,8 +121,17 @@ The service that will serve the new requested error page. !!! info "Host Header" By default, the client `Host` header value is forwarded to the configured error [service](#service). - To forward the `Host` value corresponding to the configured error service URL, the [passHostHeader](../../../routing/services/#pass-host-header) option must be set to `false`. + To forward the `Host` value corresponding to the configured error service URL, the [passHostHeader](../../../routing/services/#pass-host-header) option must be set to `false`. ### `query` -The URL for the error page (hosted by `service`). You can use the `{status}` variable in the `query` option in order to insert the status code in the URL. +The URL for the error page (hosted by [`service`](#service))). + +There are multiple variables that can be placed in the `query` option to insert values in the URL. + +The table below lists all the available variables and their associated values. + +| Variable | Value | +|------------|--------------------------------------------------------------------| +| `{status}` | The response status code. | +| `{url}` | The [escaped](https://pkg.go.dev/net/url#QueryEscape) request URL. | diff --git a/pkg/middlewares/customerrors/custom_errors.go b/pkg/middlewares/customerrors/custom_errors.go index 8a4431e8d..dd28aeccd 100644 --- a/pkg/middlewares/customerrors/custom_errors.go +++ b/pkg/middlewares/customerrors/custom_errors.go @@ -93,6 +93,7 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if len(c.backendQuery) > 0 { query = "/" + strings.TrimPrefix(c.backendQuery, "/") query = strings.ReplaceAll(query, "{status}", strconv.Itoa(code)) + query = strings.ReplaceAll(query, "{url}", url.QueryEscape(req.URL.String())) } pageReq, err := newRequest("http://" + req.Host + query) diff --git a/pkg/middlewares/customerrors/custom_errors_test.go b/pkg/middlewares/customerrors/custom_errors_test.go index 7b0a060ec..c291b15d5 100644 --- a/pkg/middlewares/customerrors/custom_errors_test.go +++ b/pkg/middlewares/customerrors/custom_errors_test.go @@ -133,6 +133,24 @@ func TestHandler(t *testing.T) { assert.Contains(t, recorder.Body.String(), "localhost") }, }, + { + desc: "full query replacement", + errorPage: &dynamic.ErrorPage{Service: "error", Query: "/?status={status}&url={url}", Status: []string{"503"}}, + backendCode: http.StatusServiceUnavailable, + backendErrorHandler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.RequestURI != "/?status=503&url=http%3A%2F%2Flocalhost%2Ftest%3Ffoo%3Dbar%26baz%3Dbuz" { + t.Log(r.RequestURI) + return + } + + _, _ = fmt.Fprintln(w, "My 503 page.") + }), + validate: func(t *testing.T, recorder *httptest.ResponseRecorder) { + t.Helper() + assert.Equal(t, http.StatusServiceUnavailable, recorder.Code, "HTTP status") + assert.Contains(t, recorder.Body.String(), "My 503 page.") + }, + }, } for _, test := range testCases { @@ -153,7 +171,7 @@ func TestHandler(t *testing.T) { errorPageHandler, err := New(context.Background(), handler, *test.errorPage, serviceBuilderMock, "test") require.NoError(t, err) - req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost/test", nil) + req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost/test?foo=bar&baz=buz", nil) recorder := httptest.NewRecorder() errorPageHandler.ServeHTTP(recorder, req)