Bring back v2 rule matchers

This commit is contained in:
Romain 2024-01-23 11:34:05 +01:00 committed by GitHub
parent 21da705ec9
commit 683e2ee5c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 3773 additions and 114 deletions

View file

@ -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.

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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` |

View file

@ -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

View file

@ -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

View file

@ -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```)

View file

@ -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```)

View file

@ -453,5 +453,8 @@
[experimental.localPlugins.LocalDescriptor1] [experimental.localPlugins.LocalDescriptor1]
moduleName = "foobar" moduleName = "foobar"
[core]
defaultRuleSyntax = "foobar"
[spiffe] [spiffe]
workloadAPIAddr = "foobar" workloadAPIAddr = "foobar"

View file

@ -486,5 +486,7 @@ experimental:
LocalDescriptor1: LocalDescriptor1:
moduleName: foobar moduleName: foobar
kubernetesGateway: true kubernetesGateway: true
core:
defaultRuleSyntax: foobar
spiffe: spiffe:
workloadAPIAddr: foobar workloadAPIAddr: foobar

View file

@ -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
View file

@ -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
View file

@ -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=

View file

@ -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

View 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]

View 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]

View file

@ -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{}{})

View file

@ -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

View file

@ -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:"-"`

View 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"`
} }

View file

@ -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

View file

@ -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")

View file

@ -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

View 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
}

File diff suppressed because it is too large Load diff

View file

@ -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)
} }

View file

@ -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

View file

@ -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
View 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
}

File diff suppressed because it is too large Load diff

View file

@ -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
} }

View file

@ -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)

View file

@ -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,

View file

@ -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,
} }

View file

@ -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"`

View file

@ -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.

View file

@ -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)
} }

View file

@ -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`))",
}, },
} }

View file

@ -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": {},

View file

@ -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": {},

View file

@ -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": {},

View file

@ -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": {},

View file

@ -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": {},

View file

@ -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
} }
} }

View file

@ -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
} }

View file

@ -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),
} }

View file

@ -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{

View file

@ -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

View file

@ -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

View file

@ -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")
} }
} }

View file

@ -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()
})) }))

View file

@ -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