mirror of
https://github.com/containous/traefik.git
synced 2025-09-21 09:44:22 +03:00
Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
dd19fc3f3e | ||
|
dd436a689f | ||
|
0c28630948 | ||
|
198320be8a | ||
|
f4fb758629 | ||
|
b40fa61783 | ||
|
683d5d5a48 | ||
|
4f92ef5fa9 | ||
|
62c3025a76 | ||
|
b5d205b78c | ||
|
dccc075f2c | ||
|
5fdec48854 | ||
|
353bd3d06f | ||
|
a7495f711b | ||
|
5072735866 |
@@ -34,8 +34,10 @@ builds:
|
||||
goarch: 386
|
||||
- goos: openbsd
|
||||
goarch: arm
|
||||
- goos: openbsd
|
||||
goarch: arm64
|
||||
- goos: freebsd
|
||||
goarch: arm
|
||||
goarch: arm64
|
||||
|
||||
changelog:
|
||||
skip: true
|
||||
|
37
CHANGELOG.md
37
CHANGELOG.md
@@ -1,5 +1,34 @@
|
||||
## [v2.1.5](https://github.com/containous/traefik/tree/v2.1.5) (2020-02-28)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.1.4...v2.1.5)
|
||||
## [v2.1.9](https://github.com/containous/traefik/tree/v2.1.9) (2020-03-23)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.1.8...v2.1.9)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[provider,sticky-session]** Fix sameSite ([#6538](https://github.com/containous/traefik/pull/6538) by [ldez](https://github.com/ldez))
|
||||
- **[server]** Force http/1.1 for upgrade ([#6554](https://github.com/containous/traefik/pull/6554) by [juliens](https://github.com/juliens))
|
||||
|
||||
**Documentation:**
|
||||
- Fix tab name ([#6543](https://github.com/containous/traefik/pull/6543) by [mavimo](https://github.com/mavimo))
|
||||
|
||||
## [v2.1.8](https://github.com/containous/traefik/tree/v2.1.8) (2020-03-19)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.1.7...v2.1.8)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[middleware,metrics]** Fix memory leak in metrics ([#6522](https://github.com/containous/traefik/pull/6522) by [juliens](https://github.com/juliens))
|
||||
|
||||
## [v2.1.7](https://github.com/containous/traefik/tree/v2.1.7) (2020-03-18)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.1.6...v2.1.7)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[logs,middleware]** Access log field quotes. ([#6484](https://github.com/containous/traefik/pull/6484) by [ldez](https://github.com/ldez))
|
||||
- **[metrics]** fix statsd scale for duration based metrics ([#6054](https://github.com/containous/traefik/pull/6054) by [ddtmachado](https://github.com/ddtmachado))
|
||||
- **[middleware]** Added support for replacement containing escaped characters ([#6413](https://github.com/containous/traefik/pull/6413) by [rtribotte](https://github.com/rtribotte))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme,docker]** Add some missing doc. ([#6422](https://github.com/containous/traefik/pull/6422) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Added wildcard ACME example ([#6423](https://github.com/containous/traefik/pull/6423) by [Basster](https://github.com/Basster))
|
||||
- **[acme]** fix typo ([#6408](https://github.com/containous/traefik/pull/6408) by [hamiltont](https://github.com/hamiltont))
|
||||
|
||||
## [v2.1.6](https://github.com/containous/traefik/tree/v2.1.6) (2020-02-28)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.1.4...v2.1.6)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Update go-acme/lego to v3.4.0 ([#6376](https://github.com/containous/traefik/pull/6376) by [ldez](https://github.com/ldez))
|
||||
@@ -28,6 +57,10 @@
|
||||
- Minor readme improvements ([#6293](https://github.com/containous/traefik/pull/6293) by [Rowayda-Khayri](https://github.com/Rowayda-Khayri))
|
||||
- Added link to community forum ([#6283](https://github.com/containous/traefik/pull/6283) by [isaacnewtonfx](https://github.com/isaacnewtonfx))
|
||||
|
||||
## [v2.1.5](https://github.com/containous/traefik/tree/v2.1.5) (2020-02-28)
|
||||
|
||||
Skipped.
|
||||
|
||||
## [v2.1.4](https://github.com/containous/traefik/tree/v2.1.4) (2020-02-06)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.1.3...v2.1.4)
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.13-alpine
|
||||
FROM golang:1.14-alpine
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
|
||||
@@ -19,7 +19,7 @@ RUN mkdir -p /usr/local/bin \
|
||||
&& chmod +x /usr/local/bin/go-bindata
|
||||
|
||||
# Download golangci-lint binary to bin folder in $GOPATH
|
||||
RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.23.0
|
||||
RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.23.8
|
||||
|
||||
# Download golangci-lint and misspell binary to bin folder in $GOPATH
|
||||
RUN GO111MODULE=off go get github.com/client9/misspell/cmd/misspell
|
||||
|
@@ -60,7 +60,7 @@ PRE_TARGET= make test-unit
|
||||
|
||||
Requirements:
|
||||
|
||||
- `go` v1.13+
|
||||
- `go` v1.14+
|
||||
- environment variable `GO111MODULE=on`
|
||||
- [go-bindata](https://github.com/containous/go-bindata) `GO111MODULE=off go get -u github.com/containous/go-bindata/...`
|
||||
|
||||
|
@@ -191,7 +191,7 @@ Use the `HTTP-01` challenge to generate and renew ACME certificates by provision
|
||||
As described on the Let's Encrypt [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72),
|
||||
when using the `HTTP-01` challenge, `certificatesResolvers.myresolver.acme.httpChallenge.entryPoint` must be reachable by Let's Encrypt through port 80.
|
||||
|
||||
??? example "Using an EntryPoint Called http for the `httpChallenge`"
|
||||
??? example "Using an EntryPoint Called web for the `httpChallenge`"
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[entryPoints]
|
||||
@@ -396,6 +396,13 @@ As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/stagi
|
||||
|
||||
### `caServer`
|
||||
|
||||
_Required, Default="https://acme-v02.api.letsencrypt.org/directory"_
|
||||
|
||||
The CA server to use:
|
||||
|
||||
- Let's Encrypt production server: https://acme-v02.api.letsencrypt.org/directory
|
||||
- Let's Encrypt staging server: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
|
||||
??? example "Using the Let's Encrypt staging server"
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
@@ -422,6 +429,8 @@ As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/stagi
|
||||
|
||||
### `storage`
|
||||
|
||||
_Required, Default="acme.json"_
|
||||
|
||||
The `storage` option sets the location where your ACME certificates are saved to.
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
@@ -446,13 +455,7 @@ certificatesResolvers:
|
||||
# ...
|
||||
```
|
||||
|
||||
The value can refer to some kinds of storage:
|
||||
|
||||
- a JSON file
|
||||
|
||||
#### In a File
|
||||
|
||||
ACME certificates can be stored in a JSON file that needs to have a `600` file mode .
|
||||
ACME certificates are stored in a JSON file that needs to have a `600` file mode.
|
||||
|
||||
In Docker you can mount either the JSON file, or the folder containing it:
|
||||
|
||||
|
@@ -37,6 +37,10 @@ spec:
|
||||
port: 8080
|
||||
tls:
|
||||
certResolver: myresolver
|
||||
domains:
|
||||
- main: company.org
|
||||
sans:
|
||||
- *.company.org
|
||||
```
|
||||
|
||||
```json tab="Marathon"
|
||||
|
@@ -52,7 +52,7 @@ labels:
|
||||
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||
```
|
||||
|
||||
```toml tab="Single Domain"
|
||||
```toml tab="File (TOML)"
|
||||
## Dynamic configuration
|
||||
[http.routers]
|
||||
[http.routers.blog]
|
||||
|
@@ -35,7 +35,7 @@ If the given format is unsupported, the default (CLF) is used instead.
|
||||
!!! info "Common Log Format"
|
||||
|
||||
```html
|
||||
<remote_IP_address> - <client_user_name_if_available> [<timestamp>] "<request_method> <request_path> <request_protocol>" <origin_server_HTTP_status> <origin_server_content_size> "<request_referrer>" "<request_user_agent>" <number_of_requests_received_since_Traefik_started> "<Traefik_frontend_name>" "<Traefik_backend_URL>" <request_duration_in_ms>ms
|
||||
<remote_IP_address> - <client_user_name_if_available> [<timestamp>] "<request_method> <request_path> <request_protocol>" <origin_server_HTTP_status> <origin_server_content_size> "<request_referrer>" "<request_user_agent>" <number_of_requests_received_since_Traefik_started> "<Traefik_router_name>" "<Traefik_server_URL>" <request_duration_in_ms>ms
|
||||
```
|
||||
|
||||
### `bufferingSize`
|
||||
@@ -195,6 +195,7 @@ accessLog:
|
||||
| `RequestMethod` | The HTTP method. |
|
||||
| `RequestPath` | The HTTP request URI, not including the scheme, host or port. |
|
||||
| `RequestProtocol` | The version of HTTP requested. |
|
||||
| `RequestScheme` | The HTTP scheme requested `http` or `https`. |
|
||||
| `RequestLine` | `RequestMethod` + `RequestPath` + `RequestProtocol` |
|
||||
| `RequestContentSize` | The number of bytes in the request entity (a.k.a. body) sent by the client. |
|
||||
| `OriginDuration` | The time taken by the origin server ('upstream') to return its response. |
|
||||
|
@@ -452,6 +452,30 @@ providers:
|
||||
|
||||
Defines the polling interval (in seconds) in Swarm Mode.
|
||||
|
||||
### `watch`
|
||||
|
||||
_Optional, Default=true_
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.docker]
|
||||
watch = false
|
||||
# ...
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
docker:
|
||||
watch: false
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.docker.watch=false
|
||||
# ...
|
||||
```
|
||||
|
||||
Watch Docker Swarm events.
|
||||
|
||||
### `constraints`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
@@ -146,6 +146,7 @@
|
||||
- "traefik.http.services.service01.loadbalancer.sticky.cookie.httponly=true"
|
||||
- "traefik.http.services.service01.loadbalancer.sticky.cookie.name=foobar"
|
||||
- "traefik.http.services.service01.loadbalancer.sticky.cookie.secure=true"
|
||||
- "traefik.http.services.service01.loadbalancer.sticky.cookie.samesite=foobar"
|
||||
- "traefik.http.services.service01.loadbalancer.server.port=foobar"
|
||||
- "traefik.http.services.service01.loadbalancer.server.scheme=foobar"
|
||||
- "traefik.tcp.routers.tcprouter0.entrypoints=foobar, foobar"
|
||||
|
@@ -43,6 +43,7 @@
|
||||
name = "foobar"
|
||||
secure = true
|
||||
httpOnly = true
|
||||
sameSite = "foobar"
|
||||
|
||||
[[http.services.Service01.loadBalancer.servers]]
|
||||
url = "foobar"
|
||||
@@ -87,6 +88,7 @@
|
||||
name = "foobar"
|
||||
secure = true
|
||||
httpOnly = true
|
||||
sameSite = "foobar"
|
||||
[http.middlewares]
|
||||
[http.middlewares.Middleware00]
|
||||
[http.middlewares.Middleware00.addPrefix]
|
||||
|
@@ -52,6 +52,7 @@ http:
|
||||
name: foobar
|
||||
secure: true
|
||||
httpOnly: true
|
||||
sameSite: foobar
|
||||
servers:
|
||||
- url: foobar
|
||||
- url: foobar
|
||||
@@ -88,6 +89,7 @@ http:
|
||||
name: foobar
|
||||
secure: true
|
||||
httpOnly: true
|
||||
sameSite: foobar
|
||||
middlewares:
|
||||
Middleware00:
|
||||
addPrefix:
|
||||
|
@@ -143,6 +143,7 @@
|
||||
"traefik.http.services.service01.loadbalancer.sticky.cookie.httponly": "true",
|
||||
"traefik.http.services.service01.loadbalancer.sticky.cookie.name": "foobar",
|
||||
"traefik.http.services.service01.loadbalancer.sticky.cookie.secure": "true",
|
||||
"traefik.http.services.service01.loadbalancer.sticky.cookie.samesite": "foobar",
|
||||
"traefik.http.services.service01.loadbalancer.server.port": "foobar",
|
||||
"traefik.http.services.service01.loadbalancer.server.scheme": "foobar",
|
||||
"traefik.tcp.routers.tcprouter0.entrypoints": "foobar, foobar",
|
||||
|
@@ -346,7 +346,7 @@ TLS key
|
||||
Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
|
||||
|
||||
`--providers.docker.watch`:
|
||||
Watch provider. (Default: ```true```)
|
||||
Watch Docker Swarm events. (Default: ```true```)
|
||||
|
||||
`--providers.file.debugloggeneratedtemplate`:
|
||||
Enable debug logging of generated configuration template. (Default: ```false```)
|
||||
@@ -580,7 +580,7 @@ Specifies the header name prefix that will be used to store baggage items in a m
|
||||
Key:Value tag to be set on all the spans.
|
||||
|
||||
`--tracing.haystack.localagenthost`:
|
||||
Set haystack-agent's host that the reporter will used. (Default: ```LocalAgentHost```)
|
||||
Set haystack-agent's host that the reporter will used. (Default: ```127.0.0.1```)
|
||||
|
||||
`--tracing.haystack.localagentport`:
|
||||
Set haystack-agent's port that the reporter will used. (Default: ```35000```)
|
||||
|
@@ -346,7 +346,7 @@ TLS key
|
||||
Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_DOCKER_WATCH`:
|
||||
Watch provider. (Default: ```true```)
|
||||
Watch Docker Swarm events. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_FILE_DEBUGLOGGENERATEDTEMPLATE`:
|
||||
Enable debug logging of generated configuration template. (Default: ```false```)
|
||||
@@ -580,7 +580,7 @@ Specifies the header name prefix that will be used to store baggage items in a m
|
||||
Key:Value tag to be set on all the spans.
|
||||
|
||||
`TRAEFIK_TRACING_HAYSTACK_LOCALAGENTHOST`:
|
||||
Set haystack-agent's host that the reporter will used. (Default: ```LocalAgentHost```)
|
||||
Set haystack-agent's host that the reporter will used. (Default: ```127.0.0.1```)
|
||||
|
||||
`TRAEFIK_TRACING_HAYSTACK_LOCALAGENTPORT`:
|
||||
Set haystack-agent's port that the reporter will used. (Default: ```35000```)
|
||||
|
@@ -225,6 +225,14 @@ you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.pass
|
||||
traefik.http.services.myservice.loadbalancer.sticky.cookie.secure=true
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.samesite`"
|
||||
|
||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||
|
||||
```yaml
|
||||
traefik.http.services.myservice.loadbalancer.sticky.cookie.samesite=none
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
|
||||
<!-- TODO doc responseforwarding in services page -->
|
||||
|
||||
|
@@ -358,6 +358,14 @@ you'd add the label `traefik.http.services.<name-of-your-choice>.loadbalancer.pa
|
||||
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.secure=true"
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.samesite`"
|
||||
|
||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||
|
||||
```yaml
|
||||
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.samesite=none"
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
|
||||
|
||||
See [response forwarding](../services/index.md#response-forwarding) for more information.
|
||||
|
@@ -195,6 +195,7 @@ Register the `IngressRoute` kind in the Kubernetes cluster before creating `Ingr
|
||||
httpOnly: true
|
||||
name: cookie
|
||||
secure: true
|
||||
sameSite: none
|
||||
strategy: RoundRobin
|
||||
weight: 10
|
||||
tls: # [9]
|
||||
|
@@ -256,6 +256,14 @@ For example, to change the passHostHeader behavior, you'd add the label `"traefi
|
||||
"traefik.http.services.myservice.loadbalancer.sticky.cookie.secure": "true"
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.samesite`"
|
||||
|
||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||
|
||||
```json
|
||||
"traefik.http.services.myservice.loadbalancer.sticky.cookie.samesite": "none"
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
|
||||
|
||||
See [response forwarding](../services/index.md#response-forwarding) for more information.
|
||||
|
@@ -262,6 +262,14 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
|
||||
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.secure=true"
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.samesite`"
|
||||
|
||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||
|
||||
```yaml
|
||||
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.samesite=none"
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
|
||||
|
||||
See [response forwarding](../services/index.md#response-forwarding) for more information.
|
||||
|
@@ -156,9 +156,12 @@ On subsequent requests, the client is forwarded to the same server.
|
||||
|
||||
The default cookie name is an abbreviation of a sha1 (ex: `_1d52e`).
|
||||
|
||||
!!! info "Secure & HTTPOnly flags"
|
||||
!!! info "Secure & HTTPOnly & SameSite flags"
|
||||
|
||||
By default, the affinity cookie is created without those flags. One however can change that through configuration.
|
||||
By default, the affinity cookie is created without those flags.
|
||||
One however can change that through configuration.
|
||||
|
||||
`SameSite` can be `none`, `lax`, `strict` or empty.
|
||||
|
||||
??? example "Adding Stickiness -- Using the [File Provider](../../providers/file.md)"
|
||||
|
||||
@@ -189,6 +192,7 @@ On subsequent requests, the client is forwarded to the same server.
|
||||
name = "my_sticky_cookie_name"
|
||||
secure = true
|
||||
httpOnly = true
|
||||
sameSite = "none"
|
||||
```
|
||||
|
||||
```yaml tab="YAML"
|
||||
|
@@ -12,7 +12,7 @@ RUN npm install
|
||||
RUN npm run build
|
||||
|
||||
# BUILD
|
||||
FROM golang:1.13-alpine as gobuild
|
||||
FROM golang:1.14-alpine as gobuild
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
|
||||
|
8
go.mod
8
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/containous/traefik/v2
|
||||
|
||||
go 1.13
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
||||
@@ -46,7 +46,7 @@ require (
|
||||
github.com/google/go-github/v28 v28.0.0
|
||||
github.com/googleapis/gnostic v0.1.0 // indirect
|
||||
github.com/gorilla/mux v1.7.3
|
||||
github.com/gorilla/websocket v1.4.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/hashicorp/consul/api v1.2.0
|
||||
github.com/hashicorp/go-version v1.2.0
|
||||
github.com/huandu/xstrings v1.2.0 // indirect
|
||||
@@ -76,7 +76,7 @@ require (
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4
|
||||
github.com/rancher/go-rancher-metadata v0.0.0-00010101000000-000000000000
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/stvp/go-udp-testing v0.0.0-20171104055251-c4434f09ec13
|
||||
github.com/tinylib/msgp v1.0.2 // indirect
|
||||
github.com/transip/gotransip v5.8.2+incompatible // indirect
|
||||
@@ -85,7 +85,7 @@ require (
|
||||
github.com/unrolled/render v1.0.1
|
||||
github.com/unrolled/secure v1.0.5
|
||||
github.com/vdemeester/shakers v0.1.0
|
||||
github.com/vulcand/oxy v1.0.0
|
||||
github.com/vulcand/oxy v1.1.0
|
||||
github.com/vulcand/predicate v1.1.0
|
||||
golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect
|
||||
|
9
go.sum
9
go.sum
@@ -282,6 +282,8 @@ github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gravitational/trace v0.0.0-20190726142706-a535a178675f h1:68WxnfBzJRYktZ30fmIjGQ74RsXYLoeH2/NITPktTMY=
|
||||
github.com/gravitational/trace v0.0.0-20190726142706-a535a178675f/go.mod h1:RvdOUHE4SHqR3oXlFFKnGzms8a5dugHygGw1bqDstYI=
|
||||
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
@@ -558,6 +560,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stvp/go-udp-testing v0.0.0-20171104055251-c4434f09ec13 h1:WYRIgR83bWdH2zjqXalfLuQYtgBG1KKxDRxinx2ygMI=
|
||||
github.com/stvp/go-udp-testing v0.0.0-20171104055251-c4434f09ec13/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
|
||||
github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7 h1:CpHxIaZzVy26GqJn8ptRyto8fuoYOd1v0fXm9bG3wQ8=
|
||||
@@ -582,8 +586,8 @@ github.com/unrolled/secure v1.0.5/go.mod h1:R6rugAuzh4TQpbFAq69oqZggyBQxFRFQIewt
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/vdemeester/shakers v0.1.0 h1:K+n9sSyUCg2ywmZkv+3c7vsYZfivcfKhMh8kRxCrONM=
|
||||
github.com/vdemeester/shakers v0.1.0/go.mod h1:IZ1HHynUOQt32iQ3rvAeVddXLd19h/6LWiKsh9RZtAQ=
|
||||
github.com/vulcand/oxy v1.0.0 h1:7vL5/pjDFzHGbtBEhmlHITUi6KLH4xXTDF33/wrdRKw=
|
||||
github.com/vulcand/oxy v1.0.0/go.mod h1:6EXgOAl6CRa46/2ZGcDJKf3ywJUp5WtT7vSlGSkvecI=
|
||||
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.1.4 h1:UnNMixYFVO0p80itc8PcweoVENyo1PasfvwKhoasR9U=
|
||||
@@ -853,6 +857,7 @@ k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86
|
||||
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
|
||||
k8s.io/utils v0.0.0-20190221042446-c2654d5206da h1:ElyM7RPonbKnQqOcw7dG2IK5uvQQn3b/WPHqD5mBvP4=
|
||||
k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
|
||||
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
|
||||
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
|
||||
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||
|
@@ -97,6 +97,7 @@ type Cookie struct {
|
||||
Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"`
|
||||
Secure bool `json:"secure,omitempty" toml:"secure,omitempty" yaml:"secure,omitempty"`
|
||||
HTTPOnly bool `json:"httpOnly,omitempty" toml:"httpOnly,omitempty" yaml:"httpOnly,omitempty"`
|
||||
SameSite string `json:"sameSite,omitempty" toml:"sameSite,omitempty" yaml:"sameSite,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
@@ -50,14 +50,14 @@ func RegisterDatadog(ctx context.Context, config *types.Datadog) Registry {
|
||||
if config.AddEntryPointsLabels {
|
||||
registry.epEnabled = config.AddEntryPointsLabels
|
||||
registry.entryPointReqsCounter = datadogClient.NewCounter(ddEntryPointReqsName, 1.0)
|
||||
registry.entryPointReqDurationHistogram = datadogClient.NewHistogram(ddEntryPointReqDurationName, 1.0)
|
||||
registry.entryPointReqDurationHistogram, _ = NewHistogramWithScale(datadogClient.NewHistogram(ddEntryPointReqDurationName, 1.0), time.Second)
|
||||
registry.entryPointOpenConnsGauge = datadogClient.NewGauge(ddEntryPointOpenConnsName)
|
||||
}
|
||||
|
||||
if config.AddServicesLabels {
|
||||
registry.svcEnabled = config.AddServicesLabels
|
||||
registry.serviceReqsCounter = datadogClient.NewCounter(ddMetricsServiceReqsName, 1.0)
|
||||
registry.serviceReqDurationHistogram = datadogClient.NewHistogram(ddMetricsServiceLatencyName, 1.0)
|
||||
registry.serviceReqDurationHistogram, _ = NewHistogramWithScale(datadogClient.NewHistogram(ddMetricsServiceLatencyName, 1.0), time.Second)
|
||||
registry.serviceRetriesCounter = datadogClient.NewCounter(ddRetriesTotalName, 1.0)
|
||||
registry.serviceOpenConnsGauge = datadogClient.NewGauge(ddOpenConnsName)
|
||||
registry.serviceServerUpGauge = datadogClient.NewGauge(ddServerUpName)
|
||||
|
@@ -64,14 +64,14 @@ func RegisterInfluxDB(ctx context.Context, config *types.InfluxDB) Registry {
|
||||
if config.AddEntryPointsLabels {
|
||||
registry.epEnabled = config.AddEntryPointsLabels
|
||||
registry.entryPointReqsCounter = influxDBClient.NewCounter(influxDBEntryPointReqsName)
|
||||
registry.entryPointReqDurationHistogram = influxDBClient.NewHistogram(influxDBEntryPointReqDurationName)
|
||||
registry.entryPointReqDurationHistogram, _ = NewHistogramWithScale(influxDBClient.NewHistogram(influxDBEntryPointReqDurationName), time.Second)
|
||||
registry.entryPointOpenConnsGauge = influxDBClient.NewGauge(influxDBEntryPointOpenConnsName)
|
||||
}
|
||||
|
||||
if config.AddServicesLabels {
|
||||
registry.svcEnabled = config.AddServicesLabels
|
||||
registry.serviceReqsCounter = influxDBClient.NewCounter(influxDBMetricsServiceReqsName)
|
||||
registry.serviceReqDurationHistogram = influxDBClient.NewHistogram(influxDBMetricsServiceLatencyName)
|
||||
registry.serviceReqDurationHistogram, _ = NewHistogramWithScale(influxDBClient.NewHistogram(influxDBMetricsServiceLatencyName), time.Second)
|
||||
registry.serviceRetriesCounter = influxDBClient.NewCounter(influxDBRetriesTotalName)
|
||||
registry.serviceOpenConnsGauge = influxDBClient.NewGauge(influxDBOpenConnsName)
|
||||
registry.serviceServerUpGauge = influxDBClient.NewGauge(influxDBServerUpName)
|
||||
|
@@ -1,6 +1,9 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/multi"
|
||||
)
|
||||
@@ -20,12 +23,12 @@ type Registry interface {
|
||||
|
||||
// entry point metrics
|
||||
EntryPointReqsCounter() metrics.Counter
|
||||
EntryPointReqDurationHistogram() metrics.Histogram
|
||||
EntryPointReqDurationHistogram() ScalableHistogram
|
||||
EntryPointOpenConnsGauge() metrics.Gauge
|
||||
|
||||
// service metrics
|
||||
ServiceReqsCounter() metrics.Counter
|
||||
ServiceReqDurationHistogram() metrics.Histogram
|
||||
ServiceReqDurationHistogram() ScalableHistogram
|
||||
ServiceOpenConnsGauge() metrics.Gauge
|
||||
ServiceRetriesCounter() metrics.Counter
|
||||
ServiceServerUpGauge() metrics.Gauge
|
||||
@@ -46,10 +49,10 @@ func NewMultiRegistry(registries []Registry) Registry {
|
||||
var lastConfigReloadSuccessGauge []metrics.Gauge
|
||||
var lastConfigReloadFailureGauge []metrics.Gauge
|
||||
var entryPointReqsCounter []metrics.Counter
|
||||
var entryPointReqDurationHistogram []metrics.Histogram
|
||||
var entryPointReqDurationHistogram []ScalableHistogram
|
||||
var entryPointOpenConnsGauge []metrics.Gauge
|
||||
var serviceReqsCounter []metrics.Counter
|
||||
var serviceReqDurationHistogram []metrics.Histogram
|
||||
var serviceReqDurationHistogram []ScalableHistogram
|
||||
var serviceOpenConnsGauge []metrics.Gauge
|
||||
var serviceRetriesCounter []metrics.Counter
|
||||
var serviceServerUpGauge []metrics.Gauge
|
||||
@@ -101,10 +104,10 @@ func NewMultiRegistry(registries []Registry) Registry {
|
||||
lastConfigReloadSuccessGauge: multi.NewGauge(lastConfigReloadSuccessGauge...),
|
||||
lastConfigReloadFailureGauge: multi.NewGauge(lastConfigReloadFailureGauge...),
|
||||
entryPointReqsCounter: multi.NewCounter(entryPointReqsCounter...),
|
||||
entryPointReqDurationHistogram: multi.NewHistogram(entryPointReqDurationHistogram...),
|
||||
entryPointReqDurationHistogram: NewMultiHistogram(entryPointReqDurationHistogram...),
|
||||
entryPointOpenConnsGauge: multi.NewGauge(entryPointOpenConnsGauge...),
|
||||
serviceReqsCounter: multi.NewCounter(serviceReqsCounter...),
|
||||
serviceReqDurationHistogram: multi.NewHistogram(serviceReqDurationHistogram...),
|
||||
serviceReqDurationHistogram: NewMultiHistogram(serviceReqDurationHistogram...),
|
||||
serviceOpenConnsGauge: multi.NewGauge(serviceOpenConnsGauge...),
|
||||
serviceRetriesCounter: multi.NewCounter(serviceRetriesCounter...),
|
||||
serviceServerUpGauge: multi.NewGauge(serviceServerUpGauge...),
|
||||
@@ -119,10 +122,10 @@ type standardRegistry struct {
|
||||
lastConfigReloadSuccessGauge metrics.Gauge
|
||||
lastConfigReloadFailureGauge metrics.Gauge
|
||||
entryPointReqsCounter metrics.Counter
|
||||
entryPointReqDurationHistogram metrics.Histogram
|
||||
entryPointReqDurationHistogram ScalableHistogram
|
||||
entryPointOpenConnsGauge metrics.Gauge
|
||||
serviceReqsCounter metrics.Counter
|
||||
serviceReqDurationHistogram metrics.Histogram
|
||||
serviceReqDurationHistogram ScalableHistogram
|
||||
serviceOpenConnsGauge metrics.Gauge
|
||||
serviceRetriesCounter metrics.Counter
|
||||
serviceServerUpGauge metrics.Gauge
|
||||
@@ -156,7 +159,7 @@ func (r *standardRegistry) EntryPointReqsCounter() metrics.Counter {
|
||||
return r.entryPointReqsCounter
|
||||
}
|
||||
|
||||
func (r *standardRegistry) EntryPointReqDurationHistogram() metrics.Histogram {
|
||||
func (r *standardRegistry) EntryPointReqDurationHistogram() ScalableHistogram {
|
||||
return r.entryPointReqDurationHistogram
|
||||
}
|
||||
|
||||
@@ -168,7 +171,7 @@ func (r *standardRegistry) ServiceReqsCounter() metrics.Counter {
|
||||
return r.serviceReqsCounter
|
||||
}
|
||||
|
||||
func (r *standardRegistry) ServiceReqDurationHistogram() metrics.Histogram {
|
||||
func (r *standardRegistry) ServiceReqDurationHistogram() ScalableHistogram {
|
||||
return r.serviceReqDurationHistogram
|
||||
}
|
||||
|
||||
@@ -183,3 +186,83 @@ func (r *standardRegistry) ServiceRetriesCounter() metrics.Counter {
|
||||
func (r *standardRegistry) ServiceServerUpGauge() metrics.Gauge {
|
||||
return r.serviceServerUpGauge
|
||||
}
|
||||
|
||||
// ScalableHistogram is a Histogram with a predefined time unit,
|
||||
// used when producing observations without explicitly setting the observed value.
|
||||
type ScalableHistogram interface {
|
||||
With(labelValues ...string) ScalableHistogram
|
||||
Observe(v float64)
|
||||
ObserveFromStart(start time.Time)
|
||||
}
|
||||
|
||||
// HistogramWithScale is a histogram that will convert its observed value to the specified unit.
|
||||
type HistogramWithScale struct {
|
||||
histogram metrics.Histogram
|
||||
unit time.Duration
|
||||
}
|
||||
|
||||
// With implements ScalableHistogram.
|
||||
func (s *HistogramWithScale) With(labelValues ...string) ScalableHistogram {
|
||||
h, _ := NewHistogramWithScale(s.histogram.With(labelValues...), s.unit)
|
||||
return h
|
||||
}
|
||||
|
||||
// ObserveFromStart implements ScalableHistogram.
|
||||
func (s *HistogramWithScale) ObserveFromStart(start time.Time) {
|
||||
if s.unit <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
d := float64(time.Since(start).Nanoseconds()) / float64(s.unit)
|
||||
if d < 0 {
|
||||
d = 0
|
||||
}
|
||||
s.histogram.Observe(d)
|
||||
}
|
||||
|
||||
// Observe implements ScalableHistogram.
|
||||
func (s *HistogramWithScale) Observe(v float64) {
|
||||
s.histogram.Observe(v)
|
||||
}
|
||||
|
||||
// NewHistogramWithScale returns a ScalableHistogram. It returns an error if the given unit is <= 0.
|
||||
func NewHistogramWithScale(histogram metrics.Histogram, unit time.Duration) (ScalableHistogram, error) {
|
||||
if unit <= 0 {
|
||||
return nil, errors.New("invalid time unit")
|
||||
}
|
||||
return &HistogramWithScale{
|
||||
histogram: histogram,
|
||||
unit: unit,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MultiHistogram collects multiple individual histograms and treats them as a unit.
|
||||
type MultiHistogram []ScalableHistogram
|
||||
|
||||
// NewMultiHistogram returns a multi-histogram, wrapping the passed histograms.
|
||||
func NewMultiHistogram(h ...ScalableHistogram) MultiHistogram {
|
||||
return MultiHistogram(h)
|
||||
}
|
||||
|
||||
// ObserveFromStart implements ScalableHistogram.
|
||||
func (h MultiHistogram) ObserveFromStart(start time.Time) {
|
||||
for _, histogram := range h {
|
||||
histogram.ObserveFromStart(start)
|
||||
}
|
||||
}
|
||||
|
||||
// Observe implements ScalableHistogram.
|
||||
func (h MultiHistogram) Observe(v float64) {
|
||||
for _, histogram := range h {
|
||||
histogram.Observe(v)
|
||||
}
|
||||
}
|
||||
|
||||
// With implements ScalableHistogram.
|
||||
func (h MultiHistogram) With(labelValues ...string) ScalableHistogram {
|
||||
next := make(MultiHistogram, len(h))
|
||||
for i := range h {
|
||||
next[i] = h[i].With(labelValues...)
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
@@ -1,18 +1,44 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/generic"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestScalableHistogram(t *testing.T) {
|
||||
h := generic.NewHistogram("test", 1)
|
||||
sh, err := NewHistogramWithScale(h, time.Millisecond)
|
||||
require.NoError(t, err)
|
||||
|
||||
ticker := time.NewTicker(500 * time.Millisecond)
|
||||
<-ticker.C
|
||||
start := time.Now()
|
||||
<-ticker.C
|
||||
sh.ObserveFromStart(start)
|
||||
|
||||
var b bytes.Buffer
|
||||
h.Print(&b)
|
||||
|
||||
extractedDurationString := strings.Split(strings.Split(b.String(), "\n")[1], " ")
|
||||
measuredDuration, err := time.ParseDuration(extractedDurationString[0] + "ms")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.InDelta(t, 500*time.Millisecond, measuredDuration, float64(1*time.Millisecond))
|
||||
}
|
||||
|
||||
func TestNewMultiRegistry(t *testing.T) {
|
||||
registries := []Registry{newCollectingRetryMetrics(), newCollectingRetryMetrics()}
|
||||
registry := NewMultiRegistry(registries)
|
||||
|
||||
registry.ServiceReqsCounter().With("key", "requests").Add(1)
|
||||
registry.ServiceReqDurationHistogram().With("key", "durations").Observe(2)
|
||||
registry.ServiceReqDurationHistogram().With("key", "durations").Observe(float64(2))
|
||||
registry.ServiceRetriesCounter().With("key", "retries").Add(3)
|
||||
|
||||
for _, collectingRegistry := range registries {
|
||||
@@ -66,11 +92,15 @@ type histogramMock struct {
|
||||
lastLabelValues []string
|
||||
}
|
||||
|
||||
func (c *histogramMock) With(labelValues ...string) metrics.Histogram {
|
||||
func (c *histogramMock) With(labelValues ...string) ScalableHistogram {
|
||||
c.lastLabelValues = labelValues
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *histogramMock) Observe(value float64) {
|
||||
c.lastHistogramValue = value
|
||||
func (c *histogramMock) Start() {}
|
||||
|
||||
func (c *histogramMock) ObserveFromStart(t time.Time) {}
|
||||
|
||||
func (c *histogramMock) Observe(v float64) {
|
||||
c.lastHistogramValue = v
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/containous/traefik/v2/pkg/log"
|
||||
@@ -152,7 +153,7 @@ func initStandardRegistry(config *types.Prometheus) Registry {
|
||||
entryPointOpenConns.gv.Describe,
|
||||
}...)
|
||||
reg.entryPointReqsCounter = entryPointReqs
|
||||
reg.entryPointReqDurationHistogram = entryPointReqDurations
|
||||
reg.entryPointReqDurationHistogram, _ = NewHistogramWithScale(entryPointReqDurations, time.Second)
|
||||
reg.entryPointOpenConnsGauge = entryPointOpenConns
|
||||
}
|
||||
if config.AddServicesLabels {
|
||||
@@ -187,7 +188,7 @@ func initStandardRegistry(config *types.Prometheus) Registry {
|
||||
}...)
|
||||
|
||||
reg.serviceReqsCounter = serviceReqs
|
||||
reg.serviceReqDurationHistogram = serviceReqDurations
|
||||
reg.serviceReqDurationHistogram, _ = NewHistogramWithScale(serviceReqDurations, time.Second)
|
||||
reg.serviceOpenConnsGauge = serviceOpenConns
|
||||
reg.serviceRetriesCounter = serviceRetries
|
||||
reg.serviceServerUpGauge = serviceServerUp
|
||||
|
@@ -55,14 +55,14 @@ func RegisterStatsd(ctx context.Context, config *types.Statsd) Registry {
|
||||
if config.AddEntryPointsLabels {
|
||||
registry.epEnabled = config.AddEntryPointsLabels
|
||||
registry.entryPointReqsCounter = statsdClient.NewCounter(statsdEntryPointReqsName, 1.0)
|
||||
registry.entryPointReqDurationHistogram = statsdClient.NewTiming(statsdEntryPointReqDurationName, 1.0)
|
||||
registry.entryPointReqDurationHistogram, _ = NewHistogramWithScale(statsdClient.NewTiming(statsdEntryPointReqDurationName, 1.0), time.Millisecond)
|
||||
registry.entryPointOpenConnsGauge = statsdClient.NewGauge(statsdEntryPointOpenConnsName)
|
||||
}
|
||||
|
||||
if config.AddServicesLabels {
|
||||
registry.svcEnabled = config.AddServicesLabels
|
||||
registry.serviceReqsCounter = statsdClient.NewCounter(statsdMetricsServiceReqsName, 1.0)
|
||||
registry.serviceReqDurationHistogram = statsdClient.NewTiming(statsdMetricsServiceLatencyName, 1.0)
|
||||
registry.serviceReqDurationHistogram, _ = NewHistogramWithScale(statsdClient.NewTiming(statsdMetricsServiceLatencyName, 1.0), time.Millisecond)
|
||||
registry.serviceRetriesCounter = statsdClient.NewCounter(statsdRetriesTotalName, 1.0)
|
||||
registry.serviceOpenConnsGauge = statsdClient.NewGauge(statsdOpenConnsName)
|
||||
registry.serviceServerUpGauge = statsdClient.NewGauge(statsdServerUpName)
|
||||
|
@@ -45,8 +45,8 @@ func (f *CommonLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
toLog(entry.Data, "request_Referer", `"-"`, true),
|
||||
toLog(entry.Data, "request_User-Agent", `"-"`, true),
|
||||
toLog(entry.Data, RequestCount, defaultValue, true),
|
||||
toLog(entry.Data, RouterName, defaultValue, true),
|
||||
toLog(entry.Data, ServiceURL, defaultValue, true),
|
||||
toLog(entry.Data, RouterName, `"-"`, true),
|
||||
toLog(entry.Data, ServiceURL, `"-"`, true),
|
||||
elapsedMillis)
|
||||
|
||||
return b.Bytes(), err
|
||||
|
@@ -36,7 +36,7 @@ func TestCommonLogFormatter_Format(t *testing.T) {
|
||||
RouterName: "",
|
||||
ServiceURL: "",
|
||||
},
|
||||
expectedLog: `10.0.0.1 - Client [10/Nov/2009:23:00:00 +0000] "GET /foo http" - - "-" "-" 0 - - 123000ms
|
||||
expectedLog: `10.0.0.1 - Client [10/Nov/2009:23:00:00 +0000] "GET /foo http" - - "-" "-" 0 "-" "-" 123000ms
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
@@ -449,7 +449,7 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
|
||||
DefaultMode: "drop",
|
||||
},
|
||||
},
|
||||
expectedLog: `- - - [-] "- - -" - - "testReferer" "testUserAgent" - - - 0ms`,
|
||||
expectedLog: `- - - [-] "- - -" - - "testReferer" "testUserAgent" - "-" "-" 0ms`,
|
||||
},
|
||||
{
|
||||
desc: "Default mode drop with override",
|
||||
@@ -464,7 +464,7 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedLog: `- - TestUser [-] "- - -" - - "testReferer" "testUserAgent" - - - 0ms`,
|
||||
expectedLog: `- - TestUser [-] "- - -" - - "testReferer" "testUserAgent" - "-" "-" 0ms`,
|
||||
},
|
||||
{
|
||||
desc: "Default mode drop with header dropped",
|
||||
@@ -482,7 +482,7 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedLog: `- - TestUser [-] "- - -" - - "-" "-" - - - 0ms`,
|
||||
expectedLog: `- - TestUser [-] "- - -" - - "-" "-" - "-" "-" 0ms`,
|
||||
},
|
||||
{
|
||||
desc: "Default mode drop with header redacted",
|
||||
@@ -500,7 +500,7 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedLog: `- - TestUser [-] "- - -" - - "REDACTED" "REDACTED" - - - 0ms`,
|
||||
expectedLog: `- - TestUser [-] "- - -" - - "REDACTED" "REDACTED" - "-" "-" 0ms`,
|
||||
},
|
||||
{
|
||||
desc: "Default mode drop with header redacted",
|
||||
@@ -521,7 +521,7 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedLog: `- - TestUser [-] "- - -" - - "REDACTED" "testUserAgent" - - - 0ms`,
|
||||
expectedLog: `- - TestUser [-] "- - -" - - "REDACTED" "testUserAgent" - "-" "-" 0ms`,
|
||||
},
|
||||
}
|
||||
|
||||
|
@@ -32,7 +32,7 @@ type metricsMiddleware struct {
|
||||
openConns int64
|
||||
next http.Handler
|
||||
reqsCounter gokitmetrics.Counter
|
||||
reqDurationHistogram gokitmetrics.Histogram
|
||||
reqDurationHistogram metrics.ScalableHistogram
|
||||
openConnsGauge gokitmetrics.Gauge
|
||||
baseLabels []string
|
||||
}
|
||||
@@ -88,13 +88,17 @@ func (m *metricsMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request)
|
||||
m.openConnsGauge.With(labelValues...).Set(float64(openConns))
|
||||
}(labels)
|
||||
|
||||
start := time.Now()
|
||||
recorder := newResponseRecorder(rw)
|
||||
start := time.Now()
|
||||
|
||||
m.next.ServeHTTP(recorder, req)
|
||||
|
||||
labels = append(labels, "code", strconv.Itoa(recorder.getCode()))
|
||||
|
||||
histograms := m.reqDurationHistogram.With(labels...)
|
||||
histograms.ObserveFromStart(start)
|
||||
|
||||
m.reqsCounter.With(labels...).Add(1)
|
||||
m.reqDurationHistogram.With(labels...).Observe(time.Since(start).Seconds())
|
||||
}
|
||||
|
||||
func getRequestProtocol(req *http.Request) string {
|
||||
|
@@ -3,6 +3,7 @@ package replacepath
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/containous/traefik/v2/pkg/log"
|
||||
@@ -40,8 +41,22 @@ func (r *replacePath) GetTracingInformation() (string, ext.SpanKindEnum) {
|
||||
}
|
||||
|
||||
func (r *replacePath) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
req.Header.Add(ReplacedPathHeader, req.URL.Path)
|
||||
req.URL.Path = r.path
|
||||
if req.URL.RawPath == "" {
|
||||
req.Header.Add(ReplacedPathHeader, req.URL.Path)
|
||||
} else {
|
||||
req.Header.Add(ReplacedPathHeader, req.URL.RawPath)
|
||||
}
|
||||
|
||||
req.URL.RawPath = r.path
|
||||
|
||||
var err error
|
||||
req.URL.Path, err = url.PathUnescape(req.URL.RawPath)
|
||||
if err != nil {
|
||||
log.FromContext(middlewares.GetLoggerCtx(context.Background(), r.name, typeName)).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
req.RequestURI = req.URL.RequestURI()
|
||||
|
||||
r.next.ServeHTTP(rw, req)
|
||||
|
@@ -3,43 +3,93 @@ package replacepath
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/containous/traefik/v2/pkg/testhelpers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestReplacePath(t *testing.T) {
|
||||
var replacementConfig = dynamic.ReplacePath{
|
||||
Path: "/replacement-path",
|
||||
testCases := []struct {
|
||||
desc string
|
||||
path string
|
||||
config dynamic.ReplacePath
|
||||
expectedPath string
|
||||
expectedRawPath string
|
||||
expectedHeader string
|
||||
}{
|
||||
{
|
||||
desc: "simple path",
|
||||
path: "/example",
|
||||
config: dynamic.ReplacePath{
|
||||
Path: "/replacement-path",
|
||||
},
|
||||
expectedPath: "/replacement-path",
|
||||
expectedRawPath: "",
|
||||
expectedHeader: "/example",
|
||||
},
|
||||
{
|
||||
desc: "long path",
|
||||
path: "/some/really/long/path",
|
||||
config: dynamic.ReplacePath{
|
||||
Path: "/replacement-path",
|
||||
},
|
||||
expectedPath: "/replacement-path",
|
||||
expectedRawPath: "",
|
||||
expectedHeader: "/some/really/long/path",
|
||||
},
|
||||
{
|
||||
desc: "path with escaped value",
|
||||
path: "/foo%2Fbar",
|
||||
config: dynamic.ReplacePath{
|
||||
Path: "/replacement-path",
|
||||
},
|
||||
expectedPath: "/replacement-path",
|
||||
expectedRawPath: "",
|
||||
expectedHeader: "/foo%2Fbar",
|
||||
},
|
||||
{
|
||||
desc: "replacement with escaped value",
|
||||
path: "/path",
|
||||
config: dynamic.ReplacePath{
|
||||
Path: "/foo%2Fbar",
|
||||
},
|
||||
expectedPath: "/foo/bar",
|
||||
expectedRawPath: "/foo%2Fbar",
|
||||
expectedHeader: "/path",
|
||||
},
|
||||
}
|
||||
|
||||
paths := []string{
|
||||
"/example",
|
||||
"/some/really/long/path",
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
t.Run(path, func(t *testing.T) {
|
||||
var expectedPath, actualHeader, requestURI string
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
var actualPath, actualRawPath, actualHeader, requestURI string
|
||||
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
expectedPath = r.URL.Path
|
||||
actualPath = r.URL.Path
|
||||
actualRawPath = r.URL.RawPath
|
||||
actualHeader = r.Header.Get(ReplacedPathHeader)
|
||||
requestURI = r.RequestURI
|
||||
})
|
||||
|
||||
handler, err := New(context.Background(), next, replacementConfig, "foo-replace-path")
|
||||
handler, err := New(context.Background(), next, test.config, "foo-replace-path")
|
||||
require.NoError(t, err)
|
||||
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost"+path, nil)
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
|
||||
handler.ServeHTTP(nil, req)
|
||||
resp, err := http.Get(server.URL + test.path)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
assert.Equal(t, expectedPath, replacementConfig.Path, "Unexpected path.")
|
||||
assert.Equal(t, path, actualHeader, "Unexpected '%s' header.", ReplacedPathHeader)
|
||||
assert.Equal(t, expectedPath, requestURI, "Unexpected request URI.")
|
||||
assert.Equal(t, test.expectedPath, actualPath, "Unexpected path.")
|
||||
assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", ReplacedPathHeader)
|
||||
|
||||
if actualRawPath == "" {
|
||||
assert.Equal(t, actualPath, requestURI, "Unexpected request URI.")
|
||||
} else {
|
||||
assert.Equal(t, actualRawPath, requestURI, "Unexpected request URI.")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
@@ -49,10 +50,31 @@ func (rp *replacePathRegex) GetTracingInformation() (string, ext.SpanKindEnum) {
|
||||
}
|
||||
|
||||
func (rp *replacePathRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
if rp.regexp != nil && len(rp.replacement) > 0 && rp.regexp.MatchString(req.URL.Path) {
|
||||
req.Header.Add(replacepath.ReplacedPathHeader, req.URL.Path)
|
||||
req.URL.Path = rp.regexp.ReplaceAllString(req.URL.Path, rp.replacement)
|
||||
var currentPath string
|
||||
if req.URL.RawPath == "" {
|
||||
currentPath = req.URL.Path
|
||||
} else {
|
||||
currentPath = req.URL.RawPath
|
||||
}
|
||||
|
||||
if rp.regexp != nil && len(rp.replacement) > 0 && rp.regexp.MatchString(currentPath) {
|
||||
req.Header.Add(replacepath.ReplacedPathHeader, currentPath)
|
||||
|
||||
req.URL.RawPath = rp.regexp.ReplaceAllString(currentPath, rp.replacement)
|
||||
|
||||
// as replacement can introduce escaped characters
|
||||
// Path must remain an unescaped version of RawPath
|
||||
// Doesn't handle multiple times encoded replacement (`/` => `%2F` => `%252F` => ...)
|
||||
var err error
|
||||
req.URL.Path, err = url.PathUnescape(req.URL.RawPath)
|
||||
if err != nil {
|
||||
log.FromContext(middlewares.GetLoggerCtx(context.Background(), rp.name, typeName)).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
req.RequestURI = req.URL.RequestURI()
|
||||
}
|
||||
|
||||
rp.next.ServeHTTP(rw, req)
|
||||
}
|
||||
|
@@ -3,23 +3,24 @@ package replacepathregex
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/containous/traefik/v2/pkg/middlewares/replacepath"
|
||||
"github.com/containous/traefik/v2/pkg/testhelpers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestReplacePathRegex(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
path string
|
||||
config dynamic.ReplacePathRegex
|
||||
expectedPath string
|
||||
expectedHeader string
|
||||
expectsError bool
|
||||
desc string
|
||||
path string
|
||||
config dynamic.ReplacePathRegex
|
||||
expectedPath string
|
||||
expectedRawPath string
|
||||
expectedHeader string
|
||||
expectsError bool
|
||||
}{
|
||||
{
|
||||
desc: "simple regex",
|
||||
@@ -28,8 +29,9 @@ func TestReplacePathRegex(t *testing.T) {
|
||||
Replacement: "/who-am-i/$1",
|
||||
Regex: `^/whoami/(.*)`,
|
||||
},
|
||||
expectedPath: "/who-am-i/and/whoami",
|
||||
expectedHeader: "/whoami/and/whoami",
|
||||
expectedPath: "/who-am-i/and/whoami",
|
||||
expectedRawPath: "/who-am-i/and/whoami",
|
||||
expectedHeader: "/whoami/and/whoami",
|
||||
},
|
||||
{
|
||||
desc: "simple replace (no regex)",
|
||||
@@ -38,8 +40,9 @@ func TestReplacePathRegex(t *testing.T) {
|
||||
Replacement: "/who-am-i",
|
||||
Regex: `/whoami`,
|
||||
},
|
||||
expectedPath: "/who-am-i/and/who-am-i",
|
||||
expectedHeader: "/whoami/and/whoami",
|
||||
expectedPath: "/who-am-i/and/who-am-i",
|
||||
expectedRawPath: "/who-am-i/and/who-am-i",
|
||||
expectedHeader: "/whoami/and/whoami",
|
||||
},
|
||||
{
|
||||
desc: "no match",
|
||||
@@ -57,8 +60,9 @@ func TestReplacePathRegex(t *testing.T) {
|
||||
Replacement: "/downloads/$1-$2",
|
||||
Regex: `^(?i)/downloads/([^/]+)/([^/]+)$`,
|
||||
},
|
||||
expectedPath: "/downloads/src-source.go",
|
||||
expectedHeader: "/downloads/src/source.go",
|
||||
expectedPath: "/downloads/src-source.go",
|
||||
expectedRawPath: "/downloads/src-source.go",
|
||||
expectedHeader: "/downloads/src/source.go",
|
||||
},
|
||||
{
|
||||
desc: "invalid regular expression",
|
||||
@@ -70,13 +74,46 @@ func TestReplacePathRegex(t *testing.T) {
|
||||
expectedPath: "/invalid/regexp/test",
|
||||
expectsError: true,
|
||||
},
|
||||
{
|
||||
desc: "replacement with escaped char",
|
||||
path: "/aaa/bbb",
|
||||
config: dynamic.ReplacePathRegex{
|
||||
Replacement: "/foo%2Fbar",
|
||||
Regex: `/aaa/bbb`,
|
||||
},
|
||||
expectedPath: "/foo/bar",
|
||||
expectedRawPath: "/foo%2Fbar",
|
||||
expectedHeader: "/aaa/bbb",
|
||||
},
|
||||
{
|
||||
desc: "path and regex with escaped char",
|
||||
path: "/aaa%2Fbbb",
|
||||
config: dynamic.ReplacePathRegex{
|
||||
Replacement: "/foo/bar",
|
||||
Regex: `/aaa%2Fbbb`,
|
||||
},
|
||||
expectedPath: "/foo/bar",
|
||||
expectedRawPath: "/foo/bar",
|
||||
expectedHeader: "/aaa%2Fbbb",
|
||||
},
|
||||
{
|
||||
desc: "path with escaped char (no match)",
|
||||
path: "/aaa%2Fbbb",
|
||||
config: dynamic.ReplacePathRegex{
|
||||
Replacement: "/foo/bar",
|
||||
Regex: `/aaa/bbb`,
|
||||
},
|
||||
expectedPath: "/aaa/bbb",
|
||||
expectedRawPath: "/aaa%2Fbbb",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
var actualPath, actualHeader, requestURI string
|
||||
var actualPath, actualRawPath, actualHeader, requestURI string
|
||||
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
actualPath = r.URL.Path
|
||||
actualRawPath = r.URL.RawPath
|
||||
actualHeader = r.Header.Get(replacepath.ReplacedPathHeader)
|
||||
requestURI = r.RequestURI
|
||||
})
|
||||
@@ -84,19 +121,29 @@ func TestReplacePathRegex(t *testing.T) {
|
||||
handler, err := New(context.Background(), next, test.config, "foo-replace-path-regexp")
|
||||
if test.expectsError {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost"+test.path, nil)
|
||||
req.RequestURI = test.path
|
||||
require.NoError(t, err)
|
||||
|
||||
handler.ServeHTTP(nil, req)
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
|
||||
assert.Equal(t, test.expectedPath, actualPath, "Unexpected path.")
|
||||
resp, err := http.Get(server.URL + test.path)
|
||||
require.NoError(t, err, "Unexpected error while making test request")
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
assert.Equal(t, test.expectedPath, actualPath, "Unexpected path.")
|
||||
assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.")
|
||||
|
||||
if actualRawPath == "" {
|
||||
assert.Equal(t, actualPath, requestURI, "Unexpected request URI.")
|
||||
if test.expectedHeader != "" {
|
||||
assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", replacepath.ReplacedPathHeader)
|
||||
}
|
||||
} else {
|
||||
assert.Equal(t, actualRawPath, requestURI, "Unexpected request URI.")
|
||||
}
|
||||
|
||||
if test.expectedHeader != "" {
|
||||
assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", replacepath.ReplacedPathHeader)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -46,7 +46,7 @@ var _ provider.Provider = (*Provider)(nil)
|
||||
// Provider holds configurations of the provider.
|
||||
type Provider struct {
|
||||
Constraints string `description:"Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container." json:"constraints,omitempty" toml:"constraints,omitempty" yaml:"constraints,omitempty" export:"true"`
|
||||
Watch bool `description:"Watch provider." json:"watch,omitempty" toml:"watch,omitempty" yaml:"watch,omitempty" export:"true"`
|
||||
Watch bool `description:"Watch Docker Swarm events." json:"watch,omitempty" toml:"watch,omitempty" yaml:"watch,omitempty" export:"true"`
|
||||
Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
|
||||
DefaultRule string `description:"Default rule." json:"defaultRule,omitempty" toml:"defaultRule,omitempty" yaml:"defaultRule,omitempty"`
|
||||
TLS *types.ClientTLS `description:"Enable Docker TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
|
||||
|
@@ -23,12 +23,12 @@ func (t *h2cTransportWrapper) RoundTrip(req *http.Request) (*http.Response, erro
|
||||
return t.Transport.RoundTrip(req)
|
||||
}
|
||||
|
||||
// createHTTPTransport creates an http.Transport configured with the Transport configuration settings.
|
||||
// createRoundtripper creates an http.Roundtripper configured with the Transport configuration settings.
|
||||
// For the settings that can't be configured in Traefik it uses the default http.Transport settings.
|
||||
// An exception to this is the MaxIdleConns setting as we only provide the option MaxIdleConnsPerHost
|
||||
// in Traefik at this point in time. Setting this value to the default of 100 could lead to confusing
|
||||
// behavior and backwards compatibility issues.
|
||||
func createHTTPTransport(transportConfiguration *static.ServersTransport) (*http.Transport, error) {
|
||||
func createRoundtripper(transportConfiguration *static.ServersTransport) (http.RoundTripper, error) {
|
||||
if transportConfiguration == nil {
|
||||
return nil, errors.New("no transport configuration given")
|
||||
}
|
||||
@@ -66,25 +66,26 @@ func createHTTPTransport(transportConfiguration *static.ServersTransport) (*http
|
||||
transport.IdleConnTimeout = time.Duration(transportConfiguration.ForwardingTimeouts.IdleConnTimeout)
|
||||
}
|
||||
|
||||
if transportConfiguration.InsecureSkipVerify {
|
||||
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
|
||||
if len(transportConfiguration.RootCAs) > 0 {
|
||||
if transportConfiguration.InsecureSkipVerify || len(transportConfiguration.RootCAs) > 0 {
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
RootCAs: createRootCACertPool(transportConfiguration.RootCAs),
|
||||
InsecureSkipVerify: transportConfiguration.InsecureSkipVerify,
|
||||
RootCAs: createRootCACertPool(transportConfiguration.RootCAs),
|
||||
}
|
||||
}
|
||||
|
||||
err := http2.ConfigureTransport(transport)
|
||||
smartTransport, err := newSmartRoundTripper(transport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return transport, nil
|
||||
return smartTransport, nil
|
||||
}
|
||||
|
||||
func createRootCACertPool(rootCAs []traefiktls.FileOrContent) *x509.CertPool {
|
||||
if len(rootCAs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
roots := x509.NewCertPool()
|
||||
|
||||
for _, cert := range rootCAs {
|
||||
@@ -100,7 +101,7 @@ func createRootCACertPool(rootCAs []traefiktls.FileOrContent) *x509.CertPool {
|
||||
}
|
||||
|
||||
func setupDefaultRoundTripper(conf *static.ServersTransport) http.RoundTripper {
|
||||
transport, err := createHTTPTransport(conf)
|
||||
transport, err := createRoundtripper(conf)
|
||||
if err != nil {
|
||||
log.WithoutContext().Errorf("Could not configure HTTP Transport, fallbacking on default transport: %v", err)
|
||||
return http.DefaultTransport
|
||||
|
@@ -283,8 +283,15 @@ func (m *Manager) getLoadBalancer(ctx context.Context, serviceName string, servi
|
||||
var cookieName string
|
||||
if service.Sticky != nil && service.Sticky.Cookie != nil {
|
||||
cookieName = cookie.GetName(service.Sticky.Cookie.Name, serviceName)
|
||||
opts := roundrobin.CookieOptions{HTTPOnly: service.Sticky.Cookie.HTTPOnly, Secure: service.Sticky.Cookie.Secure}
|
||||
|
||||
opts := roundrobin.CookieOptions{
|
||||
HTTPOnly: service.Sticky.Cookie.HTTPOnly,
|
||||
Secure: service.Sticky.Cookie.Secure,
|
||||
SameSite: convertSameSite(service.Sticky.Cookie.SameSite),
|
||||
}
|
||||
|
||||
options = append(options, roundrobin.EnableStickySession(roundrobin.NewStickySessionWithOptions(cookieName, opts)))
|
||||
|
||||
logger.Debugf("Sticky session cookie name: %v", cookieName)
|
||||
}
|
||||
|
||||
@@ -320,3 +327,16 @@ func (m *Manager) upsertServers(ctx context.Context, lb healthcheck.BalancerHand
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertSameSite(sameSite string) http.SameSite {
|
||||
switch sameSite {
|
||||
case "none":
|
||||
return http.SameSiteNoneMode
|
||||
case "lax":
|
||||
return http.SameSiteLaxMode
|
||||
case "strict":
|
||||
return http.SameSiteStrictMode
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
38
pkg/server/service/smart_roundtripper.go
Normal file
38
pkg/server/service/smart_roundtripper.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
func newSmartRoundTripper(transport *http.Transport) (http.RoundTripper, error) {
|
||||
transportHTTP1 := transport.Clone()
|
||||
|
||||
err := http2.ConfigureTransport(transport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &smartRoundTripper{
|
||||
http2: transport,
|
||||
http: transportHTTP1,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type smartRoundTripper struct {
|
||||
http2 *http.Transport
|
||||
http *http.Transport
|
||||
}
|
||||
|
||||
// smartRoundTripper implements RoundTrip while making sure that HTTP/2 is not used
|
||||
// with protocols that start with a Connection Upgrade, such as SPDY or Websocket.
|
||||
func (m *smartRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
// If we have a connection upgrade, we don't use HTTP/2
|
||||
if httpguts.HeaderValuesContainsToken(req.Header["Connection"], "Upgrade") {
|
||||
return m.http.RoundTrip(req)
|
||||
}
|
||||
|
||||
return m.http2.RoundTrip(req)
|
||||
}
|
Reference in New Issue
Block a user