Merge current v2.11 into v3.6

This commit is contained in:
mmatur
2026-01-15 11:26:40 +01:00
parent 27b27e9b1f
commit 3315a9fbec
195 changed files with 1748 additions and 1852 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ on:
env: env:
GO_VERSION: '1.24' GO_VERSION: '1.24'
GOLANGCI_LINT_VERSION: v2.0.2 GOLANGCI_LINT_VERSION: v2.8.0
MISSPELL_VERSION: v0.7.0 MISSPELL_VERSION: v0.7.0
jobs: jobs:
+20 -2
View File
@@ -36,6 +36,7 @@ linters:
- nilnil # Not relevant - nilnil # Not relevant
- nlreturn # Not relevant - nlreturn # Not relevant
- noctx # Too strict - noctx # Too strict
- noinlineerr # Too strict
- nonamedreturns # Too strict - nonamedreturns # Too strict
- paralleltest # Not relevant - paralleltest # Not relevant
- prealloc # Too many false-positive. - prealloc # Too many false-positive.
@@ -47,6 +48,7 @@ linters:
- varnamelen # Not relevant - varnamelen # Not relevant
- wrapcheck # Too strict - wrapcheck # Too strict
- wsl # Too strict - wsl # Too strict
- wsl_v5 # Too strict
settings: settings:
depguard: depguard:
@@ -295,15 +297,31 @@ linters:
source: 'errors.New\("Nomad provider' source: 'errors.New\("Nomad provider'
text: 'ST1005: error strings should not be capitalized' text: 'ST1005: error strings should not be capitalized'
- path: (.+)\.go - path: (.+)\.go
text: 'struct-tag: unknown option ''inline'' in JSON tag' text: 'omitzero: Omitempty has no effect on nested struct field'
linters:
- modernize
- path: (.+)\.go
text: 'struct-tag: unknown option "inline" in json tag'
linters: linters:
- revive - revive
- path: (.+)\.go - path: (.+)\.go
text: 'struct-tag: unknown option ''omitzero'' in TOML tag' text: 'struct-tag: unknown option "omitzero" in toml tag'
linters:
- revive
- path: (pkg/types/.+|pkg/api/.+|pkg/observability/types/.+)\.go
text: 'var-naming: avoid meaningless package names'
linters:
- revive
- path: (pkg/muxer/http/.+|pkg/provider/http/.+)\.go
text: 'var-naming: avoid package names that conflict with Go standard library package names'
linters: linters:
- revive - revive
- path: (.+)\.go$ - path: (.+)\.go$
text: 'SA1019: http.CloseNotifier has been deprecated' # FIXME must be fixed text: 'SA1019: http.CloseNotifier has been deprecated' # FIXME must be fixed
- path: (.+)\.go$
text: 'SA1019: dynamic.(TCPIPWhiteList|IPWhiteList) is deprecated: please use IPAllowList instead.'
- path: (.+)\.go$
text: 'SA1019: middlewareTCP.Spec.IPWhiteList is deprecated: please use IPAllowList instead.'
- path: (.+)\.go$ - path: (.+)\.go$
text: 'SA1019: cfg.(SSLRedirect|SSLTemporaryRedirect|SSLHost|SSLForceHost|FeaturePolicy) is deprecated' text: 'SA1019: cfg.(SSLRedirect|SSLTemporaryRedirect|SSLHost|SSLForceHost|FeaturePolicy) is deprecated'
- path: (.+)\.go$ - path: (.+)\.go$
+1
View File
@@ -10,6 +10,7 @@ import (
// TraefikCmdConfiguration wraps the static configuration and extra parameters. // TraefikCmdConfiguration wraps the static configuration and extra parameters.
type TraefikCmdConfiguration struct { type TraefikCmdConfiguration struct {
static.Configuration `export:"true"` static.Configuration `export:"true"`
// ConfigFile is the path to the configuration file. // ConfigFile is the path to the configuration file.
ConfigFile string `description:"Configuration file to use. If specified all other flags are ignored." export:"true"` ConfigFile string `description:"Configuration file to use. If specified all other flags are ignored." export:"true"`
} }
+1 -1
View File
@@ -83,7 +83,7 @@ func run(dest string) error {
return err return err
} }
return os.WriteFile(filepath.Join(dest, "marshaler.go"), []byte(fmt.Sprintf(marsh, destPkg)), 0o666) return os.WriteFile(filepath.Join(dest, "marshaler.go"), fmt.Appendf(nil, marsh, destPkg), 0o666)
} }
func cleanType(typ types.Type, base string) string { func cleanType(typ types.Type, base string) string {
@@ -373,6 +373,7 @@ spec:
description: |- description: |-
Syntax defines the router's rule syntax. Syntax defines the router's rule syntax.
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax
Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax.
type: string type: string
required: required:
@@ -584,6 +585,7 @@ spec:
description: |- description: |-
ProxyProtocol defines the PROXY protocol configuration. ProxyProtocol defines the PROXY protocol configuration.
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/service/#proxy-protocol More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/service/#proxy-protocol
Deprecated: ProxyProtocol will not be supported in future APIVersions, please use ServersTransport to configure ProxyProtocol instead. Deprecated: ProxyProtocol will not be supported in future APIVersions, please use ServersTransport to configure ProxyProtocol instead.
properties: properties:
version: version:
@@ -606,6 +608,7 @@ spec:
hence fully terminating the connection. hence fully terminating the connection.
It is a duration in milliseconds, defaulting to 100. It is a duration in milliseconds, defaulting to 100.
A negative value means an infinite deadline (i.e. the reading capability is never closed). A negative value means an infinite deadline (i.e. the reading capability is never closed).
Deprecated: TerminationDelay will not be supported in future APIVersions, please use ServersTransport to configure the TerminationDelay instead. Deprecated: TerminationDelay will not be supported in future APIVersions, please use ServersTransport to configure the TerminationDelay instead.
type: integer type: integer
tls: tls:
@@ -626,6 +629,7 @@ spec:
description: |- description: |-
Syntax defines the router's rule syntax. Syntax defines the router's rule syntax.
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax
Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax.
enum: enum:
- v3 - v3
@@ -1060,6 +1064,7 @@ spec:
description: |- description: |-
AutoDetect specifies whether to let the `Content-Type` header, if it has not been set by the backend, AutoDetect specifies whether to let the `Content-Type` header, if it has not been set by the backend,
be automatically set to a value derived from the contents of the response. be automatically set to a value derived from the contents of the response.
Deprecated: AutoDetect option is deprecated, Content-Type middleware is only meant to be used to enable the content-type detection, please remove any usage of this option. Deprecated: AutoDetect option is deprecated, Content-Type middleware is only meant to be used to enable the content-type detection, please remove any usage of this option.
type: boolean type: boolean
type: object type: object
@@ -2212,8 +2217,9 @@ spec:
description: |- description: |-
IPWhiteList defines the IPWhiteList middleware configuration. IPWhiteList defines the IPWhiteList middleware configuration.
This middleware accepts/refuses connections based on the client IP. This middleware accepts/refuses connections based on the client IP.
Deprecated: please use IPAllowList instead.
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/ipwhitelist/ More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/ipwhitelist/
Deprecated: please use IPAllowList instead.
properties: properties:
sourceRange: sourceRange:
description: SourceRange defines the allowed IPs (or ranges of description: SourceRange defines the allowed IPs (or ranges of
@@ -2370,6 +2376,7 @@ spec:
rootCAsSecrets: rootCAsSecrets:
description: |- description: |-
RootCAsSecrets defines a list of CA secret used to validate self-signed certificate. RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead. Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items: items:
type: string type: string
@@ -2524,6 +2531,7 @@ spec:
rootCAsSecrets: rootCAsSecrets:
description: |- description: |-
RootCAsSecrets defines a list of CA secret used to validate self-signed certificate. RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead. Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items: items:
type: string type: string
@@ -2659,6 +2667,7 @@ spec:
description: |- description: |-
PreferServerCipherSuites defines whether the server chooses a cipher suite among his own instead of among the client's. PreferServerCipherSuites defines whether the server chooses a cipher suite among his own instead of among the client's.
It is enabled automatically when minVersion or maxVersion is set. It is enabled automatically when minVersion or maxVersion is set.
Deprecated: https://github.com/golang/go/issues/45430 Deprecated: https://github.com/golang/go/issues/45430
type: boolean type: boolean
sniStrict: sniStrict:
@@ -1,114 +0,0 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.16.1
name: tlsoptions.traefik.containo.us
spec:
group: traefik.containo.us
names:
kind: TLSOption
listKind: TLSOptionList
plural: tlsoptions
singular: tlsoption
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: |-
TLSOption is the CRD implementation of a Traefik TLS Option, allowing to configure some parameters of the TLS connection.
More info: https://doc.traefik.io/traefik/v2.11/https/tls/#tls-options
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: TLSOptionSpec defines the desired state of a TLSOption.
properties:
alpnProtocols:
description: |-
ALPNProtocols defines the list of supported application level protocols for the TLS handshake, in order of preference.
More info: https://doc.traefik.io/traefik/v2.11/https/tls/#alpn-protocols
items:
type: string
type: array
cipherSuites:
description: |-
CipherSuites defines the list of supported cipher suites for TLS versions up to TLS 1.2.
More info: https://doc.traefik.io/traefik/v2.11/https/tls/#cipher-suites
items:
type: string
type: array
clientAuth:
description: ClientAuth defines the server's policy for TLS Client
Authentication.
properties:
clientAuthType:
description: ClientAuthType defines the client authentication
type to apply.
enum:
- NoClientCert
- RequestClientCert
- RequireAnyClientCert
- VerifyClientCertIfGiven
- RequireAndVerifyClientCert
type: string
secretNames:
description: SecretNames defines the names of the referenced Kubernetes
Secret storing certificate details.
items:
type: string
type: array
type: object
curvePreferences:
description: |-
CurvePreferences defines the preferred elliptic curves.
More info: https://doc.traefik.io/traefik/v2.11/https/tls/#curve-preferences
items:
type: string
type: array
maxVersion:
description: |-
MaxVersion defines the maximum TLS version that Traefik will accept.
Possible values: VersionTLS10, VersionTLS11, VersionTLS12, VersionTLS13.
Default: None.
type: string
minVersion:
description: |-
MinVersion defines the minimum TLS version that Traefik will accept.
Possible values: VersionTLS10, VersionTLS11, VersionTLS12, VersionTLS13.
Default: VersionTLS10.
type: string
preferServerCipherSuites:
description: |-
PreferServerCipherSuites defines whether the server chooses a cipher suite among his own instead of among the client's.
It is enabled automatically when minVersion or maxVersion is set.
Deprecated: https://github.com/golang/go/issues/45430
type: boolean
sniStrict:
description: SniStrict defines whether Traefik allows connections
from clients connections that do not specify a server_name extension.
type: boolean
type: object
required:
- metadata
- spec
type: object
served: true
storage: true
@@ -374,6 +374,7 @@ spec:
description: |- description: |-
Syntax defines the router's rule syntax. Syntax defines the router's rule syntax.
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax
Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax.
type: string type: string
required: required:
@@ -123,6 +123,7 @@ spec:
description: |- description: |-
ProxyProtocol defines the PROXY protocol configuration. ProxyProtocol defines the PROXY protocol configuration.
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/service/#proxy-protocol More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/service/#proxy-protocol
Deprecated: ProxyProtocol will not be supported in future APIVersions, please use ServersTransport to configure ProxyProtocol instead. Deprecated: ProxyProtocol will not be supported in future APIVersions, please use ServersTransport to configure ProxyProtocol instead.
properties: properties:
version: version:
@@ -145,6 +146,7 @@ spec:
hence fully terminating the connection. hence fully terminating the connection.
It is a duration in milliseconds, defaulting to 100. It is a duration in milliseconds, defaulting to 100.
A negative value means an infinite deadline (i.e. the reading capability is never closed). A negative value means an infinite deadline (i.e. the reading capability is never closed).
Deprecated: TerminationDelay will not be supported in future APIVersions, please use ServersTransport to configure the TerminationDelay instead. Deprecated: TerminationDelay will not be supported in future APIVersions, please use ServersTransport to configure the TerminationDelay instead.
type: integer type: integer
tls: tls:
@@ -165,6 +167,7 @@ spec:
description: |- description: |-
Syntax defines the router's rule syntax. Syntax defines the router's rule syntax.
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax
Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax.
enum: enum:
- v3 - v3
@@ -231,6 +231,7 @@ spec:
description: |- description: |-
AutoDetect specifies whether to let the `Content-Type` header, if it has not been set by the backend, AutoDetect specifies whether to let the `Content-Type` header, if it has not been set by the backend,
be automatically set to a value derived from the contents of the response. be automatically set to a value derived from the contents of the response.
Deprecated: AutoDetect option is deprecated, Content-Type middleware is only meant to be used to enable the content-type detection, please remove any usage of this option. Deprecated: AutoDetect option is deprecated, Content-Type middleware is only meant to be used to enable the content-type detection, please remove any usage of this option.
type: boolean type: boolean
type: object type: object
@@ -69,8 +69,9 @@ spec:
description: |- description: |-
IPWhiteList defines the IPWhiteList middleware configuration. IPWhiteList defines the IPWhiteList middleware configuration.
This middleware accepts/refuses connections based on the client IP. This middleware accepts/refuses connections based on the client IP.
Deprecated: please use IPAllowList instead.
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/ipwhitelist/ More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/ipwhitelist/
Deprecated: please use IPAllowList instead.
properties: properties:
sourceRange: sourceRange:
description: SourceRange defines the allowed IPs (or ranges of description: SourceRange defines the allowed IPs (or ranges of
@@ -139,6 +139,7 @@ spec:
rootCAsSecrets: rootCAsSecrets:
description: |- description: |-
RootCAsSecrets defines a list of CA secret used to validate self-signed certificate. RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead. Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items: items:
type: string type: string
@@ -124,6 +124,7 @@ spec:
rootCAsSecrets: rootCAsSecrets:
description: |- description: |-
RootCAsSecrets defines a list of CA secret used to validate self-signed certificate. RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead. Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items: items:
type: string type: string
@@ -103,6 +103,7 @@ spec:
description: |- description: |-
PreferServerCipherSuites defines whether the server chooses a cipher suite among his own instead of among the client's. PreferServerCipherSuites defines whether the server chooses a cipher suite among his own instead of among the client's.
It is enabled automatically when minVersion or maxVersion is set. It is enabled automatically when minVersion or maxVersion is set.
Deprecated: https://github.com/golang/go/issues/45430 Deprecated: https://github.com/golang/go/issues/45430
type: boolean type: boolean
sniStrict: sniStrict:
+1 -2
View File
@@ -245,8 +245,7 @@ func digestParts(resp *http.Response) map[string]string {
result := map[string]string{} result := map[string]string{}
if len(resp.Header["Www-Authenticate"]) > 0 { if len(resp.Header["Www-Authenticate"]) > 0 {
wantedHeaders := []string{"nonce", "realm", "qop", "opaque"} wantedHeaders := []string{"nonce", "realm", "qop", "opaque"}
responseHeaders := strings.Split(resp.Header["Www-Authenticate"][0], ",") for r := range strings.SplitSeq(resp.Header["Www-Authenticate"][0], ",") {
for _, r := range responseHeaders {
for _, w := range wantedHeaders { for _, w := range wantedHeaders {
if strings.Contains(r, w) { if strings.Contains(r, w) {
result[w] = strings.Split(r, `"`)[1] result[w] = strings.Split(r, `"`)[1]
+6 -5
View File
@@ -27,6 +27,7 @@ import (
// ACME test suites. // ACME test suites.
type AcmeSuite struct { type AcmeSuite struct {
BaseSuite BaseSuite
pebbleIP string pebbleIP string
fakeDNSServer *dns.Server fakeDNSServer *dns.Server
} }
@@ -63,11 +64,6 @@ const (
wildcardDomain = "*.acme.wtf" wildcardDomain = "*.acme.wtf"
) )
func (s *AcmeSuite) getAcmeURL() string {
return fmt.Sprintf("https://%s/dir",
net.JoinHostPort(s.pebbleIP, "14000"))
}
func setupPebbleRootCA() (*http.Transport, error) { func setupPebbleRootCA() (*http.Transport, error) {
path, err := filepath.Abs("fixtures/acme/ssl/pebble.minica.pem") path, err := filepath.Abs("fixtures/acme/ssl/pebble.minica.pem")
if err != nil { if err != nil {
@@ -540,3 +536,8 @@ func (s *AcmeSuite) retrieveAcmeCertificate(testCase acmeTestCase) {
assert.Equal(s.T(), sub.expectedAlgorithm, gotPublicKeyAlgorithm) assert.Equal(s.T(), sub.expectedAlgorithm, gotPublicKeyAlgorithm)
} }
} }
func (s *AcmeSuite) getAcmeURL() string {
return fmt.Sprintf("https://%s/dir",
net.JoinHostPort(s.pebbleIP, "14000"))
}
+42 -41
View File
@@ -16,6 +16,7 @@ import (
type ConsulCatalogSuite struct { type ConsulCatalogSuite struct {
BaseSuite BaseSuite
consulClient *api.Client consulClient *api.Client
consulAgentClient *api.Client consulAgentClient *api.Client
consulURL string consulURL string
@@ -53,47 +54,6 @@ func (s *ConsulCatalogSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite() s.BaseSuite.TearDownSuite()
} }
func (s *ConsulCatalogSuite) waitToElectConsulLeader() error {
return try.Do(15*time.Second, func() error {
leader, err := s.consulClient.Status().Leader()
if err != nil || len(leader) == 0 {
return fmt.Errorf("leader not found. %w", err)
}
return nil
})
}
func (s *ConsulCatalogSuite) waitForConnectCA() error {
return try.Do(15*time.Second, func() error {
caroots, _, err := s.consulClient.Connect().CARoots(nil)
if err != nil || len(caroots.Roots) == 0 {
return fmt.Errorf("connect CA not fully initialized. %w", err)
}
return nil
})
}
func (s *ConsulCatalogSuite) registerService(reg *api.AgentServiceRegistration, onAgent bool) error {
client := s.consulClient
if onAgent {
client = s.consulAgentClient
}
return client.Agent().ServiceRegister(reg)
}
func (s *ConsulCatalogSuite) deregisterService(id string, onAgent bool) error {
client := s.consulClient
if onAgent {
client = s.consulAgentClient
}
return client.Agent().ServiceDeregister(id)
}
func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings() { func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings() {
reg1 := &api.AgentServiceRegistration{ reg1 := &api.AgentServiceRegistration{
ID: "whoami1", ID: "whoami1",
@@ -837,3 +797,44 @@ func (s *ConsulCatalogSuite) TestConsulConnect_NotAware() {
err = s.deregisterService("whoami1", false) err = s.deregisterService("whoami1", false)
require.NoError(s.T(), err) require.NoError(s.T(), err)
} }
func (s *ConsulCatalogSuite) waitToElectConsulLeader() error {
return try.Do(15*time.Second, func() error {
leader, err := s.consulClient.Status().Leader()
if err != nil || len(leader) == 0 {
return fmt.Errorf("leader not found. %w", err)
}
return nil
})
}
func (s *ConsulCatalogSuite) waitForConnectCA() error {
return try.Do(15*time.Second, func() error {
caroots, _, err := s.consulClient.Connect().CARoots(nil)
if err != nil || len(caroots.Roots) == 0 {
return fmt.Errorf("connect CA not fully initialized. %w", err)
}
return nil
})
}
func (s *ConsulCatalogSuite) registerService(reg *api.AgentServiceRegistration, onAgent bool) error {
client := s.consulClient
if onAgent {
client = s.consulAgentClient
}
return client.Agent().ServiceRegister(reg)
}
func (s *ConsulCatalogSuite) deregisterService(id string, onAgent bool) error {
client := s.consulClient
if onAgent {
client = s.consulAgentClient
}
return client.Agent().ServiceDeregister(id)
}
+11 -10
View File
@@ -25,6 +25,7 @@ import (
// Consul test suites. // Consul test suites.
type ConsulSuite struct { type ConsulSuite struct {
BaseSuite BaseSuite
kvClient store.Store kvClient store.Store
consulURL string consulURL string
} }
@@ -162,16 +163,6 @@ func (s *ConsulSuite) TestSimpleConfiguration() {
} }
} }
func (s *ConsulSuite) assertWhoami(host string, expectedStatusCode int) {
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
require.NoError(s.T(), err)
req.Host = host
resp, err := try.ResponseUntilStatusCode(req, 15*time.Second, expectedStatusCode)
require.NoError(s.T(), err)
resp.Body.Close()
}
func (s *ConsulSuite) TestDeleteRootKey() { func (s *ConsulSuite) TestDeleteRootKey() {
// This test case reproduce the issue: https://github.com/traefik/traefik/issues/8092 // This test case reproduce the issue: https://github.com/traefik/traefik/issues/8092
@@ -220,3 +211,13 @@ func (s *ConsulSuite) TestDeleteRootKey() {
s.assertWhoami("kv1.localhost", http.StatusNotFound) s.assertWhoami("kv1.localhost", http.StatusNotFound)
s.assertWhoami("kv2.localhost", http.StatusNotFound) s.assertWhoami("kv2.localhost", http.StatusNotFound)
} }
func (s *ConsulSuite) assertWhoami(host string, expectedStatusCode int) {
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
require.NoError(s.T(), err)
req.Host = host
resp, err := try.ResponseUntilStatusCode(req, 15*time.Second, expectedStatusCode)
require.NoError(s.T(), err)
resp.Body.Close()
}
+3 -3
View File
@@ -81,7 +81,7 @@ func (s *DockerSuite) TestDefaultDockerContainers() {
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
require.NoError(s.T(), err) require.NoError(s.T(), err)
var version map[string]interface{} var version map[string]any
assert.NoError(s.T(), json.Unmarshal(body, &version)) assert.NoError(s.T(), json.Unmarshal(body, &version))
assert.Equal(s.T(), "swarm/1.0.0", version["Version"]) assert.Equal(s.T(), "swarm/1.0.0", version["Version"])
@@ -145,7 +145,7 @@ func (s *DockerSuite) TestDockerContainersWithLabels() {
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
require.NoError(s.T(), err) require.NoError(s.T(), err)
var version map[string]interface{} var version map[string]any
assert.NoError(s.T(), json.Unmarshal(body, &version)) assert.NoError(s.T(), json.Unmarshal(body, &version))
assert.Equal(s.T(), "swarm/1.0.0", version["Version"]) assert.Equal(s.T(), "swarm/1.0.0", version["Version"])
@@ -203,7 +203,7 @@ func (s *DockerSuite) TestRestartDockerContainers() {
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
require.NoError(s.T(), err) require.NoError(s.T(), err)
var version map[string]interface{} var version map[string]any
assert.NoError(s.T(), json.Unmarshal(body, &version)) assert.NoError(s.T(), json.Unmarshal(body, &version))
assert.Equal(s.T(), "swarm/1.0.0", version["Version"]) assert.Equal(s.T(), "swarm/1.0.0", version["Version"])
+1
View File
@@ -21,6 +21,7 @@ const traefikTestOTLPLogFile = "traefik_otlp.log"
// DualLoggingSuite tests that both OTLP and stdout logging can work together. // DualLoggingSuite tests that both OTLP and stdout logging can work together.
type DualLoggingSuite struct { type DualLoggingSuite struct {
BaseSuite BaseSuite
otlpLogs []string otlpLogs []string
collector *httptest.Server collector *httptest.Server
} }
+1
View File
@@ -14,6 +14,7 @@ import (
// ErrorPagesSuite test suites. // ErrorPagesSuite test suites.
type ErrorPagesSuite struct { type ErrorPagesSuite struct {
BaseSuite BaseSuite
ErrorPageIP string ErrorPageIP string
BackendIP string BackendIP string
} }
+1
View File
@@ -23,6 +23,7 @@ import (
// etcd test suites. // etcd test suites.
type EtcdSuite struct { type EtcdSuite struct {
BaseSuite BaseSuite
kvClient store.Store kvClient store.Store
etcdAddr string etcdAddr string
} }
+10 -1
View File
@@ -374,6 +374,7 @@ spec:
description: |- description: |-
Syntax defines the router's rule syntax. Syntax defines the router's rule syntax.
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax
Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax.
type: string type: string
required: required:
@@ -585,6 +586,7 @@ spec:
description: |- description: |-
ProxyProtocol defines the PROXY protocol configuration. ProxyProtocol defines the PROXY protocol configuration.
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/service/#proxy-protocol More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/service/#proxy-protocol
Deprecated: ProxyProtocol will not be supported in future APIVersions, please use ServersTransport to configure ProxyProtocol instead. Deprecated: ProxyProtocol will not be supported in future APIVersions, please use ServersTransport to configure ProxyProtocol instead.
properties: properties:
version: version:
@@ -607,6 +609,7 @@ spec:
hence fully terminating the connection. hence fully terminating the connection.
It is a duration in milliseconds, defaulting to 100. It is a duration in milliseconds, defaulting to 100.
A negative value means an infinite deadline (i.e. the reading capability is never closed). A negative value means an infinite deadline (i.e. the reading capability is never closed).
Deprecated: TerminationDelay will not be supported in future APIVersions, please use ServersTransport to configure the TerminationDelay instead. Deprecated: TerminationDelay will not be supported in future APIVersions, please use ServersTransport to configure the TerminationDelay instead.
type: integer type: integer
tls: tls:
@@ -627,6 +630,7 @@ spec:
description: |- description: |-
Syntax defines the router's rule syntax. Syntax defines the router's rule syntax.
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax
Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax.
enum: enum:
- v3 - v3
@@ -1061,6 +1065,7 @@ spec:
description: |- description: |-
AutoDetect specifies whether to let the `Content-Type` header, if it has not been set by the backend, AutoDetect specifies whether to let the `Content-Type` header, if it has not been set by the backend,
be automatically set to a value derived from the contents of the response. be automatically set to a value derived from the contents of the response.
Deprecated: AutoDetect option is deprecated, Content-Type middleware is only meant to be used to enable the content-type detection, please remove any usage of this option. Deprecated: AutoDetect option is deprecated, Content-Type middleware is only meant to be used to enable the content-type detection, please remove any usage of this option.
type: boolean type: boolean
type: object type: object
@@ -2213,8 +2218,9 @@ spec:
description: |- description: |-
IPWhiteList defines the IPWhiteList middleware configuration. IPWhiteList defines the IPWhiteList middleware configuration.
This middleware accepts/refuses connections based on the client IP. This middleware accepts/refuses connections based on the client IP.
Deprecated: please use IPAllowList instead.
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/ipwhitelist/ More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/ipwhitelist/
Deprecated: please use IPAllowList instead.
properties: properties:
sourceRange: sourceRange:
description: SourceRange defines the allowed IPs (or ranges of description: SourceRange defines the allowed IPs (or ranges of
@@ -2371,6 +2377,7 @@ spec:
rootCAsSecrets: rootCAsSecrets:
description: |- description: |-
RootCAsSecrets defines a list of CA secret used to validate self-signed certificate. RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead. Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items: items:
type: string type: string
@@ -2525,6 +2532,7 @@ spec:
rootCAsSecrets: rootCAsSecrets:
description: |- description: |-
RootCAsSecrets defines a list of CA secret used to validate self-signed certificate. RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead. Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items: items:
type: string type: string
@@ -2660,6 +2668,7 @@ spec:
description: |- description: |-
PreferServerCipherSuites defines whether the server chooses a cipher suite among his own instead of among the client's. PreferServerCipherSuites defines whether the server chooses a cipher suite among his own instead of among the client's.
It is enabled automatically when minVersion or maxVersion is set. It is enabled automatically when minVersion or maxVersion is set.
Deprecated: https://github.com/golang/go/issues/45430 Deprecated: https://github.com/golang/go/issues/45430
type: boolean type: boolean
sniStrict: sniStrict:
+1
View File
@@ -19,6 +19,7 @@ import (
// HealthCheck test suites. // HealthCheck test suites.
type HealthCheckSuite struct { type HealthCheckSuite struct {
BaseSuite BaseSuite
whoami1IP string whoami1IP string
whoami2IP string whoami2IP string
whoami3IP string whoami3IP string
+3 -3
View File
@@ -187,7 +187,7 @@ func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
s.RegisterService(&_Greeter_serviceDesc, srv) s.RegisterService(&_Greeter_serviceDesc, srv)
} }
func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _Greeter_SayHello_Handler(srv any, ctx context.Context, dec func(any) error, interceptor grpc.UnaryServerInterceptor) (any, error) {
in := new(HelloRequest) in := new(HelloRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
@@ -199,13 +199,13 @@ func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(in
Server: srv, Server: srv,
FullMethod: "/helloworld.Greeter/SayHello", FullMethod: "/helloworld.Greeter/SayHello",
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req any) (any, error) {
return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest)) return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
} }
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Greeter_StreamExample_Handler(srv interface{}, stream grpc.ServerStream) error { func _Greeter_StreamExample_Handler(srv any, stream grpc.ServerStream) error {
m := new(StreamExampleRequest) m := new(StreamExampleRequest)
if err := stream.RecvMsg(m); err != nil { if err := stream.RecvMsg(m); err != nil {
return err return err
+34 -34
View File
@@ -877,40 +877,6 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithTlsConfigurationDeletion()
require.NoError(s.T(), err) require.NoError(s.T(), err)
} }
// modifyCertificateConfFileContent replaces the content of a HTTPS configuration file.
func (s *HTTPSSuite) modifyCertificateConfFileContent(certFileName, confFileName string) {
file, err := os.OpenFile("./"+confFileName, os.O_WRONLY, os.ModeExclusive)
require.NoError(s.T(), err)
defer func() {
file.Close()
}()
err = file.Truncate(0)
require.NoError(s.T(), err)
// If certificate file is not provided, just truncate the configuration file
if len(certFileName) > 0 {
tlsConf := dynamic.Configuration{
TLS: &dynamic.TLSConfiguration{
Certificates: []*traefiktls.CertAndStores{
{
Certificate: traefiktls.Certificate{
CertFile: types.FileOrContent("fixtures/https/" + certFileName + ".cert"),
KeyFile: types.FileOrContent("fixtures/https/" + certFileName + ".key"),
},
},
},
},
}
var confBuffer bytes.Buffer
err := toml.NewEncoder(&confBuffer).Encode(tlsConf)
require.NoError(s.T(), err)
_, err = file.Write(confBuffer.Bytes())
require.NoError(s.T(), err)
}
}
func (s *HTTPSSuite) TestEntryPointHttpsRedirectAndPathModification() { func (s *HTTPSSuite) TestEntryPointHttpsRedirectAndPathModification() {
file := s.adaptFile("fixtures/https/https_redirect.toml", struct{}{}) file := s.adaptFile("fixtures/https/https_redirect.toml", struct{}{})
s.traefikCmd(withConfigFile(file)) s.traefikCmd(withConfigFile(file))
@@ -1177,6 +1143,40 @@ func (s *HTTPSSuite) TestWithInvalidTLSOption() {
} }
} }
// modifyCertificateConfFileContent replaces the content of a HTTPS configuration file.
func (s *HTTPSSuite) modifyCertificateConfFileContent(certFileName, confFileName string) {
file, err := os.OpenFile("./"+confFileName, os.O_WRONLY, os.ModeExclusive)
require.NoError(s.T(), err)
defer func() {
file.Close()
}()
err = file.Truncate(0)
require.NoError(s.T(), err)
// If certificate file is not provided, just truncate the configuration file
if len(certFileName) > 0 {
tlsConf := dynamic.Configuration{
TLS: &dynamic.TLSConfiguration{
Certificates: []*traefiktls.CertAndStores{
{
Certificate: traefiktls.Certificate{
CertFile: types.FileOrContent("fixtures/https/" + certFileName + ".cert"),
KeyFile: types.FileOrContent("fixtures/https/" + certFileName + ".key"),
},
},
},
},
}
var confBuffer bytes.Buffer
err := toml.NewEncoder(&confBuffer).Encode(tlsConf)
require.NoError(s.T(), err)
_, err = file.Write(confBuffer.Bytes())
require.NoError(s.T(), err)
}
}
func (s *SimpleSuite) TestMaxConcurrentStream() { func (s *SimpleSuite) TestMaxConcurrentStream() {
file := s.adaptFile("fixtures/https/max_concurrent_stream.toml", struct{}{}) file := s.adaptFile("fixtures/https/max_concurrent_stream.toml", struct{}{})
+37 -36
View File
@@ -71,45 +71,12 @@ type composeDeploy struct {
type BaseSuite struct { type BaseSuite struct {
suite.Suite suite.Suite
containers map[string]testcontainers.Container containers map[string]testcontainers.Container
network *testcontainers.DockerNetwork network *testcontainers.DockerNetwork
hostIP string hostIP string
} }
func (s *BaseSuite) waitForTraefik(containerName string) {
time.Sleep(1 * time.Second)
// Wait for Traefik to turn ready.
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/rawdata", nil)
require.NoError(s.T(), err)
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains(containerName))
require.NoError(s.T(), err)
}
func (s *BaseSuite) displayTraefikLogFile(path string) {
if s.T().Failed() {
if _, err := os.Stat(path); !os.IsNotExist(err) {
content, errRead := os.ReadFile(path)
// TODO TestName
// fmt.Printf("%s: Traefik logs: \n", c.TestName())
fmt.Print("Traefik logs: \n")
if errRead == nil {
fmt.Println(string(content))
} else {
fmt.Println(errRead)
}
} else {
// fmt.Printf("%s: No Traefik logs.\n", c.TestName())
fmt.Print("No Traefik logs.\n")
}
errRemove := os.Remove(path)
if errRemove != nil {
fmt.Println(errRemove)
}
}
}
func (s *BaseSuite) SetupSuite() { func (s *BaseSuite) SetupSuite() {
if isDockerDesktop(s.T()) { if isDockerDesktop(s.T()) {
_, err := os.Stat(tailscaleSecretFilePath) _, err := os.Stat(tailscaleSecretFilePath)
@@ -409,7 +376,7 @@ func (s *BaseSuite) displayTraefikLog(output *bytes.Buffer) {
if output == nil || output.Len() == 0 { if output == nil || output.Len() == 0 {
log.Info().Msg("No Traefik logs.") log.Info().Msg("No Traefik logs.")
} else { } else {
for _, line := range strings.Split(output.String(), "\n") { for line := range strings.SplitSeq(output.String(), "\n") {
log.Info().Msg(line) log.Info().Msg(line)
} }
} }
@@ -425,7 +392,7 @@ func (s *BaseSuite) getDockerHost() string {
return dockerHost return dockerHost
} }
func (s *BaseSuite) adaptFile(path string, tempObjects interface{}) string { func (s *BaseSuite) adaptFile(path string, tempObjects any) string {
// Load file // Load file
tmpl, err := template.ParseFiles(path) tmpl, err := template.ParseFiles(path)
require.NoError(s.T(), err) require.NoError(s.T(), err)
@@ -513,3 +480,37 @@ func (s *BaseSuite) composeExec(service string, args ...string) string {
return string(content) return string(content)
} }
func (s *BaseSuite) waitForTraefik(containerName string) {
time.Sleep(1 * time.Second)
// Wait for Traefik to turn ready.
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/rawdata", nil)
require.NoError(s.T(), err)
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains(containerName))
require.NoError(s.T(), err)
}
func (s *BaseSuite) displayTraefikLogFile(path string) {
if s.T().Failed() {
if _, err := os.Stat(path); !os.IsNotExist(err) {
content, errRead := os.ReadFile(path)
// TODO TestName
// fmt.Printf("%s: Traefik logs: \n", c.TestName())
fmt.Print("Traefik logs: \n")
if errRead == nil {
fmt.Println(string(content))
} else {
fmt.Println(errRead)
}
} else {
// fmt.Printf("%s: No Traefik logs.\n", c.TestName())
fmt.Print("No Traefik logs.\n")
}
errRemove := os.Remove(path)
if errRemove != nil {
fmt.Println(errRemove)
}
}
}
-1
View File
@@ -1,5 +1,4 @@
//go:build !windows //go:build !windows
// +build !windows
package integration package integration
+6 -3
View File
@@ -3,6 +3,7 @@ package integration
import ( import (
"bufio" "bufio"
"net" "net"
"strings"
"testing" "testing"
"time" "time"
@@ -15,6 +16,7 @@ import (
type ProxyProtocolSuite struct { type ProxyProtocolSuite struct {
BaseSuite BaseSuite
whoamiIP string whoamiIP string
} }
@@ -124,15 +126,16 @@ func proxyProtoRequest(address string, version byte) (string, error) {
} }
// Read the response from the server // Read the response from the server
var content string var content strings.Builder
scanner := bufio.NewScanner(conn) scanner := bufio.NewScanner(conn)
for scanner.Scan() { for scanner.Scan() {
content += scanner.Text() + "\n" content.WriteString(scanner.Text())
content.WriteString("\n")
} }
if scanner.Err() != nil { if scanner.Err() != nil {
return "", err return "", err
} }
return content, nil return content.String(), nil
} }
+1
View File
@@ -13,6 +13,7 @@ import (
type RateLimitSuite struct { type RateLimitSuite struct {
BaseSuite BaseSuite
ServerIP string ServerIP string
RedisEndpoint string RedisEndpoint string
} }
+31 -30
View File
@@ -28,6 +28,7 @@ import (
// Redis test suites. // Redis test suites.
type RedisSentinelSuite struct { type RedisSentinelSuite struct {
BaseSuite BaseSuite
kvClient store.Store kvClient store.Store
redisEndpoints []string redisEndpoints []string
} }
@@ -75,36 +76,6 @@ func (s *RedisSentinelSuite) TearDownSuite() {
} }
} }
func (s *RedisSentinelSuite) setupSentinelConfiguration(ports []string) {
for i, port := range ports {
templateValue := struct{ SentinelPort string }{SentinelPort: port}
// Load file
templateFile := "resources/compose/config/sentinel_template.conf"
tmpl, err := template.ParseFiles(templateFile)
require.NoError(s.T(), err)
folder, prefix := filepath.Split(templateFile)
fileName := fmt.Sprintf("%s/sentinel%d.conf", folder, i+1)
tmpFile, err := os.Create(fileName)
require.NoError(s.T(), err)
defer tmpFile.Close()
err = tmpFile.Chmod(0o666)
require.NoError(s.T(), err)
model := structs.Map(templateValue)
model["SelfFilename"] = tmpFile.Name()
err = tmpl.ExecuteTemplate(tmpFile, prefix, model)
require.NoError(s.T(), err)
err = tmpFile.Sync()
require.NoError(s.T(), err)
}
}
func (s *RedisSentinelSuite) TestSentinelConfiguration() { func (s *RedisSentinelSuite) TestSentinelConfiguration() {
file := s.adaptFile("fixtures/redis/sentinel.toml", struct{ RedisAddress string }{ file := s.adaptFile("fixtures/redis/sentinel.toml", struct{ RedisAddress string }{
RedisAddress: strings.Join(s.redisEndpoints, `","`), RedisAddress: strings.Join(s.redisEndpoints, `","`),
@@ -201,3 +172,33 @@ func (s *RedisSentinelSuite) TestSentinelConfiguration() {
log.Info().Msg(text) log.Info().Msg(text)
} }
} }
func (s *RedisSentinelSuite) setupSentinelConfiguration(ports []string) {
for i, port := range ports {
templateValue := struct{ SentinelPort string }{SentinelPort: port}
// Load file
templateFile := "resources/compose/config/sentinel_template.conf"
tmpl, err := template.ParseFiles(templateFile)
require.NoError(s.T(), err)
folder, prefix := filepath.Split(templateFile)
fileName := fmt.Sprintf("%s/sentinel%d.conf", folder, i+1)
tmpFile, err := os.Create(fileName)
require.NoError(s.T(), err)
defer tmpFile.Close()
err = tmpFile.Chmod(0o666)
require.NoError(s.T(), err)
model := structs.Map(templateValue)
model["SelfFilename"] = tmpFile.Name()
err = tmpl.ExecuteTemplate(tmpFile, prefix, model)
require.NoError(s.T(), err)
err = tmpFile.Sync()
require.NoError(s.T(), err)
}
}
+1
View File
@@ -24,6 +24,7 @@ import (
// Redis test suites. // Redis test suites.
type RedisSuite struct { type RedisSuite struct {
BaseSuite BaseSuite
kvClient store.Store kvClient store.Store
redisEndpoints []string redisEndpoints []string
} }
+1
View File
@@ -18,6 +18,7 @@ import (
type RestSuite struct { type RestSuite struct {
BaseSuite BaseSuite
whoamiAddr string whoamiAddr string
} }
+1
View File
@@ -15,6 +15,7 @@ import (
type RetrySuite struct { type RetrySuite struct {
BaseSuite BaseSuite
whoamiIP string whoamiIP string
} }
+1
View File
@@ -15,6 +15,7 @@ import (
// TCPHealthCheckSuite test suite for TCP health checks. // TCPHealthCheckSuite test suite for TCP health checks.
type TCPHealthCheckSuite struct { type TCPHealthCheckSuite struct {
BaseSuite BaseSuite
whoamitcp1IP string whoamitcp1IP string
whoamitcp2IP string whoamitcp2IP string
} }
+1
View File
@@ -21,6 +21,7 @@ import (
type TracingSuite struct { type TracingSuite struct {
BaseSuite BaseSuite
whoamiIP string whoamiIP string
whoamiPort int whoamiPort int
tempoIP string tempoIP string
+2 -4
View File
@@ -19,6 +19,7 @@ const (
type timedAction func(timeout time.Duration, operation DoCondition) error type timedAction func(timeout time.Duration, operation DoCondition) error
// Sleep pauses the current goroutine for at least the duration d. // Sleep pauses the current goroutine for at least the duration d.
//
// Deprecated: Use only when use another Try[...] functions is not possible. // Deprecated: Use only when use another Try[...] functions is not possible.
func Sleep(d time.Duration) { func Sleep(d time.Duration) {
d = applyCIMultiplier(d) d = applyCIMultiplier(d)
@@ -92,10 +93,7 @@ func Do(timeout time.Duration, operation DoCondition) error {
panic("timeout must be larger than zero") panic("timeout must be larger than zero")
} }
interval := time.Duration(math.Ceil(float64(timeout) / 15.0)) interval := min(time.Duration(math.Ceil(float64(timeout)/15.0)), maxInterval)
if interval > maxInterval {
interval = maxInterval
}
timeout = applyCIMultiplier(timeout) timeout = applyCIMultiplier(timeout)
+1
View File
@@ -24,6 +24,7 @@ import (
// Zk test suites. // Zk test suites.
type ZookeeperSuite struct { type ZookeeperSuite struct {
BaseSuite BaseSuite
kvClient store.Store kvClient store.Store
zookeeperAddr string zookeeperAddr string
} }
+1 -1
View File
@@ -196,7 +196,7 @@ type errWriter struct {
err error err error
} }
func (ew *errWriter) writeln(a ...interface{}) { func (ew *errWriter) writeln(a ...any) {
if ew.err != nil { if ew.err != nil {
return return
} }
+2 -11
View File
@@ -84,13 +84,7 @@ func (c *searchCriterion) filterMiddleware(mns []string) bool {
return true return true
} }
for _, mn := range mns { return slices.Contains(mns, c.MiddlewareName)
if c.MiddlewareName == mn {
return true
}
}
return false
} }
func pagination(request *http.Request, maximum int) (pageInfo, error) { func pagination(request *http.Request, maximum int) (pageInfo, error) {
@@ -109,10 +103,7 @@ func pagination(request *http.Request, maximum int) (pageInfo, error) {
return pageInfo{}, fmt.Errorf("invalid request: page: %d, per_page: %d", page, perPage) return pageInfo{}, fmt.Errorf("invalid request: page: %d, per_page: %d", page, perPage)
} }
endIndex := startIndex + perPage endIndex := min(startIndex+perPage, maximum)
if endIndex >= maximum {
endIndex = maximum
}
nextPage := 1 nextPage := 1
if page*perPage < maximum { if page*perPage < maximum {
+1 -1
View File
@@ -15,7 +15,7 @@ func init() {
expvar.Publish("Goroutines2", expvar.Func(goroutines)) expvar.Publish("Goroutines2", expvar.Func(goroutines))
} }
func goroutines() interface{} { func goroutines() any {
return runtime.NumGoroutine() return runtime.NumGoroutine()
} }
+3 -1
View File
@@ -30,11 +30,13 @@ func writeError(rw http.ResponseWriter, msg string, code int) {
type serviceInfoRepresentation struct { type serviceInfoRepresentation struct {
*runtime.ServiceInfo *runtime.ServiceInfo
ServerStatus map[string]string `json:"serverStatus,omitempty"` ServerStatus map[string]string `json:"serverStatus,omitempty"`
} }
type tcpServiceInfoRepresentation struct { type tcpServiceInfoRepresentation struct {
*runtime.TCPServiceInfo *runtime.TCPServiceInfo
ServerStatus map[string]string `json:"serverStatus,omitempty"` ServerStatus map[string]string `json:"serverStatus,omitempty"`
} }
@@ -164,7 +166,7 @@ func getProviderName(id string) string {
return strings.SplitN(id, "@", 2)[1] return strings.SplitN(id, "@", 2)[1]
} }
func extractType(element interface{}) string { func extractType(element any) string {
v := reflect.ValueOf(element).Elem() v := reflect.ValueOf(element).Elem()
for i := range v.NumField() { for i := range v.NumField() {
field := v.Field(i) field := v.Field(i)
+1
View File
@@ -15,6 +15,7 @@ import (
type entryPointRepresentation struct { type entryPointRepresentation struct {
*static.EntryPoint *static.EntryPoint
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
} }
+1 -1
View File
@@ -235,7 +235,7 @@ func TestHandler_EntryPoints(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
if *updateExpected { if *updateExpected {
var results interface{} var results any
err := json.Unmarshal(contents, &results) err := json.Unmarshal(contents, &results)
require.NoError(t, err) require.NoError(t, err)
+3
View File
@@ -16,6 +16,7 @@ import (
type routerRepresentation struct { type routerRepresentation struct {
*runtime.RouterInfo *runtime.RouterInfo
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"` Provider string `json:"provider,omitempty"`
} }
@@ -34,6 +35,7 @@ func newRouterRepresentation(name string, rt *runtime.RouterInfo) routerRepresen
type serviceRepresentation struct { type serviceRepresentation struct {
*runtime.ServiceInfo *runtime.ServiceInfo
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"` Provider string `json:"provider,omitempty"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
@@ -52,6 +54,7 @@ func newServiceRepresentation(name string, si *runtime.ServiceInfo) serviceRepre
type middlewareRepresentation struct { type middlewareRepresentation struct {
*runtime.MiddlewareInfo *runtime.MiddlewareInfo
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"` Provider string `json:"provider,omitempty"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
+1 -1
View File
@@ -1028,7 +1028,7 @@ func TestHandler_HTTP(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
if *updateExpected { if *updateExpected {
var results interface{} var results any
err := json.Unmarshal(contents, &results) err := json.Unmarshal(contents, &results)
require.NoError(t, err) require.NoError(t, err)
+2 -2
View File
@@ -239,7 +239,7 @@ func TestHandler_Overview(t *testing.T) {
KubernetesCRD: &crd.Provider{}, KubernetesCRD: &crd.Provider{},
Rest: &rest.Provider{}, Rest: &rest.Provider{},
Plugin: map[string]static.PluginConf{ Plugin: map[string]static.PluginConf{
"test": map[string]interface{}{}, "test": map[string]any{},
}, },
}, },
}, },
@@ -292,7 +292,7 @@ func TestHandler_Overview(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
if *updateExpected { if *updateExpected {
var results interface{} var results any
err := json.Unmarshal(contents, &results) err := json.Unmarshal(contents, &results)
require.NoError(t, err) require.NoError(t, err)
+3
View File
@@ -15,6 +15,7 @@ import (
type tcpRouterRepresentation struct { type tcpRouterRepresentation struct {
*runtime.TCPRouterInfo *runtime.TCPRouterInfo
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"` Provider string `json:"provider,omitempty"`
} }
@@ -29,6 +30,7 @@ func newTCPRouterRepresentation(name string, rt *runtime.TCPRouterInfo) tcpRoute
type tcpServiceRepresentation struct { type tcpServiceRepresentation struct {
*runtime.TCPServiceInfo *runtime.TCPServiceInfo
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"` Provider string `json:"provider,omitempty"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
@@ -47,6 +49,7 @@ func newTCPServiceRepresentation(name string, si *runtime.TCPServiceInfo) tcpSer
type tcpMiddlewareRepresentation struct { type tcpMiddlewareRepresentation struct {
*runtime.TCPMiddlewareInfo *runtime.TCPMiddlewareInfo
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"` Provider string `json:"provider,omitempty"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
+1 -1
View File
@@ -964,7 +964,7 @@ func TestHandler_TCP(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
if *updateExpected { if *updateExpected {
var results interface{} var results any
err := json.Unmarshal(contents, &results) err := json.Unmarshal(contents, &results)
require.NoError(t, err) require.NoError(t, err)
+1 -1
View File
@@ -178,7 +178,7 @@ func TestHandler_GetMiddleware(t *testing.T) {
middlewareName string middlewareName string
conf runtime.Configuration conf runtime.Configuration
expectedStatus int expectedStatus int
expected interface{} expected any
}{ }{
{ {
desc: "Middleware not found", desc: "Middleware not found",
+2
View File
@@ -15,6 +15,7 @@ import (
type udpRouterRepresentation struct { type udpRouterRepresentation struct {
*runtime.UDPRouterInfo *runtime.UDPRouterInfo
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"` Provider string `json:"provider,omitempty"`
} }
@@ -29,6 +30,7 @@ func newUDPRouterRepresentation(name string, rt *runtime.UDPRouterInfo) udpRoute
type udpServiceRepresentation struct { type udpServiceRepresentation struct {
*runtime.UDPServiceInfo *runtime.UDPServiceInfo
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Provider string `json:"provider,omitempty"` Provider string `json:"provider,omitempty"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
+1 -1
View File
@@ -594,7 +594,7 @@ func TestHandler_UDP(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
if *updateExpected { if *updateExpected {
var results interface{} var results any
err := json.Unmarshal(contents, &results) err := json.Unmarshal(contents, &results)
require.NoError(t, err) require.NoError(t, err)
+6 -6
View File
@@ -82,7 +82,7 @@ func logDeprecations(arguments []string) (bool, error) {
if filePath != "" { if filePath != "" {
// We don't rely on the Parser file loader here to avoid issues with unknown fields. // We don't rely on the Parser file loader here to avoid issues with unknown fields.
// Parse file content into a generic map. // Parse file content into a generic map.
var fileConfig map[string]interface{} var fileConfig map[string]any
if err := file.Decode(filePath, &fileConfig); err != nil { if err := file.Decode(filePath, &fileConfig); err != nil {
return false, fmt.Errorf("decoding configuration file %s: %w", filePath, err) return false, fmt.Errorf("decoding configuration file %s: %w", filePath, err)
} }
@@ -106,7 +106,7 @@ func logDeprecations(arguments []string) (bool, error) {
if len(vars) > 0 { if len(vars) > 0 {
// We don't rely on the Parser env loader here to avoid issues with unknown fields. // We don't rely on the Parser env loader here to avoid issues with unknown fields.
// Decode environment variables to a generic map. // Decode environment variables to a generic map.
var envConfig map[string]interface{} var envConfig map[string]any
if err := env.Decode(vars, env.DefaultNamePrefix, &envConfig); err != nil { if err := env.Decode(vars, env.DefaultNamePrefix, &envConfig); err != nil {
return false, fmt.Errorf("decoding environment variables: %w", err) return false, fmt.Errorf("decoding environment variables: %w", err)
} }
@@ -130,9 +130,9 @@ func logDeprecations(arguments []string) (bool, error) {
// flattenToLabels recursively flattens a nested map into label key-value pairs. // flattenToLabels recursively flattens a nested map into label key-value pairs.
// Example: {"experimental": {"http3": true}} -> {"traefik.experimental.http3": "true"}. // Example: {"experimental": {"http3": true}} -> {"traefik.experimental.http3": "true"}.
func flattenToLabels(config interface{}, currKey string, labels map[string]string) { func flattenToLabels(config any, currKey string, labels map[string]string) {
switch v := config.(type) { switch v := config.(type) {
case map[string]interface{}: case map[string]any:
for key, value := range v { for key, value := range v {
newKey := key newKey := key
if currKey != "" { if currKey != "" {
@@ -140,7 +140,7 @@ func flattenToLabels(config interface{}, currKey string, labels map[string]strin
} }
flattenToLabels(value, newKey, labels) flattenToLabels(value, newKey, labels)
} }
case []interface{}: case []any:
for i, item := range v { for i, item := range v {
newKey := currKey + "[" + strconv.Itoa(i) + "]" newKey := currKey + "[" + strconv.Itoa(i) + "]"
flattenToLabels(item, newKey, labels) flattenToLabels(item, newKey, labels)
@@ -168,7 +168,7 @@ func parseDeprecatedConfig(labels map[string]string) (*configuration, error) {
// Filter unknown nodes and check for deprecated options. // Filter unknown nodes and check for deprecated options.
config := &configuration{} config := &configuration{}
filterUnknownNodes(reflect.TypeOf(config), node) filterUnknownNodes(reflect.TypeFor[*configuration](), node)
// If no config remains we can return without error, to allow other loaders to proceed. // If no config remains we can return without error, to allow other loaders to proceed.
if node == nil || len(node.Children) == 0 { if node == nil || len(node.Children) == 0 {
+1 -1
View File
@@ -62,7 +62,7 @@ func (f *FileLoader) Load(args []string, cmd *cli.Command) (bool, error) {
// loadConfigFiles tries to decode the given configuration file and all default locations for the configuration file. // loadConfigFiles tries to decode the given configuration file and all default locations for the configuration file.
// It stops as soon as decoding one of them is successful. // It stops as soon as decoding one of them is successful.
func loadConfigFiles(configFile string, element interface{}) (string, error) { func loadConfigFiles(configFile string, element any) (string, error) {
finder := cli.Finder{ finder := cli.Finder{
BasePaths: []string{"/etc/traefik/traefik", "$XDG_CONFIG_HOME/traefik", "$HOME/.config/traefik", "./traefik"}, BasePaths: []string{"/etc/traefik/traefik", "$XDG_CONFIG_HOME/traefik", "$HOME/.config/traefik", "./traefik"},
Extensions: []string{"toml", "yaml", "yml"}, Extensions: []string{"toml", "yaml", "yml"},
+2 -2
View File
@@ -18,7 +18,7 @@ const (
) )
// Hydrate hydrates a configuration. // Hydrate hydrates a configuration.
func Hydrate(element interface{}) error { func Hydrate(element any) error {
field := reflect.ValueOf(element) field := reflect.ValueOf(element)
return fill(field) return fill(field)
} }
@@ -81,7 +81,7 @@ func fill(field reflect.Value) error {
return nil return nil
} }
func setTyped(field reflect.Value, i interface{}) { func setTyped(field reflect.Value, i any) {
baseValue := reflect.ValueOf(i) baseValue := reflect.ValueOf(i)
if field.Kind().String() == field.Type().String() { if field.Kind().String() == field.Type().String() {
field.Set(baseValue) field.Set(baseValue)
+2 -2
View File
@@ -21,7 +21,7 @@ func TestDeepCopy(t *testing.T) {
cfgDeepCopy := cfg.DeepCopy() cfgDeepCopy := cfg.DeepCopy()
assert.NotEqual(t, reflect.ValueOf(cfgDeepCopy), reflect.ValueOf(cfg)) assert.NotEqual(t, reflect.ValueOf(cfgDeepCopy), reflect.ValueOf(cfg))
assert.Equal(t, reflect.TypeOf(cfgDeepCopy), reflect.TypeOf(cfg)) assert.Equal(t, reflect.TypeOf(cfgDeepCopy), reflect.TypeOf(cfg)) //nolint:modernize // Comparing runtime types of two values.
assert.Equal(t, cfgDeepCopy, cfg) assert.Equal(t, cfgDeepCopy, cfg)
// Update cfg // Update cfg
@@ -32,6 +32,6 @@ func TestDeepCopy(t *testing.T) {
assert.Equal(t, cfgCopy, cfg) assert.Equal(t, cfgCopy, cfg)
assert.NotEqual(t, reflect.ValueOf(cfgDeepCopy), reflect.ValueOf(cfg)) assert.NotEqual(t, reflect.ValueOf(cfgDeepCopy), reflect.ValueOf(cfg))
assert.Equal(t, reflect.TypeOf(cfgDeepCopy), reflect.TypeOf(cfg)) assert.Equal(t, reflect.TypeOf(cfgDeepCopy), reflect.TypeOf(cfg)) //nolint:modernize // Comparing runtime types of two values.
assert.NotEqual(t, cfgDeepCopy, cfg) assert.NotEqual(t, cfgDeepCopy, cfg)
} }
+2
View File
@@ -69,6 +69,7 @@ type GrpcWeb struct {
type ContentType struct { type ContentType struct {
// AutoDetect specifies whether to let the `Content-Type` header, if it has not been set by the backend, // AutoDetect specifies whether to let the `Content-Type` header, if it has not been set by the backend,
// be automatically set to a value derived from the contents of the response. // be automatically set to a value derived from the contents of the response.
//
// Deprecated: AutoDetect option is deprecated, Content-Type middleware is only meant to be used to enable the content-type detection, please remove any usage of this option. // Deprecated: AutoDetect option is deprecated, Content-Type middleware is only meant to be used to enable the content-type detection, please remove any usage of this option.
AutoDetect *bool `json:"autoDetect,omitempty" toml:"autoDetect,omitempty" yaml:"autoDetect,omitempty" export:"true"` AutoDetect *bool `json:"autoDetect,omitempty" toml:"autoDetect,omitempty" yaml:"autoDetect,omitempty" export:"true"`
} }
@@ -481,6 +482,7 @@ func (s *IPStrategy) Get() (ip.Strategy, error) {
// IPWhiteList holds the IP whitelist middleware configuration. // IPWhiteList holds the IP whitelist middleware configuration.
// This middleware limits allowed requests based on the client IP. // This middleware limits allowed requests based on the client IP.
// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipwhitelist/ // More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipwhitelist/
//
// Deprecated: please use IPAllowList instead. // Deprecated: please use IPAllowList instead.
type IPWhiteList struct { type IPWhiteList struct {
// SourceRange defines the set of allowed IPs (or ranges of allowed IPs by using CIDR notation). Required. // SourceRange defines the set of allowed IPs (or ranges of allowed IPs by using CIDR notation). Required.
+3 -3
View File
@@ -44,11 +44,11 @@ func TestPluginConf_DeepCopy_mapOfStruct(t *testing.T) {
} }
func TestPluginConf_DeepCopy_map(t *testing.T) { func TestPluginConf_DeepCopy_map(t *testing.T) {
m := map[string]interface{}{ m := map[string]any{
"name": "bar", "name": "bar",
} }
p := PluginConf{ p := PluginConf{
"config": map[string]interface{}{ "config": map[string]any{
"foo": m, "foo": m,
}, },
} }
@@ -64,7 +64,7 @@ func TestPluginConf_DeepCopy_map(t *testing.T) {
func TestPluginConf_DeepCopy_panic(t *testing.T) { func TestPluginConf_DeepCopy_panic(t *testing.T) {
p := &PluginConf{ p := &PluginConf{
"config": map[string]interface{}{ "config": map[string]any{
"foo": &Foo{Name: "gigi"}, "foo": &Foo{Name: "gigi"},
}, },
} }
+2
View File
@@ -88,6 +88,7 @@ type TCPServersLoadBalancer struct {
Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server" export:"true"` Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server" export:"true"`
ServersTransport string `json:"serversTransport,omitempty" toml:"serversTransport,omitempty" yaml:"serversTransport,omitempty" export:"true"` ServersTransport string `json:"serversTransport,omitempty" toml:"serversTransport,omitempty" yaml:"serversTransport,omitempty" export:"true"`
// ProxyProtocol holds the PROXY Protocol configuration. // ProxyProtocol holds the PROXY Protocol configuration.
//
// Deprecated: use ServersTransport to configure ProxyProtocol instead. // Deprecated: use ServersTransport to configure ProxyProtocol instead.
ProxyProtocol *ProxyProtocol `json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` ProxyProtocol *ProxyProtocol `json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
// TerminationDelay, corresponds to the deadline that the proxy sets, after one // TerminationDelay, corresponds to the deadline that the proxy sets, after one
@@ -95,6 +96,7 @@ type TCPServersLoadBalancer struct {
// connection, to close the reading capability as well, hence fully terminating the // connection, to close the reading capability as well, hence fully terminating the
// connection. It is a duration in milliseconds, defaulting to 100. A negative value // connection. It is a duration in milliseconds, defaulting to 100. A negative value
// means an infinite deadline (i.e. the reading capability is never closed). // means an infinite deadline (i.e. the reading capability is never closed).
//
// Deprecated: use ServersTransport to configure the TerminationDelay instead. // Deprecated: use ServersTransport to configure the TerminationDelay instead.
TerminationDelay *int `json:"terminationDelay,omitempty" toml:"terminationDelay,omitempty" yaml:"terminationDelay,omitempty" export:"true"` TerminationDelay *int `json:"terminationDelay,omitempty" toml:"terminationDelay,omitempty" yaml:"terminationDelay,omitempty" export:"true"`
HealthCheck *TCPServerHealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` HealthCheck *TCPServerHealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
+1
View File
@@ -26,6 +26,7 @@ type TCPInFlightConn struct {
// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen=true
// TCPIPWhiteList holds the TCP IPWhiteList middleware configuration. // TCPIPWhiteList holds the TCP IPWhiteList middleware configuration.
//
// Deprecated: please use IPAllowList instead. // Deprecated: please use IPAllowList instead.
type TCPIPWhiteList struct { type TCPIPWhiteList struct {
// SourceRange defines the allowed IPs (or ranges of allowed IPs by using CIDR notation). // SourceRange defines the allowed IPs (or ranges of allowed IPs by using CIDR notation).
+2 -2
View File
@@ -13,7 +13,7 @@ import (
// KV pairs -> tree of untyped nodes // KV pairs -> tree of untyped nodes
// untyped nodes -> nodes augmented with metadata such as kind (inferred from element) // untyped nodes -> nodes augmented with metadata such as kind (inferred from element)
// "typed" nodes -> typed element. // "typed" nodes -> typed element.
func Decode(pairs []*store.KVPair, element interface{}, rootName string) error { func Decode(pairs []*store.KVPair, element any, rootName string) error {
if element == nil { if element == nil {
return nil return nil
} }
@@ -34,7 +34,7 @@ func Decode(pairs []*store.KVPair, element interface{}, rootName string) error {
return parser.Fill(element, node, parser.FillerOpts{AllowSliceAsStruct: false}) return parser.Fill(element, node, parser.FillerOpts{AllowSliceAsStruct: false})
} }
func getRootFieldNames(rootName string, element interface{}) []string { func getRootFieldNames(rootName string, element any) []string {
if element == nil { if element == nil {
return nil return nil
} }
+1 -1
View File
@@ -31,6 +31,6 @@ func EncodeConfiguration(conf *dynamic.Configuration) (map[string]string, error)
// Decode converts the labels to an element. // Decode converts the labels to an element.
// labels -> [ node -> node + metadata (type) ] -> element (node). // labels -> [ node -> node + metadata (type) ] -> element (node).
func Decode(labels map[string]string, element interface{}, filters ...string) error { func Decode(labels map[string]string, element any, filters ...string) error {
return parser.Decode(labels, element, parser.DefaultRootName, filters...) return parser.Decode(labels, element, parser.DefaultRootName, filters...)
} }
+11 -17
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"maps"
"slices" "slices"
"sort" "sort"
"sync" "sync"
@@ -74,6 +75,7 @@ func unique(src []string) []string {
// RouterInfo holds information about a currently running HTTP router. // RouterInfo holds information about a currently running HTTP router.
type RouterInfo struct { type RouterInfo struct {
*dynamic.Router // dynamic configuration *dynamic.Router // dynamic configuration
// Err contains all the errors that occurred during router's creation. // Err contains all the errors that occurred during router's creation.
Err []string `json:"error,omitempty"` Err []string `json:"error,omitempty"`
// Status reports whether the router is disabled, in a warning state, or all good (enabled). // Status reports whether the router is disabled, in a warning state, or all good (enabled).
@@ -91,10 +93,8 @@ type RouterInfo struct {
// AddError adds err to r.Err, if it does not already exist. // AddError adds err to r.Err, if it does not already exist.
// If critical is set, r is marked as disabled. // If critical is set, r is marked as disabled.
func (r *RouterInfo) AddError(err error, critical bool) { func (r *RouterInfo) AddError(err error, critical bool) {
for _, value := range r.Err { if slices.Contains(r.Err, err.Error()) {
if value == err.Error() { return
return
}
} }
r.Err = append(r.Err, err.Error()) r.Err = append(r.Err, err.Error())
@@ -112,6 +112,7 @@ func (r *RouterInfo) AddError(err error, critical bool) {
// MiddlewareInfo holds information about a currently running middleware. // MiddlewareInfo holds information about a currently running middleware.
type MiddlewareInfo struct { type MiddlewareInfo struct {
*dynamic.Middleware // dynamic configuration *dynamic.Middleware // dynamic configuration
// Err contains all the errors that occurred during service creation. // Err contains all the errors that occurred during service creation.
Err []string `json:"error,omitempty"` Err []string `json:"error,omitempty"`
Status string `json:"status,omitempty"` Status string `json:"status,omitempty"`
@@ -121,10 +122,8 @@ type MiddlewareInfo struct {
// AddError adds err to s.Err, if it does not already exist. // AddError adds err to s.Err, if it does not already exist.
// If critical is set, m is marked as disabled. // If critical is set, m is marked as disabled.
func (m *MiddlewareInfo) AddError(err error, critical bool) { func (m *MiddlewareInfo) AddError(err error, critical bool) {
for _, value := range m.Err { if slices.Contains(m.Err, err.Error()) {
if value == err.Error() { return
return
}
} }
m.Err = append(m.Err, err.Error()) m.Err = append(m.Err, err.Error())
@@ -142,6 +141,7 @@ func (m *MiddlewareInfo) AddError(err error, critical bool) {
// ServiceInfo holds information about a currently running service. // ServiceInfo holds information about a currently running service.
type ServiceInfo struct { type ServiceInfo struct {
*dynamic.Service // dynamic configuration *dynamic.Service // dynamic configuration
// Err contains all the errors that occurred during service creation. // Err contains all the errors that occurred during service creation.
Err []string `json:"error,omitempty"` Err []string `json:"error,omitempty"`
// Status reports whether the service is disabled, in a warning state, or all good (enabled). // Status reports whether the service is disabled, in a warning state, or all good (enabled).
@@ -157,10 +157,8 @@ type ServiceInfo struct {
// AddError adds err to s.Err, if it does not already exist. // AddError adds err to s.Err, if it does not already exist.
// If critical is set, s is marked as disabled. // If critical is set, s is marked as disabled.
func (s *ServiceInfo) AddError(err error, critical bool) { func (s *ServiceInfo) AddError(err error, critical bool) {
for _, value := range s.Err { if slices.Contains(s.Err, err.Error()) {
if value == err.Error() { return
return
}
} }
s.Err = append(s.Err, err.Error()) s.Err = append(s.Err, err.Error())
@@ -197,9 +195,5 @@ func (s *ServiceInfo) GetAllStatus() map[string]string {
return nil return nil
} }
allStatus := make(map[string]string, len(s.serverStatus)) return maps.Clone(s.serverStatus)
for k, v := range s.serverStatus {
allStatus[k] = v
}
return allStatus
} }
+15 -19
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"maps"
"slices" "slices"
"sync" "sync"
@@ -49,8 +50,9 @@ func (c *Configuration) GetTCPRoutersByEntryPoints(ctx context.Context, entryPoi
// TCPRouterInfo holds information about a currently running TCP router. // TCPRouterInfo holds information about a currently running TCP router.
type TCPRouterInfo struct { type TCPRouterInfo struct {
*dynamic.TCPRouter // dynamic configuration *dynamic.TCPRouter // dynamic configuration
Err []string `json:"error,omitempty"` // initialization error
Err []string `json:"error,omitempty"` // initialization error
// Status reports whether the router is disabled, in a warning state, or all good (enabled). // Status reports whether the router is disabled, in a warning state, or all good (enabled).
// If not in "enabled" state, the reason for it should be in the list of Err. // If not in "enabled" state, the reason for it should be in the list of Err.
// It is the caller's responsibility to set the initial status. // It is the caller's responsibility to set the initial status.
@@ -61,10 +63,8 @@ type TCPRouterInfo struct {
// AddError adds err to r.Err, if it does not already exist. // AddError adds err to r.Err, if it does not already exist.
// If critical is set, r is marked as disabled. // If critical is set, r is marked as disabled.
func (r *TCPRouterInfo) AddError(err error, critical bool) { func (r *TCPRouterInfo) AddError(err error, critical bool) {
for _, value := range r.Err { if slices.Contains(r.Err, err.Error()) {
if value == err.Error() { return
return
}
} }
r.Err = append(r.Err, err.Error()) r.Err = append(r.Err, err.Error())
@@ -81,8 +81,9 @@ func (r *TCPRouterInfo) AddError(err error, critical bool) {
// TCPServiceInfo holds information about a currently running TCP service. // TCPServiceInfo holds information about a currently running TCP service.
type TCPServiceInfo struct { type TCPServiceInfo struct {
*dynamic.TCPService // dynamic configuration *dynamic.TCPService // dynamic configuration
Err []string `json:"error,omitempty"` // initialization error
Err []string `json:"error,omitempty"` // initialization error
// Status reports whether the service is disabled, in a warning state, or all good (enabled). // Status reports whether the service is disabled, in a warning state, or all good (enabled).
// If not in "enabled" state, the reason for it should be in the list of Err. // If not in "enabled" state, the reason for it should be in the list of Err.
// It is the caller's responsibility to set the initial status. // It is the caller's responsibility to set the initial status.
@@ -96,10 +97,8 @@ type TCPServiceInfo struct {
// AddError adds err to s.Err, if it does not already exist. // AddError adds err to s.Err, if it does not already exist.
// If critical is set, s is marked as disabled. // If critical is set, s is marked as disabled.
func (s *TCPServiceInfo) AddError(err error, critical bool) { func (s *TCPServiceInfo) AddError(err error, critical bool) {
for _, value := range s.Err { if slices.Contains(s.Err, err.Error()) {
if value == err.Error() { return
return
}
} }
s.Err = append(s.Err, err.Error()) s.Err = append(s.Err, err.Error())
@@ -135,15 +134,14 @@ func (s *TCPServiceInfo) GetAllStatus() map[string]string {
} }
allStatus := make(map[string]string, len(s.serverStatus)) allStatus := make(map[string]string, len(s.serverStatus))
for k, v := range s.serverStatus { maps.Copy(allStatus, s.serverStatus)
allStatus[k] = v
}
return allStatus return allStatus
} }
// TCPMiddlewareInfo holds information about a currently running middleware. // TCPMiddlewareInfo holds information about a currently running middleware.
type TCPMiddlewareInfo struct { type TCPMiddlewareInfo struct {
*dynamic.TCPMiddleware // dynamic configuration *dynamic.TCPMiddleware // dynamic configuration
// Err contains all the errors that occurred during service creation. // Err contains all the errors that occurred during service creation.
Err []string `json:"error,omitempty"` Err []string `json:"error,omitempty"`
Status string `json:"status,omitempty"` Status string `json:"status,omitempty"`
@@ -153,10 +151,8 @@ type TCPMiddlewareInfo struct {
// AddError adds err to s.Err, if it does not already exist. // AddError adds err to s.Err, if it does not already exist.
// If critical is set, m is marked as disabled. // If critical is set, m is marked as disabled.
func (m *TCPMiddlewareInfo) AddError(err error, critical bool) { func (m *TCPMiddlewareInfo) AddError(err error, critical bool) {
for _, value := range m.Err { if slices.Contains(m.Err, err.Error()) {
if value == err.Error() { return
return
}
} }
m.Err = append(m.Err, err.Error()) m.Err = append(m.Err, err.Error())
+10 -12
View File
@@ -54,8 +54,9 @@ func (c *Configuration) GetUDPRoutersByEntryPoints(ctx context.Context, entryPoi
// UDPRouterInfo holds information about a currently running UDP router. // UDPRouterInfo holds information about a currently running UDP router.
type UDPRouterInfo struct { type UDPRouterInfo struct {
*dynamic.UDPRouter // dynamic configuration *dynamic.UDPRouter // dynamic configuration
Err []string `json:"error,omitempty"` // initialization error
Err []string `json:"error,omitempty"` // initialization error
// Status reports whether the router is disabled, in a warning state, or all good (enabled). // Status reports whether the router is disabled, in a warning state, or all good (enabled).
// If not in "enabled" state, the reason for it should be in the list of Err. // If not in "enabled" state, the reason for it should be in the list of Err.
// It is the caller's responsibility to set the initial status. // It is the caller's responsibility to set the initial status.
@@ -66,10 +67,8 @@ type UDPRouterInfo struct {
// AddError adds err to r.Err, if it does not already exist. // AddError adds err to r.Err, if it does not already exist.
// If critical is set, r is marked as disabled. // If critical is set, r is marked as disabled.
func (r *UDPRouterInfo) AddError(err error, critical bool) { func (r *UDPRouterInfo) AddError(err error, critical bool) {
for _, value := range r.Err { if slices.Contains(r.Err, err.Error()) {
if value == err.Error() { return
return
}
} }
r.Err = append(r.Err, err.Error()) r.Err = append(r.Err, err.Error())
@@ -86,8 +85,9 @@ func (r *UDPRouterInfo) AddError(err error, critical bool) {
// UDPServiceInfo holds information about a currently running UDP service. // UDPServiceInfo holds information about a currently running UDP service.
type UDPServiceInfo struct { type UDPServiceInfo struct {
*dynamic.UDPService // dynamic configuration *dynamic.UDPService // dynamic configuration
Err []string `json:"error,omitempty"` // initialization error
Err []string `json:"error,omitempty"` // initialization error
// Status reports whether the service is disabled, in a warning state, or all good (enabled). // Status reports whether the service is disabled, in a warning state, or all good (enabled).
// If not in "enabled" state, the reason for it should be in the list of Err. // If not in "enabled" state, the reason for it should be in the list of Err.
// It is the caller's responsibility to set the initial status. // It is the caller's responsibility to set the initial status.
@@ -98,10 +98,8 @@ type UDPServiceInfo struct {
// AddError adds err to s.Err, if it does not already exist. // AddError adds err to s.Err, if it does not already exist.
// If critical is set, s is marked as disabled. // If critical is set, s is marked as disabled.
func (s *UDPServiceInfo) AddError(err error, critical bool) { func (s *UDPServiceInfo) AddError(err error, critical bool) {
for _, value := range s.Err { if slices.Contains(s.Err, err.Error()) {
if value == err.Error() { return
return
}
} }
s.Err = append(s.Err, err.Error()) s.Err = append(s.Err, err.Error())
+1 -1
View File
@@ -1,4 +1,4 @@
package static package static
// PluginConf holds the plugin configuration. // PluginConf holds the plugin configuration.
type PluginConf map[string]interface{} type PluginConf map[string]any
+15 -15
View File
@@ -393,21 +393,6 @@ func (c *Configuration) SetEffectiveConfiguration() {
c.initACMEProvider() c.initACMEProvider()
} }
func (c *Configuration) hasUserDefinedEntrypoint() bool {
return len(c.EntryPoints) != 0
}
func (c *Configuration) initACMEProvider() {
for _, resolver := range c.CertificatesResolvers {
if resolver.ACME != nil {
resolver.ACME.CAServer = getSafeACMECAServer(resolver.ACME.CAServer)
}
}
logger := logs.NoLevel(log.Logger, zerolog.DebugLevel).With().Str("lib", "lego").Logger()
legolog.Logger = logs.NewLogrusWrapper(logger)
}
// ValidateConfiguration validate that configuration is coherent. // ValidateConfiguration validate that configuration is coherent.
func (c *Configuration) ValidateConfiguration() error { func (c *Configuration) ValidateConfiguration() error {
for name, resolver := range c.CertificatesResolvers { for name, resolver := range c.CertificatesResolvers {
@@ -494,6 +479,21 @@ func (c *Configuration) ValidateConfiguration() error {
return nil return nil
} }
func (c *Configuration) hasUserDefinedEntrypoint() bool {
return len(c.EntryPoints) != 0
}
func (c *Configuration) initACMEProvider() {
for _, resolver := range c.CertificatesResolvers {
if resolver.ACME != nil {
resolver.ACME.CAServer = getSafeACMECAServer(resolver.ACME.CAServer)
}
}
logger := logs.NoLevel(log.Logger, zerolog.DebugLevel).With().Str("lib", "lego").Logger()
legolog.Logger = logs.NewLogrusWrapper(logger)
}
func getSafeACMECAServer(caServerSrc string) string { func getSafeACMECAServer(caServerSrc string) string {
if len(caServerSrc) == 0 { if len(caServerSrc) == 0 {
return DefaultAcmeCAServer return DefaultAcmeCAServer
+1 -1
View File
@@ -391,7 +391,7 @@ func (p *PassiveServiceHealthChecker) WrapHandler(ctx context.Context, next http
} }
// We need to guarantee that only one goroutine (request) will update the status and create a timer for the target. // We need to guarantee that only one goroutine (request) will update the status and create a timer for the target.
_, _, _ = p.timersGroup.Do(targetURL, func() (interface{}, error) { _, _, _ = p.timersGroup.Do(targetURL, func() (any, error) {
// A timer is already running for this target; // A timer is already running for this target;
// it means that the target is already considered unhealthy. // it means that the target is already considered unhealthy.
if _, ok := p.timers.Load(targetURL); ok { if _, ok := p.timers.Load(targetURL); ok {
+1
View File
@@ -165,6 +165,7 @@ type testLoadBalancer struct {
// RWMutex needed due to parallel test execution: Both the system-under-test // RWMutex needed due to parallel test execution: Both the system-under-test
// and the test assertions reference the counters. // and the test assertions reference the counters.
*sync.RWMutex *sync.RWMutex
numRemovedServers int numRemovedServers int
numUpsertedServers int numUpsertedServers int
} }
+1
View File
@@ -17,6 +17,7 @@ const (
// If operation() takes more than MinJobInterval, Reset() is called in NextBackOff(). // If operation() takes more than MinJobInterval, Reset() is called in NextBackOff().
type BackOff struct { type BackOff struct {
*backoff.ExponentialBackOff *backoff.ExponentialBackOff
MinJobInterval time.Duration MinJobInterval time.Duration
} }
@@ -12,7 +12,7 @@ import (
func TestConcatFieldHandler_ServeHTTP(t *testing.T) { func TestConcatFieldHandler_ServeHTTP(t *testing.T) {
testCases := []struct { testCases := []struct {
desc string desc string
existingValue interface{} existingValue any
newValue string newValue string
expectedResult string expectedResult string
}{ }{
+1 -1
View File
@@ -129,7 +129,7 @@ func init() {
} }
// CoreLogData holds the fields computed from the request/response. // CoreLogData holds the fields computed from the request/response.
type CoreLogData map[string]interface{} type CoreLogData map[string]any
// LogData is the data captured by the middleware so that it can be logged. // LogData is the data captured by the middleware so that it can be logged.
type LogData struct { type LogData struct {
+51 -51
View File
@@ -74,20 +74,6 @@ type Handler struct {
wg sync.WaitGroup wg sync.WaitGroup
} }
// AliceConstructor returns an alice.Constructor that wraps the Handler (conditionally) in a middleware chain.
func (h *Handler) AliceConstructor() alice.Constructor {
return func(next http.Handler) (http.Handler, error) {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if h == nil {
next.ServeHTTP(rw, req)
return
}
h.ServeHTTP(rw, req, next)
}), nil
}
}
// NewHandler creates a new Handler. // NewHandler creates a new Handler.
func NewHandler(ctx context.Context, config *otypes.AccessLog) (*Handler, error) { func NewHandler(ctx context.Context, config *otypes.AccessLog) (*Handler, error) {
var file io.WriteCloser = noopCloser{os.Stdout} var file io.WriteCloser = noopCloser{os.Stdout}
@@ -183,28 +169,18 @@ func NewHandler(ctx context.Context, config *otypes.AccessLog) (*Handler, error)
return logHandler, nil return logHandler, nil
} }
func openAccessLogFile(filePath string) (*os.File, error) { // AliceConstructor returns an alice.Constructor that wraps the Handler (conditionally) in a middleware chain.
dir := filepath.Dir(filePath) func (h *Handler) AliceConstructor() alice.Constructor {
return func(next http.Handler) (http.Handler, error) {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if h == nil {
next.ServeHTTP(rw, req)
return
}
if err := os.MkdirAll(dir, 0o755); err != nil { h.ServeHTTP(rw, req, next)
return nil, fmt.Errorf("failed to create log path %s: %w", dir, err) }), nil
} }
file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o664)
if err != nil {
return nil, fmt.Errorf("error opening file %s: %w", filePath, err)
}
return file, nil
}
// GetLogData gets the request context object that contains logging data.
// This creates data as the request passes through the middleware chain.
func GetLogData(req *http.Request) *LogData {
if ld, ok := req.Context().Value(DataTableKey).(*LogData); ok {
return ld
}
return nil
} }
func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http.Handler) { func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http.Handler) {
@@ -335,23 +311,6 @@ func (h *Handler) Rotate() error {
return nil return nil
} }
func silentSplitHostPort(value string) (host, port string) {
host, port, err := net.SplitHostPort(value)
if err != nil {
return value, "-"
}
return host, port
}
func usernameIfPresent(theURL *url.URL) string {
if theURL.User != nil {
if name := theURL.User.Username(); name != "" {
return name
}
}
return "-"
}
// Logging handler to log frontend name, backend name, and elapsed time. // Logging handler to log frontend name, backend name, and elapsed time.
func (h *Handler) logTheRoundTrip(ctx context.Context, logDataTable *LogData) { func (h *Handler) logTheRoundTrip(ctx context.Context, logDataTable *LogData) {
core := logDataTable.Core core := logDataTable.Core
@@ -458,6 +417,47 @@ func (h *Handler) keepAccessLog(statusCode, retryAttempts int, duration time.Dur
return false return false
} }
// GetLogData gets the request context object that contains logging data.
// This creates data as the request passes through the middleware chain.
func GetLogData(req *http.Request) *LogData {
if ld, ok := req.Context().Value(DataTableKey).(*LogData); ok {
return ld
}
return nil
}
func openAccessLogFile(filePath string) (*os.File, error) {
dir := filepath.Dir(filePath)
if err := os.MkdirAll(dir, 0o755); err != nil {
return nil, fmt.Errorf("failed to create log path %s: %w", dir, err)
}
file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o664)
if err != nil {
return nil, fmt.Errorf("error opening file %s: %w", filePath, err)
}
return file, nil
}
func silentSplitHostPort(value string) (host, port string) {
host, port, err := net.SplitHostPort(value)
if err != nil {
return value, "-"
}
return host, port
}
func usernameIfPresent(theURL *url.URL) string {
if theURL.User != nil {
if name := theURL.User.Username(); name != "" {
return name
}
}
return "-"
}
var requestCounter uint64 // Request ID var requestCounter uint64 // Request ID
func nextRequestCount() uint64 { func nextRequestCount() uint64 {
@@ -81,7 +81,7 @@ func (f *GenericCLFLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
return b.Bytes(), err return b.Bytes(), err
} }
func toLog(fields logrus.Fields, key, defaultValue string, quoted bool) interface{} { func toLog(fields logrus.Fields, key, defaultValue string, quoted bool) any {
if v, ok := fields[key]; ok { if v, ok := fields[key]; ok {
if v == nil { if v == nil {
return defaultValue return defaultValue
@@ -15,12 +15,12 @@ func TestCommonLogFormatter_Format(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
data map[string]interface{} data map[string]any
expectedLog string expectedLog string
}{ }{
{ {
name: "DownstreamStatus & DownstreamContentSize are nil", name: "DownstreamStatus & DownstreamContentSize are nil",
data: map[string]interface{}{ data: map[string]any{
StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second, Duration: 123 * time.Second,
ClientHost: "10.0.0.1", ClientHost: "10.0.0.1",
@@ -41,7 +41,7 @@ func TestCommonLogFormatter_Format(t *testing.T) {
}, },
{ {
name: "all data", name: "all data",
data: map[string]interface{}{ data: map[string]any{
StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second, Duration: 123 * time.Second,
ClientHost: "10.0.0.1", ClientHost: "10.0.0.1",
@@ -62,7 +62,7 @@ func TestCommonLogFormatter_Format(t *testing.T) {
}, },
{ {
name: "all data with local time", name: "all data with local time",
data: map[string]interface{}{ data: map[string]any{
StartLocal: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), StartLocal: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second, Duration: 123 * time.Second,
ClientHost: "10.0.0.1", ClientHost: "10.0.0.1",
@@ -106,12 +106,12 @@ func TestGenericCLFLogFormatter_Format(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
data map[string]interface{} data map[string]any
expectedLog string expectedLog string
}{ }{
{ {
name: "DownstreamStatus & DownstreamContentSize are nil", name: "DownstreamStatus & DownstreamContentSize are nil",
data: map[string]interface{}{ data: map[string]any{
StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second, Duration: 123 * time.Second,
ClientHost: "10.0.0.1", ClientHost: "10.0.0.1",
@@ -132,7 +132,7 @@ func TestGenericCLFLogFormatter_Format(t *testing.T) {
}, },
{ {
name: "all data", name: "all data",
data: map[string]interface{}{ data: map[string]any{
StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second, Duration: 123 * time.Second,
ClientHost: "10.0.0.1", ClientHost: "10.0.0.1",
@@ -153,7 +153,7 @@ func TestGenericCLFLogFormatter_Format(t *testing.T) {
}, },
{ {
name: "all data with local time", name: "all data with local time",
data: map[string]interface{}{ data: map[string]any{
StartLocal: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), StartLocal: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Duration: 123 * time.Second, Duration: 123 * time.Second,
ClientHost: "10.0.0.1", ClientHost: "10.0.0.1",
@@ -199,7 +199,7 @@ func Test_toLog(t *testing.T) {
fieldName string fieldName string
defaultValue string defaultValue string
quoted bool quoted bool
expectedLog interface{} expectedLog any
}{ }{
{ {
desc: "Should return int 1", desc: "Should return int 1",
+21 -21
View File
@@ -261,7 +261,7 @@ func lineCount(t *testing.T, fileName string) int {
} }
count := 0 count := 0
for _, line := range strings.Split(string(fileContents), "\n") { for line := range strings.SplitSeq(string(fileContents), "\n") {
if strings.TrimSpace(line) == "" { if strings.TrimSpace(line) == "" {
continue continue
} }
@@ -439,32 +439,32 @@ func TestLoggerGenericCLFWithBufferingSize(t *testing.T) {
assertValidGenericCLFLogData(t, expectedLog, logData) assertValidGenericCLFLogData(t, expectedLog, logData)
} }
func assertString(exp string) func(t *testing.T, actual interface{}) { func assertString(exp string) func(t *testing.T, actual any) {
return func(t *testing.T, actual interface{}) { return func(t *testing.T, actual any) {
t.Helper() t.Helper()
assert.Equal(t, exp, actual) assert.Equal(t, exp, actual)
} }
} }
func assertNotEmpty() func(t *testing.T, actual interface{}) { func assertNotEmpty() func(t *testing.T, actual any) {
return func(t *testing.T, actual interface{}) { return func(t *testing.T, actual any) {
t.Helper() t.Helper()
assert.NotEmpty(t, actual) assert.NotEmpty(t, actual)
} }
} }
func assertFloat64(exp float64) func(t *testing.T, actual interface{}) { func assertFloat64(exp float64) func(t *testing.T, actual any) {
return func(t *testing.T, actual interface{}) { return func(t *testing.T, actual any) {
t.Helper() t.Helper()
assert.InDelta(t, exp, actual, delta) assert.InDelta(t, exp, actual, delta)
} }
} }
func assertFloat64NotZero() func(t *testing.T, actual interface{}) { func assertFloat64NotZero() func(t *testing.T, actual any) {
return func(t *testing.T, actual interface{}) { return func(t *testing.T, actual any) {
t.Helper() t.Helper()
assert.NotZero(t, actual) assert.NotZero(t, actual)
@@ -477,7 +477,7 @@ func TestLoggerJSON(t *testing.T) {
config *otypes.AccessLog config *otypes.AccessLog
tls bool tls bool
tracing bool tracing bool
expected map[string]func(t *testing.T, value interface{}) expected map[string]func(t *testing.T, value any)
}{ }{
{ {
desc: "default config without tracing", desc: "default config without tracing",
@@ -485,7 +485,7 @@ func TestLoggerJSON(t *testing.T) {
FilePath: "", FilePath: "",
Format: JSONFormat, Format: JSONFormat,
}, },
expected: map[string]func(t *testing.T, value interface{}){ expected: map[string]func(t *testing.T, value any){
RequestContentSize: assertFloat64(0), RequestContentSize: assertFloat64(0),
RequestHost: assertString(testHostname), RequestHost: assertString(testHostname),
RequestAddr: assertString(testHostname), RequestAddr: assertString(testHostname),
@@ -525,7 +525,7 @@ func TestLoggerJSON(t *testing.T) {
Format: JSONFormat, Format: JSONFormat,
}, },
tracing: true, tracing: true,
expected: map[string]func(t *testing.T, value interface{}){ expected: map[string]func(t *testing.T, value any){
RequestContentSize: assertFloat64(0), RequestContentSize: assertFloat64(0),
RequestHost: assertString(testHostname), RequestHost: assertString(testHostname),
RequestAddr: assertString(testHostname), RequestAddr: assertString(testHostname),
@@ -567,7 +567,7 @@ func TestLoggerJSON(t *testing.T) {
Format: JSONFormat, Format: JSONFormat,
}, },
tls: true, tls: true,
expected: map[string]func(t *testing.T, value interface{}){ expected: map[string]func(t *testing.T, value any){
RequestContentSize: assertFloat64(0), RequestContentSize: assertFloat64(0),
RequestHost: assertString(testHostname), RequestHost: assertString(testHostname),
RequestAddr: assertString(testHostname), RequestAddr: assertString(testHostname),
@@ -612,7 +612,7 @@ func TestLoggerJSON(t *testing.T) {
DefaultMode: "drop", DefaultMode: "drop",
}, },
}, },
expected: map[string]func(t *testing.T, value interface{}){ expected: map[string]func(t *testing.T, value any){
"level": assertString("info"), "level": assertString("info"),
"msg": assertString(""), "msg": assertString(""),
"time": assertNotEmpty(), "time": assertNotEmpty(),
@@ -633,7 +633,7 @@ func TestLoggerJSON(t *testing.T) {
}, },
}, },
}, },
expected: map[string]func(t *testing.T, value interface{}){ expected: map[string]func(t *testing.T, value any){
"level": assertString("info"), "level": assertString("info"),
"msg": assertString(""), "msg": assertString(""),
"time": assertNotEmpty(), "time": assertNotEmpty(),
@@ -651,7 +651,7 @@ func TestLoggerJSON(t *testing.T) {
}, },
}, },
}, },
expected: map[string]func(t *testing.T, value interface{}){ expected: map[string]func(t *testing.T, value any){
"level": assertString("info"), "level": assertString("info"),
"msg": assertString(""), "msg": assertString(""),
"time": assertNotEmpty(), "time": assertNotEmpty(),
@@ -678,7 +678,7 @@ func TestLoggerJSON(t *testing.T) {
}, },
}, },
}, },
expected: map[string]func(t *testing.T, value interface{}){ expected: map[string]func(t *testing.T, value any){
RequestHost: assertString(testHostname), RequestHost: assertString(testHostname),
"level": assertString("info"), "level": assertString("info"),
"msg": assertString(""), "msg": assertString(""),
@@ -704,7 +704,7 @@ func TestLoggerJSON(t *testing.T) {
}, },
}, },
}, },
expected: map[string]func(t *testing.T, value interface{}){ expected: map[string]func(t *testing.T, value any){
RequestHost: assertString(testHostname), RequestHost: assertString(testHostname),
"level": assertString("info"), "level": assertString("info"),
"msg": assertString(""), "msg": assertString(""),
@@ -730,7 +730,7 @@ func TestLoggerJSON(t *testing.T) {
logData, err := os.ReadFile(logFilePath) logData, err := os.ReadFile(logFilePath)
require.NoError(t, err) require.NoError(t, err)
jsonData := make(map[string]interface{}) jsonData := make(map[string]any)
err = json.Unmarshal(logData, &jsonData) err = json.Unmarshal(logData, &jsonData)
require.NoError(t, err) require.NoError(t, err)
@@ -744,7 +744,7 @@ func TestLoggerJSON(t *testing.T) {
} }
func TestLogger_AbortedRequest(t *testing.T) { func TestLogger_AbortedRequest(t *testing.T) {
expected := map[string]func(t *testing.T, value interface{}){ expected := map[string]func(t *testing.T, value any){
RequestContentSize: assertFloat64(0), RequestContentSize: assertFloat64(0),
RequestHost: assertString(testHostname), RequestHost: assertString(testHostname),
RequestAddr: assertString(testHostname), RequestAddr: assertString(testHostname),
@@ -787,7 +787,7 @@ func TestLogger_AbortedRequest(t *testing.T) {
logData, err := os.ReadFile(config.FilePath) logData, err := os.ReadFile(config.FilePath)
require.NoError(t, err) require.NoError(t, err)
jsonData := make(map[string]interface{}) jsonData := make(map[string]any)
err = json.Unmarshal(logData, &jsonData) err = json.Unmarshal(logData, &jsonData)
require.NoError(t, err) require.NoError(t, err)
+1 -1
View File
@@ -22,7 +22,7 @@ func RemoveConnectionHeaders(req *http.Request) {
} }
for _, f := range req.Header[connectionHeader] { for _, f := range req.Header[connectionHeader] {
for _, sf := range strings.Split(f, ",") { for sf := range strings.SplitSeq(f, ",") {
if sf = textproto.TrimString(sf); sf != "" { if sf = textproto.TrimString(sf); sf != "" {
req.Header.Del(sf) req.Header.Del(sf)
} }
+14 -14
View File
@@ -96,20 +96,6 @@ func (c *Capture) Reset(next http.Handler) http.Handler {
}) })
} }
func (c *Capture) renew(rw http.ResponseWriter, req *http.Request) (http.ResponseWriter, *http.Request) {
ctx := context.WithValue(req.Context(), capturedData, c)
newReq := req.WithContext(ctx)
if newReq.Body != nil {
readCounter := &readCounter{source: newReq.Body}
c.rr = readCounter
newReq.Body = readCounter
}
c.crw = &captureResponseWriter{rw: rw}
return c.crw, newReq
}
func (c *Capture) ResponseSize() int64 { func (c *Capture) ResponseSize() int64 {
return c.crw.Size() return c.crw.Size()
} }
@@ -127,6 +113,20 @@ func (c *Capture) RequestSize() int64 {
return c.rr.size return c.rr.size
} }
func (c *Capture) renew(rw http.ResponseWriter, req *http.Request) (http.ResponseWriter, *http.Request) {
ctx := context.WithValue(req.Context(), capturedData, c)
newReq := req.WithContext(ctx)
if newReq.Body != nil {
readCounter := &readCounter{source: newReq.Body}
c.rr = readCounter
newReq.Body = readCounter
}
c.crw = &captureResponseWriter{rw: rw}
return c.crw, newReq
}
type readCounter struct { type readCounter struct {
// source ReadCloser from where the request body is read. // source ReadCloser from where the request body is read.
source io.ReadCloser source io.ReadCloser
+1 -1
View File
@@ -63,7 +63,7 @@ func parseAcceptableEncodings(acceptEncoding []string, supportedEncodings map[st
var encodings []Encoding var encodings []Encoding
for _, line := range acceptEncoding { for _, line := range acceptEncoding {
for _, item := range strings.Split(strings.ReplaceAll(line, " ", ""), ",") { for item := range strings.SplitSeq(strings.ReplaceAll(line, " ", ""), ",") {
parsed := strings.SplitN(item, ";", 2) parsed := strings.SplitN(item, ";", 2)
if len(parsed) == 0 { if len(parsed) == 0 {
continue continue
+4 -4
View File
@@ -167,6 +167,10 @@ func (c *compress) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
c.chooseHandler(c.getCompressionEncoding(acceptEncoding), rw, req) c.chooseHandler(c.getCompressionEncoding(acceptEncoding), rw, req)
} }
func (c *compress) GetTracingInformation() (string, string) {
return c.name, typeName
}
func (c *compress) chooseHandler(typ string, rw http.ResponseWriter, req *http.Request) { func (c *compress) chooseHandler(typ string, rw http.ResponseWriter, req *http.Request) {
switch typ { switch typ {
case zstdName: case zstdName:
@@ -180,10 +184,6 @@ func (c *compress) chooseHandler(typ string, rw http.ResponseWriter, req *http.R
} }
} }
func (c *compress) GetTracingInformation() (string, string) {
return c.name, typeName
}
func (c *compress) newGzipHandler() (http.Handler, error) { func (c *compress) newGzipHandler() (http.Handler, error) {
var wrapper func(http.Handler) http.HandlerFunc var wrapper func(http.Handler) http.HandlerFunc
var err error var err error
@@ -156,6 +156,7 @@ func (c *CompressionHandler) putCompressionWriter(writer *compressionWriterWrapp
type compressionWriterWrapper struct { type compressionWriterWrapper struct {
CompressionWriter CompressionWriter
algo string algo string
} }
@@ -874,10 +874,7 @@ func Test_FlushExcludedContentTypes(t *testing.T) {
for len(tb) > 0 { for len(tb) > 0 {
// Write 100 bytes per run // Write 100 bytes per run
// Detection should not be affected (we send 100 bytes) // Detection should not be affected (we send 100 bytes)
toWrite := 100 toWrite := min(100, len(tb))
if toWrite > len(tb) {
toWrite = len(tb)
}
_, err := rw.Write(tb[:toWrite]) _, err := rw.Write(tb[:toWrite])
require.NoError(t, err) require.NoError(t, err)
@@ -998,10 +995,7 @@ func Test_FlushIncludedContentTypes(t *testing.T) {
for len(tb) > 0 { for len(tb) > 0 {
// Write 100 bytes per run // Write 100 bytes per run
// Detection should not be affected (we send 100 bytes) // Detection should not be affected (we send 100 bytes)
toWrite := 100 toWrite := min(100, len(tb))
if toWrite > len(tb) {
toWrite = len(tb)
}
_, err := rw.Write(tb[:toWrite]) _, err := rw.Write(tb[:toWrite])
require.NoError(t, err) require.NoError(t, err)
+17 -22
View File
@@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"context" "context"
"fmt" "fmt"
"maps"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@@ -198,16 +199,6 @@ func (cc *codeCatcher) Header() http.Header {
return cc.headerMap return cc.headerMap
} }
func (cc *codeCatcher) getCode() int {
return cc.code
}
// isFilteredCode returns whether the codeCatcher received a response code among the ones it is watching,
// and for which the response should be deferred to the error handler.
func (cc *codeCatcher) isFilteredCode() bool {
return cc.caughtFilteredCode
}
func (cc *codeCatcher) Write(buf []byte) (int, error) { func (cc *codeCatcher) Write(buf []byte) (int, error) {
// If WriteHeader was already called from the caller, this is a NOOP. // If WriteHeader was already called from the caller, this is a NOOP.
// Otherwise, cc.code is actually a 200 here. // Otherwise, cc.code is actually a 200 here.
@@ -233,9 +224,7 @@ func (cc *codeCatcher) WriteHeader(code int) {
if code >= 100 && code <= 199 { if code >= 100 && code <= 199 {
// Multiple informational status codes can be used, // Multiple informational status codes can be used,
// so here the copy is not appending the values to not repeat them. // so here the copy is not appending the values to not repeat them.
for k, v := range cc.Header() { maps.Copy(cc.responseWriter.Header(), cc.Header())
cc.responseWriter.Header()[k] = v
}
cc.responseWriter.WriteHeader(code) cc.responseWriter.WriteHeader(code)
return return
@@ -253,9 +242,8 @@ func (cc *codeCatcher) WriteHeader(code int) {
// The copy is not appending the values, // The copy is not appending the values,
// to not repeat them in case any informational status code has been written. // to not repeat them in case any informational status code has been written.
for k, v := range cc.Header() { maps.Copy(cc.responseWriter.Header(), cc.Header())
cc.responseWriter.Header()[k] = v
}
cc.responseWriter.WriteHeader(cc.code) cc.responseWriter.WriteHeader(cc.code)
cc.headersSent = true cc.headersSent = true
} }
@@ -288,6 +276,16 @@ func (cc *codeCatcher) Flush() {
} }
} }
func (cc *codeCatcher) getCode() int {
return cc.code
}
// isFilteredCode returns whether the codeCatcher received a response code among the ones it is watching,
// and for which the response should be deferred to the error handler.
func (cc *codeCatcher) isFilteredCode() bool {
return cc.caughtFilteredCode
}
// codeModifier forwards a response back to the client, // codeModifier forwards a response back to the client,
// while enforcing a given response code. // while enforcing a given response code.
type codeModifier struct { type codeModifier struct {
@@ -343,17 +341,14 @@ func (r *codeModifier) WriteHeader(code int) {
if code >= 100 && code <= 199 { if code >= 100 && code <= 199 {
// Multiple informational status codes can be used, // Multiple informational status codes can be used,
// so here the copy is not appending the values to not repeat them. // so here the copy is not appending the values to not repeat them.
for k, v := range r.headerMap { maps.Copy(r.responseWriter.Header(), r.headerMap)
r.responseWriter.Header()[k] = v
}
r.responseWriter.WriteHeader(code) r.responseWriter.WriteHeader(code)
return return
} }
for k, v := range r.headerMap { maps.Copy(r.responseWriter.Header(), r.headerMap)
r.responseWriter.Header()[k] = v
}
r.responseWriter.WriteHeader(r.code) r.responseWriter.WriteHeader(r.code)
r.headerSent = true r.headerSent = true
} }
@@ -81,18 +81,11 @@ func NewXForwarded(insecure bool, trustedIPs []string, connectionHeaders []strin
}, nil }, nil
} }
func (x *XForwarded) isTrustedIP(ip string) bool {
if x.ipChecker == nil {
return false
}
return x.ipChecker.IsAuthorized(ip) == nil
}
// removeIPv6Zone removes the zone if the given IP is an ipv6 address and it has {zone} information in it, // removeIPv6Zone removes the zone if the given IP is an ipv6 address and it has {zone} information in it,
// like "[fe80::d806:a55d:eb1b:49cc%vEthernet (vmxnet3 Ethernet Adapter - Virtual Switch)]:64692". // like "[fe80::d806:a55d:eb1b:49cc%vEthernet (vmxnet3 Ethernet Adapter - Virtual Switch)]:64692".
func removeIPv6Zone(clientIP string) string { func removeIPv6Zone(clientIP string) string {
if idx := strings.Index(clientIP, "%"); idx != -1 { if before, _, found := strings.Cut(clientIP, "%"); found {
return clientIP[:idx] return before
} }
return clientIP return clientIP
} }
@@ -138,6 +131,28 @@ func forwardedPort(req *http.Request) string {
return "80" return "80"
} }
// ServeHTTP implements http.Handler.
func (x *XForwarded) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !x.insecure && !x.isTrustedIP(r.RemoteAddr) {
for _, h := range xHeaders {
unsafeHeader(r.Header).Del(h)
}
}
x.rewrite(r)
x.removeConnectionHeaders(r)
x.next.ServeHTTP(w, r)
}
func (x *XForwarded) isTrustedIP(ip string) bool {
if x.ipChecker == nil {
return false
}
return x.ipChecker.IsAuthorized(ip) == nil
}
func (x *XForwarded) rewrite(outreq *http.Request) { func (x *XForwarded) rewrite(outreq *http.Request) {
if clientIP, _, err := net.SplitHostPort(outreq.RemoteAddr); err == nil { if clientIP, _, err := net.SplitHostPort(outreq.RemoteAddr); err == nil {
clientIP = removeIPv6Zone(clientIP) clientIP = removeIPv6Zone(clientIP)
@@ -186,21 +201,6 @@ func (x *XForwarded) rewrite(outreq *http.Request) {
} }
} }
// ServeHTTP implements http.Handler.
func (x *XForwarded) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !x.insecure && !x.isTrustedIP(r.RemoteAddr) {
for _, h := range xHeaders {
unsafeHeader(r.Header).Del(h)
}
}
x.rewrite(r)
x.removeConnectionHeaders(r)
x.next.ServeHTTP(w, r)
}
func (x *XForwarded) removeConnectionHeaders(req *http.Request) { func (x *XForwarded) removeConnectionHeaders(req *http.Request) {
var reqUpType string var reqUpType string
if httpguts.HeaderValuesContainsToken(req.Header[connection], upgrade) { if httpguts.HeaderValuesContainsToken(req.Header[connection], upgrade) {
@@ -209,7 +209,7 @@ func (x *XForwarded) removeConnectionHeaders(req *http.Request) {
var connectionHopByHopHeaders []string var connectionHopByHopHeaders []string
for _, f := range req.Header[connection] { for _, f := range req.Header[connection] {
for _, sf := range strings.Split(f, ",") { for sf := range strings.SplitSeq(f, ",") {
if sf = textproto.TrimString(sf); sf != "" { if sf = textproto.TrimString(sf); sf != "" {
// Connection header cannot dictate to remove X- headers managed by Traefik, // Connection header cannot dictate to remove X- headers managed by Traefik,
// as per rfc7230 https://datatracker.ietf.org/doc/html/rfc7230#section-6.1, // as per rfc7230 https://datatracker.ietf.org/doc/html/rfc7230#section-6.1,
@@ -1,6 +1,7 @@
package headermodifier package headermodifier
import ( import (
"maps"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@@ -105,9 +106,7 @@ func TestRequestHeaderModifier(t *testing.T) {
handler := NewRequestHeaderModifier(t.Context(), next, test.config, "foo-request-header-modifier") handler := NewRequestHeaderModifier(t.Context(), next, test.config, "foo-request-header-modifier")
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil) req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil)
for h, v := range test.requestHeaders { maps.Copy(req.Header, test.requestHeaders)
req.Header[h] = v
}
resp := httptest.NewRecorder() resp := httptest.NewRecorder()
handler.ServeHTTP(resp, req) handler.ServeHTTP(resp, req)
@@ -1,6 +1,7 @@
package headermodifier package headermodifier
import ( import (
"maps"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@@ -107,9 +108,7 @@ func TestResponseHeaderModifier(t *testing.T) {
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil) req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil)
resp := httptest.NewRecorder() resp := httptest.NewRecorder()
for k, v := range test.responseHeaders { maps.Copy(resp.Header(), test.responseHeaders)
resp.Header()[k] = v
}
handler.ServeHTTP(resp, req) handler.ServeHTTP(resp, req)
+21 -21
View File
@@ -64,27 +64,6 @@ func (s *Header) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
} }
} }
// modifyCustomRequestHeaders sets or deletes custom request headers.
func (s *Header) modifyCustomRequestHeaders(req *http.Request) {
// Loop through Custom request headers
for header, value := range s.headers.CustomRequestHeaders {
switch {
// Handling https://github.com/golang/go/commit/ecdbffd4ec68b509998792f120868fec319de59b.
case value == "" && header == forward.XForwardedFor:
req.Header[header] = nil
case value == "":
req.Header.Del(header)
case strings.EqualFold(header, "Host"):
req.Host = value
default:
req.Header.Set(header, value)
}
}
}
// PostRequestModifyResponseHeaders set or delete response headers. // PostRequestModifyResponseHeaders set or delete response headers.
// This method is called AFTER the response is generated from the backend // This method is called AFTER the response is generated from the backend
// and can merge/override headers from the backend response. // and can merge/override headers from the backend response.
@@ -134,6 +113,27 @@ func (s *Header) PostRequestModifyResponseHeaders(res *http.Response) error {
return nil return nil
} }
// modifyCustomRequestHeaders sets or deletes custom request headers.
func (s *Header) modifyCustomRequestHeaders(req *http.Request) {
// Loop through Custom request headers
for header, value := range s.headers.CustomRequestHeaders {
switch {
// Handling https://github.com/golang/go/commit/ecdbffd4ec68b509998792f120868fec319de59b.
case value == "" && header == forward.XForwardedFor:
req.Header[header] = nil
case value == "":
req.Header.Del(header)
case strings.EqualFold(header, "Host"):
req.Host = value
default:
req.Header.Set(header, value)
}
}
}
// processCorsHeaders processes the incoming request, // processCorsHeaders processes the incoming request,
// and returns if it is a preflight request. // and returns if it is a preflight request.
// If not a preflight, it handles the preRequestModifyCorsResponseHeaders. // If not a preflight, it handles the preRequestModifyCorsResponseHeaders.
@@ -65,7 +65,7 @@ func DetailedTracingEnabled(ctx context.Context) bool {
} }
// SetStatusErrorf flags the span as in error and log an event. // SetStatusErrorf flags the span as in error and log an event.
func SetStatusErrorf(ctx context.Context, format string, args ...interface{}) { func SetStatusErrorf(ctx context.Context, format string, args ...any) {
if span := trace.SpanFromContext(ctx); span != nil { if span := trace.SpanFromContext(ctx); span != nil {
span.SetStatus(codes.Error, fmt.Sprintf(format, args...)) span.SetStatus(codes.Error, fmt.Sprintf(format, args...))
} }
@@ -13,6 +13,7 @@ func newStatusCodeRecorder(rw http.ResponseWriter, status int) *statusCodeRecord
type statusCodeRecorder struct { type statusCodeRecorder struct {
http.ResponseWriter http.ResponseWriter
status int status int
} }
+4 -4
View File
@@ -7,14 +7,14 @@ import (
) )
type Rediser interface { type Rediser interface {
Eval(ctx context.Context, script string, keys []string, args ...interface{}) *redis.Cmd Eval(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd
EvalSha(ctx context.Context, sha1 string, keys []string, args ...interface{}) *redis.Cmd EvalSha(ctx context.Context, sha1 string, keys []string, args ...any) *redis.Cmd
ScriptExists(ctx context.Context, hashes ...string) *redis.BoolSliceCmd ScriptExists(ctx context.Context, hashes ...string) *redis.BoolSliceCmd
ScriptLoad(ctx context.Context, script string) *redis.StringCmd ScriptLoad(ctx context.Context, script string) *redis.StringCmd
Del(ctx context.Context, keys ...string) *redis.IntCmd Del(ctx context.Context, keys ...string) *redis.IntCmd
EvalRO(ctx context.Context, script string, keys []string, args ...interface{}) *redis.Cmd EvalRO(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd
EvalShaRO(ctx context.Context, sha1 string, keys []string, args ...interface{}) *redis.Cmd EvalShaRO(ctx context.Context, sha1 string, keys []string, args ...any) *redis.Cmd
} }
//nolint:dupword //nolint:dupword
+1 -4
View File
@@ -61,10 +61,7 @@ func New(ctx context.Context, next http.Handler, config dynamic.RateLimit, name
return nil, fmt.Errorf("getting source extractor: %w", err) return nil, fmt.Errorf("getting source extractor: %w", err)
} }
burst := config.Burst burst := max(config.Burst, 1)
if burst < 1 {
burst = 1
}
period := time.Duration(config.Period) period := time.Duration(config.Period)
if period < 0 { if period < 0 {
@@ -300,11 +300,8 @@ func TestInMemoryRateLimit(t *testing.T) {
stop := time.Now() stop := time.Now()
elapsed := stop.Sub(start) elapsed := stop.Sub(start)
burst := test.config.Burst // actual default value if burst < 1
if burst < 1 { burst := max(test.config.Burst, 1)
// actual default value
burst = 1
}
period := time.Duration(test.config.Period) period := time.Duration(test.config.Period)
if period == 0 { if period == 0 {
@@ -510,11 +507,8 @@ func TestRedisRateLimit(t *testing.T) {
stop := time.Now() stop := time.Now()
elapsed := stop.Sub(start) elapsed := stop.Sub(start)
burst := test.config.Burst // actual default value
if burst < 1 { burst := max(test.config.Burst, 1)
// actual default value
burst = 1
}
period := time.Duration(test.config.Period) period := time.Duration(test.config.Period)
if period == 0 { if period == 0 {
@@ -570,7 +564,7 @@ func newMockRedisClient(ttl int) Rediser {
} }
} }
func (m *mockRedisClient) EvalSha(ctx context.Context, _ string, keys []string, args ...interface{}) *redis.Cmd { func (m *mockRedisClient) EvalSha(ctx context.Context, _ string, keys []string, args ...any) *redis.Cmd {
state := lua.NewState() state := lua.NewState()
defer state.Close() defer state.Close()
@@ -641,7 +635,7 @@ func (m *mockRedisClient) EvalSha(ctx context.Context, _ string, keys []string,
return cmd return cmd
} }
var resultSlice []interface{} var resultSlice []any
resultTable.ForEach(func(_ lua.LValue, value lua.LValue) { resultTable.ForEach(func(_ lua.LValue, value lua.LValue) {
valueNbr, ok := value.(lua.LNumber) valueNbr, ok := value.(lua.LNumber)
if !ok { if !ok {
@@ -661,7 +655,7 @@ func (m *mockRedisClient) EvalSha(ctx context.Context, _ string, keys []string,
return cmd return cmd
} }
func (m *mockRedisClient) Eval(ctx context.Context, script string, keys []string, args ...interface{}) *redis.Cmd { func (m *mockRedisClient) Eval(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd {
return m.EvalSha(ctx, script, keys, args...) return m.EvalSha(ctx, script, keys, args...)
} }
@@ -677,11 +671,11 @@ func (m *mockRedisClient) Del(ctx context.Context, keys ...string) *redis.IntCmd
return nil return nil
} }
func (m *mockRedisClient) EvalRO(ctx context.Context, script string, keys []string, args ...interface{}) *redis.Cmd { func (m *mockRedisClient) EvalRO(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd {
return nil return nil
} }
func (m *mockRedisClient) EvalShaRO(ctx context.Context, sha1 string, keys []string, args ...interface{}) *redis.Cmd { func (m *mockRedisClient) EvalShaRO(ctx context.Context, sha1 string, keys []string, args ...any) *redis.Cmd {
return nil return nil
} }
+2 -2
View File
@@ -91,7 +91,7 @@ func (r *redisLimiter) evaluateScript(ctx context.Context, key string) (bool, *t
return true, nil, nil return true, nil, nil
} }
params := []interface{}{ params := []any{
float64(r.rate / 1000000), float64(r.rate / 1000000),
r.burst, r.burst,
r.ttl, r.ttl,
@@ -103,7 +103,7 @@ func (r *redisLimiter) evaluateScript(ctx context.Context, key string) (bool, *t
return false, nil, fmt.Errorf("running script: %w", err) return false, nil, fmt.Errorf("running script: %w", err)
} }
values := v.([]interface{}) values := v.([]any)
ok, err := strconv.ParseBool(values[0].(string)) ok, err := strconv.ParseBool(values[0].(string))
if err != nil { if err != nil {
return false, nil, fmt.Errorf("parsing ok value from redis rate lua script: %w", err) return false, nil, fmt.Errorf("parsing ok value from redis rate lua script: %w", err)
+1 -1
View File
@@ -56,7 +56,7 @@ func recoverFunc(rw recoveryResponseWriter, req *http.Request) {
// https://github.com/golang/go/blob/a0d6420d8be2ae7164797051ec74fa2a2df466a1/src/net/http/server.go#L1761-L1775 // https://github.com/golang/go/blob/a0d6420d8be2ae7164797051ec74fa2a2df466a1/src/net/http/server.go#L1761-L1775
// https://github.com/golang/go/blob/c33153f7b416c03983324b3e8f869ce1116d84bc/src/net/http/httputil/reverseproxy.go#L284 // https://github.com/golang/go/blob/c33153f7b416c03983324b3e8f869ce1116d84bc/src/net/http/httputil/reverseproxy.go#L284
func shouldLogPanic(panicValue interface{}) bool { func shouldLogPanic(panicValue any) bool {
//nolint:errorlint // false-positive because panicValue is an interface. //nolint:errorlint // false-positive because panicValue is an interface.
return panicValue != nil && panicValue != http.ErrAbortHandler return panicValue != nil && panicValue != http.ErrAbortHandler
} }
+2 -4
View File
@@ -5,6 +5,7 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"maps"
"math" "math"
"net" "net"
"net/http" "net/http"
@@ -249,10 +250,7 @@ func (r *responseWriter) WriteHeader(code int) {
// to write headers to the backend : we are not going to perform any further retry. // to write headers to the backend : we are not going to perform any further retry.
// So it is now safe to alter current response headers with headers collected during // So it is now safe to alter current response headers with headers collected during
// the latest try before writing headers to client. // the latest try before writing headers to client.
headers := r.responseWriter.Header() maps.Copy(r.responseWriter.Header(), r.headers)
for header, value := range r.headers {
headers[header] = value
}
r.responseWriter.WriteHeader(code) r.responseWriter.WriteHeader(code)
+2 -14
View File
@@ -184,13 +184,7 @@ func header(tree *matchersTree, headers ...string) error {
key, value := http.CanonicalHeaderKey(headers[0]), headers[1] key, value := http.CanonicalHeaderKey(headers[0]), headers[1]
tree.matcher = func(req *http.Request) bool { tree.matcher = func(req *http.Request) bool {
for _, headerValue := range req.Header[key] { return slices.Contains(req.Header[key], value)
if headerValue == value {
return true
}
}
return false
} }
return nil return nil
@@ -205,13 +199,7 @@ func headerRegexp(tree *matchersTree, headers ...string) error {
} }
tree.matcher = func(req *http.Request) bool { tree.matcher = func(req *http.Request) bool {
for _, headerValue := range req.Header[key] { return slices.ContainsFunc(req.Header[key], re.MatchString)
if re.MatchString(headerValue) {
return true
}
}
return false
} }
return nil return nil
+2 -1
View File
@@ -22,7 +22,8 @@ type MatcherFunc func(*http.Request) bool
// Muxer handles routing with rules. // Muxer handles routing with rules.
type Muxer struct { type Muxer struct {
routes routes routes routes
parser SyntaxParser parser SyntaxParser
defaultHandler http.Handler defaultHandler http.Handler
} }
+2 -7
View File
@@ -3,6 +3,7 @@ package tcp
import ( import (
"fmt" "fmt"
"regexp" "regexp"
"slices"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
@@ -37,13 +38,7 @@ func alpn(tree *matchersTree, protos ...string) error {
} }
tree.matcher = func(meta ConnData) bool { tree.matcher = func(meta ConnData) bool {
for _, alpnProto := range meta.alpnProtos { return slices.Contains(meta.alpnProtos, proto)
if alpnProto == proto {
return true
}
}
return false
} }
return nil return nil
+3 -4
View File
@@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"regexp" "regexp"
"slices"
"strconv" "strconv"
"strings" "strings"
@@ -56,10 +57,8 @@ func alpnV2(tree *matchersTree, protos ...string) error {
tree.matcher = func(meta ConnData) bool { tree.matcher = func(meta ConnData) bool {
for _, proto := range meta.alpnProtos { for _, proto := range meta.alpnProtos {
for _, filter := range protos { if slices.Contains(protos, proto) {
if proto == filter { return true
return true
}
} }
} }
+1 -1
View File
@@ -121,7 +121,7 @@ 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, syntax string, priority int, handler tcp.Handler) error { func (m *Muxer) AddRoute(rule string, syntax string, priority int, handler tcp.Handler) error {
var parse interface{} var parse any
var err error var err error
var matcherFuncs map[string]func(*matchersTree, ...string) error var matcherFuncs map[string]func(*matchersTree, ...string) error
+2 -2
View File
@@ -7,10 +7,10 @@ import (
func NewAWSWrapper(logger zerolog.Logger) logging.LoggerFunc { func NewAWSWrapper(logger zerolog.Logger) logging.LoggerFunc {
if logger.GetLevel() > zerolog.DebugLevel { if logger.GetLevel() > zerolog.DebugLevel {
return func(classification logging.Classification, format string, args ...interface{}) {} return func(classification logging.Classification, format string, args ...any) {}
} }
return func(classification logging.Classification, format string, args ...interface{}) { return func(classification logging.Classification, format string, args ...any) {
logger.Debug().CallerSkipFrame(2).MsgFunc(msgFunc(args...)) logger.Debug().CallerSkipFrame(2).MsgFunc(msgFunc(args...))
} }
} }

Some files were not shown because too many files have changed in this diff Show More