forked from Ivasoft/traefik
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7758880f3f | ||
|
|
d04903edb2 | ||
|
|
9cd54baca4 | ||
|
|
7ac687a0a9 | ||
|
|
83ae1021f6 | ||
|
|
67e3bc6380 | ||
|
|
89870ad539 | ||
|
|
a4b447256b | ||
|
|
1c9a7b8c61 | ||
|
|
d06573de6c | ||
|
|
8ddc37d528 | ||
|
|
0cb2652f51 | ||
|
|
fe8e7ab5b8 | ||
|
|
56a1ed4220 | ||
|
|
37b6edb28c | ||
|
|
44a2b85dba | ||
|
|
77c8d60092 | ||
|
|
b33c8cec0b | ||
|
|
12dccc4fdd |
29
CHANGELOG.md
29
CHANGELOG.md
@@ -1,3 +1,32 @@
|
||||
## [v2.8.6](https://github.com/traefik/traefik/tree/v2.8.6) (2022-09-23)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.8.5...v2.8.6)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[consulcatalog]** Fix UDP loadbalancer tags not being used with Consul Catalog ([#9357](https://github.com/traefik/traefik/pull/9357) by [t3hchipmunk](https://github.com/t3hchipmunk))
|
||||
- **[docker,rancher,ecs,provider]** Simplify AddServer algorithm ([#9358](https://github.com/traefik/traefik/pull/9358) by [ldez](https://github.com/ldez))
|
||||
- **[plugins]** Allow empty plugin configuration ([#9338](https://github.com/traefik/traefik/pull/9338) by [ldez](https://github.com/ldez))
|
||||
- **[rules]** Fix query parameter matching with equal ([#9369](https://github.com/traefik/traefik/pull/9369) by [ldez](https://github.com/ldez))
|
||||
- **[server]** Optimize websocket headers handling ([#9360](https://github.com/traefik/traefik/pull/9360) by [juliens](https://github.com/juliens))
|
||||
|
||||
**Documentation:**
|
||||
- **[ecs]** Add documentation for ECS constraints option ([#9354](https://github.com/traefik/traefik/pull/9354) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[k8s/gatewayapi]** Fix link to RouteNamespaces ([#9349](https://github.com/traefik/traefik/pull/9349) by [ldez](https://github.com/ldez))
|
||||
- Add documentation for json schema usage to validate config in the FAQ ([#9340](https://github.com/traefik/traefik/pull/9340) by [rtribotte](https://github.com/rtribotte))
|
||||
- Add a note on case insensitive regex matching ([#9322](https://github.com/traefik/traefik/pull/9322) by [NEwa-05](https://github.com/NEwa-05))
|
||||
|
||||
## [v2.8.5](https://github.com/traefik/traefik/tree/v2.8.5) (2022-09-13)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.8.4...v2.8.5)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[plugins]** Update Yaegi to v0.14.2 ([#9327](https://github.com/traefik/traefik/pull/9327) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[server]** Fix IPv6 addr with square brackets ([#9313](https://github.com/traefik/traefik/pull/9313) by [moonlightwatch](https://github.com/moonlightwatch))
|
||||
- **[webui,api]** Display default TLS options in the dashboard ([#9312](https://github.com/traefik/traefik/pull/9312) by [skwair](https://github.com/skwair))
|
||||
|
||||
**Documentation:**
|
||||
- **[docker]** Add healthcheck timeout seconds to value ([#9306](https://github.com/traefik/traefik/pull/9306) by [fty4](https://github.com/fty4))
|
||||
- Update deprecation notes about Pilot ([#9314](https://github.com/traefik/traefik/pull/9314) by [nmengin](https://github.com/nmengin))
|
||||
- Added resources for businesses ([#9268](https://github.com/traefik/traefik/pull/9268) by [tomatokoolaid](https://github.com/tomatokoolaid))
|
||||
|
||||
## [v2.8.4](https://github.com/traefik/traefik/tree/v2.8.4) (2022-09-02)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.8.3...v2.8.4)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ This page is maintained and updated periodically to reflect our roadmap and any
|
||||
|
||||
### Pilot Dashboard (Metrics)
|
||||
|
||||
Metrics will continue to function normally up to 2.9, when they will be disabled.
|
||||
Metrics will continue to function normally up to 2.8, when they will be disabled.
|
||||
In 3.0, the Pilot platform and all Traefik integration code will be permanently removed.
|
||||
|
||||
### Pilot Plugins
|
||||
|
||||
@@ -39,3 +39,5 @@ You no longer need to create and synchronize configuration files cluttered with
|
||||
!!! question "How does Traefik discover the services?"
|
||||
|
||||
Traefik is able to use your cluster API to discover the services and read the attached information. In Traefik, these connectors are called [providers](../providers/overview.md) because they _provide_ the configuration to Traefik. To learn more about them, read the [provider overview](../providers/overview.md) section.
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -94,17 +94,4 @@ All the configuration options are documented in their related section.
|
||||
|
||||
You can browse the available features in the menu, the [providers](../providers/overview.md), or the [routing section](../routing/overview.md) to see them in action.
|
||||
|
||||
!!! question "Using Traefik for Business Applications?"
|
||||
|
||||
If you are using Traefik for commercial applications,
|
||||
consider the [Enterprise Edition](https://traefik.io/traefik-enterprise/).
|
||||
You can use it as your:
|
||||
|
||||
- [Kubernetes Ingress Controller](https://traefik.io/solutions/kubernetes-ingress/)
|
||||
- [Load Balancer](https://traefik.io/solutions/docker-swarm-ingress/)
|
||||
- [API Gateway](https://traefik.io/solutions/api-gateway/)
|
||||
|
||||
Traefik Enterprise enables centralized access management,
|
||||
distributed Let's Encrypt,
|
||||
and other advanced capabilities.
|
||||
Learn more in [this 15-minute technical walkthrough](https://info.traefik.io/watch-traefikee-demo).
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -157,3 +157,27 @@ By default, the following headers are automatically added when proxying requests
|
||||
|
||||
For more details,
|
||||
please check out the [forwarded header](../routing/entrypoints.md#forwarded-headers) documentation.
|
||||
|
||||
## What does the "field not found" error mean?
|
||||
|
||||
```shell
|
||||
error: field not found, node: -badField-
|
||||
```
|
||||
|
||||
The "field not found" error occurs, when an unknown property is encountered in the dynamic or static configuration.
|
||||
|
||||
One easy way to check whether a configuration file is well-formed, is to validate it with:
|
||||
|
||||
- [JSON Schema of the static configuration](https://json.schemastore.org/traefik-v2.json)
|
||||
- [JSON Schema of the dynamic configuration](https://json.schemastore.org/traefik-v2-file-provider.json)
|
||||
|
||||
## Why are some resources (routers, middlewares, services...) not created/applied?
|
||||
|
||||
As a common tip, if a resource is dropped/not created by Traefik after the dynamic configuration was evaluated,
|
||||
one should look for an error in the logs.
|
||||
|
||||
If found, the error obviously confirms that something went wrong while creating the resource,
|
||||
and the message should help in figuring out the mistake(s) in the configuration, and how to fix it.
|
||||
|
||||
When using the file provider,
|
||||
one easy way to check if the dynamic configuration is well-formed is to validate it with the [JSON Schema of the dynamic configuration](https://json.schemastore.org/traefik-v2-file-provider.json).
|
||||
|
||||
@@ -179,17 +179,4 @@ And run it:
|
||||
|
||||
All the details are available in the [Contributing Guide](../contributing/building-testing.md)
|
||||
|
||||
!!! question "Using Traefik for Business Applications?"
|
||||
|
||||
If you are using Traefik for commercial applications,
|
||||
consider the [Enterprise Edition](https://traefik.io/traefik-enterprise/).
|
||||
You can use it as your:
|
||||
|
||||
- [Kubernetes Ingress Controller](https://traefik.io/solutions/kubernetes-ingress/)
|
||||
- [Load Balancer](https://traefik.io/solutions/docker-swarm-ingress/)
|
||||
- [API Gateway](https://traefik.io/solutions/api-gateway/)
|
||||
|
||||
Traefik Enterprise enables centralized access management,
|
||||
distributed Let's Encrypt,
|
||||
and other advanced capabilities.
|
||||
Learn more in [this 15-minute technical walkthrough](https://info.traefik.io/watch-traefikee-demo).
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -116,17 +116,4 @@ IP: 172.27.0.4
|
||||
|
||||
Now that you have a basic understanding of how Traefik can automatically create the routes to your services and load balance them, it is time to dive into [the documentation](/) and let Traefik work for you!
|
||||
|
||||
!!! question "Using Traefik for Business Applications?"
|
||||
|
||||
If you are using Traefik for commercial applications,
|
||||
consider the [Enterprise Edition](https://traefik.io/traefik-enterprise/).
|
||||
You can use it as your:
|
||||
|
||||
- [Kubernetes Ingress Controller](https://traefik.io/solutions/kubernetes-ingress/)
|
||||
- [Load Balancer](https://traefik.io/solutions/docker-swarm-ingress/)
|
||||
- [API Gateway](https://traefik.io/solutions/api-gateway/)
|
||||
|
||||
Traefik Enterprise enables centralized access management,
|
||||
distributed Let's Encrypt,
|
||||
and other advanced capabilities.
|
||||
Learn more in [this 15-minute technical walkthrough](https://info.traefik.io/watch-traefikee-demo).
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -661,23 +661,10 @@ certificatesResolvers:
|
||||
If Let's Encrypt is not reachable, the following certificates will apply:
|
||||
|
||||
1. Previously generated ACME certificates (before downtime)
|
||||
1. Expired ACME certificates
|
||||
1. Provided certificates
|
||||
2. Expired ACME certificates
|
||||
3. Provided certificates
|
||||
|
||||
!!! important
|
||||
For new (sub)domains which need Let's Encrypt authentication, the default Traefik certificate will be used until Traefik is restarted.
|
||||
|
||||
!!! question "Using Traefik for Business Applications?"
|
||||
|
||||
If you are using Traefik for commercial applications,
|
||||
consider the [Enterprise Edition](https://traefik.io/traefik-enterprise/).
|
||||
You can use it as your:
|
||||
|
||||
- [Kubernetes Ingress Controller](https://traefik.io/solutions/kubernetes-ingress/)
|
||||
- [Load Balancer](https://traefik.io/solutions/docker-swarm-ingress/)
|
||||
- [API Gateway](https://traefik.io/solutions/api-gateway/)
|
||||
|
||||
Traefik Enterprise enables centralized access management,
|
||||
distributed Let's Encrypt,
|
||||
and other advanced capabilities.
|
||||
Learn more in [this 15-minute technical walkthrough](https://info.traefik.io/watch-traefikee-demo).
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -19,3 +19,5 @@ The next sections of this documentation explain how to configure the TLS connect
|
||||
That is to say, how to obtain [TLS certificates](./tls.md#certificates-definition):
|
||||
either through a definition in the dynamic configuration, or through [Let's Encrypt](./acme.md) (ACME).
|
||||
And how to configure [TLS options](./tls.md#tls-options), and [certificates stores](./tls.md#certificates-stores).
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -490,3 +490,5 @@ spec:
|
||||
- secretCA
|
||||
clientAuthType: RequireAndVerifyClientCert
|
||||
```
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
4
docs/content/includes/.markdownlint.json
Normal file
4
docs/content/includes/.markdownlint.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../.markdownlint.json",
|
||||
"MD041": false
|
||||
}
|
||||
16
docs/content/includes/traefik-for-business-applications.md
Normal file
16
docs/content/includes/traefik-for-business-applications.md
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
|
||||
!!! question "Using Traefik for Business Applications?"
|
||||
|
||||
If you are using Traefik for commercial applications,
|
||||
consider the [Enterprise Edition](https://traefik.io/traefik-enterprise/).
|
||||
You can use it as your:
|
||||
|
||||
- [Kubernetes Ingress Controller](https://traefik.io/solutions/kubernetes-ingress/)
|
||||
- [Docker Swarm Ingress Controller](https://traefik.io/solutions/docker-swarm-ingress/)
|
||||
- [API Gateway](https://traefik.io/solutions/api-gateway/)
|
||||
|
||||
Traefik Enterprise enables centralized access management,
|
||||
distributed Let's Encrypt,
|
||||
and other advanced capabilities.
|
||||
Learn more in [this 15-minute technical walkthrough](https://info.traefik.io/watch-traefikee-demo).
|
||||
@@ -469,3 +469,5 @@ The `permissionsPolicy` allows sites to control browser features.
|
||||
Set `isDevelopment` to `true` when developing to mitigate the unwanted effects of the `AllowedHosts`, SSL, and STS options.
|
||||
Usually testing takes place using HTTP, not HTTPS, and on `localhost`, not your production domain.
|
||||
If you would like your development environment to mimic production with complete Host blocking, SSL redirects, and STS headers, leave this as `false`.
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -157,3 +157,5 @@ http:
|
||||
## Community Middlewares
|
||||
|
||||
Please take a look at the community-contributed plugins in the [plugin catalog](https://pilot.traefik.io/plugins).
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -129,3 +129,5 @@ http:
|
||||
A list of HTTP middlewares can be found [here](http/overview.md).
|
||||
|
||||
A list of TCP middlewares can be found [here](tcp/overview.md).
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -445,7 +445,7 @@ To enable HTTP/3 on an EntryPoint, please check out the [HTTP/3 configuration](.
|
||||
### Kubernetes Gateway API Provider
|
||||
|
||||
In `v2.6`, the [Kubernetes Gateway API provider](../providers/kubernetes-gateway.md) now only supports the version [v1alpha2](https://gateway-api.sigs.k8s.io/v1alpha2/guides/getting-started/) of the specification and
|
||||
[route namespaces](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.RouteNamespaces) selectors, which requires Traefik to fetch and watch the cluster namespaces.
|
||||
[route namespaces](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.RouteNamespaces) selectors, which requires Traefik to fetch and watch the cluster namespaces.
|
||||
Therefore, the [RBAC](../reference/dynamic-configuration/kubernetes-gateway.md#rbac) and [CRD](../reference/dynamic-configuration/kubernetes-gateway.md#definitions) definitions must be updated.
|
||||
|
||||
## v2.6.0 to v2.6.1
|
||||
|
||||
@@ -128,3 +128,5 @@ api:
|
||||
|
||||
You can now access the dashboard on the port `8080` of the Traefik instance,
|
||||
at the following URL: `http://<Traefik IP>:8080/dashboard/` (trailing slash is mandatory).
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -715,17 +715,4 @@ providers:
|
||||
--providers.docker.tls.insecureSkipVerify=true
|
||||
```
|
||||
|
||||
!!! question "Using Traefik for Business Applications?"
|
||||
|
||||
If you are using Traefik for commercial applications,
|
||||
consider the [Enterprise Edition](https://traefik.io/traefik-enterprise/).
|
||||
You can use it as your:
|
||||
|
||||
- [Kubernetes Ingress Controller](https://traefik.io/solutions/kubernetes-ingress/)
|
||||
- [Load Balancer](https://traefik.io/solutions/docker-swarm-ingress/)
|
||||
- [API Gateway](https://traefik.io/solutions/api-gateway/)
|
||||
|
||||
Traefik Enterprise enables centralized access management,
|
||||
distributed Let's Encrypt,
|
||||
and other advanced capabilities.
|
||||
Learn more in [this 15-minute technical walkthrough](https://info.traefik.io/watch-traefikee-demo).
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -137,6 +137,70 @@ providers:
|
||||
# ...
|
||||
```
|
||||
|
||||
### `constraints`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
The `constraints` option can be set to an expression that Traefik matches against the container labels (task),
|
||||
to determine whether to create any route for that container.
|
||||
If none of the container labels match the expression, no route for that container is created.
|
||||
If the expression is empty, all detected containers are included.
|
||||
|
||||
The expression syntax is based on the `Label("key", "value")`, and `LabelRegex("key", "value")` functions,
|
||||
as well as the usual boolean logic, as shown in examples below.
|
||||
|
||||
??? example "Constraints Expression Examples"
|
||||
|
||||
```toml
|
||||
# Includes only containers having a label with key `a.label.name` and value `foo`
|
||||
constraints = "Label(`a.label.name`, `foo`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Excludes containers having any label with key `a.label.name` and value `foo`
|
||||
constraints = "!Label(`a.label.name`, `value`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND.
|
||||
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical OR.
|
||||
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND and OR, with precedence set by parentheses.
|
||||
constraints = "Label(`a.label.name`, `valueA`) && (Label(`another.label.name`, `valueB`) || Label(`yet.another.label.name`, `valueC`))"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Includes only containers having a label with key `a.label.name` and a value matching the `a.+` regular expression.
|
||||
constraints = "LabelRegex(`a.label.name`, `a.+`)"
|
||||
```
|
||||
|
||||
For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery).
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
ecs:
|
||||
constraints: "Label(`a.label.name`,`foo`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.ecs]
|
||||
constraints = "Label(`a.label.name`,`foo`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.ecs.constraints=Label(`a.label.name`,`foo`)
|
||||
# ...
|
||||
```
|
||||
|
||||
### `defaultRule`
|
||||
|
||||
_Optional, Default=```Host(`{{ normalize .Name }}`)```_
|
||||
|
||||
@@ -291,3 +291,5 @@ To illustrate, it is possible to easily define multiple routers, services, and T
|
||||
# ...
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -344,3 +344,5 @@ providers:
|
||||
## Full Example
|
||||
|
||||
For additional information, refer to the [full example](../user-guides/crd-acme/index.md) with Let's Encrypt.
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -502,17 +502,4 @@ providers:
|
||||
To learn more about the various aspects of the Ingress specification that Traefik supports,
|
||||
many examples of Ingresses definitions are located in the test [examples](https://github.com/traefik/traefik/tree/v2.8/pkg/provider/kubernetes/ingress/fixtures) of the Traefik repository.
|
||||
|
||||
!!! question "Using Traefik for Business Applications?"
|
||||
|
||||
If you are using Traefik for commercial applications,
|
||||
consider the [Enterprise Edition](https://traefik.io/traefik-enterprise/).
|
||||
You can use it as your:
|
||||
|
||||
- [Kubernetes Ingress Controller](https://traefik.io/solutions/kubernetes-ingress/)
|
||||
- [Load Balancer](https://traefik.io/solutions/docker-swarm-ingress/)
|
||||
- [API Gateway](https://traefik.io/solutions/api-gateway/)
|
||||
|
||||
Traefik Enterprise enables centralized access management,
|
||||
distributed Let's Encrypt,
|
||||
and other advanced capabilities.
|
||||
Learn more in [this 15-minute technical walkthrough](https://info.traefik.io/watch-traefikee-demo).
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -213,6 +213,7 @@ you can do so in two different ways:
|
||||
List of providers that support these features:
|
||||
|
||||
- [Docker](./docker.md#exposedbydefault)
|
||||
- [ECS](./ecs.md#exposedbydefault)
|
||||
- [Consul Catalog](./consul-catalog.md#exposedbydefault)
|
||||
- [Nomad](./nomad.md#exposedbydefault)
|
||||
- [Rancher](./rancher.md#exposedbydefault)
|
||||
@@ -223,6 +224,7 @@ List of providers that support these features:
|
||||
List of providers that support constraints:
|
||||
|
||||
- [Docker](./docker.md#constraints)
|
||||
- [ECS](./ecs.md#constraints)
|
||||
- [Consul Catalog](./consul-catalog.md#constraints)
|
||||
- [Nomad](./nomad.md#constraints)
|
||||
- [Rancher](./rancher.md#constraints)
|
||||
@@ -230,3 +232,5 @@ List of providers that support constraints:
|
||||
- [Kubernetes CRD](./kubernetes-crd.md#labelselector)
|
||||
- [Kubernetes Ingress](./kubernetes-ingress.md#labelselector)
|
||||
- [Kubernetes Gateway](./kubernetes-gateway.md#labelselector)
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -968,17 +968,4 @@ entrypoints.foo.address=:8000/udp
|
||||
entrypoints.foo.udp.timeout=10s
|
||||
```
|
||||
|
||||
!!! question "Using Traefik for Business Applications?"
|
||||
|
||||
If you are using Traefik for commercial applications,
|
||||
consider the [Enterprise Edition](https://traefik.io/traefik-enterprise/).
|
||||
You can use it as your:
|
||||
|
||||
- [Kubernetes Ingress Controller](https://traefik.io/solutions/kubernetes-ingress/)
|
||||
- [Load Balancer](https://traefik.io/solutions/docker-swarm-ingress/)
|
||||
- [API Gateway](https://traefik.io/solutions/api-gateway/)
|
||||
|
||||
Traefik Enterprise enables centralized access management,
|
||||
distributed Let's Encrypt,
|
||||
and other advanced capabilities.
|
||||
Learn more in [this 15-minute technical walkthrough](https://info.traefik.io/watch-traefikee-demo).
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -406,3 +406,5 @@ serversTransport:
|
||||
## Static configuration
|
||||
--serversTransport.forwardingTimeouts.idleConnTimeout=1s
|
||||
```
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -360,7 +360,7 @@ you'd add the label `traefik.http.services.<name-of-your-choice>.loadbalancer.pa
|
||||
See [health check](../services/index.md#health-check) for more information.
|
||||
|
||||
```yaml
|
||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10"
|
||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10s"
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.followredirects`"
|
||||
|
||||
@@ -1782,3 +1782,5 @@ If the ServersTransport CRD is defined in another provider the cross-provider fo
|
||||
## Further
|
||||
|
||||
Also see the [full example](../../user-guides/crd-acme/index.md) with Let's Encrypt.
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -947,3 +947,5 @@ This will allow users to create a "default router" that will match all unmatched
|
||||
to avoid this global ingress from satisfying requests that could match other ingresses.
|
||||
|
||||
To do this, use the `traefik.ingress.kubernetes.io/router.priority` annotation (as seen in [Annotations on Ingress](#on-ingress)) on your ingresses accordingly.
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -233,18 +233,18 @@ If the rule is verified, the router becomes active, calls middlewares, and then
|
||||
|
||||
The table below lists all the available matchers:
|
||||
|
||||
| Rule | Description |
|
||||
|------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
|
||||
| ```Headers(`key`, `value`)``` | Check if there is a key `key`defined in the headers, with the value `value` |
|
||||
| ```HeadersRegexp(`key`, `regexp`)``` | Check if there is a key `key`defined in the headers, with a value that matches the regular expression `regexp` |
|
||||
| ```Host(`example.com`, ...)``` | Check if the request domain (host header value) targets one of the given `domains`. |
|
||||
| ```HostHeader(`example.com`, ...)``` | Same as `Host`, only exists for historical reasons. |
|
||||
| ```HostRegexp(`example.com`, `{subdomain:[a-z]+}.example.com`, ...)``` | Match the request domain. See "Regexp Syntax" below. |
|
||||
| ```Method(`GET`, ...)``` | Check if the request method is one of the given `methods` (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`) |
|
||||
| ```Path(`/path`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`, ...)``` | Match exact request path. See "Regexp Syntax" below. |
|
||||
| ```PathPrefix(`/products/`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`)``` | Match request prefix path. See "Regexp Syntax" below. |
|
||||
| ```Query(`foo=bar`, `bar=baz`)``` | Match Query String parameters. It accepts a sequence of key=value pairs. |
|
||||
| ```ClientIP(`10.0.0.0/16`, `::1`)``` | Match if the request client IP is one of the given IP/CIDR. It accepts IPv4, IPv6 and CIDR formats. |
|
||||
| Rule | Description |
|
||||
|--------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
|
||||
| ```Headers(`key`, `value`)``` | Check if there is a key `key`defined in the headers, with the value `value` |
|
||||
| ```HeadersRegexp(`key`, `regexp`)``` | Check if there is a key `key`defined in the headers, with a value that matches the regular expression `regexp` |
|
||||
| ```Host(`example.com`, ...)``` | Check if the request domain (host header value) targets one of the given `domains`. |
|
||||
| ```HostHeader(`example.com`, ...)``` | Same as `Host`, only exists for historical reasons. |
|
||||
| ```HostRegexp(`example.com`, `{subdomain:[a-z]+}.example.com`, ...)``` | Match the request domain. See "Regexp Syntax" below. |
|
||||
| ```Method(`GET`, ...)``` | Check if the request method is one of the given `methods` (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`) |
|
||||
| ```Path(`/path`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`, ...)``` | Match exact request path. See "Regexp Syntax" below. |
|
||||
| ```PathPrefix(`/products/`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`)``` | Match request prefix path. See "Regexp Syntax" below. |
|
||||
| ```Query(`foo=bar`, `bar=baz`)``` | Match Query String parameters. It accepts a sequence of key=value pairs. |
|
||||
| ```ClientIP(`10.0.0.0/16`, `::1`)``` | Match if the request client IP is one of the given IP/CIDR. It accepts IPv4, IPv6 and CIDR formats. |
|
||||
|
||||
!!! important "Non-ASCII Domain Names"
|
||||
|
||||
@@ -259,6 +259,7 @@ The table below lists all the available matchers:
|
||||
The regexp name (`name` in the above example) is an arbitrary value, that exists only for historical reasons.
|
||||
|
||||
Any `regexp` supported by [Go's regexp package](https://golang.org/pkg/regexp/) may be used.
|
||||
For example, here is a case insensitive path matcher syntax: ```Path(`/{path:(?i:Products)}`)```.
|
||||
|
||||
!!! info "Combining Matchers Using Operators and Parenthesis"
|
||||
|
||||
@@ -1322,17 +1323,4 @@ Services are the target for the router.
|
||||
|
||||
!!! important "UDP routers can only target UDP services (and not HTTP or TCP services)."
|
||||
|
||||
!!! question "Using Traefik for Business Applications?"
|
||||
|
||||
If you are using Traefik for commercial applications,
|
||||
consider the [Enterprise Edition](https://traefik.io/traefik-enterprise/).
|
||||
You can use it as your:
|
||||
|
||||
- [Kubernetes Ingress Controller](https://traefik.io/solutions/kubernetes-ingress/)
|
||||
- [Load Balancer](https://traefik.io/solutions/docker-swarm-ingress/)
|
||||
- [API Gateway](https://traefik.io/solutions/api-gateway/)
|
||||
|
||||
Traefik Enterprise enables centralized access management,
|
||||
distributed Let's Encrypt,
|
||||
and other advanced capabilities.
|
||||
Learn more in [this 15-minute technical walkthrough](https://info.traefik.io/watch-traefikee-demo).
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -1646,17 +1646,4 @@ udp:
|
||||
address = "private-ip-server-2:8080/"
|
||||
```
|
||||
|
||||
!!! question "Using Traefik for Business Applications?"
|
||||
|
||||
If you are using Traefik for commercial applications,
|
||||
consider the [Enterprise Edition](https://traefik.io/traefik-enterprise/).
|
||||
You can use it as your:
|
||||
|
||||
- [Kubernetes Ingress Controller](https://traefik.io/solutions/kubernetes-ingress/)
|
||||
- [Load Balancer](https://traefik.io/solutions/docker-swarm-ingress/)
|
||||
- [API Gateway](https://traefik.io/solutions/api-gateway/)
|
||||
|
||||
Traefik Enterprise enables centralized access management,
|
||||
distributed Let's Encrypt,
|
||||
and other advanced capabilities.
|
||||
Learn more in [this 15-minute technical walkthrough](https://info.traefik.io/watch-traefikee-demo).
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -93,3 +93,5 @@ whoami:
|
||||
# Allow request only from the predefined entry point named "web"
|
||||
- "traefik.http.routers.whoami.entrypoints=web"
|
||||
```
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -55,9 +55,9 @@ markdown_extensions:
|
||||
- pymdownx.tasklist
|
||||
- pymdownx.snippets:
|
||||
check_paths: true
|
||||
# - markdown_include.include:
|
||||
# base_path: content/includes/
|
||||
# encoding: utf-8
|
||||
- markdown_include.include:
|
||||
base_path: content/includes/
|
||||
encoding: utf-8
|
||||
- toc:
|
||||
permalink: true
|
||||
|
||||
|
||||
2
go.mod
2
go.mod
@@ -56,7 +56,7 @@ require (
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154
|
||||
github.com/traefik/paerser v0.1.9
|
||||
github.com/traefik/yaegi v0.14.1
|
||||
github.com/traefik/yaegi v0.14.2
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible
|
||||
github.com/uber/jaeger-lib v2.2.0+incompatible
|
||||
github.com/unrolled/render v1.0.2
|
||||
|
||||
4
go.sum
4
go.sum
@@ -1905,8 +1905,8 @@ github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305 h1:y/1cL5AL2oRcfz
|
||||
github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305/go.mod h1:gXOLibKqQTRAVuVZ9gX7G9Ykky8ll8yb4slxsEMoY0c=
|
||||
github.com/traefik/paerser v0.1.9 h1:x5hZafOt/yogLvr6upoSOYIAn2nh2GsnLb236MOzd4I=
|
||||
github.com/traefik/paerser v0.1.9/go.mod h1:Dk3Bfz6Zyj13/S8pJyRdx/FNvXlsVRVbtp0UK4ZSiA0=
|
||||
github.com/traefik/yaegi v0.14.1 h1:t0ssyzeZCWTFGd/JnVuDxH/slMQfYg+2CDD4dLW/rU0=
|
||||
github.com/traefik/yaegi v0.14.1/go.mod h1:AVRxhaI2G+nUsaM1zyktzwXn69G3t/AuTDrCiTds9p0=
|
||||
github.com/traefik/yaegi v0.14.2 h1:9t9xepIfar6BrYdwJHGc+XRKo6qFoJCl6Z46N3hUtUw=
|
||||
github.com/traefik/yaegi v0.14.2/go.mod h1:AVRxhaI2G+nUsaM1zyktzwXn69G3t/AuTDrCiTds9p0=
|
||||
github.com/transip/gotransip/v6 v6.6.1 h1:nsCU1ErZS5G0FeOpgGXc4FsWvBff9GPswSMggsC4564=
|
||||
github.com/transip/gotransip/v6 v6.6.1/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8=
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/tls"
|
||||
)
|
||||
|
||||
type routerRepresentation struct {
|
||||
@@ -20,6 +21,10 @@ type routerRepresentation struct {
|
||||
}
|
||||
|
||||
func newRouterRepresentation(name string, rt *runtime.RouterInfo) routerRepresentation {
|
||||
if rt.TLS != nil && rt.TLS.Options == "" {
|
||||
rt.TLS.Options = tls.DefaultTLSConfigName
|
||||
}
|
||||
|
||||
return routerRepresentation{
|
||||
RouterInfo: rt,
|
||||
Name: name,
|
||||
|
||||
@@ -223,6 +223,52 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
jsonFile: "testdata/router-bar.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "one router by id, implicitly using default TLS options",
|
||||
path: "/api/http/routers/baz@myprovider",
|
||||
conf: runtime.Configuration{
|
||||
Routers: map[string]*runtime.RouterInfo{
|
||||
"baz@myprovider": {
|
||||
Router: &dynamic.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.baz`)",
|
||||
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
|
||||
TLS: &dynamic.RouterTLSConfig{},
|
||||
},
|
||||
Status: "enabled",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
statusCode: http.StatusOK,
|
||||
jsonFile: "testdata/router-baz-default-tls-options.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "one router by id, using specific TLS options",
|
||||
path: "/api/http/routers/baz@myprovider",
|
||||
conf: runtime.Configuration{
|
||||
Routers: map[string]*runtime.RouterInfo{
|
||||
"baz@myprovider": {
|
||||
Router: &dynamic.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service@myprovider",
|
||||
Rule: "Host(`foo.baz`)",
|
||||
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
|
||||
TLS: &dynamic.RouterTLSConfig{
|
||||
Options: "myTLS",
|
||||
},
|
||||
},
|
||||
Status: "enabled",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
statusCode: http.StatusOK,
|
||||
jsonFile: "testdata/router-baz-custom-tls-options.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "one router by id, that does not exist",
|
||||
path: "/api/http/routers/foo@myprovider",
|
||||
@@ -811,6 +857,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
// To lazily initialize the Statuses.
|
||||
rtConf.PopulateUsedBy()
|
||||
rtConf.GetRoutersByEntryPoints(context.Background(), []string{"web"}, false)
|
||||
rtConf.GetRoutersByEntryPoints(context.Background(), []string{"web"}, true)
|
||||
|
||||
handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf)
|
||||
server := httptest.NewServer(handler.createRouter())
|
||||
|
||||
20
pkg/api/testdata/router-baz-custom-tls-options.json
vendored
Normal file
20
pkg/api/testdata/router-baz-custom-tls-options.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"middlewares": [
|
||||
"auth",
|
||||
"addPrefixTest@anotherprovider"
|
||||
],
|
||||
"name": "baz@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.baz`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"tls": {
|
||||
"options": "myTLS"
|
||||
},
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
}
|
||||
20
pkg/api/testdata/router-baz-default-tls-options.json
vendored
Normal file
20
pkg/api/testdata/router-baz-default-tls-options.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"middlewares": [
|
||||
"auth",
|
||||
"addPrefixTest@anotherprovider"
|
||||
],
|
||||
"name": "baz@myprovider",
|
||||
"provider": "myprovider",
|
||||
"rule": "Host(`foo.baz`)",
|
||||
"service": "foo-service@myprovider",
|
||||
"tls": {
|
||||
"options": "default"
|
||||
},
|
||||
"status": "enabled",
|
||||
"using": [
|
||||
"web"
|
||||
]
|
||||
}
|
||||
@@ -49,11 +49,16 @@ func (r *RequestDecorator) ServeHTTP(rw http.ResponseWriter, req *http.Request,
|
||||
|
||||
func parseHost(addr string) string {
|
||||
if !strings.Contains(addr, ":") {
|
||||
// IPv4 without port or empty address
|
||||
return addr
|
||||
}
|
||||
|
||||
// IPv4 with port or IPv6
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
if addr[0] == '[' && addr[len(addr)-1] == ']' {
|
||||
return addr[1 : len(addr)-1]
|
||||
}
|
||||
return addr
|
||||
}
|
||||
return host
|
||||
|
||||
@@ -104,7 +104,7 @@ func TestRequestFlattening(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestHostParseHost(t *testing.T) {
|
||||
func Test_parseHost(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
host string
|
||||
@@ -130,6 +130,46 @@ func TestRequestHostParseHost(t *testing.T) {
|
||||
host: "127.0.0.1:",
|
||||
expected: "127.0.0.1",
|
||||
},
|
||||
{
|
||||
desc: "host with : and without port",
|
||||
host: "fe80::215:5dff:fe20:cd6a",
|
||||
expected: "fe80::215:5dff:fe20:cd6a",
|
||||
},
|
||||
{
|
||||
desc: "IPv6 host with : and with port",
|
||||
host: "[fe80::215:5dff:fe20:cd6a]:123",
|
||||
expected: "fe80::215:5dff:fe20:cd6a",
|
||||
},
|
||||
{
|
||||
desc: "IPv6 host with : and without port",
|
||||
host: "[fe80::215:5dff:fe20:cd6a]:",
|
||||
expected: "fe80::215:5dff:fe20:cd6a",
|
||||
},
|
||||
{
|
||||
desc: "IPv6 host without : and without port",
|
||||
host: "[fe80::215:5dff:fe20:cd6a]",
|
||||
expected: "fe80::215:5dff:fe20:cd6a",
|
||||
},
|
||||
{
|
||||
desc: "invalid IPv6: missing [",
|
||||
host: "fe80::215:5dff:fe20:cd6a]",
|
||||
expected: "fe80::215:5dff:fe20:cd6a]",
|
||||
},
|
||||
{
|
||||
desc: "invalid IPv6: missing ]",
|
||||
host: "[fe80::215:5dff:fe20:cd6a",
|
||||
expected: "[fe80::215:5dff:fe20:cd6a",
|
||||
},
|
||||
{
|
||||
desc: "empty address",
|
||||
host: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
desc: "only :",
|
||||
host: ":",
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
||||
@@ -237,7 +237,7 @@ func headersRegexp(route *mux.Route, headers ...string) error {
|
||||
func query(route *mux.Route, query ...string) error {
|
||||
var queries []string
|
||||
for _, elem := range query {
|
||||
queries = append(queries, strings.Split(elem, "=")...)
|
||||
queries = append(queries, strings.SplitN(elem, "=", 2)...)
|
||||
}
|
||||
|
||||
route.Queries(queries...)
|
||||
|
||||
@@ -252,6 +252,14 @@ func Test_addRoute(t *testing.T) {
|
||||
"http://localhost/foo?bar=baz": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Query with multiple equals",
|
||||
rule: "Query(`foo=b=ar`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo?foo=b=ar": http.StatusOK,
|
||||
"http://localhost/foo?foo=bar": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with simple path",
|
||||
rule: `Path("/a")`,
|
||||
|
||||
@@ -84,6 +84,9 @@ func (p middlewareBuilder) createConfig(config map[string]interface{}) (reflect.
|
||||
}
|
||||
|
||||
vConfig := results[0]
|
||||
if len(config) == 0 {
|
||||
return vConfig, nil
|
||||
}
|
||||
|
||||
cfg := &mapstructure.DecoderConfig{
|
||||
DecodeHook: mapstructure.StringToSliceHookFunc(","),
|
||||
|
||||
@@ -198,29 +198,27 @@ func (p *Provider) addServerTCP(item itemData, loadBalancer *dynamic.TCPServersL
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var port string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
port = loadBalancer.Servers[0].Port
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
loadBalancer.Servers = []dynamic.TCPServer{{}}
|
||||
}
|
||||
|
||||
if item.Port != "" && port == "" {
|
||||
port = item.Port
|
||||
}
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
if item.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
port := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
port = item.Port
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
loadBalancer.Servers[0].Address = net.JoinHostPort(item.Address, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -233,21 +231,23 @@ func (p *Provider) addServerUDP(item itemData, loadBalancer *dynamic.UDPServersL
|
||||
loadBalancer.Servers = []dynamic.UDPServer{{}}
|
||||
}
|
||||
|
||||
var port string
|
||||
if item.Port != "" {
|
||||
if item.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
port := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
port = item.Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
if item.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
loadBalancer.Servers[0].Address = net.JoinHostPort(item.Address, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -256,11 +256,6 @@ func (p *Provider) addServer(item itemData, loadBalancer *dynamic.ServersLoadBal
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var port string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
port = loadBalancer.Servers[0].Port
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.Server{}
|
||||
server.SetDefaults()
|
||||
@@ -268,17 +263,19 @@ func (p *Provider) addServer(item itemData, loadBalancer *dynamic.ServersLoadBal
|
||||
loadBalancer.Servers = []dynamic.Server{server}
|
||||
}
|
||||
|
||||
if item.Port != "" && port == "" {
|
||||
port = item.Port
|
||||
if item.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
port := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
port = item.Port
|
||||
}
|
||||
|
||||
if item.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
scheme := loadBalancer.Servers[0].Scheme
|
||||
|
||||
@@ -2220,7 +2220,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Labels: map[string]string{
|
||||
"traefik.tcp.routers.foo.rule": "HostSNI(`foo.bar`)",
|
||||
"traefik.tcp.routers.foo.tls.options": "foo",
|
||||
"traefik.tcp.services.foo.loadbalancer.server.port": "80",
|
||||
"traefik.tcp.services.foo.loadbalancer.server.port": "8080",
|
||||
},
|
||||
Address: "127.0.0.1",
|
||||
Port: "80",
|
||||
@@ -2244,7 +2244,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||
Servers: []dynamic.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1:80",
|
||||
Address: "127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
@@ -2611,6 +2611,57 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "UDP service with labels only",
|
||||
ConnectAware: true,
|
||||
items: []itemData{
|
||||
{
|
||||
ID: "1",
|
||||
Node: "Node1",
|
||||
Datacenter: "dc1",
|
||||
Name: "Test",
|
||||
Namespace: "ns",
|
||||
Labels: map[string]string{
|
||||
"traefik.udp.routers.test-udp-label.service": "test-udp-label-service",
|
||||
"traefik.udp.routers.test-udp-label.entryPoints": "udp",
|
||||
"traefik.udp.services.test-udp-label-service.loadBalancer.server.port": "21116",
|
||||
},
|
||||
Address: "127.0.0.1",
|
||||
Port: "80",
|
||||
Status: api.HealthPassing,
|
||||
},
|
||||
},
|
||||
expected: &dynamic.Configuration{
|
||||
TCP: &dynamic.TCPConfiguration{
|
||||
Routers: map[string]*dynamic.TCPRouter{},
|
||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||
Services: map[string]*dynamic.TCPService{},
|
||||
},
|
||||
UDP: &dynamic.UDPConfiguration{
|
||||
Routers: map[string]*dynamic.UDPRouter{
|
||||
"test-udp-label": {
|
||||
EntryPoints: []string{"udp"},
|
||||
Service: "test-udp-label-service",
|
||||
},
|
||||
},
|
||||
Services: map[string]*dynamic.UDPService{
|
||||
"test-udp-label-service": {
|
||||
LoadBalancer: &dynamic.UDPServersLoadBalancer{
|
||||
Servers: []dynamic.UDPServer{
|
||||
{Address: "127.0.0.1:21116"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: map[string]*dynamic.Router{},
|
||||
Middlewares: map[string]*dynamic.Middleware{},
|
||||
Services: map[string]*dynamic.Service{},
|
||||
ServersTransports: map[string]*dynamic.ServersTransport{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
||||
@@ -187,28 +187,24 @@ func (p *Provider) addServerTCP(ctx context.Context, container dockerData, loadB
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var serverPort string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
loadBalancer.Servers = []dynamic.TCPServer{{}}
|
||||
}
|
||||
|
||||
serverPort := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
ip, port, err := p.getIPPort(ctx, container, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.TCPServer{}
|
||||
|
||||
loadBalancer.Servers = []dynamic.TCPServer{server}
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
loadBalancer.Servers[0].Address = net.JoinHostPort(ip, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -217,28 +213,24 @@ func (p *Provider) addServerUDP(ctx context.Context, container dockerData, loadB
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var serverPort string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
loadBalancer.Servers = []dynamic.UDPServer{{}}
|
||||
}
|
||||
|
||||
serverPort := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
ip, port, err := p.getIPPort(ctx, container, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.UDPServer{}
|
||||
|
||||
loadBalancer.Servers = []dynamic.UDPServer{server}
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
loadBalancer.Servers[0].Address = net.JoinHostPort(ip, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -247,17 +239,6 @@ func (p *Provider) addServer(ctx context.Context, container dockerData, loadBala
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var serverPort string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
}
|
||||
|
||||
ip, port, err := p.getIPPort(ctx, container, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.Server{}
|
||||
server.SetDefaults()
|
||||
@@ -265,6 +246,14 @@ func (p *Provider) addServer(ctx context.Context, container dockerData, loadBala
|
||||
loadBalancer.Servers = []dynamic.Server{server}
|
||||
}
|
||||
|
||||
serverPort := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
ip, port, err := p.getIPPort(ctx, container, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ func (p *Provider) filterInstance(ctx context.Context, instance ecsInstance) boo
|
||||
|
||||
matches, err := constraints.MatchLabels(instance.Labels, p.Constraints)
|
||||
if err != nil {
|
||||
logger.Errorf("Error matching constraints expression: %v", err)
|
||||
logger.Errorf("Error matching constraint expression: %v", err)
|
||||
return false
|
||||
}
|
||||
if !matches {
|
||||
@@ -201,28 +201,24 @@ func (p *Provider) addServerTCP(instance ecsInstance, loadBalancer *dynamic.TCPS
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var serverPort string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
loadBalancer.Servers = []dynamic.TCPServer{{}}
|
||||
}
|
||||
|
||||
serverPort := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
ip, port, err := p.getIPPort(instance, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.TCPServer{}
|
||||
|
||||
loadBalancer.Servers = []dynamic.TCPServer{server}
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
loadBalancer.Servers[0].Address = net.JoinHostPort(ip, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -231,28 +227,24 @@ func (p *Provider) addServerUDP(instance ecsInstance, loadBalancer *dynamic.UDPS
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var serverPort string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
loadBalancer.Servers = []dynamic.UDPServer{{}}
|
||||
}
|
||||
|
||||
serverPort := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
ip, port, err := p.getIPPort(instance, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.UDPServer{}
|
||||
|
||||
loadBalancer.Servers = []dynamic.UDPServer{server}
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
loadBalancer.Servers[0].Address = net.JoinHostPort(ip, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -261,17 +253,6 @@ func (p *Provider) addServer(instance ecsInstance, loadBalancer *dynamic.Servers
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var serverPort string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
}
|
||||
|
||||
ip, port, err := p.getIPPort(instance, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.Server{}
|
||||
server.SetDefaults()
|
||||
@@ -279,6 +260,14 @@ func (p *Provider) addServer(instance ecsInstance, loadBalancer *dynamic.Servers
|
||||
loadBalancer.Servers = []dynamic.Server{server}
|
||||
}
|
||||
|
||||
serverPort := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
ip, port, err := p.getIPPort(instance, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
@@ -172,29 +172,27 @@ func (p *Provider) addServerTCP(i item, lb *dynamic.TCPServersLoadBalancer) erro
|
||||
return errors.New("load-balancer is missing")
|
||||
}
|
||||
|
||||
var port string
|
||||
if len(lb.Servers) > 0 {
|
||||
port = lb.Servers[0].Port
|
||||
}
|
||||
|
||||
if len(lb.Servers) == 0 {
|
||||
lb.Servers = []dynamic.TCPServer{{}}
|
||||
}
|
||||
|
||||
if i.Port != 0 && port == "" {
|
||||
port = strconv.Itoa(i.Port)
|
||||
}
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
if i.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
port := lb.Servers[0].Port
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" && i.Port > 0 {
|
||||
port = strconv.Itoa(i.Port)
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
lb.Servers[0].Address = net.JoinHostPort(i.Address, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -203,29 +201,27 @@ func (p *Provider) addServerUDP(i item, lb *dynamic.UDPServersLoadBalancer) erro
|
||||
return errors.New("load-balancer is missing")
|
||||
}
|
||||
|
||||
var port string
|
||||
if len(lb.Servers) > 0 {
|
||||
port = lb.Servers[0].Port
|
||||
}
|
||||
|
||||
if len(lb.Servers) == 0 {
|
||||
lb.Servers = []dynamic.UDPServer{{}}
|
||||
}
|
||||
|
||||
if i.Port != 0 && port == "" {
|
||||
port = strconv.Itoa(i.Port)
|
||||
}
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
if i.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
port := lb.Servers[0].Port
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" && i.Port > 0 {
|
||||
port = strconv.Itoa(i.Port)
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
lb.Servers[0].Address = net.JoinHostPort(i.Address, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -234,11 +230,6 @@ func (p *Provider) addServer(i item, lb *dynamic.ServersLoadBalancer) error {
|
||||
return errors.New("load-balancer is missing")
|
||||
}
|
||||
|
||||
var port string
|
||||
if len(lb.Servers) > 0 {
|
||||
port = lb.Servers[0].Port
|
||||
}
|
||||
|
||||
if len(lb.Servers) == 0 {
|
||||
server := dynamic.Server{}
|
||||
server.SetDefaults()
|
||||
@@ -246,19 +237,21 @@ func (p *Provider) addServer(i item, lb *dynamic.ServersLoadBalancer) error {
|
||||
lb.Servers = []dynamic.Server{server}
|
||||
}
|
||||
|
||||
if i.Port != 0 && port == "" {
|
||||
if i.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
port := lb.Servers[0].Port
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" && i.Port > 0 {
|
||||
port = strconv.Itoa(i.Port)
|
||||
}
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
if i.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
scheme := lb.Servers[0].Scheme
|
||||
lb.Servers[0].Scheme = ""
|
||||
lb.Servers[0].URL = fmt.Sprintf("%s://%s", scheme, net.JoinHostPort(i.Address, port))
|
||||
|
||||
@@ -160,7 +160,7 @@ func (p *Provider) keepService(ctx context.Context, service rancherData) bool {
|
||||
|
||||
matches, err := constraints.MatchLabels(service.Labels, p.Constraints)
|
||||
if err != nil {
|
||||
logger.Errorf("Error matching constraints expression: %v", err)
|
||||
logger.Errorf("Error matching constraint expression: %v", err)
|
||||
return false
|
||||
}
|
||||
if !matches {
|
||||
@@ -185,23 +185,19 @@ func (p *Provider) keepService(ctx context.Context, service rancherData) bool {
|
||||
func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBalancer *dynamic.TCPServersLoadBalancer) error {
|
||||
log.FromContext(ctx).Debugf("Trying to add servers for service %s \n", service.Name)
|
||||
|
||||
serverPort := ""
|
||||
|
||||
if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
if loadBalancer == nil {
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
port := getServicePort(service)
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.TCPServer{}
|
||||
|
||||
loadBalancer.Servers = []dynamic.TCPServer{server}
|
||||
loadBalancer.Servers = []dynamic.TCPServer{{}}
|
||||
}
|
||||
|
||||
if serverPort != "" {
|
||||
port = serverPort
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
port := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
port = getServicePort(service)
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
@@ -216,29 +212,26 @@ func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBa
|
||||
}
|
||||
|
||||
loadBalancer.Servers = servers
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) addServerUDP(ctx context.Context, service rancherData, loadBalancer *dynamic.UDPServersLoadBalancer) error {
|
||||
log.FromContext(ctx).Debugf("Trying to add servers for service %s \n", service.Name)
|
||||
|
||||
serverPort := ""
|
||||
|
||||
if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
if loadBalancer == nil {
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
port := getServicePort(service)
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.UDPServer{}
|
||||
|
||||
loadBalancer.Servers = []dynamic.UDPServer{server}
|
||||
loadBalancer.Servers = []dynamic.UDPServer{{}}
|
||||
}
|
||||
|
||||
if serverPort != "" {
|
||||
port = serverPort
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
port := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
port = getServicePort(service)
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
@@ -253,14 +246,16 @@ func (p *Provider) addServerUDP(ctx context.Context, service rancherData, loadBa
|
||||
}
|
||||
|
||||
loadBalancer.Servers = servers
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) addServers(ctx context.Context, service rancherData, loadBalancer *dynamic.ServersLoadBalancer) error {
|
||||
log.FromContext(ctx).Debugf("Trying to add servers for service %s \n", service.Name)
|
||||
|
||||
serverPort := getLBServerPort(loadBalancer)
|
||||
port := getServicePort(service)
|
||||
if loadBalancer == nil {
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.Server{}
|
||||
@@ -269,9 +264,11 @@ func (p *Provider) addServers(ctx context.Context, service rancherData, loadBala
|
||||
loadBalancer.Servers = []dynamic.Server{server}
|
||||
}
|
||||
|
||||
if serverPort != "" {
|
||||
port = serverPort
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
port := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
port = getServicePort(service)
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
@@ -286,14 +283,8 @@ func (p *Provider) addServers(ctx context.Context, service rancherData, loadBala
|
||||
}
|
||||
|
||||
loadBalancer.Servers = servers
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLBServerPort(loadBalancer *dynamic.ServersLoadBalancer) string {
|
||||
if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
|
||||
return loadBalancer.Servers[0].Port
|
||||
}
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func getServicePort(data rancherData) string {
|
||||
|
||||
@@ -2,7 +2,6 @@ package middleware
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/plugins"
|
||||
@@ -30,9 +29,5 @@ func findPluginConfig(rawConfig map[string]dynamic.PluginConf) (string, map[stri
|
||||
return "", nil, errors.New("missing plugin type")
|
||||
}
|
||||
|
||||
if len(rawPluginConfig) == 0 {
|
||||
return "", nil, fmt.Errorf("missing plugin configuration: %s", pluginType)
|
||||
}
|
||||
|
||||
return pluginType, rawPluginConfig, nil
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"golang.org/x/net/http/httpguts"
|
||||
)
|
||||
|
||||
// StatusClientClosedRequest non-standard HTTP status code for client disconnection.
|
||||
@@ -67,16 +68,18 @@ func buildProxy(passHostHeader *bool, responseForwarding *dynamic.ResponseForwar
|
||||
// some servers need Sec-WebSocket-Key, Sec-WebSocket-Extensions, Sec-WebSocket-Accept,
|
||||
// Sec-WebSocket-Protocol and Sec-WebSocket-Version to be case-sensitive.
|
||||
// https://tools.ietf.org/html/rfc6455#page-20
|
||||
outReq.Header["Sec-WebSocket-Key"] = outReq.Header["Sec-Websocket-Key"]
|
||||
outReq.Header["Sec-WebSocket-Extensions"] = outReq.Header["Sec-Websocket-Extensions"]
|
||||
outReq.Header["Sec-WebSocket-Accept"] = outReq.Header["Sec-Websocket-Accept"]
|
||||
outReq.Header["Sec-WebSocket-Protocol"] = outReq.Header["Sec-Websocket-Protocol"]
|
||||
outReq.Header["Sec-WebSocket-Version"] = outReq.Header["Sec-Websocket-Version"]
|
||||
delete(outReq.Header, "Sec-Websocket-Key")
|
||||
delete(outReq.Header, "Sec-Websocket-Extensions")
|
||||
delete(outReq.Header, "Sec-Websocket-Accept")
|
||||
delete(outReq.Header, "Sec-Websocket-Protocol")
|
||||
delete(outReq.Header, "Sec-Websocket-Version")
|
||||
if isWebSocketUpgrade(outReq) {
|
||||
outReq.Header["Sec-WebSocket-Key"] = outReq.Header["Sec-Websocket-Key"]
|
||||
outReq.Header["Sec-WebSocket-Extensions"] = outReq.Header["Sec-Websocket-Extensions"]
|
||||
outReq.Header["Sec-WebSocket-Accept"] = outReq.Header["Sec-Websocket-Accept"]
|
||||
outReq.Header["Sec-WebSocket-Protocol"] = outReq.Header["Sec-Websocket-Protocol"]
|
||||
outReq.Header["Sec-WebSocket-Version"] = outReq.Header["Sec-Websocket-Version"]
|
||||
delete(outReq.Header, "Sec-Websocket-Key")
|
||||
delete(outReq.Header, "Sec-Websocket-Extensions")
|
||||
delete(outReq.Header, "Sec-Websocket-Accept")
|
||||
delete(outReq.Header, "Sec-Websocket-Protocol")
|
||||
delete(outReq.Header, "Sec-Websocket-Version")
|
||||
}
|
||||
},
|
||||
Transport: roundTripper,
|
||||
FlushInterval: time.Duration(flushInterval),
|
||||
@@ -112,6 +115,14 @@ func buildProxy(passHostHeader *bool, responseForwarding *dynamic.ResponseForwar
|
||||
return proxy, nil
|
||||
}
|
||||
|
||||
func isWebSocketUpgrade(req *http.Request) bool {
|
||||
if !httpguts.HeaderValuesContainsToken(req.Header["Connection"], "Upgrade") {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.EqualFold(req.Header.Get("Upgrade"), "websocket")
|
||||
}
|
||||
|
||||
func statusText(statusCode int) string {
|
||||
if statusCode == StatusClientClosedRequest {
|
||||
return StatusClientClosedRequestText
|
||||
|
||||
@@ -4,11 +4,11 @@ RepositoryName = "traefik"
|
||||
OutputType = "file"
|
||||
FileName = "traefik_changelog.md"
|
||||
|
||||
# example new bugfix v2.8.4
|
||||
# example new bugfix v2.8.6
|
||||
CurrentRef = "v2.8"
|
||||
PreviousRef = "v2.8.3"
|
||||
PreviousRef = "v2.8.5"
|
||||
BaseBranch = "v2.8"
|
||||
FutureCurrentRefName = "v2.8.4"
|
||||
FutureCurrentRefName = "v2.8.6"
|
||||
|
||||
ThresholdPreviousRef = 10
|
||||
ThresholdCurrentRef = 10
|
||||
|
||||
Reference in New Issue
Block a user