Bring back v2 rule matchers
This commit is contained in:
parent
21da705ec9
commit
683e2ee5c6
54 changed files with 3773 additions and 114 deletions
|
@ -526,21 +526,16 @@ All Pilot related configuration should be removed from the static configuration.
|
||||||
|
|
||||||
## Dynamic configuration
|
## Dynamic configuration
|
||||||
|
|
||||||
### IPWhiteList
|
### Router Rule Matchers
|
||||||
|
|
||||||
In v3, we renamed the `IPWhiteList` middleware to `IPAllowList` without changing anything to the configuration.
|
In v3, a new rule matchers syntax has been introduced for HTTP and TCP routers.
|
||||||
|
The default rule matchers syntax is now the v3 one, but for backward compatibility this can be configured.
|
||||||
|
The v2 rule matchers syntax is deprecated and its support will be removed in the next major version.
|
||||||
|
For this reason, we encourage migrating to the new syntax.
|
||||||
|
|
||||||
### Deprecated Options Removal
|
#### New V3 Syntax Notable Changes
|
||||||
|
|
||||||
- The `tracing.datadog.globaltag` option has been removed.
|
The `Headers` and `HeadersRegexp` matchers have been renamed to `Header` and `HeaderRegexp` respectively.
|
||||||
- The `tls.caOptional` option has been removed from the ForwardAuth middleware, as well as from the HTTP, Consul, Etcd, Redis, ZooKeeper, Consul Catalog, and Docker providers.
|
|
||||||
- `sslRedirect`, `sslTemporaryRedirect`, `sslHost`, `sslForceHost` and `featurePolicy` options of the Headers middleware have been removed.
|
|
||||||
- The `forceSlash` option of the StripPrefix middleware has been removed.
|
|
||||||
- The `preferServerCipherSuites` option has been removed.
|
|
||||||
|
|
||||||
### Matchers
|
|
||||||
|
|
||||||
In v3, the `Headers` and `HeadersRegexp` matchers have been renamed to `Header` and `HeaderRegexp` respectively.
|
|
||||||
|
|
||||||
`PathPrefix` no longer uses regular expressions to match path prefixes.
|
`PathPrefix` no longer uses regular expressions to match path prefixes.
|
||||||
|
|
||||||
|
@ -555,6 +550,87 @@ and should be explicitly combined using logical operators to mimic previous beha
|
||||||
|
|
||||||
`HostHeader` has been removed, use `Host` instead.
|
`HostHeader` has been removed, use `Host` instead.
|
||||||
|
|
||||||
|
#### Remediation
|
||||||
|
|
||||||
|
##### Configure the Default Syntax In Static Configuration
|
||||||
|
|
||||||
|
The default rule matchers syntax is the expected syntax for any router that is not self opt-out from this default value.
|
||||||
|
It can be configured in the static configuration.
|
||||||
|
|
||||||
|
??? example "An example configuration for the default rule matchers syntax"
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
# static configuration
|
||||||
|
core:
|
||||||
|
defaultRuleSyntax: v2
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
# static configuration
|
||||||
|
[core]
|
||||||
|
defaultRuleSyntax="v2"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="CLI"
|
||||||
|
# static configuration
|
||||||
|
--core.defaultRuleSyntax=v2
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Configure the Syntax Per Router
|
||||||
|
|
||||||
|
The rule syntax can also be configured on a per-router basis.
|
||||||
|
This allows to have heterogeneous router configurations and ease migration.
|
||||||
|
|
||||||
|
??? example "An example router with syntax configuration"
|
||||||
|
|
||||||
|
```yaml tab="Docker & Swarm"
|
||||||
|
labels:
|
||||||
|
- "traefik.http.routers.test.ruleSyntax=v2"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes"
|
||||||
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test.route
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
routes:
|
||||||
|
- match: PathPrefix(`/foo`, `/bar`)
|
||||||
|
syntax: v2
|
||||||
|
kind: Rule
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Consul Catalog"
|
||||||
|
- "traefik.http.routers.test.ruleSyntax=v2"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
http:
|
||||||
|
routers:
|
||||||
|
test:
|
||||||
|
ruleSyntax: v2
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[http.routers]
|
||||||
|
[http.routers.test]
|
||||||
|
ruleSyntax = "v2"
|
||||||
|
```
|
||||||
|
|
||||||
|
### IPWhiteList
|
||||||
|
|
||||||
|
In v3, we renamed the `IPWhiteList` middleware to `IPAllowList` without changing anything to the configuration.
|
||||||
|
|
||||||
|
### Deprecated Options Removal
|
||||||
|
|
||||||
|
- The `tracing.datadog.globaltag` option has been removed.
|
||||||
|
- The `tls.caOptional` option has been removed from the ForwardAuth middleware, as well as from the HTTP, Consul, Etcd, Redis, ZooKeeper, Consul Catalog, and Docker providers.
|
||||||
|
- `sslRedirect`, `sslTemporaryRedirect`, `sslHost`, `sslForceHost` and `featurePolicy` options of the Headers middleware have been removed.
|
||||||
|
- The `forceSlash` option of the StripPrefix middleware has been removed.
|
||||||
|
- The `preferServerCipherSuites` option has been removed.
|
||||||
|
|
||||||
### TCP LoadBalancer `terminationDelay` option
|
### TCP LoadBalancer `terminationDelay` option
|
||||||
|
|
||||||
The TCP LoadBalancer `terminationDelay` option has been removed.
|
The TCP LoadBalancer `terminationDelay` option has been removed.
|
||||||
|
|
|
@ -132,6 +132,7 @@
|
||||||
- "traefik.http.routers.router0.middlewares=foobar, foobar"
|
- "traefik.http.routers.router0.middlewares=foobar, foobar"
|
||||||
- "traefik.http.routers.router0.priority=42"
|
- "traefik.http.routers.router0.priority=42"
|
||||||
- "traefik.http.routers.router0.rule=foobar"
|
- "traefik.http.routers.router0.rule=foobar"
|
||||||
|
- "traefik.http.routers.router0.rulesyntax=foobar"
|
||||||
- "traefik.http.routers.router0.service=foobar"
|
- "traefik.http.routers.router0.service=foobar"
|
||||||
- "traefik.http.routers.router0.tls=true"
|
- "traefik.http.routers.router0.tls=true"
|
||||||
- "traefik.http.routers.router0.tls.certresolver=foobar"
|
- "traefik.http.routers.router0.tls.certresolver=foobar"
|
||||||
|
@ -144,6 +145,7 @@
|
||||||
- "traefik.http.routers.router1.middlewares=foobar, foobar"
|
- "traefik.http.routers.router1.middlewares=foobar, foobar"
|
||||||
- "traefik.http.routers.router1.priority=42"
|
- "traefik.http.routers.router1.priority=42"
|
||||||
- "traefik.http.routers.router1.rule=foobar"
|
- "traefik.http.routers.router1.rule=foobar"
|
||||||
|
- "traefik.http.routers.router1.rulesyntax=foobar"
|
||||||
- "traefik.http.routers.router1.service=foobar"
|
- "traefik.http.routers.router1.service=foobar"
|
||||||
- "traefik.http.routers.router1.tls=true"
|
- "traefik.http.routers.router1.tls=true"
|
||||||
- "traefik.http.routers.router1.tls.certresolver=foobar"
|
- "traefik.http.routers.router1.tls.certresolver=foobar"
|
||||||
|
@ -183,6 +185,7 @@
|
||||||
- "traefik.tcp.routers.tcprouter0.middlewares=foobar, foobar"
|
- "traefik.tcp.routers.tcprouter0.middlewares=foobar, foobar"
|
||||||
- "traefik.tcp.routers.tcprouter0.priority=42"
|
- "traefik.tcp.routers.tcprouter0.priority=42"
|
||||||
- "traefik.tcp.routers.tcprouter0.rule=foobar"
|
- "traefik.tcp.routers.tcprouter0.rule=foobar"
|
||||||
|
- "traefik.tcp.routers.tcprouter0.rulesyntax=foobar"
|
||||||
- "traefik.tcp.routers.tcprouter0.service=foobar"
|
- "traefik.tcp.routers.tcprouter0.service=foobar"
|
||||||
- "traefik.tcp.routers.tcprouter0.tls=true"
|
- "traefik.tcp.routers.tcprouter0.tls=true"
|
||||||
- "traefik.tcp.routers.tcprouter0.tls.certresolver=foobar"
|
- "traefik.tcp.routers.tcprouter0.tls.certresolver=foobar"
|
||||||
|
@ -196,6 +199,7 @@
|
||||||
- "traefik.tcp.routers.tcprouter1.middlewares=foobar, foobar"
|
- "traefik.tcp.routers.tcprouter1.middlewares=foobar, foobar"
|
||||||
- "traefik.tcp.routers.tcprouter1.priority=42"
|
- "traefik.tcp.routers.tcprouter1.priority=42"
|
||||||
- "traefik.tcp.routers.tcprouter1.rule=foobar"
|
- "traefik.tcp.routers.tcprouter1.rule=foobar"
|
||||||
|
- "traefik.tcp.routers.tcprouter1.rulesyntax=foobar"
|
||||||
- "traefik.tcp.routers.tcprouter1.service=foobar"
|
- "traefik.tcp.routers.tcprouter1.service=foobar"
|
||||||
- "traefik.tcp.routers.tcprouter1.tls=true"
|
- "traefik.tcp.routers.tcprouter1.tls=true"
|
||||||
- "traefik.tcp.routers.tcprouter1.tls.certresolver=foobar"
|
- "traefik.tcp.routers.tcprouter1.tls.certresolver=foobar"
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
middlewares = ["foobar", "foobar"]
|
middlewares = ["foobar", "foobar"]
|
||||||
service = "foobar"
|
service = "foobar"
|
||||||
rule = "foobar"
|
rule = "foobar"
|
||||||
|
ruleSyntax = "foobar"
|
||||||
priority = 42
|
priority = 42
|
||||||
[http.routers.Router0.tls]
|
[http.routers.Router0.tls]
|
||||||
options = "foobar"
|
options = "foobar"
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
middlewares = ["foobar", "foobar"]
|
middlewares = ["foobar", "foobar"]
|
||||||
service = "foobar"
|
service = "foobar"
|
||||||
rule = "foobar"
|
rule = "foobar"
|
||||||
|
ruleSyntax = "foobar"
|
||||||
priority = 42
|
priority = 42
|
||||||
[http.routers.Router1.tls]
|
[http.routers.Router1.tls]
|
||||||
options = "foobar"
|
options = "foobar"
|
||||||
|
@ -353,6 +355,7 @@
|
||||||
middlewares = ["foobar", "foobar"]
|
middlewares = ["foobar", "foobar"]
|
||||||
service = "foobar"
|
service = "foobar"
|
||||||
rule = "foobar"
|
rule = "foobar"
|
||||||
|
ruleSyntax = "foobar"
|
||||||
priority = 42
|
priority = 42
|
||||||
[tcp.routers.TCPRouter0.tls]
|
[tcp.routers.TCPRouter0.tls]
|
||||||
passthrough = true
|
passthrough = true
|
||||||
|
@ -371,6 +374,7 @@
|
||||||
middlewares = ["foobar", "foobar"]
|
middlewares = ["foobar", "foobar"]
|
||||||
service = "foobar"
|
service = "foobar"
|
||||||
rule = "foobar"
|
rule = "foobar"
|
||||||
|
ruleSyntax = "foobar"
|
||||||
priority = 42
|
priority = 42
|
||||||
[tcp.routers.TCPRouter1.tls]
|
[tcp.routers.TCPRouter1.tls]
|
||||||
passthrough = true
|
passthrough = true
|
||||||
|
|
|
@ -11,6 +11,7 @@ http:
|
||||||
- foobar
|
- foobar
|
||||||
service: foobar
|
service: foobar
|
||||||
rule: foobar
|
rule: foobar
|
||||||
|
ruleSyntax: foobar
|
||||||
priority: 42
|
priority: 42
|
||||||
tls:
|
tls:
|
||||||
options: foobar
|
options: foobar
|
||||||
|
@ -33,6 +34,7 @@ http:
|
||||||
- foobar
|
- foobar
|
||||||
service: foobar
|
service: foobar
|
||||||
rule: foobar
|
rule: foobar
|
||||||
|
ruleSyntax: foobar
|
||||||
priority: 42
|
priority: 42
|
||||||
tls:
|
tls:
|
||||||
options: foobar
|
options: foobar
|
||||||
|
@ -409,6 +411,7 @@ tcp:
|
||||||
- foobar
|
- foobar
|
||||||
service: foobar
|
service: foobar
|
||||||
rule: foobar
|
rule: foobar
|
||||||
|
ruleSyntax: foobar
|
||||||
priority: 42
|
priority: 42
|
||||||
tls:
|
tls:
|
||||||
passthrough: true
|
passthrough: true
|
||||||
|
@ -432,6 +435,7 @@ tcp:
|
||||||
- foobar
|
- foobar
|
||||||
service: foobar
|
service: foobar
|
||||||
rule: foobar
|
rule: foobar
|
||||||
|
ruleSyntax: foobar
|
||||||
priority: 42
|
priority: 42
|
||||||
tls:
|
tls:
|
||||||
passthrough: true
|
passthrough: true
|
||||||
|
|
|
@ -195,6 +195,10 @@ spec:
|
||||||
- name
|
- name
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
|
syntax:
|
||||||
|
description: 'Syntax defines the router''s rule syntax. More
|
||||||
|
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax'
|
||||||
|
type: string
|
||||||
required:
|
required:
|
||||||
- kind
|
- kind
|
||||||
- match
|
- match
|
||||||
|
@ -402,6 +406,10 @@ spec:
|
||||||
- port
|
- port
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
|
syntax:
|
||||||
|
description: 'Syntax defines the router''s rule syntax. More
|
||||||
|
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax_1'
|
||||||
|
type: string
|
||||||
required:
|
required:
|
||||||
- match
|
- match
|
||||||
type: object
|
type: object
|
||||||
|
|
|
@ -158,6 +158,7 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
||||||
| `traefik/http/routers/Router0/middlewares/1` | `foobar` |
|
| `traefik/http/routers/Router0/middlewares/1` | `foobar` |
|
||||||
| `traefik/http/routers/Router0/priority` | `42` |
|
| `traefik/http/routers/Router0/priority` | `42` |
|
||||||
| `traefik/http/routers/Router0/rule` | `foobar` |
|
| `traefik/http/routers/Router0/rule` | `foobar` |
|
||||||
|
| `traefik/http/routers/Router0/ruleSyntax` | `foobar` |
|
||||||
| `traefik/http/routers/Router0/service` | `foobar` |
|
| `traefik/http/routers/Router0/service` | `foobar` |
|
||||||
| `traefik/http/routers/Router0/tls/certResolver` | `foobar` |
|
| `traefik/http/routers/Router0/tls/certResolver` | `foobar` |
|
||||||
| `traefik/http/routers/Router0/tls/domains/0/main` | `foobar` |
|
| `traefik/http/routers/Router0/tls/domains/0/main` | `foobar` |
|
||||||
|
@ -173,6 +174,7 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
||||||
| `traefik/http/routers/Router1/middlewares/1` | `foobar` |
|
| `traefik/http/routers/Router1/middlewares/1` | `foobar` |
|
||||||
| `traefik/http/routers/Router1/priority` | `42` |
|
| `traefik/http/routers/Router1/priority` | `42` |
|
||||||
| `traefik/http/routers/Router1/rule` | `foobar` |
|
| `traefik/http/routers/Router1/rule` | `foobar` |
|
||||||
|
| `traefik/http/routers/Router1/ruleSyntax` | `foobar` |
|
||||||
| `traefik/http/routers/Router1/service` | `foobar` |
|
| `traefik/http/routers/Router1/service` | `foobar` |
|
||||||
| `traefik/http/routers/Router1/tls/certResolver` | `foobar` |
|
| `traefik/http/routers/Router1/tls/certResolver` | `foobar` |
|
||||||
| `traefik/http/routers/Router1/tls/domains/0/main` | `foobar` |
|
| `traefik/http/routers/Router1/tls/domains/0/main` | `foobar` |
|
||||||
|
@ -273,6 +275,7 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
||||||
| `traefik/tcp/routers/TCPRouter0/middlewares/1` | `foobar` |
|
| `traefik/tcp/routers/TCPRouter0/middlewares/1` | `foobar` |
|
||||||
| `traefik/tcp/routers/TCPRouter0/priority` | `42` |
|
| `traefik/tcp/routers/TCPRouter0/priority` | `42` |
|
||||||
| `traefik/tcp/routers/TCPRouter0/rule` | `foobar` |
|
| `traefik/tcp/routers/TCPRouter0/rule` | `foobar` |
|
||||||
|
| `traefik/tcp/routers/TCPRouter0/ruleSyntax` | `foobar` |
|
||||||
| `traefik/tcp/routers/TCPRouter0/service` | `foobar` |
|
| `traefik/tcp/routers/TCPRouter0/service` | `foobar` |
|
||||||
| `traefik/tcp/routers/TCPRouter0/tls/certResolver` | `foobar` |
|
| `traefik/tcp/routers/TCPRouter0/tls/certResolver` | `foobar` |
|
||||||
| `traefik/tcp/routers/TCPRouter0/tls/domains/0/main` | `foobar` |
|
| `traefik/tcp/routers/TCPRouter0/tls/domains/0/main` | `foobar` |
|
||||||
|
@ -289,6 +292,7 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
||||||
| `traefik/tcp/routers/TCPRouter1/middlewares/1` | `foobar` |
|
| `traefik/tcp/routers/TCPRouter1/middlewares/1` | `foobar` |
|
||||||
| `traefik/tcp/routers/TCPRouter1/priority` | `42` |
|
| `traefik/tcp/routers/TCPRouter1/priority` | `42` |
|
||||||
| `traefik/tcp/routers/TCPRouter1/rule` | `foobar` |
|
| `traefik/tcp/routers/TCPRouter1/rule` | `foobar` |
|
||||||
|
| `traefik/tcp/routers/TCPRouter1/ruleSyntax` | `foobar` |
|
||||||
| `traefik/tcp/routers/TCPRouter1/service` | `foobar` |
|
| `traefik/tcp/routers/TCPRouter1/service` | `foobar` |
|
||||||
| `traefik/tcp/routers/TCPRouter1/tls/certResolver` | `foobar` |
|
| `traefik/tcp/routers/TCPRouter1/tls/certResolver` | `foobar` |
|
||||||
| `traefik/tcp/routers/TCPRouter1/tls/domains/0/main` | `foobar` |
|
| `traefik/tcp/routers/TCPRouter1/tls/domains/0/main` | `foobar` |
|
||||||
|
|
|
@ -195,6 +195,10 @@ spec:
|
||||||
- name
|
- name
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
|
syntax:
|
||||||
|
description: 'Syntax defines the router''s rule syntax. More
|
||||||
|
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax'
|
||||||
|
type: string
|
||||||
required:
|
required:
|
||||||
- kind
|
- kind
|
||||||
- match
|
- match
|
||||||
|
|
|
@ -129,6 +129,10 @@ spec:
|
||||||
- port
|
- port
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
|
syntax:
|
||||||
|
description: 'Syntax defines the router''s rule syntax. More
|
||||||
|
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax_1'
|
||||||
|
type: string
|
||||||
required:
|
required:
|
||||||
- match
|
- match
|
||||||
type: object
|
type: object
|
||||||
|
|
|
@ -105,6 +105,9 @@ Activate TLS-ALPN-01 Challenge. (Default: ```true```)
|
||||||
`--certificatesresolvers.<name>.tailscale`:
|
`--certificatesresolvers.<name>.tailscale`:
|
||||||
Enables Tailscale certificate resolution. (Default: ```true```)
|
Enables Tailscale certificate resolution. (Default: ```true```)
|
||||||
|
|
||||||
|
`--core.defaultrulesyntax`:
|
||||||
|
Defines the rule parser default syntax (v2 or v3) (Default: ```v3```)
|
||||||
|
|
||||||
`--entrypoints.<name>`:
|
`--entrypoints.<name>`:
|
||||||
Entry points definition. (Default: ```false```)
|
Entry points definition. (Default: ```false```)
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,9 @@ Activate TLS-ALPN-01 Challenge. (Default: ```true```)
|
||||||
`TRAEFIK_CERTIFICATESRESOLVERS_<NAME>_TAILSCALE`:
|
`TRAEFIK_CERTIFICATESRESOLVERS_<NAME>_TAILSCALE`:
|
||||||
Enables Tailscale certificate resolution. (Default: ```true```)
|
Enables Tailscale certificate resolution. (Default: ```true```)
|
||||||
|
|
||||||
|
`TRAEFIK_CORE_DEFAULTRULESYNTAX`:
|
||||||
|
Defines the rule parser default syntax (v2 or v3) (Default: ```v3```)
|
||||||
|
|
||||||
`TRAEFIK_ENTRYPOINTS_<NAME>`:
|
`TRAEFIK_ENTRYPOINTS_<NAME>`:
|
||||||
Entry points definition. (Default: ```false```)
|
Entry points definition. (Default: ```false```)
|
||||||
|
|
||||||
|
|
|
@ -453,5 +453,8 @@
|
||||||
[experimental.localPlugins.LocalDescriptor1]
|
[experimental.localPlugins.LocalDescriptor1]
|
||||||
moduleName = "foobar"
|
moduleName = "foobar"
|
||||||
|
|
||||||
|
[core]
|
||||||
|
defaultRuleSyntax = "foobar"
|
||||||
|
|
||||||
[spiffe]
|
[spiffe]
|
||||||
workloadAPIAddr = "foobar"
|
workloadAPIAddr = "foobar"
|
||||||
|
|
|
@ -486,5 +486,7 @@ experimental:
|
||||||
LocalDescriptor1:
|
LocalDescriptor1:
|
||||||
moduleName: foobar
|
moduleName: foobar
|
||||||
kubernetesGateway: true
|
kubernetesGateway: true
|
||||||
|
core:
|
||||||
|
defaultRuleSyntax: foobar
|
||||||
spiffe:
|
spiffe:
|
||||||
workloadAPIAddr: foobar
|
workloadAPIAddr: foobar
|
||||||
|
|
|
@ -515,6 +515,60 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul
|
||||||
|
|
||||||
In this configuration, the priority is configured to allow `Router-2` to handle requests with the `foobar.traefik.com` host.
|
In this configuration, the priority is configured to allow `Router-2` to handle requests with the `foobar.traefik.com` host.
|
||||||
|
|
||||||
|
### RuleSyntax
|
||||||
|
|
||||||
|
In Traefik v3 a new rule syntax has been introduced ([migration guide](../../migration/v2-to-v3.md#router-rule-matchers)).
|
||||||
|
`ruleSyntax` option allows to configure the rule syntax to be used for parsing the rule on a per-router basis.
|
||||||
|
This allows to have heterogeneous router configurations and ease migration.
|
||||||
|
|
||||||
|
??? example "Set rule syntax -- using the [File Provider](../../providers/file.md)"
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
## Dynamic configuration
|
||||||
|
http:
|
||||||
|
routers:
|
||||||
|
Router-v3:
|
||||||
|
rule: HostRegexp(`[a-z]+\\.traefik\\.com`)
|
||||||
|
ruleSyntax: v3
|
||||||
|
Router-v2:
|
||||||
|
rule: HostRegexp(`{subdomain:[a-z]+}.traefik.com`)
|
||||||
|
ruleSyntax: v2
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
## Dynamic configuration
|
||||||
|
[http.routers]
|
||||||
|
[http.routers.Router-v3]
|
||||||
|
rule = "HostRegexp(`[a-z]+\\.traefik\\.com`)"
|
||||||
|
ruleSyntax = v3
|
||||||
|
[http.routers.Router-v2]
|
||||||
|
rule = "HostRegexp(`{subdomain:[a-z]+}.traefik.com`)"
|
||||||
|
ruleSyntax = v2
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes traefik.io/v1alpha1"
|
||||||
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test.route
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
routes:
|
||||||
|
# route v3
|
||||||
|
- match: HostRegexp(`[a-z]+\\.traefik\\.com`)
|
||||||
|
syntax: v3
|
||||||
|
kind: Rule
|
||||||
|
|
||||||
|
# route v2
|
||||||
|
- match: HostRegexp(`{subdomain:[a-z]+}.traefik.com`)
|
||||||
|
syntax: v2
|
||||||
|
kind: Rule
|
||||||
|
```
|
||||||
|
|
||||||
|
In this configuration, the ruleSyntax is configured to allow `Router-v2` to use v2 syntax,
|
||||||
|
while for `Router-v3` it is configured to use v3 syntax.
|
||||||
|
|
||||||
### Middlewares
|
### Middlewares
|
||||||
|
|
||||||
You can attach a list of [middlewares](../../middlewares/overview.md) to each HTTP router.
|
You can attach a list of [middlewares](../../middlewares/overview.md) to each HTTP router.
|
||||||
|
@ -1161,6 +1215,60 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul
|
||||||
|
|
||||||
In this configuration, the priority is configured so that `Router-1` will handle requests from `192.168.0.12`.
|
In this configuration, the priority is configured so that `Router-1` will handle requests from `192.168.0.12`.
|
||||||
|
|
||||||
|
### RuleSyntax
|
||||||
|
|
||||||
|
In Traefik v3 a new rule syntax has been introduced ([migration guide](../../migration/v2-to-v3.md#router-rule-matchers)).
|
||||||
|
`ruleSyntax` option allows to configure the rule syntax to be used for parsing the rule on a per-router basis.
|
||||||
|
This allows to have heterogeneous router configurations and ease migration.
|
||||||
|
|
||||||
|
??? example "Set rule syntax -- using the [File Provider](../../providers/file.md)"
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
## Dynamic configuration
|
||||||
|
tcp:
|
||||||
|
routers:
|
||||||
|
Router-v3:
|
||||||
|
rule: ClientIP(`192.168.0.11`) || ClientIP(`192.168.0.12`)
|
||||||
|
ruleSyntax: v3
|
||||||
|
Router-v2:
|
||||||
|
rule: ClientIP(`192.168.0.11`, `192.168.0.12`)
|
||||||
|
ruleSyntax: v2
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
## Dynamic configuration
|
||||||
|
[tcp.routers]
|
||||||
|
[tcp.routers.Router-v3]
|
||||||
|
rule = "ClientIP(`192.168.0.11`) || ClientIP(`192.168.0.12`)"
|
||||||
|
ruleSyntax = v3
|
||||||
|
[tcp.routers.Router-v2]
|
||||||
|
rule = "ClientIP(`192.168.0.11`, `192.168.0.12`)"
|
||||||
|
ruleSyntax = v2
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes traefik.io/v1alpha1"
|
||||||
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: IngressRouteTCP
|
||||||
|
metadata:
|
||||||
|
name: test.route
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
routes:
|
||||||
|
# route v3
|
||||||
|
- match: ClientIP(`192.168.0.11`) || ClientIP(`192.168.0.12`)
|
||||||
|
syntax: v3
|
||||||
|
kind: Rule
|
||||||
|
|
||||||
|
# route v2
|
||||||
|
- match: ClientIP(`192.168.0.11`, `192.168.0.12`)
|
||||||
|
syntax: v2
|
||||||
|
kind: Rule
|
||||||
|
```
|
||||||
|
|
||||||
|
In this configuration, the ruleSyntax is configured to allow `Router-v2` to use v2 syntax,
|
||||||
|
while for `Router-v3` it is configured to use v3 syntax.
|
||||||
|
|
||||||
### Middlewares
|
### Middlewares
|
||||||
|
|
||||||
You can attach a list of [middlewares](../../middlewares/overview.md) to each TCP router.
|
You can attach a list of [middlewares](../../middlewares/overview.md) to each TCP router.
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -28,7 +28,7 @@ require (
|
||||||
github.com/hashicorp/go-multierror v1.1.1
|
github.com/hashicorp/go-multierror v1.1.1
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.4
|
github.com/hashicorp/go-retryablehttp v0.7.4
|
||||||
github.com/hashicorp/go-version v1.6.0
|
github.com/hashicorp/go-version v1.6.0
|
||||||
github.com/hashicorp/nomad/api v0.0.0-20231213195942-64e3dca9274b
|
github.com/hashicorp/nomad/api v0.0.0-20240122103822-8a4bd61caf74
|
||||||
github.com/http-wasm/http-wasm-host-go v0.5.2
|
github.com/http-wasm/http-wasm-host-go v0.5.2
|
||||||
github.com/influxdata/influxdb-client-go/v2 v2.7.0
|
github.com/influxdata/influxdb-client-go/v2 v2.7.0
|
||||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d
|
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -598,8 +598,8 @@ github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/
|
||||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=
|
github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=
|
||||||
github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
|
github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
|
||||||
github.com/hashicorp/nomad/api v0.0.0-20231213195942-64e3dca9274b h1:R1UDhkwGltpSPY9bCBBxIMQd+NY9BkN0vFHnJo/8o8w=
|
github.com/hashicorp/nomad/api v0.0.0-20240122103822-8a4bd61caf74 h1:Q+WuGTnZkL2cJ7yNsg4Go4GNnRkcahGLiQP/WD41TTA=
|
||||||
github.com/hashicorp/nomad/api v0.0.0-20231213195942-64e3dca9274b/go.mod h1:ijDwa6o1uG1jFSq6kERiX2PamKGpZzTmo0XOFNeFZgw=
|
github.com/hashicorp/nomad/api v0.0.0-20240122103822-8a4bd61caf74/go.mod h1:ijDwa6o1uG1jFSq6kERiX2PamKGpZzTmo0XOFNeFZgw=
|
||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
|
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
|
||||||
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
||||||
|
|
|
@ -195,6 +195,10 @@ spec:
|
||||||
- name
|
- name
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
|
syntax:
|
||||||
|
description: 'Syntax defines the router''s rule syntax. More
|
||||||
|
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax'
|
||||||
|
type: string
|
||||||
required:
|
required:
|
||||||
- kind
|
- kind
|
||||||
- match
|
- match
|
||||||
|
@ -402,6 +406,10 @@ spec:
|
||||||
- port
|
- port
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
|
syntax:
|
||||||
|
description: 'Syntax defines the router''s rule syntax. More
|
||||||
|
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax_1'
|
||||||
|
type: string
|
||||||
required:
|
required:
|
||||||
- match
|
- match
|
||||||
type: object
|
type: object
|
||||||
|
|
41
integration/fixtures/with_default_rule_syntax.toml
Normal file
41
integration/fixtures/with_default_rule_syntax.toml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
[global]
|
||||||
|
checkNewVersion = false
|
||||||
|
sendAnonymousUsage = false
|
||||||
|
|
||||||
|
[core]
|
||||||
|
defaultRuleSyntax = "v2"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
level = "DEBUG"
|
||||||
|
noColor = true
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.web]
|
||||||
|
address = ":8000"
|
||||||
|
|
||||||
|
[api]
|
||||||
|
insecure = true
|
||||||
|
|
||||||
|
[providers.file]
|
||||||
|
filename = "{{ .SelfFilename }}"
|
||||||
|
|
||||||
|
## dynamic configuration ##
|
||||||
|
|
||||||
|
[http.routers]
|
||||||
|
[http.routers.router1]
|
||||||
|
service = "service1"
|
||||||
|
rule = "PathPrefix(`/foo`, `/bar`)"
|
||||||
|
|
||||||
|
[http.routers.router2]
|
||||||
|
service = "service1"
|
||||||
|
rule = "QueryRegexp(`foo`, `bar`)"
|
||||||
|
|
||||||
|
[http.routers.router3]
|
||||||
|
service = "service1"
|
||||||
|
rule = "PathPrefix(`/foo`, `/bar`)"
|
||||||
|
ruleSyntax = "v3"
|
||||||
|
|
||||||
|
[http.services]
|
||||||
|
[http.services.service1]
|
||||||
|
[http.services.service1.loadBalancer]
|
||||||
|
[http.services.service1.loadBalancer.servers]
|
38
integration/fixtures/without_default_rule_syntax.toml
Normal file
38
integration/fixtures/without_default_rule_syntax.toml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
[global]
|
||||||
|
checkNewVersion = false
|
||||||
|
sendAnonymousUsage = false
|
||||||
|
|
||||||
|
[log]
|
||||||
|
level = "DEBUG"
|
||||||
|
noColor = true
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.web]
|
||||||
|
address = ":8000"
|
||||||
|
|
||||||
|
[api]
|
||||||
|
insecure = true
|
||||||
|
|
||||||
|
[providers.file]
|
||||||
|
filename = "{{ .SelfFilename }}"
|
||||||
|
|
||||||
|
## dynamic configuration ##
|
||||||
|
|
||||||
|
[http.routers]
|
||||||
|
[http.routers.router1]
|
||||||
|
service = "service1"
|
||||||
|
rule = "PathPrefix(`/foo`) || PathPrefix(`/bar`)"
|
||||||
|
|
||||||
|
[http.routers.router2]
|
||||||
|
service = "service1"
|
||||||
|
rule = "PathPrefix(`/foo`, `/bar`)"
|
||||||
|
|
||||||
|
[http.routers.router3]
|
||||||
|
service = "service1"
|
||||||
|
rule = "QueryRegexp(`foo`, `bar`)"
|
||||||
|
ruleSyntax = "v2"
|
||||||
|
|
||||||
|
[http.services]
|
||||||
|
[http.services.service1]
|
||||||
|
[http.services.service1.loadBalancer]
|
||||||
|
[http.services.service1.loadBalancer.servers]
|
|
@ -659,6 +659,66 @@ func (s *SimpleSuite) TestSimpleConfigurationHostRequestTrailingPeriod() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SimpleSuite) TestWithDefaultRuleSyntax() {
|
||||||
|
file := s.adaptFile("fixtures/with_default_rule_syntax.toml", struct{}{})
|
||||||
|
|
||||||
|
s.traefikCmd(withConfigFile(file))
|
||||||
|
|
||||||
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
// router1 has no error
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router1@file", 1*time.Second, try.BodyContains(`"status":"enabled"`))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8000/notfound", 1*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8000/foo", 1*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8000/bar", 1*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
// router2 has an error because it uses the wrong rule syntax (v3 instead of v2)
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router2@file", 1*time.Second, try.BodyContains("error while parsing rule QueryRegexp(`foo`, `bar`): unsupported function: QueryRegexp"))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
// router3 has an error because it uses the wrong rule syntax (v2 instead of v3)
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router3@file", 1*time.Second, try.BodyContains("error while adding rule PathPrefix: unexpected number of parameters; got 2, expected one of [1]"))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SimpleSuite) TestWithoutDefaultRuleSyntax() {
|
||||||
|
file := s.adaptFile("fixtures/without_default_rule_syntax.toml", struct{}{})
|
||||||
|
|
||||||
|
s.traefikCmd(withConfigFile(file))
|
||||||
|
|
||||||
|
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
// router1 has no error
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router1@file", 1*time.Second, try.BodyContains(`"status":"enabled"`))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8000/notfound", 1*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8000/foo", 1*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8000/bar", 1*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
// router2 has an error because it uses the wrong rule syntax (v3 instead of v2)
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router2@file", 1*time.Second, try.BodyContains("error while adding rule PathPrefix: unexpected number of parameters; got 2, expected one of [1]"))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
// router2 has an error because it uses the wrong rule syntax (v2 instead of v3)
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router3@file", 1*time.Second, try.BodyContains("error while parsing rule QueryRegexp(`foo`, `bar`): unsupported function: QueryRegexp"))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SimpleSuite) TestRouterConfigErrors() {
|
func (s *SimpleSuite) TestRouterConfigErrors() {
|
||||||
file := s.adaptFile("fixtures/router_errors.toml", struct{}{})
|
file := s.adaptFile("fixtures/router_errors.toml", struct{}{})
|
||||||
|
|
||||||
|
|
5
integration/testdata/rawdata-gateway.json
vendored
5
integration/testdata/rawdata-gateway.json
vendored
|
@ -34,6 +34,7 @@
|
||||||
],
|
],
|
||||||
"service": "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
|
"service": "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
|
||||||
"rule": "Host(`foo.com`) \u0026\u0026 Path(`/bar`)",
|
"rule": "Host(`foo.com`) \u0026\u0026 Path(`/bar`)",
|
||||||
|
"ruleSyntax": "v3",
|
||||||
"priority": 31,
|
"priority": 31,
|
||||||
"status": "enabled",
|
"status": "enabled",
|
||||||
"using": [
|
"using": [
|
||||||
|
@ -46,6 +47,7 @@
|
||||||
],
|
],
|
||||||
"service": "default-http-app-1-my-https-gateway-websecure-1c0cf64bde37d9d0df06-wrr",
|
"service": "default-http-app-1-my-https-gateway-websecure-1c0cf64bde37d9d0df06-wrr",
|
||||||
"rule": "Host(`foo.com`) \u0026\u0026 Path(`/bar`)",
|
"rule": "Host(`foo.com`) \u0026\u0026 Path(`/bar`)",
|
||||||
|
"ruleSyntax": "v3",
|
||||||
"priority": 31,
|
"priority": 31,
|
||||||
"tls": {},
|
"tls": {},
|
||||||
"status": "enabled",
|
"status": "enabled",
|
||||||
|
@ -152,6 +154,7 @@
|
||||||
],
|
],
|
||||||
"service": "default-tcp-app-1-my-tcp-gateway-footcp-e3b0c44298fc1c149afb-wrr-0",
|
"service": "default-tcp-app-1-my-tcp-gateway-footcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
"rule": "HostSNI(`*`)",
|
"rule": "HostSNI(`*`)",
|
||||||
|
"ruleSyntax": "v3",
|
||||||
"priority": -1,
|
"priority": -1,
|
||||||
"status": "enabled",
|
"status": "enabled",
|
||||||
"using": [
|
"using": [
|
||||||
|
@ -164,6 +167,7 @@
|
||||||
],
|
],
|
||||||
"service": "default-tcp-app-1-my-tls-gateway-footlsterminate-e3b0c44298fc1c149afb-wrr-0",
|
"service": "default-tcp-app-1-my-tls-gateway-footlsterminate-e3b0c44298fc1c149afb-wrr-0",
|
||||||
"rule": "HostSNI(`*`)",
|
"rule": "HostSNI(`*`)",
|
||||||
|
"ruleSyntax": "v3",
|
||||||
"priority": -1,
|
"priority": -1,
|
||||||
"tls": {
|
"tls": {
|
||||||
"passthrough": false
|
"passthrough": false
|
||||||
|
@ -179,6 +183,7 @@
|
||||||
],
|
],
|
||||||
"service": "default-tls-app-1-my-tls-gateway-footlspassthrough-2279fe75c5156dc5eb26-wrr-0",
|
"service": "default-tls-app-1-my-tls-gateway-footlspassthrough-2279fe75c5156dc5eb26-wrr-0",
|
||||||
"rule": "HostSNI(`foo.bar`)",
|
"rule": "HostSNI(`foo.bar`)",
|
||||||
|
"ruleSyntax": "v3",
|
||||||
"priority": 18,
|
"priority": 18,
|
||||||
"tls": {
|
"tls": {
|
||||||
"passthrough": true
|
"passthrough": true
|
||||||
|
|
|
@ -39,6 +39,7 @@ type HTTPConfiguration struct {
|
||||||
type Model struct {
|
type Model struct {
|
||||||
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
||||||
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
|
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
|
||||||
|
DefaultRuleSyntax string `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen=true
|
// +k8s:deepcopy-gen=true
|
||||||
|
@ -59,6 +60,7 @@ type Router struct {
|
||||||
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
||||||
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"`
|
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"`
|
||||||
Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
|
Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
|
||||||
|
RuleSyntax string `json:"ruleSyntax,omitempty" toml:"ruleSyntax,omitempty" yaml:"ruleSyntax,omitempty" export:"true"`
|
||||||
Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"`
|
Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"`
|
||||||
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
|
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
|
||||||
DefaultRule bool `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
|
DefaultRule bool `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
|
||||||
|
|
|
@ -16,11 +16,19 @@ type TCPConfiguration struct {
|
||||||
Routers map[string]*TCPRouter `json:"routers,omitempty" toml:"routers,omitempty" yaml:"routers,omitempty" export:"true"`
|
Routers map[string]*TCPRouter `json:"routers,omitempty" toml:"routers,omitempty" yaml:"routers,omitempty" export:"true"`
|
||||||
Services map[string]*TCPService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty" export:"true"`
|
Services map[string]*TCPService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty" export:"true"`
|
||||||
Middlewares map[string]*TCPMiddleware `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
Middlewares map[string]*TCPMiddleware `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
||||||
|
Models map[string]*TCPModel `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
|
||||||
ServersTransports map[string]*TCPServersTransport `json:"serversTransports,omitempty" toml:"serversTransports,omitempty" yaml:"serversTransports,omitempty" label:"-" export:"true"`
|
ServersTransports map[string]*TCPServersTransport `json:"serversTransports,omitempty" toml:"serversTransports,omitempty" yaml:"serversTransports,omitempty" label:"-" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen=true
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
|
// TCPModel is a set of default router's values.
|
||||||
|
type TCPModel struct {
|
||||||
|
DefaultRuleSyntax string `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// TCPService holds a tcp service configuration (can only be of one type at the same time).
|
// TCPService holds a tcp service configuration (can only be of one type at the same time).
|
||||||
type TCPService struct {
|
type TCPService struct {
|
||||||
LoadBalancer *TCPServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty" export:"true"`
|
LoadBalancer *TCPServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty" export:"true"`
|
||||||
|
@ -56,6 +64,7 @@ type TCPRouter struct {
|
||||||
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
||||||
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"`
|
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"`
|
||||||
Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
|
Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
|
||||||
|
RuleSyntax string `json:"ruleSyntax,omitempty" toml:"ruleSyntax,omitempty" yaml:"ruleSyntax,omitempty" export:"true"`
|
||||||
Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"`
|
Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"`
|
||||||
TLS *RouterTCPTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
|
TLS *RouterTCPTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1435,6 +1435,21 @@ func (in *TCPConfiguration) DeepCopyInto(out *TCPConfiguration) {
|
||||||
(*out)[key] = outVal
|
(*out)[key] = outVal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if in.Models != nil {
|
||||||
|
in, out := &in.Models, &out.Models
|
||||||
|
*out = make(map[string]*TCPModel, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
var outVal *TCPModel
|
||||||
|
if val == nil {
|
||||||
|
(*out)[key] = nil
|
||||||
|
} else {
|
||||||
|
in, out := &val, &outVal
|
||||||
|
*out = new(TCPModel)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
(*out)[key] = outVal
|
||||||
|
}
|
||||||
|
}
|
||||||
if in.ServersTransports != nil {
|
if in.ServersTransports != nil {
|
||||||
in, out := &in.ServersTransports, &out.ServersTransports
|
in, out := &in.ServersTransports, &out.ServersTransports
|
||||||
*out = make(map[string]*TCPServersTransport, len(*in))
|
*out = make(map[string]*TCPServersTransport, len(*in))
|
||||||
|
@ -1552,6 +1567,22 @@ func (in *TCPMiddleware) DeepCopy() *TCPMiddleware {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *TCPModel) DeepCopyInto(out *TCPModel) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPModel.
|
||||||
|
func (in *TCPModel) DeepCopy() *TCPModel {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(TCPModel)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *TCPRouter) DeepCopyInto(out *TCPRouter) {
|
func (in *TCPRouter) DeepCopyInto(out *TCPRouter) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|
|
@ -71,11 +71,23 @@ type Configuration struct {
|
||||||
|
|
||||||
CertificatesResolvers map[string]CertificateResolver `description:"Certificates resolvers configuration." json:"certificatesResolvers,omitempty" toml:"certificatesResolvers,omitempty" yaml:"certificatesResolvers,omitempty" export:"true"`
|
CertificatesResolvers map[string]CertificateResolver `description:"Certificates resolvers configuration." json:"certificatesResolvers,omitempty" toml:"certificatesResolvers,omitempty" yaml:"certificatesResolvers,omitempty" export:"true"`
|
||||||
|
|
||||||
Experimental *Experimental `description:"experimental features." json:"experimental,omitempty" toml:"experimental,omitempty" yaml:"experimental,omitempty" export:"true"`
|
Experimental *Experimental `description:"Experimental features." json:"experimental,omitempty" toml:"experimental,omitempty" yaml:"experimental,omitempty" export:"true"`
|
||||||
|
|
||||||
|
Core *Core `description:"Core controls." json:"core,omitempty" toml:"core,omitempty" yaml:"core,omitempty" export:"true"`
|
||||||
|
|
||||||
Spiffe *SpiffeClientConfig `description:"SPIFFE integration configuration." json:"spiffe,omitempty" toml:"spiffe,omitempty" yaml:"spiffe,omitempty" export:"true"`
|
Spiffe *SpiffeClientConfig `description:"SPIFFE integration configuration." json:"spiffe,omitempty" toml:"spiffe,omitempty" yaml:"spiffe,omitempty" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Core configures Traefik core behavior.
|
||||||
|
type Core struct {
|
||||||
|
DefaultRuleSyntax string `description:"Defines the rule parser default syntax (v2 or v3)" json:"defaultRuleSyntax,omitempty" toml:"defaultRuleSyntax,omitempty" yaml:"defaultRuleSyntax,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaults sets the default values.
|
||||||
|
func (c *Core) SetDefaults() {
|
||||||
|
c.DefaultRuleSyntax = "v3"
|
||||||
|
}
|
||||||
|
|
||||||
// SpiffeClientConfig defines the SPIFFE client configuration.
|
// SpiffeClientConfig defines the SPIFFE client configuration.
|
||||||
type SpiffeClientConfig struct {
|
type SpiffeClientConfig struct {
|
||||||
WorkloadAPIAddr string `description:"Defines the workload API address." json:"workloadAPIAddr,omitempty" toml:"workloadAPIAddr,omitempty" yaml:"workloadAPIAddr,omitempty"`
|
WorkloadAPIAddr string `description:"Defines the workload API address." json:"workloadAPIAddr,omitempty" toml:"workloadAPIAddr,omitempty" yaml:"workloadAPIAddr,omitempty"`
|
||||||
|
@ -317,6 +329,17 @@ func (c *Configuration) ValidateConfiguration() error {
|
||||||
acmeEmail = resolver.ACME.Email
|
acmeEmail = resolver.ACME.Email
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Core != nil {
|
||||||
|
switch c.Core.DefaultRuleSyntax {
|
||||||
|
case "v3": // NOOP
|
||||||
|
case "v2":
|
||||||
|
// TODO: point to migration guide.
|
||||||
|
log.Warn().Msgf("v2 rules syntax is now deprecated, please use v3 instead...")
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported default rule syntax configuration: %q", c.Core.DefaultRuleSyntax)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if c.Tracing != nil && c.Tracing.OTLP != nil {
|
if c.Tracing != nil && c.Tracing.OTLP != nil {
|
||||||
if c.Tracing.OTLP.HTTP == nil && c.Tracing.OTLP.GRPC == nil {
|
if c.Tracing.OTLP.HTTP == nil && c.Tracing.OTLP.GRPC == nil {
|
||||||
return errors.New("tracing OTLP: at least one of HTTP and gRPC options should be defined")
|
return errors.New("tracing OTLP: at least one of HTTP and gRPC options should be defined")
|
||||||
|
|
|
@ -73,7 +73,7 @@ func TestClientIPMatcher(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, handler)
|
err = muxer.AddRoute(test.rule, "", 0, handler)
|
||||||
if test.expectedError {
|
if test.expectedError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -147,7 +147,7 @@ func TestMethodMatcher(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, handler)
|
err = muxer.AddRoute(test.rule, "", 0, handler)
|
||||||
if test.expectedError {
|
if test.expectedError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -265,7 +265,7 @@ func TestHostMatcher(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, handler)
|
err = muxer.AddRoute(test.rule, "", 0, handler)
|
||||||
if test.expectedError {
|
if test.expectedError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -365,7 +365,7 @@ func TestHostRegexpMatcher(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, handler)
|
err = muxer.AddRoute(test.rule, "", 0, handler)
|
||||||
if test.expectedError {
|
if test.expectedError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -439,7 +439,7 @@ func TestPathMatcher(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, handler)
|
err = muxer.AddRoute(test.rule, "", 0, handler)
|
||||||
if test.expectedError {
|
if test.expectedError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -532,7 +532,7 @@ func TestPathRegexpMatcher(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, handler)
|
err = muxer.AddRoute(test.rule, "", 0, handler)
|
||||||
if test.expectedError {
|
if test.expectedError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -604,7 +604,7 @@ func TestPathPrefixMatcher(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, handler)
|
err = muxer.AddRoute(test.rule, "", 0, handler)
|
||||||
if test.expectedError {
|
if test.expectedError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -692,7 +692,7 @@ func TestHeaderMatcher(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, handler)
|
err = muxer.AddRoute(test.rule, "", 0, handler)
|
||||||
if test.expectedError {
|
if test.expectedError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -800,7 +800,7 @@ func TestHeaderRegexpMatcher(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, handler)
|
err = muxer.AddRoute(test.rule, "", 0, handler)
|
||||||
if test.expectedError {
|
if test.expectedError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -889,7 +889,7 @@ func TestQueryMatcher(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, handler)
|
err = muxer.AddRoute(test.rule, "", 0, handler)
|
||||||
if test.expectedError {
|
if test.expectedError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -1003,7 +1003,7 @@ func TestQueryRegexpMatcher(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, handler)
|
err = muxer.AddRoute(test.rule, "", 0, handler)
|
||||||
if test.expectedError {
|
if test.expectedError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
|
226
pkg/muxer/http/matcher_v2.go
Normal file
226
pkg/muxer/http/matcher_v2.go
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/traefik/traefik/v3/pkg/ip"
|
||||||
|
"github.com/traefik/traefik/v3/pkg/middlewares/requestdecorator"
|
||||||
|
)
|
||||||
|
|
||||||
|
var httpFuncsV2 = map[string]func(*matchersTree, ...string) error{
|
||||||
|
"Host": hostV2,
|
||||||
|
"HostHeader": hostV2,
|
||||||
|
"HostRegexp": hostRegexpV2,
|
||||||
|
"ClientIP": clientIPV2,
|
||||||
|
"Path": pathV2,
|
||||||
|
"PathPrefix": pathPrefixV2,
|
||||||
|
"Method": methodsV2,
|
||||||
|
"Headers": headersV2,
|
||||||
|
"HeadersRegexp": headersRegexpV2,
|
||||||
|
"Query": queryV2,
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathV2(tree *matchersTree, paths ...string) error {
|
||||||
|
for _, path := range paths {
|
||||||
|
if !strings.HasPrefix(path, "/") {
|
||||||
|
return fmt.Errorf("path %q does not start with a '/'", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.matcher = func(req *http.Request) bool {
|
||||||
|
for _, path := range paths {
|
||||||
|
if req.URL.Path == path {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathPrefixV2(tree *matchersTree, paths ...string) error {
|
||||||
|
for _, path := range paths {
|
||||||
|
if !strings.HasPrefix(path, "/") {
|
||||||
|
return fmt.Errorf("path %q does not start with a '/'", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.matcher = func(req *http.Request) bool {
|
||||||
|
for _, path := range paths {
|
||||||
|
if strings.HasPrefix(req.URL.Path, path) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hostV2(tree *matchersTree, hosts ...string) error {
|
||||||
|
for i, host := range hosts {
|
||||||
|
if !IsASCII(host) {
|
||||||
|
return fmt.Errorf("invalid value %q for \"Host\" matcher, non-ASCII characters are not allowed", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts[i] = strings.ToLower(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.matcher = func(req *http.Request) bool {
|
||||||
|
reqHost := requestdecorator.GetCanonizedHost(req.Context())
|
||||||
|
if len(reqHost) == 0 {
|
||||||
|
// If the request is an HTTP/1.0 request, then a Host may not be defined.
|
||||||
|
if req.ProtoAtLeast(1, 1) {
|
||||||
|
log.Ctx(req.Context()).Warn().Msgf("Could not retrieve CanonizedHost, rejecting %s", req.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
flatH := requestdecorator.GetCNAMEFlatten(req.Context())
|
||||||
|
if len(flatH) > 0 {
|
||||||
|
for _, host := range hosts {
|
||||||
|
if strings.EqualFold(reqHost, host) || strings.EqualFold(flatH, host) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
log.Ctx(req.Context()).Debug().Msgf("CNAMEFlattening: request %s which resolved to %s, is not matched to route %s", reqHost, flatH, host)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range hosts {
|
||||||
|
if reqHost == host {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for match on trailing period on host
|
||||||
|
if last := len(host) - 1; last >= 0 && host[last] == '.' {
|
||||||
|
h := host[:last]
|
||||||
|
if reqHost == h {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for match on trailing period on request
|
||||||
|
if last := len(reqHost) - 1; last >= 0 && reqHost[last] == '.' {
|
||||||
|
h := reqHost[:last]
|
||||||
|
if h == host {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func clientIPV2(tree *matchersTree, clientIPs ...string) error {
|
||||||
|
checker, err := ip.NewChecker(clientIPs)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not initialize IP Checker for \"ClientIP\" matcher: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
strategy := ip.RemoteAddrStrategy{}
|
||||||
|
|
||||||
|
tree.matcher = func(req *http.Request) bool {
|
||||||
|
ok, err := checker.Contains(strategy.GetIP(req))
|
||||||
|
if err != nil {
|
||||||
|
log.Ctx(req.Context()).Warn().Err(err).Msg("\"ClientIP\" matcher: could not match remote address")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func methodsV2(tree *matchersTree, methods ...string) error {
|
||||||
|
route := mux.NewRouter().NewRoute()
|
||||||
|
route.Methods(methods...)
|
||||||
|
if err := route.GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.matcher = func(req *http.Request) bool {
|
||||||
|
return route.Match(req, &mux.RouteMatch{})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func headersV2(tree *matchersTree, headers ...string) error {
|
||||||
|
route := mux.NewRouter().NewRoute()
|
||||||
|
route.Headers(headers...)
|
||||||
|
if err := route.GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.matcher = func(req *http.Request) bool {
|
||||||
|
return route.Match(req, &mux.RouteMatch{})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryV2(tree *matchersTree, query ...string) error {
|
||||||
|
var queries []string
|
||||||
|
for _, elem := range query {
|
||||||
|
queries = append(queries, strings.SplitN(elem, "=", 2)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
route := mux.NewRouter().NewRoute()
|
||||||
|
route.Queries(queries...)
|
||||||
|
if err := route.GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.matcher = func(req *http.Request) bool {
|
||||||
|
return route.Match(req, &mux.RouteMatch{})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hostRegexpV2(tree *matchersTree, hosts ...string) error {
|
||||||
|
router := mux.NewRouter()
|
||||||
|
|
||||||
|
for _, host := range hosts {
|
||||||
|
if !IsASCII(host) {
|
||||||
|
return fmt.Errorf("invalid value %q for HostRegexp matcher, non-ASCII characters are not allowed", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpRt := router.Host(host)
|
||||||
|
if tmpRt.GetError() != nil {
|
||||||
|
return tmpRt.GetError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.matcher = func(req *http.Request) bool {
|
||||||
|
return router.Match(req, &mux.RouteMatch{})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func headersRegexpV2(tree *matchersTree, headers ...string) error {
|
||||||
|
route := mux.NewRouter().NewRoute()
|
||||||
|
route.HeadersRegexp(headers...)
|
||||||
|
if err := route.GetError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.matcher = func(req *http.Request) bool {
|
||||||
|
return route.Match(req, &mux.RouteMatch{})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
1535
pkg/muxer/http/matcher_v2_test.go
Normal file
1535
pkg/muxer/http/matcher_v2_test.go
Normal file
File diff suppressed because it is too large
Load diff
|
@ -14,6 +14,7 @@ import (
|
||||||
type Muxer struct {
|
type Muxer struct {
|
||||||
routes routes
|
routes routes
|
||||||
parser predicate.Parser
|
parser predicate.Parser
|
||||||
|
parserV2 predicate.Parser
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMuxer returns a new muxer instance.
|
// NewMuxer returns a new muxer instance.
|
||||||
|
@ -28,8 +29,19 @@ func NewMuxer() (*Muxer, error) {
|
||||||
return nil, fmt.Errorf("error while creating parser: %w", err)
|
return nil, fmt.Errorf("error while creating parser: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var matchersV2 []string
|
||||||
|
for matcher := range httpFuncsV2 {
|
||||||
|
matchersV2 = append(matchersV2, matcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
parserV2, err := rules.NewParser(matchersV2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error while creating v2 parser: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return &Muxer{
|
return &Muxer{
|
||||||
parser: parser,
|
parser: parser,
|
||||||
|
parserV2: parserV2,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,19 +65,35 @@ func GetRulePriority(rule string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddRoute add a new route to the router.
|
// AddRoute add a new route to the router.
|
||||||
func (m *Muxer) AddRoute(rule string, priority int, handler http.Handler) error {
|
func (m *Muxer) AddRoute(rule string, syntax string, priority int, handler http.Handler) error {
|
||||||
parse, err := m.parser.Parse(rule)
|
var parse interface{}
|
||||||
|
var err error
|
||||||
|
var matcherFuncs map[string]func(*matchersTree, ...string) error
|
||||||
|
|
||||||
|
switch syntax {
|
||||||
|
case "v2":
|
||||||
|
parse, err = m.parserV2.Parse(rule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while parsing rule %s: %w", rule, err)
|
return fmt.Errorf("error while parsing rule %s: %w", rule, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
matcherFuncs = httpFuncsV2
|
||||||
|
default:
|
||||||
|
parse, err = m.parser.Parse(rule)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while parsing rule %s: %w", rule, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
matcherFuncs = httpFuncs
|
||||||
|
}
|
||||||
|
|
||||||
buildTree, ok := parse.(rules.TreeBuilder)
|
buildTree, ok := parse.(rules.TreeBuilder)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("error while parsing rule %s", rule)
|
return fmt.Errorf("error while parsing rule %s", rule)
|
||||||
}
|
}
|
||||||
|
|
||||||
var matchers matchersTree
|
var matchers matchersTree
|
||||||
err = matchers.addRule(buildTree())
|
err = matchers.addRule(buildTree(), matcherFuncs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while adding rule %s: %w", rule, err)
|
return fmt.Errorf("error while adding rule %s: %w", rule, err)
|
||||||
}
|
}
|
||||||
|
@ -87,6 +115,9 @@ func ParseDomains(rule string) ([]string, error) {
|
||||||
for matcher := range httpFuncs {
|
for matcher := range httpFuncs {
|
||||||
matchers = append(matchers, matcher)
|
matchers = append(matchers, matcher)
|
||||||
}
|
}
|
||||||
|
for matcher := range httpFuncsV2 {
|
||||||
|
matchers = append(matchers, matcher)
|
||||||
|
}
|
||||||
|
|
||||||
parser, err := rules.NewParser(matchers)
|
parser, err := rules.NewParser(matchers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -166,25 +197,27 @@ func (m *matchersTree) match(req *http.Request) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *matchersTree) addRule(rule *rules.Tree) error {
|
type matcherFuncs map[string]func(*matchersTree, ...string) error
|
||||||
|
|
||||||
|
func (m *matchersTree) addRule(rule *rules.Tree, funcs matcherFuncs) error {
|
||||||
switch rule.Matcher {
|
switch rule.Matcher {
|
||||||
case "and", "or":
|
case "and", "or":
|
||||||
m.operator = rule.Matcher
|
m.operator = rule.Matcher
|
||||||
m.left = &matchersTree{}
|
m.left = &matchersTree{}
|
||||||
err := m.left.addRule(rule.RuleLeft)
|
err := m.left.addRule(rule.RuleLeft, funcs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while adding rule %s: %w", rule.Matcher, err)
|
return fmt.Errorf("error while adding rule %s: %w", rule.Matcher, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.right = &matchersTree{}
|
m.right = &matchersTree{}
|
||||||
return m.right.addRule(rule.RuleRight)
|
return m.right.addRule(rule.RuleRight, funcs)
|
||||||
default:
|
default:
|
||||||
err := rules.CheckRule(rule)
|
err := rules.CheckRule(rule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while checking rule %s: %w", rule.Matcher, err)
|
return fmt.Errorf("error while checking rule %s: %w", rule.Matcher, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = httpFuncs[rule.Matcher](m, rule.Value...)
|
err = funcs[rule.Matcher](m, rule.Value...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while adding rule %s: %w", rule.Matcher, err)
|
return fmt.Errorf("error while adding rule %s: %w", rule.Matcher, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,7 +231,7 @@ func TestMuxer(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||||
err = muxer.AddRoute(test.rule, 0, handler)
|
err = muxer.AddRoute(test.rule, "", 0, handler)
|
||||||
if test.expectedError {
|
if test.expectedError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -394,7 +394,7 @@ func Test_addRoutePriority(t *testing.T) {
|
||||||
route.priority = GetRulePriority(route.rule)
|
route.priority = GetRulePriority(route.rule)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := muxer.AddRoute(route.rule, route.priority, handler)
|
err := muxer.AddRoute(route.rule, "", route.priority, handler)
|
||||||
require.NoError(t, err, route.rule)
|
require.NoError(t, err, route.rule)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,7 +519,7 @@ func TestEmptyHost(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, handler)
|
err = muxer.AddRoute(test.rule, "", 0, handler)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// RequestDecorator is necessary for the host rule
|
// RequestDecorator is necessary for the host rule
|
||||||
|
|
|
@ -38,7 +38,7 @@ func Test_HostSNICatchAll(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
err = muxer.AddRoute(test.rule, "", 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
handler, catchAll := muxer.Match(ConnData{
|
handler, catchAll := muxer.Match(ConnData{
|
||||||
|
@ -144,7 +144,7 @@ func Test_HostSNI(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
err = muxer.AddRoute(test.rule, "", 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
||||||
if test.buildErr {
|
if test.buildErr {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -227,7 +227,7 @@ func Test_HostSNIRegexp(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
err = muxer.AddRoute(test.rule, "", 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
||||||
if test.buildErr {
|
if test.buildErr {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -299,7 +299,7 @@ func Test_ClientIP(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
err = muxer.AddRoute(test.rule, "", 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
||||||
if test.buildErr {
|
if test.buildErr {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -363,7 +363,7 @@ func Test_ALPN(t *testing.T) {
|
||||||
muxer, err := NewMuxer()
|
muxer, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
err = muxer.AddRoute(test.rule, "", 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
|
||||||
if test.buildErr {
|
if test.buildErr {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
|
240
pkg/muxer/tcp/matcher_v2.go
Normal file
240
pkg/muxer/tcp/matcher_v2.go
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
package tcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/traefik/traefik/v3/pkg/ip"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tcpFuncsV2 = map[string]func(*matchersTree, ...string) error{
|
||||||
|
"ALPN": alpnV2,
|
||||||
|
"ClientIP": clientIPV2,
|
||||||
|
"HostSNI": hostSNIV2,
|
||||||
|
"HostSNIRegexp": hostSNIRegexpV2,
|
||||||
|
}
|
||||||
|
|
||||||
|
func clientIPV2(tree *matchersTree, clientIPs ...string) error {
|
||||||
|
checker, err := ip.NewChecker(clientIPs)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not initialize IP Checker for \"ClientIP\" matcher: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.matcher = func(meta ConnData) bool {
|
||||||
|
if meta.remoteIP == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := checker.Contains(meta.remoteIP)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("ClientIP matcher: could not match remote address")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// alpnV2 checks if any of the connection ALPN protocols matches one of the matcher protocols.
|
||||||
|
func alpnV2(tree *matchersTree, protos ...string) error {
|
||||||
|
if len(protos) == 0 {
|
||||||
|
return errors.New("empty value for \"ALPN\" matcher is not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, proto := range protos {
|
||||||
|
if proto == tlsalpn01.ACMETLS1Protocol {
|
||||||
|
return fmt.Errorf("invalid protocol value for \"ALPN\" matcher, %q is not allowed", proto)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.matcher = func(meta ConnData) bool {
|
||||||
|
for _, proto := range meta.alpnProtos {
|
||||||
|
for _, filter := range protos {
|
||||||
|
if proto == filter {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hostSNIV2 checks if the SNI Host of the connection match the matcher host.
|
||||||
|
func hostSNIV2(tree *matchersTree, hosts ...string) error {
|
||||||
|
if len(hosts) == 0 {
|
||||||
|
return errors.New("empty value for \"HostSNI\" matcher is not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, host := range hosts {
|
||||||
|
// Special case to allow global wildcard
|
||||||
|
if host == "*" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hostOrIP.MatchString(host) {
|
||||||
|
return fmt.Errorf("invalid value for \"HostSNI\" matcher, %q is not a valid hostname or IP", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts[i] = strings.ToLower(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.matcher = func(meta ConnData) bool {
|
||||||
|
// Since a HostSNI(`*`) rule has been provided as catchAll for non-TLS TCP,
|
||||||
|
// it allows matching with an empty serverName.
|
||||||
|
// Which is why we make sure to take that case into account before
|
||||||
|
// checking meta.serverName.
|
||||||
|
if hosts[0] == "*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if meta.serverName == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range hosts {
|
||||||
|
if host == "*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if host == meta.serverName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim trailing period in case of FQDN
|
||||||
|
host = strings.TrimSuffix(host, ".")
|
||||||
|
if host == meta.serverName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hostSNIRegexpV2 checks if the SNI Host of the connection matches the matcher host regexp.
|
||||||
|
func hostSNIRegexpV2(tree *matchersTree, templates ...string) error {
|
||||||
|
if len(templates) == 0 {
|
||||||
|
return fmt.Errorf("empty value for \"HostSNIRegexp\" matcher is not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
var regexps []*regexp.Regexp
|
||||||
|
|
||||||
|
for _, template := range templates {
|
||||||
|
preparedPattern, err := preparePattern(template)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid pattern value for \"HostSNIRegexp\" matcher, %q is not a valid pattern: %w", template, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
regexp, err := regexp.Compile(preparedPattern)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
regexps = append(regexps, regexp)
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.matcher = func(meta ConnData) bool {
|
||||||
|
for _, regexp := range regexps {
|
||||||
|
if regexp.MatchString(meta.serverName) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// preparePattern builds a regexp pattern from the initial user defined expression.
|
||||||
|
// This function reuses the code dedicated to host matching of the newRouteRegexp func from the gorilla/mux library.
|
||||||
|
// https://github.com/containous/mux/tree/8ffa4f6d063c1e2b834a73be6a1515cca3992618.
|
||||||
|
func preparePattern(template string) (string, error) {
|
||||||
|
// Check if it is well-formed.
|
||||||
|
idxs, errBraces := braceIndices(template)
|
||||||
|
if errBraces != nil {
|
||||||
|
return "", errBraces
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultPattern := "[^.]+"
|
||||||
|
pattern := bytes.NewBufferString("")
|
||||||
|
|
||||||
|
// Host SNI matching is case-insensitive
|
||||||
|
_, _ = fmt.Fprint(pattern, "(?i)")
|
||||||
|
|
||||||
|
pattern.WriteByte('^')
|
||||||
|
var end int
|
||||||
|
for i := 0; i < len(idxs); i += 2 {
|
||||||
|
// Set all values we are interested in.
|
||||||
|
raw := template[end:idxs[i]]
|
||||||
|
end = idxs[i+1]
|
||||||
|
parts := strings.SplitN(template[idxs[i]+1:end-1], ":", 2)
|
||||||
|
name := parts[0]
|
||||||
|
|
||||||
|
patt := defaultPattern
|
||||||
|
if len(parts) == 2 {
|
||||||
|
patt = parts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name or pattern can't be empty.
|
||||||
|
if name == "" || patt == "" {
|
||||||
|
return "", fmt.Errorf("mux: missing name or pattern in %q",
|
||||||
|
template[idxs[i]:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the regexp pattern.
|
||||||
|
_, _ = fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(i/2), patt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the remaining.
|
||||||
|
raw := template[end:]
|
||||||
|
pattern.WriteString(regexp.QuoteMeta(raw))
|
||||||
|
pattern.WriteByte('$')
|
||||||
|
|
||||||
|
return pattern.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// varGroupName builds a capturing group name for the indexed variable.
|
||||||
|
// This function is a copy of varGroupName func from the gorilla/mux library.
|
||||||
|
// https://github.com/containous/mux/tree/8ffa4f6d063c1e2b834a73be6a1515cca3992618.
|
||||||
|
func varGroupName(idx int) string {
|
||||||
|
return "v" + strconv.Itoa(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// braceIndices returns the first level curly brace indices from a string.
|
||||||
|
// This function is a copy of braceIndices func from the gorilla/mux library.
|
||||||
|
// https://github.com/containous/mux/tree/8ffa4f6d063c1e2b834a73be6a1515cca3992618.
|
||||||
|
func braceIndices(s string) ([]int, error) {
|
||||||
|
var level, idx int
|
||||||
|
var idxs []int
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
switch s[i] {
|
||||||
|
case '{':
|
||||||
|
if level++; level == 1 {
|
||||||
|
idx = i
|
||||||
|
}
|
||||||
|
case '}':
|
||||||
|
if level--; level == 0 {
|
||||||
|
idxs = append(idxs, idx, i+1)
|
||||||
|
} else if level < 0 {
|
||||||
|
return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if level != 0 {
|
||||||
|
return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
|
||||||
|
}
|
||||||
|
return idxs, nil
|
||||||
|
}
|
1008
pkg/muxer/tcp/matcher_v2_test.go
Normal file
1008
pkg/muxer/tcp/matcher_v2_test.go
Normal file
File diff suppressed because it is too large
Load diff
|
@ -43,6 +43,7 @@ func NewConnData(serverName string, conn tcp.WriteCloser, alpnProtos []string) (
|
||||||
type Muxer struct {
|
type Muxer struct {
|
||||||
routes routes
|
routes routes
|
||||||
parser predicate.Parser
|
parser predicate.Parser
|
||||||
|
parserV2 predicate.Parser
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMuxer returns a TCP muxer.
|
// NewMuxer returns a TCP muxer.
|
||||||
|
@ -57,7 +58,20 @@ func NewMuxer() (*Muxer, error) {
|
||||||
return nil, fmt.Errorf("error while creating rules parser: %w", err)
|
return nil, fmt.Errorf("error while creating rules parser: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Muxer{parser: parser}, nil
|
var matchersV2 []string
|
||||||
|
for matcher := range tcpFuncsV2 {
|
||||||
|
matchersV2 = append(matchersV2, matcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
parserV2, err := rules.NewParser(matchersV2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error while creating v2 rules parser: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Muxer{
|
||||||
|
parser: parser,
|
||||||
|
parserV2: parserV2,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match returns the handler of the first route matching the connection metadata,
|
// Match returns the handler of the first route matching the connection metadata,
|
||||||
|
@ -106,12 +120,28 @@ func GetRulePriority(rule string) int {
|
||||||
|
|
||||||
// AddRoute adds a new route, associated to the given handler, at the given
|
// AddRoute adds a new route, associated to the given handler, at the given
|
||||||
// priority, to the muxer.
|
// priority, to the muxer.
|
||||||
func (m *Muxer) AddRoute(rule string, priority int, handler tcp.Handler) error {
|
func (m *Muxer) AddRoute(rule string, syntax string, priority int, handler tcp.Handler) error {
|
||||||
parse, err := m.parser.Parse(rule)
|
var parse interface{}
|
||||||
|
var err error
|
||||||
|
var matcherFuncs map[string]func(*matchersTree, ...string) error
|
||||||
|
|
||||||
|
switch syntax {
|
||||||
|
case "v2":
|
||||||
|
parse, err = m.parserV2.Parse(rule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while parsing rule %s: %w", rule, err)
|
return fmt.Errorf("error while parsing rule %s: %w", rule, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
matcherFuncs = tcpFuncsV2
|
||||||
|
default:
|
||||||
|
parse, err = m.parser.Parse(rule)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while parsing rule %s: %w", rule, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
matcherFuncs = tcpFuncs
|
||||||
|
}
|
||||||
|
|
||||||
buildTree, ok := parse.(rules.TreeBuilder)
|
buildTree, ok := parse.(rules.TreeBuilder)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("error while parsing rule %s", rule)
|
return fmt.Errorf("error while parsing rule %s", rule)
|
||||||
|
@ -120,7 +150,7 @@ func (m *Muxer) AddRoute(rule string, priority int, handler tcp.Handler) error {
|
||||||
ruleTree := buildTree()
|
ruleTree := buildTree()
|
||||||
|
|
||||||
var matchers matchersTree
|
var matchers matchersTree
|
||||||
err = matchers.addRule(ruleTree)
|
err = matchers.addRule(ruleTree, matcherFuncs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while adding rule %s: %w", rule, err)
|
return fmt.Errorf("error while adding rule %s: %w", rule, err)
|
||||||
}
|
}
|
||||||
|
@ -155,6 +185,9 @@ func ParseHostSNI(rule string) ([]string, error) {
|
||||||
for matcher := range tcpFuncs {
|
for matcher := range tcpFuncs {
|
||||||
matchers = append(matchers, matcher)
|
matchers = append(matchers, matcher)
|
||||||
}
|
}
|
||||||
|
for matcher := range tcpFuncsV2 {
|
||||||
|
matchers = append(matchers, matcher)
|
||||||
|
}
|
||||||
|
|
||||||
parser, err := rules.NewParser(matchers)
|
parser, err := rules.NewParser(matchers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -237,25 +270,27 @@ func (m *matchersTree) match(meta ConnData) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *matchersTree) addRule(rule *rules.Tree) error {
|
type matcherFuncs map[string]func(*matchersTree, ...string) error
|
||||||
|
|
||||||
|
func (m *matchersTree) addRule(rule *rules.Tree, funcs matcherFuncs) error {
|
||||||
switch rule.Matcher {
|
switch rule.Matcher {
|
||||||
case "and", "or":
|
case "and", "or":
|
||||||
m.operator = rule.Matcher
|
m.operator = rule.Matcher
|
||||||
m.left = &matchersTree{}
|
m.left = &matchersTree{}
|
||||||
err := m.left.addRule(rule.RuleLeft)
|
err := m.left.addRule(rule.RuleLeft, funcs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.right = &matchersTree{}
|
m.right = &matchersTree{}
|
||||||
return m.right.addRule(rule.RuleRight)
|
return m.right.addRule(rule.RuleRight, funcs)
|
||||||
default:
|
default:
|
||||||
err := rules.CheckRule(rule)
|
err := rules.CheckRule(rule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tcpFuncs[rule.Matcher](m, rule.Value...)
|
err = funcs[rule.Matcher](m, rule.Value...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,7 +277,7 @@ func Test_addTCPRoute(t *testing.T) {
|
||||||
router, err := NewMuxer()
|
router, err := NewMuxer()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = router.AddRoute(test.rule, 0, handler)
|
err = router.AddRoute(test.rule, "", 0, handler)
|
||||||
if test.routeErr {
|
if test.routeErr {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
|
@ -447,7 +447,7 @@ func Test_Priority(t *testing.T) {
|
||||||
matchedRule := ""
|
matchedRule := ""
|
||||||
for rule, priority := range test.rules {
|
for rule, priority := range test.rules {
|
||||||
rule := rule
|
rule := rule
|
||||||
err := muxer.AddRoute(rule, priority, tcp.HandlerFunc(func(conn tcp.WriteCloser) {
|
err := muxer.AddRoute(rule, "", priority, tcp.HandlerFunc(func(conn tcp.WriteCloser) {
|
||||||
matchedRule = rule
|
matchedRule = rule
|
||||||
}))
|
}))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -112,6 +112,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
|
||||||
r := &dynamic.Router{
|
r := &dynamic.Router{
|
||||||
Middlewares: mds,
|
Middlewares: mds,
|
||||||
Priority: route.Priority,
|
Priority: route.Priority,
|
||||||
|
RuleSyntax: route.Syntax,
|
||||||
EntryPoints: ingressRoute.Spec.EntryPoints,
|
EntryPoints: ingressRoute.Spec.EntryPoints,
|
||||||
Rule: route.Match,
|
Rule: route.Match,
|
||||||
Service: serviceName,
|
Service: serviceName,
|
||||||
|
|
|
@ -102,6 +102,7 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
|
||||||
Middlewares: mds,
|
Middlewares: mds,
|
||||||
Rule: route.Match,
|
Rule: route.Match,
|
||||||
Priority: route.Priority,
|
Priority: route.Priority,
|
||||||
|
RuleSyntax: route.Syntax,
|
||||||
Service: serviceName,
|
Service: serviceName,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,9 @@ type Route struct {
|
||||||
// Priority defines the router's priority.
|
// Priority defines the router's priority.
|
||||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority
|
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority
|
||||||
Priority int `json:"priority,omitempty"`
|
Priority int `json:"priority,omitempty"`
|
||||||
|
// Syntax defines the router's rule syntax.
|
||||||
|
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax
|
||||||
|
Syntax string `json:"syntax,omitempty"`
|
||||||
// Services defines the list of Service.
|
// Services defines the list of Service.
|
||||||
// It can contain any combination of TraefikService and/or reference to a Kubernetes Service.
|
// It can contain any combination of TraefikService and/or reference to a Kubernetes Service.
|
||||||
Services []Service `json:"services,omitempty"`
|
Services []Service `json:"services,omitempty"`
|
||||||
|
|
|
@ -29,6 +29,9 @@ type RouteTCP struct {
|
||||||
// Priority defines the router's priority.
|
// Priority defines the router's priority.
|
||||||
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority_1
|
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority_1
|
||||||
Priority int `json:"priority,omitempty"`
|
Priority int `json:"priority,omitempty"`
|
||||||
|
// Syntax defines the router's rule syntax.
|
||||||
|
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax_1
|
||||||
|
Syntax string `json:"syntax,omitempty"`
|
||||||
// Services defines the list of TCP services.
|
// Services defines the list of TCP services.
|
||||||
Services []ServiceTCP `json:"services,omitempty"`
|
Services []ServiceTCP `json:"services,omitempty"`
|
||||||
// Middlewares defines the list of references to MiddlewareTCP resources.
|
// Middlewares defines the list of references to MiddlewareTCP resources.
|
||||||
|
|
|
@ -770,6 +770,7 @@ func (p *Provider) gatewayHTTPRouteToHTTPConf(ctx context.Context, ep string, li
|
||||||
|
|
||||||
router := dynamic.Router{
|
router := dynamic.Router{
|
||||||
Rule: rule,
|
Rule: rule,
|
||||||
|
RuleSyntax: "v3",
|
||||||
EntryPoints: []string{ep},
|
EntryPoints: []string{ep},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -908,6 +909,7 @@ func gatewayTCPRouteToTCPConf(ctx context.Context, ep string, listener gatev1.Li
|
||||||
router := dynamic.TCPRouter{
|
router := dynamic.TCPRouter{
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
EntryPoints: []string{ep},
|
EntryPoints: []string{ep},
|
||||||
|
RuleSyntax: "v3",
|
||||||
}
|
}
|
||||||
|
|
||||||
if listener.Protocol == gatev1.TLSProtocolType && listener.TLS != nil {
|
if listener.Protocol == gatev1.TLSProtocolType && listener.TLS != nil {
|
||||||
|
@ -1072,6 +1074,7 @@ func gatewayTLSRouteToTCPConf(ctx context.Context, ep string, listener gatev1.Li
|
||||||
|
|
||||||
router := dynamic.TCPRouter{
|
router := dynamic.TCPRouter{
|
||||||
Rule: rule,
|
Rule: rule,
|
||||||
|
RuleSyntax: "v3",
|
||||||
EntryPoints: []string{ep},
|
EntryPoints: []string{ep},
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: listener.TLS.Mode != nil && *listener.TLS.Mode == gatev1.TLSModePassthrough,
|
Passthrough: listener.TLS.Mode != nil && *listener.TLS.Mode == gatev1.TLSModePassthrough,
|
||||||
|
@ -1395,7 +1398,7 @@ func extractHeaderRules(headers []gatev1.HTTPHeaderMatch) ([]string, error) {
|
||||||
|
|
||||||
switch *header.Type {
|
switch *header.Type {
|
||||||
case gatev1.HeaderMatchExact:
|
case gatev1.HeaderMatchExact:
|
||||||
headerRules = append(headerRules, fmt.Sprintf("Headers(`%s`,`%s`)", header.Name, header.Value))
|
headerRules = append(headerRules, fmt.Sprintf("Header(`%s`,`%s`)", header.Name, header.Value))
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported header match type %s", *header.Type)
|
return nil, fmt.Errorf("unsupported header match type %s", *header.Type)
|
||||||
}
|
}
|
||||||
|
|
|
@ -550,6 +550,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
|
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
|
||||||
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
@ -609,6 +610,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "api@internal",
|
Service: "api@internal",
|
||||||
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
@ -641,6 +643,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
|
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
|
||||||
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
@ -704,6 +707,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"websecure"},
|
EntryPoints: []string{"websecure"},
|
||||||
Service: "default-http-app-1-my-gateway-websecure-1c0cf64bde37d9d0df06-wrr",
|
Service: "default-http-app-1-my-gateway-websecure-1c0cf64bde37d9d0df06-wrr",
|
||||||
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTLSConfig{},
|
TLS: &dynamic.RouterTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -773,6 +777,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-1-my-gateway-web-66e726cd8903b49727ae-wrr",
|
Service: "default-http-app-1-my-gateway-web-66e726cd8903b49727ae-wrr",
|
||||||
Rule: "(Host(`foo.com`) || Host(`bar.com`)) && PathPrefix(`/`)",
|
Rule: "(Host(`foo.com`) || Host(`bar.com`)) && PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
@ -832,6 +837,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-1-my-gateway-web-3b78e2feb3295ddd87f0-wrr",
|
Service: "default-http-app-1-my-gateway-web-3b78e2feb3295ddd87f0-wrr",
|
||||||
Rule: "(Host(`foo.com`) || HostRegexp(`^[a-zA-Z0-9-]+\\.bar\\.com$`)) && PathPrefix(`/`)",
|
Rule: "(Host(`foo.com`) || HostRegexp(`^[a-zA-Z0-9-]+\\.bar\\.com$`)) && PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
@ -891,6 +897,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-1-my-gateway-web-b0521a61fb43068694b4-wrr",
|
Service: "default-http-app-1-my-gateway-web-b0521a61fb43068694b4-wrr",
|
||||||
Rule: "(Host(`foo.com`) || HostRegexp(`^[a-zA-Z0-9-]+\\.foo\\.com$`)) && PathPrefix(`/`)",
|
Rule: "(Host(`foo.com`) || HostRegexp(`^[a-zA-Z0-9-]+\\.foo\\.com$`)) && PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
@ -949,11 +956,13 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
"default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06": {
|
"default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06": {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
|
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
|
||||||
},
|
},
|
||||||
"default-http-app-1-my-gateway-web-d737b4933fa88e68ab8a": {
|
"default-http-app-1-my-gateway-web-d737b4933fa88e68ab8a": {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Rule: "Host(`foo.com`) && Path(`/bir`)",
|
Rule: "Host(`foo.com`) && Path(`/bir`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
Service: "default-http-app-1-my-gateway-web-d737b4933fa88e68ab8a-wrr",
|
Service: "default-http-app-1-my-gateway-web-d737b4933fa88e68ab8a-wrr",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1039,6 +1048,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
"default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06": {
|
"default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06": {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
|
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1124,11 +1134,13 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-1-my-gateway-http-web-1c0cf64bde37d9d0df06-wrr",
|
Service: "default-http-app-1-my-gateway-http-web-1c0cf64bde37d9d0df06-wrr",
|
||||||
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"default-http-app-1-my-gateway-https-websecure-1c0cf64bde37d9d0df06": {
|
"default-http-app-1-my-gateway-https-websecure-1c0cf64bde37d9d0df06": {
|
||||||
EntryPoints: []string{"websecure"},
|
EntryPoints: []string{"websecure"},
|
||||||
Service: "default-http-app-1-my-gateway-https-websecure-1c0cf64bde37d9d0df06-wrr",
|
Service: "default-http-app-1-my-gateway-https-websecure-1c0cf64bde37d9d0df06-wrr",
|
||||||
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTLSConfig{},
|
TLS: &dynamic.RouterTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1213,11 +1225,13 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
|
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
|
||||||
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"default-http-app-1-my-gateway-websecure-1c0cf64bde37d9d0df06": {
|
"default-http-app-1-my-gateway-websecure-1c0cf64bde37d9d0df06": {
|
||||||
EntryPoints: []string{"websecure"},
|
EntryPoints: []string{"websecure"},
|
||||||
Service: "default-http-app-1-my-gateway-websecure-1c0cf64bde37d9d0df06-wrr",
|
Service: "default-http-app-1-my-gateway-websecure-1c0cf64bde37d9d0df06-wrr",
|
||||||
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
Rule: "Host(`foo.com`) && Path(`/bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTLSConfig{},
|
TLS: &dynamic.RouterTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1293,20 +1307,22 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
},
|
},
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Routers: map[string]*dynamic.Router{
|
Routers: map[string]*dynamic.Router{
|
||||||
"default-http-app-1-my-gateway-web-330d644a7f2079e8f454": {
|
"default-http-app-1-my-gateway-web-4a1b73e6f83804949a37": {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-1-my-gateway-web-330d644a7f2079e8f454-wrr",
|
Service: "default-http-app-1-my-gateway-web-4a1b73e6f83804949a37-wrr",
|
||||||
Rule: "Host(`foo.com`) && PathPrefix(`/bar`) && Headers(`my-header`,`foo`) && Headers(`my-header2`,`bar`)",
|
Rule: "Host(`foo.com`) && PathPrefix(`/bar`) && Header(`my-header`,`foo`) && Header(`my-header2`,`bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"default-http-app-1-my-gateway-web-fe80e69a38713941ea22": {
|
"default-http-app-1-my-gateway-web-aaba0f24fd26e1ca2276": {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-1-my-gateway-web-fe80e69a38713941ea22-wrr",
|
Service: "default-http-app-1-my-gateway-web-aaba0f24fd26e1ca2276-wrr",
|
||||||
Rule: "Host(`foo.com`) && Path(`/bar`) && Headers(`my-header`,`bar`)",
|
Rule: "Host(`foo.com`) && Path(`/bar`) && Header(`my-header`,`bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
Services: map[string]*dynamic.Service{
|
Services: map[string]*dynamic.Service{
|
||||||
"default-http-app-1-my-gateway-web-330d644a7f2079e8f454-wrr": {
|
"default-http-app-1-my-gateway-web-4a1b73e6f83804949a37-wrr": {
|
||||||
Weighted: &dynamic.WeightedRoundRobin{
|
Weighted: &dynamic.WeightedRoundRobin{
|
||||||
Services: []dynamic.WRRService{
|
Services: []dynamic.WRRService{
|
||||||
{
|
{
|
||||||
|
@ -1316,7 +1332,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"default-http-app-1-my-gateway-web-fe80e69a38713941ea22-wrr": {
|
"default-http-app-1-my-gateway-web-aaba0f24fd26e1ca2276-wrr": {
|
||||||
Weighted: &dynamic.WeightedRoundRobin{
|
Weighted: &dynamic.WeightedRoundRobin{
|
||||||
Services: []dynamic.WRRService{
|
Services: []dynamic.WRRService{
|
||||||
{
|
{
|
||||||
|
@ -1371,6 +1387,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-default-my-gateway-web-efde1997778109a1f6eb-wrr",
|
Service: "default-http-app-default-my-gateway-web-efde1997778109a1f6eb-wrr",
|
||||||
Rule: "Host(`foo.com`) && Path(`/foo`)",
|
Rule: "Host(`foo.com`) && Path(`/foo`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
@ -1430,11 +1447,13 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-default-my-gateway-web-efde1997778109a1f6eb-wrr",
|
Service: "default-http-app-default-my-gateway-web-efde1997778109a1f6eb-wrr",
|
||||||
Rule: "Host(`foo.com`) && Path(`/foo`)",
|
Rule: "Host(`foo.com`) && Path(`/foo`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"bar-http-app-bar-my-gateway-web-66f5c78d03d948e36597": {
|
"bar-http-app-bar-my-gateway-web-66f5c78d03d948e36597": {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "bar-http-app-bar-my-gateway-web-66f5c78d03d948e36597-wrr",
|
Service: "bar-http-app-bar-my-gateway-web-66f5c78d03d948e36597-wrr",
|
||||||
Rule: "Host(`bar.com`) && Path(`/bar`)",
|
Rule: "Host(`bar.com`) && Path(`/bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
@ -1520,6 +1539,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "bar-http-app-bar-my-gateway-web-66f5c78d03d948e36597-wrr",
|
Service: "bar-http-app-bar-my-gateway-web-66f5c78d03d948e36597-wrr",
|
||||||
Rule: "Host(`bar.com`) && Path(`/bar`)",
|
Rule: "Host(`bar.com`) && Path(`/bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.Middleware{},
|
Middlewares: map[string]*dynamic.Middleware{},
|
||||||
|
@ -1579,6 +1599,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-1-my-gateway-web-364ce6ec04c3d49b19c4-wrr",
|
Service: "default-http-app-1-my-gateway-web-364ce6ec04c3d49b19c4-wrr",
|
||||||
Rule: "Host(`example.org`) && PathPrefix(`/`)",
|
Rule: "Host(`example.org`) && PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
Middlewares: []string{"default-http-app-1-my-gateway-web-364ce6ec04c3d49b19c4-requestredirect-0"},
|
Middlewares: []string{"default-http-app-1-my-gateway-web-364ce6ec04c3d49b19c4-requestredirect-0"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1647,6 +1668,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-1-my-gateway-web-364ce6ec04c3d49b19c4-wrr",
|
Service: "default-http-app-1-my-gateway-web-364ce6ec04c3d49b19c4-wrr",
|
||||||
Rule: "Host(`example.org`) && PathPrefix(`/`)",
|
Rule: "Host(`example.org`) && PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
Middlewares: []string{"default-http-app-1-my-gateway-web-364ce6ec04c3d49b19c4-requestredirect-0"},
|
Middlewares: []string{"default-http-app-1-my-gateway-web-364ce6ec04c3d49b19c4-requestredirect-0"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1912,6 +1934,7 @@ func TestLoadTCPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "default-tcp-app-1-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-1-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||||
|
@ -1969,11 +1992,13 @@ func TestLoadTCPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp-1"},
|
EntryPoints: []string{"tcp-1"},
|
||||||
Service: "default-tcp-app-1-my-tcp-gateway-tcp-1-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-1-my-tcp-gateway-tcp-1-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"default-tcp-app-2-my-tcp-gateway-tcp-2-e3b0c44298fc1c149afb": {
|
"default-tcp-app-2-my-tcp-gateway-tcp-2-e3b0c44298fc1c149afb": {
|
||||||
EntryPoints: []string{"tcp-2"},
|
EntryPoints: []string{"tcp-2"},
|
||||||
Service: "default-tcp-app-2-my-tcp-gateway-tcp-2-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-2-my-tcp-gateway-tcp-2-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||||
|
@ -2053,6 +2078,7 @@ func TestLoadTCPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp-1"},
|
EntryPoints: []string{"tcp-1"},
|
||||||
Service: "default-tcp-app-my-tcp-gateway-tcp-1-e3b0c44298fc1c149afb-wrr",
|
Service: "default-tcp-app-my-tcp-gateway-tcp-1-e3b0c44298fc1c149afb-wrr",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||||
|
@ -2144,6 +2170,7 @@ func TestLoadTCPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "default-tcp-app-1-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-1-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||||
|
@ -2203,6 +2230,7 @@ func TestLoadTCPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tls"},
|
EntryPoints: []string{"tls"},
|
||||||
Service: "default-tcp-app-1-my-gateway-tls-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-1-my-gateway-tls-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{},
|
TLS: &dynamic.RouterTCPTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -2266,6 +2294,7 @@ func TestLoadTCPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "default-tcp-app-default-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-default-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||||
|
@ -2321,11 +2350,13 @@ func TestLoadTCPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "default-tcp-app-default-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-default-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"bar-tcp-app-bar-my-tcp-gateway-tcp-e3b0c44298fc1c149afb": {
|
"bar-tcp-app-bar-my-tcp-gateway-tcp-e3b0c44298fc1c149afb": {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "bar-tcp-app-bar-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
Service: "bar-tcp-app-bar-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||||
|
@ -2403,6 +2434,7 @@ func TestLoadTCPRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "bar-tcp-app-bar-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
Service: "bar-tcp-app-bar-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||||
|
@ -2696,6 +2728,7 @@ func TestLoadTLSRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "default-tcp-app-1-my-tls-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-1-my-tls-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{},
|
TLS: &dynamic.RouterTCPTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -2761,6 +2794,7 @@ func TestLoadTLSRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "default-tcp-app-1-my-tls-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-1-my-tls-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -2819,6 +2853,7 @@ func TestLoadTLSRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "default-tls-app-1-my-tls-gateway-tcp-f0dd0dd89f82eae1c270-wrr-0",
|
Service: "default-tls-app-1-my-tls-gateway-tcp-f0dd0dd89f82eae1c270-wrr-0",
|
||||||
Rule: "HostSNI(`foo.example.com`)",
|
Rule: "HostSNI(`foo.example.com`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -2878,12 +2913,14 @@ func TestLoadTLSRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tls"},
|
EntryPoints: []string{"tls"},
|
||||||
Service: "default-tcp-app-1-my-tls-gateway-tls-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-1-my-tls-gateway-tls-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{},
|
TLS: &dynamic.RouterTCPTLSConfig{},
|
||||||
},
|
},
|
||||||
"default-tls-app-1-my-tls-gateway-tcp-673acf455cb2dab0b43a": {
|
"default-tls-app-1-my-tls-gateway-tcp-673acf455cb2dab0b43a": {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "default-tls-app-1-my-tls-gateway-tcp-673acf455cb2dab0b43a-wrr-0",
|
Service: "default-tls-app-1-my-tls-gateway-tcp-673acf455cb2dab0b43a-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -2973,6 +3010,7 @@ func TestLoadTLSRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tls"},
|
EntryPoints: []string{"tls"},
|
||||||
Service: "default-tcp-app-1-my-gateway-tls-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-1-my-gateway-tls-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{},
|
TLS: &dynamic.RouterTCPTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -3042,6 +3080,7 @@ func TestLoadTLSRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tls"},
|
EntryPoints: []string{"tls"},
|
||||||
Service: "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0",
|
Service: "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0",
|
||||||
Rule: "HostSNI(`foo.example.com`)",
|
Rule: "HostSNI(`foo.example.com`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -3100,6 +3139,7 @@ func TestLoadTLSRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tls"},
|
EntryPoints: []string{"tls"},
|
||||||
Service: "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0",
|
Service: "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0",
|
||||||
Rule: "HostSNI(`foo.example.com`)",
|
Rule: "HostSNI(`foo.example.com`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -3158,6 +3198,7 @@ func TestLoadTLSRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tls"},
|
EntryPoints: []string{"tls"},
|
||||||
Service: "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0",
|
Service: "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0",
|
||||||
Rule: "HostSNI(`foo.example.com`)",
|
Rule: "HostSNI(`foo.example.com`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -3216,6 +3257,7 @@ func TestLoadTLSRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tls"},
|
EntryPoints: []string{"tls"},
|
||||||
Service: "default-tls-app-1-my-gateway-tls-d5342d75658583f03593-wrr-0",
|
Service: "default-tls-app-1-my-gateway-tls-d5342d75658583f03593-wrr-0",
|
||||||
Rule: "HostSNI(`foo.example.com`) || HostSNI(`bar.example.com`)",
|
Rule: "HostSNI(`foo.example.com`) || HostSNI(`bar.example.com`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -3274,6 +3316,7 @@ func TestLoadTLSRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tls"},
|
EntryPoints: []string{"tls"},
|
||||||
Service: "default-tls-app-default-my-gateway-tls-06ae57dcf13ab4c60ee5-wrr-0",
|
Service: "default-tls-app-default-my-gateway-tls-06ae57dcf13ab4c60ee5-wrr-0",
|
||||||
Rule: "HostSNI(`foo.default`)",
|
Rule: "HostSNI(`foo.default`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -3332,6 +3375,7 @@ func TestLoadTLSRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tls"},
|
EntryPoints: []string{"tls"},
|
||||||
Service: "default-tls-app-default-my-gateway-tls-06ae57dcf13ab4c60ee5-wrr-0",
|
Service: "default-tls-app-default-my-gateway-tls-06ae57dcf13ab4c60ee5-wrr-0",
|
||||||
Rule: "HostSNI(`foo.default`)",
|
Rule: "HostSNI(`foo.default`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -3340,6 +3384,7 @@ func TestLoadTLSRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tls"},
|
EntryPoints: []string{"tls"},
|
||||||
Service: "bar-tls-app-bar-my-gateway-tls-2279fe75c5156dc5eb26-wrr-0",
|
Service: "bar-tls-app-bar-my-gateway-tls-2279fe75c5156dc5eb26-wrr-0",
|
||||||
Rule: "HostSNI(`foo.bar`)",
|
Rule: "HostSNI(`foo.bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -3420,6 +3465,7 @@ func TestLoadTLSRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tls"},
|
EntryPoints: []string{"tls"},
|
||||||
Service: "bar-tls-app-bar-my-gateway-tls-2279fe75c5156dc5eb26-wrr-0",
|
Service: "bar-tls-app-bar-my-gateway-tls-2279fe75c5156dc5eb26-wrr-0",
|
||||||
Rule: "HostSNI(`foo.bar`)",
|
Rule: "HostSNI(`foo.bar`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -3478,6 +3524,7 @@ func TestLoadTLSRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp-1"},
|
EntryPoints: []string{"tcp-1"},
|
||||||
Service: "default-tls-app-my-gateway-tcp-1-673acf455cb2dab0b43a-wrr",
|
Service: "default-tls-app-my-gateway-tcp-1-673acf455cb2dab0b43a-wrr",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -3702,17 +3749,20 @@ func TestLoadMixedRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "default-tcp-app-1-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-1-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"default-tcp-app-1-my-gateway-tls-1-e3b0c44298fc1c149afb": {
|
"default-tcp-app-1-my-gateway-tls-1-e3b0c44298fc1c149afb": {
|
||||||
EntryPoints: []string{"tls-1"},
|
EntryPoints: []string{"tls-1"},
|
||||||
Service: "default-tcp-app-1-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-1-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{},
|
TLS: &dynamic.RouterTCPTLSConfig{},
|
||||||
},
|
},
|
||||||
"default-tls-app-1-my-gateway-tls-2-59130f7db6718b7700c1": {
|
"default-tls-app-1-my-gateway-tls-2-59130f7db6718b7700c1": {
|
||||||
EntryPoints: []string{"tls-2"},
|
EntryPoints: []string{"tls-2"},
|
||||||
Service: "default-tls-app-1-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0",
|
Service: "default-tls-app-1-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0",
|
||||||
Rule: "HostSNI(`pass.tls.foo.example.com`)",
|
Rule: "HostSNI(`pass.tls.foo.example.com`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -3771,11 +3821,13 @@ func TestLoadMixedRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-1-my-gateway-web-a431b128267aabc954fd-wrr",
|
Service: "default-http-app-1-my-gateway-web-a431b128267aabc954fd-wrr",
|
||||||
Rule: "PathPrefix(`/`)",
|
Rule: "PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"default-http-app-1-my-gateway-websecure-a431b128267aabc954fd": {
|
"default-http-app-1-my-gateway-websecure-a431b128267aabc954fd": {
|
||||||
EntryPoints: []string{"websecure"},
|
EntryPoints: []string{"websecure"},
|
||||||
Service: "default-http-app-1-my-gateway-websecure-a431b128267aabc954fd-wrr",
|
Service: "default-http-app-1-my-gateway-websecure-a431b128267aabc954fd-wrr",
|
||||||
Rule: "PathPrefix(`/`)",
|
Rule: "PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTLSConfig{},
|
TLS: &dynamic.RouterTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -3881,17 +3933,20 @@ func TestLoadMixedRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "default-tcp-app-default-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-default-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"default-tcp-app-default-my-gateway-tls-1-e3b0c44298fc1c149afb": {
|
"default-tcp-app-default-my-gateway-tls-1-e3b0c44298fc1c149afb": {
|
||||||
EntryPoints: []string{"tls-1"},
|
EntryPoints: []string{"tls-1"},
|
||||||
Service: "default-tcp-app-default-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-default-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{},
|
TLS: &dynamic.RouterTCPTLSConfig{},
|
||||||
},
|
},
|
||||||
"default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1": {
|
"default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1": {
|
||||||
EntryPoints: []string{"tls-2"},
|
EntryPoints: []string{"tls-2"},
|
||||||
Service: "default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0",
|
Service: "default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0",
|
||||||
Rule: "HostSNI(`pass.tls.foo.example.com`)",
|
Rule: "HostSNI(`pass.tls.foo.example.com`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -3950,11 +4005,13 @@ func TestLoadMixedRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-default-my-gateway-web-a431b128267aabc954fd-wrr",
|
Service: "default-http-app-default-my-gateway-web-a431b128267aabc954fd-wrr",
|
||||||
Rule: "PathPrefix(`/`)",
|
Rule: "PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"default-http-app-default-my-gateway-websecure-a431b128267aabc954fd": {
|
"default-http-app-default-my-gateway-websecure-a431b128267aabc954fd": {
|
||||||
EntryPoints: []string{"websecure"},
|
EntryPoints: []string{"websecure"},
|
||||||
Service: "default-http-app-default-my-gateway-websecure-a431b128267aabc954fd-wrr",
|
Service: "default-http-app-default-my-gateway-websecure-a431b128267aabc954fd-wrr",
|
||||||
Rule: "PathPrefix(`/`)",
|
Rule: "PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTLSConfig{},
|
TLS: &dynamic.RouterTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -4032,17 +4089,20 @@ func TestLoadMixedRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "default-tcp-app-default-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-default-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"default-tcp-app-default-my-gateway-tls-1-e3b0c44298fc1c149afb": {
|
"default-tcp-app-default-my-gateway-tls-1-e3b0c44298fc1c149afb": {
|
||||||
EntryPoints: []string{"tls-1"},
|
EntryPoints: []string{"tls-1"},
|
||||||
Service: "default-tcp-app-default-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-default-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{},
|
TLS: &dynamic.RouterTCPTLSConfig{},
|
||||||
},
|
},
|
||||||
"default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1": {
|
"default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1": {
|
||||||
EntryPoints: []string{"tls-2"},
|
EntryPoints: []string{"tls-2"},
|
||||||
Service: "default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0",
|
Service: "default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0",
|
||||||
Rule: "HostSNI(`pass.tls.foo.example.com`)",
|
Rule: "HostSNI(`pass.tls.foo.example.com`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -4051,11 +4111,13 @@ func TestLoadMixedRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "bar-tcp-app-bar-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
Service: "bar-tcp-app-bar-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"bar-tcp-app-bar-my-gateway-tls-1-e3b0c44298fc1c149afb": {
|
"bar-tcp-app-bar-my-gateway-tls-1-e3b0c44298fc1c149afb": {
|
||||||
EntryPoints: []string{"tls-1"},
|
EntryPoints: []string{"tls-1"},
|
||||||
Service: "bar-tcp-app-bar-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
|
Service: "bar-tcp-app-bar-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{},
|
TLS: &dynamic.RouterTCPTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -4144,22 +4206,26 @@ func TestLoadMixedRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-default-my-gateway-web-a431b128267aabc954fd-wrr",
|
Service: "default-http-app-default-my-gateway-web-a431b128267aabc954fd-wrr",
|
||||||
Rule: "PathPrefix(`/`)",
|
Rule: "PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"default-http-app-default-my-gateway-websecure-a431b128267aabc954fd": {
|
"default-http-app-default-my-gateway-websecure-a431b128267aabc954fd": {
|
||||||
EntryPoints: []string{"websecure"},
|
EntryPoints: []string{"websecure"},
|
||||||
Service: "default-http-app-default-my-gateway-websecure-a431b128267aabc954fd-wrr",
|
Service: "default-http-app-default-my-gateway-websecure-a431b128267aabc954fd-wrr",
|
||||||
Rule: "PathPrefix(`/`)",
|
Rule: "PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTLSConfig{},
|
TLS: &dynamic.RouterTLSConfig{},
|
||||||
},
|
},
|
||||||
"bar-http-app-bar-my-gateway-web-a431b128267aabc954fd": {
|
"bar-http-app-bar-my-gateway-web-a431b128267aabc954fd": {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "bar-http-app-bar-my-gateway-web-a431b128267aabc954fd-wrr",
|
Service: "bar-http-app-bar-my-gateway-web-a431b128267aabc954fd-wrr",
|
||||||
Rule: "PathPrefix(`/`)",
|
Rule: "PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"bar-http-app-bar-my-gateway-websecure-a431b128267aabc954fd": {
|
"bar-http-app-bar-my-gateway-websecure-a431b128267aabc954fd": {
|
||||||
EntryPoints: []string{"websecure"},
|
EntryPoints: []string{"websecure"},
|
||||||
Service: "bar-http-app-bar-my-gateway-websecure-a431b128267aabc954fd-wrr",
|
Service: "bar-http-app-bar-my-gateway-websecure-a431b128267aabc954fd-wrr",
|
||||||
Rule: "PathPrefix(`/`)",
|
Rule: "PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTLSConfig{},
|
TLS: &dynamic.RouterTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -4273,17 +4339,20 @@ func TestLoadMixedRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "bar-tcp-app-bar-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
Service: "bar-tcp-app-bar-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"bar-tcp-app-bar-my-gateway-tls-1-e3b0c44298fc1c149afb": {
|
"bar-tcp-app-bar-my-gateway-tls-1-e3b0c44298fc1c149afb": {
|
||||||
EntryPoints: []string{"tls-1"},
|
EntryPoints: []string{"tls-1"},
|
||||||
Service: "bar-tcp-app-bar-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
|
Service: "bar-tcp-app-bar-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{},
|
TLS: &dynamic.RouterTCPTLSConfig{},
|
||||||
},
|
},
|
||||||
"bar-tls-app-bar-my-gateway-tls-2-59130f7db6718b7700c1": {
|
"bar-tls-app-bar-my-gateway-tls-2-59130f7db6718b7700c1": {
|
||||||
EntryPoints: []string{"tls-2"},
|
EntryPoints: []string{"tls-2"},
|
||||||
Service: "bar-tls-app-bar-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0",
|
Service: "bar-tls-app-bar-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0",
|
||||||
Rule: "HostSNI(`pass.tls.foo.example.com`)",
|
Rule: "HostSNI(`pass.tls.foo.example.com`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{
|
TLS: &dynamic.RouterTCPTLSConfig{
|
||||||
Passthrough: true,
|
Passthrough: true,
|
||||||
},
|
},
|
||||||
|
@ -4342,11 +4411,13 @@ func TestLoadMixedRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "bar-http-app-bar-my-gateway-web-a431b128267aabc954fd-wrr",
|
Service: "bar-http-app-bar-my-gateway-web-a431b128267aabc954fd-wrr",
|
||||||
Rule: "PathPrefix(`/`)",
|
Rule: "PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"bar-http-app-bar-my-gateway-websecure-a431b128267aabc954fd": {
|
"bar-http-app-bar-my-gateway-websecure-a431b128267aabc954fd": {
|
||||||
EntryPoints: []string{"websecure"},
|
EntryPoints: []string{"websecure"},
|
||||||
Service: "bar-http-app-bar-my-gateway-websecure-a431b128267aabc954fd-wrr",
|
Service: "bar-http-app-bar-my-gateway-websecure-a431b128267aabc954fd-wrr",
|
||||||
Rule: "PathPrefix(`/`)",
|
Rule: "PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTLSConfig{},
|
TLS: &dynamic.RouterTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -4423,11 +4494,13 @@ func TestLoadMixedRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"tcp"},
|
EntryPoints: []string{"tcp"},
|
||||||
Service: "default-tcp-app-default-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-default-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"default-tcp-app-default-my-gateway-tls-e3b0c44298fc1c149afb": {
|
"default-tcp-app-default-my-gateway-tls-e3b0c44298fc1c149afb": {
|
||||||
EntryPoints: []string{"tls"},
|
EntryPoints: []string{"tls"},
|
||||||
Service: "default-tcp-app-default-my-gateway-tls-e3b0c44298fc1c149afb-wrr-0",
|
Service: "default-tcp-app-default-my-gateway-tls-e3b0c44298fc1c149afb-wrr-0",
|
||||||
Rule: "HostSNI(`*`)",
|
Rule: "HostSNI(`*`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTCPTLSConfig{},
|
TLS: &dynamic.RouterTCPTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -4474,11 +4547,13 @@ func TestLoadMixedRoutes(t *testing.T) {
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "default-http-app-default-my-gateway-web-a431b128267aabc954fd-wrr",
|
Service: "default-http-app-default-my-gateway-web-a431b128267aabc954fd-wrr",
|
||||||
Rule: "PathPrefix(`/`)",
|
Rule: "PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
},
|
},
|
||||||
"default-http-app-default-my-gateway-websecure-a431b128267aabc954fd": {
|
"default-http-app-default-my-gateway-websecure-a431b128267aabc954fd": {
|
||||||
EntryPoints: []string{"websecure"},
|
EntryPoints: []string{"websecure"},
|
||||||
Service: "default-http-app-default-my-gateway-websecure-a431b128267aabc954fd-wrr",
|
Service: "default-http-app-default-my-gateway-websecure-a431b128267aabc954fd-wrr",
|
||||||
Rule: "PathPrefix(`/`)",
|
Rule: "PathPrefix(`/`)",
|
||||||
|
RuleSyntax: "v3",
|
||||||
TLS: &dynamic.RouterTLSConfig{},
|
TLS: &dynamic.RouterTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -4807,7 +4882,7 @@ func Test_extractRule(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRule: "Path(`/foo/`) || Headers(`my-header`,`foo`)",
|
expectedRule: "Path(`/foo/`) || Header(`my-header`,`foo`)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Path && Header rules",
|
desc: "Path && Header rules",
|
||||||
|
@ -4828,7 +4903,7 @@ func Test_extractRule(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRule: "Path(`/foo/`) && Headers(`my-header`,`foo`)",
|
expectedRule: "Path(`/foo/`) && Header(`my-header`,`foo`)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Host && Path && Header rules",
|
desc: "Host && Path && Header rules",
|
||||||
|
@ -4850,7 +4925,7 @@ func Test_extractRule(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRule: "Host(`foo.com`) && Path(`/foo/`) && Headers(`my-header`,`foo`)",
|
expectedRule: "Host(`foo.com`) && Path(`/foo/`) && Header(`my-header`,`foo`)",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Host && (Path || Header) rules",
|
desc: "Host && (Path || Header) rules",
|
||||||
|
@ -4874,7 +4949,7 @@ func Test_extractRule(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRule: "Host(`foo.com`) && (Path(`/foo/`) || Headers(`my-header`,`foo`))",
|
expectedRule: "Host(`foo.com`) && (Path(`/foo/`) || Header(`my-header`,`foo`))",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,11 @@
|
||||||
"priority": 2147483645
|
"priority": 2147483645
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"services": {
|
||||||
|
"api": {},
|
||||||
|
"dashboard": {},
|
||||||
|
"noop": {}
|
||||||
|
},
|
||||||
"middlewares": {
|
"middlewares": {
|
||||||
"dashboard_redirect": {
|
"dashboard_redirect": {
|
||||||
"redirectRegex": {
|
"redirectRegex": {
|
||||||
|
@ -38,11 +43,6 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"services": {
|
|
||||||
"api": {},
|
|
||||||
"dashboard": {},
|
|
||||||
"noop": {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tcp": {},
|
"tcp": {},
|
||||||
|
|
|
@ -54,6 +54,14 @@
|
||||||
"priority": 2147483647
|
"priority": 2147483647
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"services": {
|
||||||
|
"api": {},
|
||||||
|
"dashboard": {},
|
||||||
|
"noop": {},
|
||||||
|
"ping": {},
|
||||||
|
"prometheus": {},
|
||||||
|
"rest": {}
|
||||||
|
},
|
||||||
"middlewares": {
|
"middlewares": {
|
||||||
"dashboard_redirect": {
|
"dashboard_redirect": {
|
||||||
"redirectRegex": {
|
"redirectRegex": {
|
||||||
|
@ -70,14 +78,6 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"services": {
|
|
||||||
"api": {},
|
|
||||||
"dashboard": {},
|
|
||||||
"noop": {},
|
|
||||||
"ping": {},
|
|
||||||
"prometheus": {},
|
|
||||||
"rest": {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tcp": {},
|
"tcp": {},
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
"rule": "HostRegexp(`^.+$`)"
|
"rule": "HostRegexp(`^.+$`)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"services": {
|
||||||
|
"noop": {}
|
||||||
|
},
|
||||||
"middlewares": {
|
"middlewares": {
|
||||||
"redirect-web-to-websecure": {
|
"redirect-web-to-websecure": {
|
||||||
"redirectScheme": {
|
"redirectScheme": {
|
||||||
|
@ -20,9 +23,6 @@
|
||||||
"permanent": true
|
"permanent": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"services": {
|
|
||||||
"noop": {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tcp": {},
|
"tcp": {},
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
"rule": "HostRegexp(`^.+$`)"
|
"rule": "HostRegexp(`^.+$`)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"services": {
|
||||||
|
"noop": {}
|
||||||
|
},
|
||||||
"middlewares": {
|
"middlewares": {
|
||||||
"redirect-web-to-443": {
|
"redirect-web-to-443": {
|
||||||
"redirectScheme": {
|
"redirectScheme": {
|
||||||
|
@ -20,9 +23,6 @@
|
||||||
"permanent": true
|
"permanent": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"services": {
|
|
||||||
"noop": {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tcp": {},
|
"tcp": {},
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
"rule": "HostRegexp(`^.+$`)"
|
"rule": "HostRegexp(`^.+$`)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"services": {
|
||||||
|
"noop": {}
|
||||||
|
},
|
||||||
"middlewares": {
|
"middlewares": {
|
||||||
"redirect-web-to-websecure": {
|
"redirect-web-to-websecure": {
|
||||||
"redirectScheme": {
|
"redirectScheme": {
|
||||||
|
@ -20,9 +23,6 @@
|
||||||
"permanent": true
|
"permanent": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"services": {
|
|
||||||
"noop": {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tcp": {},
|
"tcp": {},
|
||||||
|
|
|
@ -65,6 +65,7 @@ func (i *Provider) createConfiguration(ctx context.Context) *dynamic.Configurati
|
||||||
TCP: &dynamic.TCPConfiguration{
|
TCP: &dynamic.TCPConfiguration{
|
||||||
Routers: make(map[string]*dynamic.TCPRouter),
|
Routers: make(map[string]*dynamic.TCPRouter),
|
||||||
Services: make(map[string]*dynamic.TCPService),
|
Services: make(map[string]*dynamic.TCPService),
|
||||||
|
Models: make(map[string]*dynamic.TCPModel),
|
||||||
ServersTransports: make(map[string]*dynamic.TCPServersTransport),
|
ServersTransports: make(map[string]*dynamic.TCPServersTransport),
|
||||||
},
|
},
|
||||||
TLS: &dynamic.TLSConfiguration{
|
TLS: &dynamic.TLSConfiguration{
|
||||||
|
@ -191,8 +192,13 @@ func (i *Provider) getEntryPointPort(name string, def *static.Redirections) (str
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Provider) entryPointModels(cfg *dynamic.Configuration) {
|
func (i *Provider) entryPointModels(cfg *dynamic.Configuration) {
|
||||||
|
defaultRuleSyntax := ""
|
||||||
|
if i.staticCfg.Core != nil && i.staticCfg.Core.DefaultRuleSyntax != "" {
|
||||||
|
defaultRuleSyntax = i.staticCfg.Core.DefaultRuleSyntax
|
||||||
|
}
|
||||||
|
|
||||||
for name, ep := range i.staticCfg.EntryPoints {
|
for name, ep := range i.staticCfg.EntryPoints {
|
||||||
if len(ep.HTTP.Middlewares) == 0 && ep.HTTP.TLS == nil {
|
if len(ep.HTTP.Middlewares) == 0 && ep.HTTP.TLS == nil && defaultRuleSyntax == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +214,19 @@ func (i *Provider) entryPointModels(cfg *dynamic.Configuration) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.DefaultRuleSyntax = defaultRuleSyntax
|
||||||
|
|
||||||
cfg.HTTP.Models[name] = m
|
cfg.HTTP.Models[name] = m
|
||||||
|
|
||||||
|
if cfg.TCP == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mTCP := &dynamic.TCPModel{
|
||||||
|
DefaultRuleSyntax: defaultRuleSyntax,
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.TCP.Models[name] = mTCP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint
|
||||||
Routers: make(map[string]*dynamic.TCPRouter),
|
Routers: make(map[string]*dynamic.TCPRouter),
|
||||||
Services: make(map[string]*dynamic.TCPService),
|
Services: make(map[string]*dynamic.TCPService),
|
||||||
Middlewares: make(map[string]*dynamic.TCPMiddleware),
|
Middlewares: make(map[string]*dynamic.TCPMiddleware),
|
||||||
|
Models: make(map[string]*dynamic.TCPModel),
|
||||||
ServersTransports: make(map[string]*dynamic.TCPServersTransport),
|
ServersTransports: make(map[string]*dynamic.TCPServersTransport),
|
||||||
},
|
},
|
||||||
UDP: &dynamic.UDPConfiguration{
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
@ -152,6 +153,13 @@ func applyModel(cfg dynamic.Configuration) dynamic.Configuration {
|
||||||
for name, rt := range cfg.HTTP.Routers {
|
for name, rt := range cfg.HTTP.Routers {
|
||||||
router := rt.DeepCopy()
|
router := rt.DeepCopy()
|
||||||
|
|
||||||
|
if !router.DefaultRule && router.RuleSyntax == "" {
|
||||||
|
for _, model := range cfg.HTTP.Models {
|
||||||
|
router.RuleSyntax = model.DefaultRuleSyntax
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
eps := router.EntryPoints
|
eps := router.EntryPoints
|
||||||
router.EntryPoints = nil
|
router.EntryPoints = nil
|
||||||
|
|
||||||
|
@ -183,6 +191,25 @@ func applyModel(cfg dynamic.Configuration) dynamic.Configuration {
|
||||||
|
|
||||||
cfg.HTTP.Routers = rts
|
cfg.HTTP.Routers = rts
|
||||||
|
|
||||||
|
if cfg.TCP == nil || len(cfg.TCP.Models) == 0 {
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
tcpRouters := make(map[string]*dynamic.TCPRouter)
|
||||||
|
|
||||||
|
for _, rt := range cfg.TCP.Routers {
|
||||||
|
router := rt.DeepCopy()
|
||||||
|
|
||||||
|
if router.RuleSyntax == "" {
|
||||||
|
for _, model := range cfg.TCP.Models {
|
||||||
|
router.RuleSyntax = model.DefaultRuleSyntax
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.TCP.Routers = tcpRouters
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -473,6 +473,7 @@ func Test_mergeConfiguration_defaultTCPEntryPoint(t *testing.T) {
|
||||||
Services: map[string]*dynamic.TCPService{
|
Services: map[string]*dynamic.TCPService{
|
||||||
"service-1@provider-1": {},
|
"service-1@provider-1": {},
|
||||||
},
|
},
|
||||||
|
Models: map[string]*dynamic.TCPModel{},
|
||||||
ServersTransports: make(map[string]*dynamic.TCPServersTransport),
|
ServersTransports: make(map[string]*dynamic.TCPServersTransport),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,7 @@ func TestNewConfigurationWatcher(t *testing.T) {
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||||
Services: map[string]*dynamic.TCPService{},
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
Models: map[string]*dynamic.TCPModel{},
|
||||||
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
||||||
},
|
},
|
||||||
TLS: &dynamic.TLSConfiguration{
|
TLS: &dynamic.TLSConfiguration{
|
||||||
|
@ -231,6 +232,7 @@ func TestIgnoreTransientConfiguration(t *testing.T) {
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||||
Services: map[string]*dynamic.TCPService{},
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
Models: map[string]*dynamic.TCPModel{},
|
||||||
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
||||||
},
|
},
|
||||||
UDP: &dynamic.UDPConfiguration{
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
@ -400,6 +402,7 @@ func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) {
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||||
Services: map[string]*dynamic.TCPService{},
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
Models: map[string]*dynamic.TCPModel{},
|
||||||
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
||||||
},
|
},
|
||||||
UDP: &dynamic.UDPConfiguration{
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
@ -490,6 +493,7 @@ func TestListenProvidersIgnoreSameConfig(t *testing.T) {
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||||
Services: map[string]*dynamic.TCPService{},
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
Models: map[string]*dynamic.TCPModel{},
|
||||||
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
||||||
},
|
},
|
||||||
UDP: &dynamic.UDPConfiguration{
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
@ -625,6 +629,7 @@ func TestListenProvidersIgnoreIntermediateConfigs(t *testing.T) {
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||||
Services: map[string]*dynamic.TCPService{},
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
Models: map[string]*dynamic.TCPModel{},
|
||||||
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
||||||
},
|
},
|
||||||
UDP: &dynamic.UDPConfiguration{
|
UDP: &dynamic.UDPConfiguration{
|
||||||
|
@ -693,6 +698,7 @@ func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) {
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||||
Services: map[string]*dynamic.TCPService{},
|
Services: map[string]*dynamic.TCPService{},
|
||||||
|
Models: map[string]*dynamic.TCPModel{},
|
||||||
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
||||||
},
|
},
|
||||||
TLS: &dynamic.TLSConfiguration{
|
TLS: &dynamic.TLSConfiguration{
|
||||||
|
|
|
@ -131,7 +131,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = muxer.AddRoute(routerConfig.Rule, routerConfig.Priority, handler); err != nil {
|
if err = muxer.AddRoute(routerConfig.Rule, routerConfig.RuleSyntax, routerConfig.Priority, handler); err != nil {
|
||||||
routerConfig.AddError(err, true)
|
routerConfig.AddError(err, true)
|
||||||
logger.Error().Err(err).Send()
|
logger.Error().Err(err).Send()
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -311,7 +311,7 @@ func (m *Manager) addTCPHandlers(ctx context.Context, configs map[string]*runtim
|
||||||
if routerConfig.TLS == nil {
|
if routerConfig.TLS == nil {
|
||||||
logger.Debug().Msgf("Adding route for %q", routerConfig.Rule)
|
logger.Debug().Msgf("Adding route for %q", routerConfig.Rule)
|
||||||
|
|
||||||
if err := router.AddRoute(routerConfig.Rule, routerConfig.Priority, handler); err != nil {
|
if err := router.muxerTCP.AddRoute(routerConfig.Rule, routerConfig.RuleSyntax, routerConfig.Priority, handler); err != nil {
|
||||||
routerConfig.AddError(err, true)
|
routerConfig.AddError(err, true)
|
||||||
logger.Error().Err(err).Send()
|
logger.Error().Err(err).Send()
|
||||||
}
|
}
|
||||||
|
@ -321,7 +321,7 @@ func (m *Manager) addTCPHandlers(ctx context.Context, configs map[string]*runtim
|
||||||
if routerConfig.TLS.Passthrough {
|
if routerConfig.TLS.Passthrough {
|
||||||
logger.Debug().Msgf("Adding Passthrough route for %q", routerConfig.Rule)
|
logger.Debug().Msgf("Adding Passthrough route for %q", routerConfig.Rule)
|
||||||
|
|
||||||
if err := router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.Priority, handler); err != nil {
|
if err := router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.RuleSyntax, routerConfig.Priority, handler); err != nil {
|
||||||
routerConfig.AddError(err, true)
|
routerConfig.AddError(err, true)
|
||||||
logger.Error().Err(err).Send()
|
logger.Error().Err(err).Send()
|
||||||
}
|
}
|
||||||
|
@ -355,7 +355,7 @@ func (m *Manager) addTCPHandlers(ctx context.Context, configs map[string]*runtim
|
||||||
|
|
||||||
logger.Debug().Msgf("Adding special TLS closing route for %q because broken TLS options %s", routerConfig.Rule, tlsOptionsName)
|
logger.Debug().Msgf("Adding special TLS closing route for %q because broken TLS options %s", routerConfig.Rule, tlsOptionsName)
|
||||||
|
|
||||||
if err := router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.Priority, &brokenTLSRouter{}); err != nil {
|
if err := router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.RuleSyntax, routerConfig.Priority, &brokenTLSRouter{}); err != nil {
|
||||||
routerConfig.AddError(err, true)
|
routerConfig.AddError(err, true)
|
||||||
logger.Error().Err(err).Send()
|
logger.Error().Err(err).Send()
|
||||||
}
|
}
|
||||||
|
@ -389,7 +389,7 @@ func (m *Manager) addTCPHandlers(ctx context.Context, configs map[string]*runtim
|
||||||
|
|
||||||
logger.Debug().Msgf("Adding TLS route for %q", routerConfig.Rule)
|
logger.Debug().Msgf("Adding TLS route for %q", routerConfig.Rule)
|
||||||
|
|
||||||
if err := router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.Priority, handler); err != nil {
|
if err := router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.RuleSyntax, routerConfig.Priority, handler); err != nil {
|
||||||
routerConfig.AddError(err, true)
|
routerConfig.AddError(err, true)
|
||||||
logger.Error().Err(err).Send()
|
logger.Error().Err(err).Send()
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -201,9 +201,9 @@ func (r *Router) ServeTCP(conn tcp.WriteCloser) {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddRoute defines a handler for the given rule.
|
// AddTCPRoute defines a handler for the given rule.
|
||||||
func (r *Router) AddRoute(rule string, priority int, target tcp.Handler) error {
|
func (r *Router) AddTCPRoute(rule string, priority int, target tcp.Handler) error {
|
||||||
return r.muxerTCP.AddRoute(rule, priority, target)
|
return r.muxerTCP.AddRoute(rule, "", priority, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddHTTPTLSConfig defines a handler for a given sniHost and sets the matching tlsConfig.
|
// AddHTTPTLSConfig defines a handler for a given sniHost and sets the matching tlsConfig.
|
||||||
|
@ -267,7 +267,7 @@ func (r *Router) SetHTTPSForwarder(handler tcp.Handler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rule := "HostSNI(`" + sniHost + "`)"
|
rule := "HostSNI(`" + sniHost + "`)"
|
||||||
if err := r.muxerHTTPS.AddRoute(rule, tcpmuxer.GetRulePriority(rule), tcpHandler); err != nil {
|
if err := r.muxerHTTPS.AddRoute(rule, "", tcpmuxer.GetRulePriority(rule), tcpHandler); err != nil {
|
||||||
log.Error().Err(err).Msg("Error while adding route for host")
|
log.Error().Err(err).Msg("Error while adding route for host")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -947,10 +947,10 @@ func TestPostgres(t *testing.T) {
|
||||||
// This test requires to have a TLS route, but does not actually check the
|
// This test requires to have a TLS route, but does not actually check the
|
||||||
// content of the handler. It would require to code a TLS handshake to
|
// content of the handler. It would require to code a TLS handshake to
|
||||||
// check the SNI and content of the handlerFunc.
|
// check the SNI and content of the handlerFunc.
|
||||||
err = router.muxerTCPTLS.AddRoute("HostSNI(`test.localhost`)", 0, nil)
|
err = router.muxerTCPTLS.AddRoute("HostSNI(`test.localhost`)", "", 0, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = router.AddRoute("HostSNI(`*`)", 0, tcp2.HandlerFunc(func(conn tcp2.WriteCloser) {
|
err = router.muxerTCP.AddRoute("HostSNI(`*`)", "", 0, tcp2.HandlerFunc(func(conn tcp2.WriteCloser) {
|
||||||
_, _ = conn.Write([]byte("OK"))
|
_, _ = conn.Write([]byte("OK"))
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -47,7 +47,7 @@ func TestShutdownTCP(t *testing.T) {
|
||||||
router, err := tcprouter.NewRouter()
|
router, err := tcprouter.NewRouter()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = router.AddRoute("HostSNI(`*`)", 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {
|
err = router.AddTCPRoute("HostSNI(`*`)", 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {
|
||||||
_, err := http.ReadRequest(bufio.NewReader(conn))
|
_, err := http.ReadRequest(bufio.NewReader(conn))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in a new issue