Merge branch v2.4 into master

This commit is contained in:
kevinpollet 2021-01-25 12:42:23 +01:00
commit 2461e36ed4
No known key found for this signature in database
GPG key ID: 0C9A5DDD1B292453
14 changed files with 214 additions and 56 deletions

View file

@ -1,5 +1,5 @@
FROM alpine:3.10 as alpine
FROM alpine:3.13 as alpine
RUN apk --no-cache --no-progress add \
libcurl \

View file

@ -324,10 +324,12 @@ For complete details, refer to your provider's _Additional configuration_ link.
| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | [Additional configuration](https://go-acme.github.io/lego/dns/iij) |
| [Infomaniak](https://www.infomaniak.com) | `infomaniak` | `INFOMANIAK_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/infomaniak) |
| [INWX](https://www.inwx.de/en) | `inwx` | `INWX_USERNAME`, `INWX_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/inwx) |
| [ionos](https://ionos.com/) | `ionos` | `IONOS_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ionos) |
| [Joker.com](https://joker.com) | `joker` | `JOKER_API_MODE` with `JOKER_API_KEY` or `JOKER_USERNAME`, `JOKER_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/joker) |
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/lightsail) |
| [Linode v4](https://www.linode.com) | `linode` | `LINODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/linode) |
| [Liquid Web](https://www.liquidweb.com/) | `liquidweb` | `LIQUID_WEB_PASSWORD`, `LIQUID_WEB_USERNAME`, `LIQUID_WEB_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/liquidweb) |
| [Loopia](https://loopia.com/) | `loopia` | `LOOPIA_API_PASSWORD`, `LOOPIA_API_USER` | [Additional configuration](https://go-acme.github.io/lego/dns/loopia) |
| [LuaDNS](https://luadns.com) | `luadns` | `LUADNS_API_USERNAME`, `LUADNS_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/luadns) |
| manual | `manual` | none, but you need to run Traefik interactively [^4], turn on debug log to see instructions and press <kbd>Enter</kbd>. | |
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mydnsjp) |

View file

@ -145,7 +145,7 @@ _Optional, Default=empty_
```yaml tab="File (YAML)"
providers:
kubernetesGateway:
token = "mytoken"
token: "mytoken"
# ...
```

View file

@ -1,4 +1,4 @@
FROM alpine:3.12
FROM alpine:3.13
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/bin

2
go.mod
View file

@ -30,7 +30,7 @@ require (
github.com/elazarl/go-bindata-assetfs v1.0.0
github.com/fatih/structs v1.1.0
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2
github.com/go-acme/lego/v4 v4.1.3
github.com/go-acme/lego/v4 v4.2.0
github.com/go-check/check v0.0.0-00010101000000-000000000000
github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea
github.com/golang/protobuf v1.4.3

16
go.sum
View file

@ -227,8 +227,8 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpu/goacmedns v0.0.3 h1:QOeMpIEsIdm1LSASSswjaTf8CXmzcrgy5OeCfHjppA4=
github.com/cpu/goacmedns v0.0.3/go.mod h1:4MipLkI+qScwqtVxcNO6okBhbgRrr7/tKXUSgSL0teQ=
github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4=
github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@ -319,8 +319,8 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-acme/lego/v4 v4.1.3 h1:D8nnzrijQFUAqdNPwnbvm6tJ3AJAzQAlnROeecUNG/4=
github.com/go-acme/lego/v4 v4.1.3/go.mod h1:pIFm5tWkXSgiAEfJ/XQCQIvX1cEvHFwbgLZyx8OVSUE=
github.com/go-acme/lego/v4 v4.2.0 h1:zEvpcDLqvzOlNUGBMA0MCKPpb9UBbnBzgWwCIbTEt2g=
github.com/go-acme/lego/v4 v4.2.0/go.mod h1:jmhqxBaangB8txXZKjRLTPXFXUwPCTU2fU8S9/eQzBI=
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
@ -529,8 +529,8 @@ github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.6.7 h1:8/CAEZt/+F7kR7GevNHulKkUjLht3CPmn7egmhieNKo=
github.com/hashicorp/go-retryablehttp v0.6.7/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
@ -974,8 +974,8 @@ github.com/vulcand/oxy v1.1.0 h1:DbBijGo1+6cFqR9jarkMxasdj0lgWwrrFtue6ijek4Q=
github.com/vulcand/oxy v1.1.0/go.mod h1:ADiMYHi8gkGl2987yQIzDRoXZilANF4WtKaQ92OppKY=
github.com/vulcand/predicate v1.1.0 h1:Gq/uWopa4rx/tnZu2opOSBqHK63Yqlou/SzrbwdJiNg=
github.com/vulcand/predicate v1.1.0/go.mod h1:mlccC5IRBoc2cIFmCB8ZM62I3VDb6p2GXESMHa3CnZg=
github.com/vultr/govultr v0.5.0 h1:iQzYhzbokmpDARbvIkvTkoyS7WMH82zVTKAL1PZ4JOA=
github.com/vultr/govultr v0.5.0/go.mod h1:wZZXZbYbqyY1n3AldoeYNZK4Wnmmoq6dNFkvd5TV3ss=
github.com/vultr/govultr/v2 v2.0.0 h1:+lAtqfWy3g9VwL7tT2Fpyad8Vv4MxOhT/NU8O5dk+EQ=
github.com/vultr/govultr/v2 v2.0.0/go.mod h1:2PsEeg+gs3p/Fo5Pw8F9mv+DUBEOlrNZ8GmCTGmhOhs=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=

View file

@ -8,6 +8,7 @@ import (
assetfs "github.com/elazarl/go-bindata-assetfs"
"github.com/gorilla/mux"
"github.com/traefik/traefik/v2/pkg/config/dynamic"
"github.com/traefik/traefik/v2/pkg/config/runtime"
"github.com/traefik/traefik/v2/pkg/config/static"
"github.com/traefik/traefik/v2/pkg/log"
@ -157,6 +158,13 @@ func extractType(element interface{}) string {
v := reflect.ValueOf(element).Elem()
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
if field.Kind() == reflect.Map && field.Type().Elem() == reflect.TypeOf(dynamic.PluginConf{}) {
if keys := field.MapKeys(); len(keys) == 1 {
return keys[0].String()
}
}
if field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct {
if !field.IsNil() {
return v.Type().Field(i).Name

View file

@ -171,3 +171,112 @@ func TestHandler_RawData(t *testing.T) {
})
}
}
func TestHandler_GetMiddleware(t *testing.T) {
testCases := []struct {
desc string
middlewareName string
conf runtime.Configuration
expectedStatus int
expected interface{}
}{
{
desc: "Middleware not found",
middlewareName: "auth@myprovider",
conf: runtime.Configuration{
Middlewares: map[string]*runtime.MiddlewareInfo{},
},
expectedStatus: http.StatusNotFound,
},
{
desc: "Get middleware",
middlewareName: "auth@myprovider",
conf: runtime.Configuration{
Middlewares: map[string]*runtime.MiddlewareInfo{
"auth@myprovider": {
Middleware: &dynamic.Middleware{
BasicAuth: &dynamic.BasicAuth{
Users: []string{"admin:admin"},
},
},
},
},
},
expectedStatus: http.StatusOK,
expected: middlewareRepresentation{
MiddlewareInfo: &runtime.MiddlewareInfo{
Middleware: &dynamic.Middleware{
BasicAuth: &dynamic.BasicAuth{
Users: []string{"admin:admin"},
},
},
},
Name: "auth@myprovider",
Provider: "myprovider",
Type: "basicauth",
},
},
{
desc: "Get plugin middleware",
middlewareName: "myplugin@myprovider",
conf: runtime.Configuration{
Middlewares: map[string]*runtime.MiddlewareInfo{
"myplugin@myprovider": {
Middleware: &dynamic.Middleware{
Plugin: map[string]dynamic.PluginConf{
"mysuperplugin": {
"foo": "bar",
},
},
},
},
},
},
expectedStatus: http.StatusOK,
expected: middlewareRepresentation{
MiddlewareInfo: &runtime.MiddlewareInfo{
Middleware: &dynamic.Middleware{
Plugin: map[string]dynamic.PluginConf{
"mysuperplugin": {
"foo": "bar",
},
},
},
},
Name: "myplugin@myprovider",
Provider: "myprovider",
Type: "mysuperplugin",
},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, &test.conf)
server := httptest.NewServer(handler.createRouter())
resp, err := http.DefaultClient.Get(server.URL + "/api/http/middlewares/" + test.middlewareName)
require.NoError(t, err)
assert.Equal(t, test.expectedStatus, resp.StatusCode)
if test.expected == nil {
return
}
data, err := ioutil.ReadAll(resp.Body)
require.NoError(t, err)
err = resp.Body.Close()
require.NoError(t, err)
expected, err := json.Marshal(test.expected)
require.NoError(t, err)
assert.JSONEq(t, string(expected), string(data))
})
}
}

View file

@ -26,6 +26,18 @@ const (
forwardedTypeName = "ForwardedAuthType"
)
// hopHeaders Hop-by-hop headers to be removed in the authentication request.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
// Proxy-Authorization header is forwarded to the authentication server (see https://tools.ietf.org/html/rfc7235#section-4.4).
var hopHeaders = []string{
forward.Connection,
forward.KeepAlive,
forward.Te, // canonicalized version of "TE"
forward.Trailers,
forward.TransferEncoding,
forward.Upgrade,
}
type forwardAuth struct {
address string
authResponseHeaders []string
@ -131,7 +143,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
logger.Debugf("Remote error %s. StatusCode: %d", fa.address, forwardResponse.StatusCode)
utils.CopyHeaders(rw.Header(), forwardResponse.Header)
utils.RemoveHeaders(rw.Header(), forward.HopHeaders...)
utils.RemoveHeaders(rw.Header(), hopHeaders...)
// Grab the location header, if any.
redirectURL, err := forwardResponse.Location()
@ -187,7 +199,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
func writeHeader(req, forwardReq *http.Request, trustForwardHeader bool, allowedHeaders []string) {
utils.CopyHeaders(forwardReq.Header, req.Header)
utils.RemoveHeaders(forwardReq.Header, forward.HopHeaders...)
utils.RemoveHeaders(forwardReq.Header, hopHeaders...)
forwardReq.Header = filterForwardRequestHeaders(forwardReq.Header, allowedHeaders)

View file

@ -26,6 +26,7 @@ func TestForwardAuthFail(t *testing.T) {
})
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set(forward.ProxyAuthenticate, "test")
http.Error(w, "Forbidden", http.StatusForbidden)
}))
t.Cleanup(server.Close)
@ -48,6 +49,7 @@ func TestForwardAuthFail(t *testing.T) {
err = res.Body.Close()
require.NoError(t, err)
assert.Equal(t, "test", res.Header.Get(forward.ProxyAuthenticate))
assert.Equal(t, "Forbidden\n", string(body))
}
@ -142,7 +144,7 @@ func TestForwardAuthRedirect(t *testing.T) {
func TestForwardAuthRemoveHopByHopHeaders(t *testing.T) {
authTs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
headers := w.Header()
for _, header := range forward.HopHeaders {
for _, header := range hopHeaders {
if header == forward.TransferEncoding {
headers.Set(header, "chunked")
} else {
@ -367,11 +369,13 @@ func Test_writeHeader(t *testing.T) {
},
trustForwardHeader: false,
expectedHeaders: map[string]string{
"X-CustomHeader": "CustomHeader",
"X-Forwarded-Proto": "http",
"X-Forwarded-Host": "foo.bar",
"X-Forwarded-Uri": "/path?q=1",
"X-Forwarded-Method": "GET",
"X-CustomHeader": "CustomHeader",
"X-Forwarded-Proto": "http",
"X-Forwarded-Host": "foo.bar",
"X-Forwarded-Uri": "/path?q=1",
"X-Forwarded-Method": "GET",
forward.ProxyAuthenticate: "ProxyAuthenticate",
forward.ProxyAuthorization: "ProxyAuthorization",
},
checkForUnexpectedHeaders: true,
},

View file

@ -84,19 +84,28 @@ func (x *XForwarded) isTrustedIP(ip string) bool {
// removeIPv6Zone removes the zone if the given IP is an ipv6 address and it has {zone} information in it,
// like "[fe80::d806:a55d:eb1b:49cc%vEthernet (vmxnet3 Ethernet Adapter - Virtual Switch)]:64692".
func removeIPv6Zone(clientIP string) string {
return strings.Split(clientIP, "%")[0]
if idx := strings.Index(clientIP, "%"); idx != -1 {
return clientIP[:idx]
}
return clientIP
}
// isWebsocketRequest returns whether the specified HTTP request is a websocket handshake request.
func isWebsocketRequest(req *http.Request) bool {
containsHeader := func(name, value string) bool {
items := strings.Split(req.Header.Get(name), ",")
for _, item := range items {
if value == strings.ToLower(strings.TrimSpace(item)) {
h := unsafeHeader(req.Header).Get(name)
for {
pos := strings.Index(h, ",")
if pos == -1 {
return strings.EqualFold(value, strings.TrimSpace(h))
}
if strings.EqualFold(value, strings.TrimSpace(h[:pos])) {
return true
}
h = h[pos:]
}
return false
}
return containsHeader(connection, "upgrade") && containsHeader(upgrade, "websocket")
}
@ -110,7 +119,7 @@ func forwardedPort(req *http.Request) string {
return port
}
if req.Header.Get(xForwardedProto) == "https" || req.Header.Get(xForwardedProto) == "wss" {
if unsafeHeader(req.Header).Get(xForwardedProto) == "https" || unsafeHeader(req.Header).Get(xForwardedProto) == "wss" {
return "443"
}
@ -125,38 +134,38 @@ func (x *XForwarded) rewrite(outreq *http.Request) {
if clientIP, _, err := net.SplitHostPort(outreq.RemoteAddr); err == nil {
clientIP = removeIPv6Zone(clientIP)
if outreq.Header.Get(xRealIP) == "" {
outreq.Header.Set(xRealIP, clientIP)
if unsafeHeader(outreq.Header).Get(xRealIP) == "" {
unsafeHeader(outreq.Header).Set(xRealIP, clientIP)
}
}
xfProto := outreq.Header.Get(xForwardedProto)
xfProto := unsafeHeader(outreq.Header).Get(xForwardedProto)
if xfProto == "" {
if isWebsocketRequest(outreq) {
if outreq.TLS != nil {
outreq.Header.Set(xForwardedProto, "wss")
unsafeHeader(outreq.Header).Set(xForwardedProto, "wss")
} else {
outreq.Header.Set(xForwardedProto, "ws")
unsafeHeader(outreq.Header).Set(xForwardedProto, "ws")
}
} else {
if outreq.TLS != nil {
outreq.Header.Set(xForwardedProto, "https")
unsafeHeader(outreq.Header).Set(xForwardedProto, "https")
} else {
outreq.Header.Set(xForwardedProto, "http")
unsafeHeader(outreq.Header).Set(xForwardedProto, "http")
}
}
}
if xfPort := outreq.Header.Get(xForwardedPort); xfPort == "" {
outreq.Header.Set(xForwardedPort, forwardedPort(outreq))
if xfPort := unsafeHeader(outreq.Header).Get(xForwardedPort); xfPort == "" {
unsafeHeader(outreq.Header).Set(xForwardedPort, forwardedPort(outreq))
}
if xfHost := outreq.Header.Get(xForwardedHost); xfHost == "" && outreq.Host != "" {
outreq.Header.Set(xForwardedHost, outreq.Host)
if xfHost := unsafeHeader(outreq.Header).Get(xForwardedHost); xfHost == "" && outreq.Host != "" {
unsafeHeader(outreq.Header).Set(xForwardedHost, outreq.Host)
}
if x.hostname != "" {
outreq.Header.Set(xForwardedServer, x.hostname)
unsafeHeader(outreq.Header).Set(xForwardedServer, x.hostname)
}
}
@ -164,7 +173,7 @@ func (x *XForwarded) rewrite(outreq *http.Request) {
func (x *XForwarded) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !x.insecure && !x.isTrustedIP(r.RemoteAddr) {
for _, h := range xHeaders {
r.Header.Del(h)
unsafeHeader(r.Header).Del(h)
}
}
@ -172,3 +181,22 @@ func (x *XForwarded) ServeHTTP(w http.ResponseWriter, r *http.Request) {
x.next.ServeHTTP(w, r)
}
// unsafeHeader allows to manage Header values.
// Must be used only when the header name is already a canonical key.
type unsafeHeader map[string][]string
func (h unsafeHeader) Set(key, value string) {
h[key] = []string{value}
}
func (h unsafeHeader) Get(key string) string {
if len(h[key]) == 0 {
return ""
}
return h[key][0]
}
func (h unsafeHeader) Del(key string) {
delete(h, key)
}

View file

@ -10,42 +10,41 @@ import (
)
const (
typeName = "Recovery"
typeName = "Recovery"
middlewareName = "traefik-internal-recovery"
)
type recovery struct {
next http.Handler
name string
}
// New creates recovery middleware.
func New(ctx context.Context, next http.Handler, name string) (http.Handler, error) {
log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
func New(ctx context.Context, next http.Handler) (http.Handler, error) {
log.FromContext(middlewares.GetLoggerCtx(ctx, middlewareName, typeName)).Debug("Creating middleware")
return &recovery{
next: next,
name: name,
}, nil
}
func (re *recovery) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
defer recoverFunc(middlewares.GetLoggerCtx(req.Context(), re.name, typeName), rw, req)
defer recoverFunc(rw, req)
re.next.ServeHTTP(rw, req)
}
func recoverFunc(ctx context.Context, rw http.ResponseWriter, r *http.Request) {
func recoverFunc(rw http.ResponseWriter, r *http.Request) {
if err := recover(); err != nil {
logger := log.FromContext(middlewares.GetLoggerCtx(r.Context(), middlewareName, typeName))
if !shouldLogPanic(err) {
log.FromContext(ctx).Debugf("Request has been aborted [%s - %s]: %v", r.RemoteAddr, r.URL, err)
logger.Debugf("Request has been aborted [%s - %s]: %v", r.RemoteAddr, r.URL, err)
return
}
log.FromContext(ctx).Errorf("Recovered from panic in HTTP handler [%s - %s]: %+v", r.RemoteAddr, r.URL, err)
logger.Errorf("Recovered from panic in HTTP handler [%s - %s]: %+v", r.RemoteAddr, r.URL, err)
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
log.FromContext(ctx).Errorf("Stack: %s", buf)
logger.Errorf("Stack: %s", buf)
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}

View file

@ -14,7 +14,7 @@ func TestRecoverHandler(t *testing.T) {
fn := func(w http.ResponseWriter, r *http.Request) {
panic("I love panicing!")
}
recovery, err := New(context.Background(), http.HandlerFunc(fn), "foo-recovery")
recovery, err := New(context.Background(), http.HandlerFunc(fn))
require.NoError(t, err)
server := httptest.NewServer(recovery)

View file

@ -16,10 +16,6 @@ import (
"github.com/traefik/traefik/v2/pkg/server/provider"
)
const (
recoveryMiddlewareName = "traefik-internal-recovery"
)
type middlewareBuilder interface {
BuildChain(ctx context.Context, names []string) *alice.Chain
}
@ -130,7 +126,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
chain := alice.New()
chain = chain.Append(func(next http.Handler) (http.Handler, error) {
return recovery.New(ctx, next, recoveryMiddlewareName)
return recovery.New(ctx, next)
})
return chain.Then(router)