refactor(server): use errgroup to supervise services (#3755)

Uses the errgroup package and pattern for supervising services like servers etc.
pull/3808/head
James Elliott 2022-08-09 07:50:12 +10:00 committed by GitHub
parent a59a3c62f9
commit 342497a869
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 459 additions and 162 deletions

View File

@ -51,13 +51,6 @@ server:
## Useful to allow overriding of specific static assets.
# asset_path: /config/assets/
## Buffers usually should be configured to be the same value.
## Explanation at https://www.authelia.com/c/server#buffer-sizes
## Read buffer size adjusts the server's max incoming request size in bytes.
## Write buffer size does the same for outgoing responses.
read_buffer_size: 4096
write_buffer_size: 4096
## Enables the pprof endpoint.
enable_pprof: false
@ -85,6 +78,32 @@ server:
## The CSP Template. Read the docs.
csp_template: ""
## Server Buffers configuration.
# buffers:
## Buffers usually should be configured to be the same value.
## Explanation at https://www.authelia.com/c/server#buffer-sizes
## Read buffer size adjusts the server's max incoming request size in bytes.
## Write buffer size does the same for outgoing responses.
## Read buffer.
# read: 4096
## Write buffer.
# write: 4096
## Server Timeouts configuration.
# timeouts:
## Read timeout.
# read: 2s
## Write timeout.
# write: 2s
## Idle timeout.
# idle: 30s
##
## Log Configuration
##
@ -116,6 +135,27 @@ telemetry:
## The address to listen on for metrics. This should be on a different port to the main server.port value.
address: tcp://0.0.0.0:9959
## Metrics Server Buffers configuration.
# buffers:
## Read buffer.
# read: 4096
## Write buffer.
# write: 4096
## Metrics Server Timeouts configuration.
# timeouts:
## Read timeout.
# read: 2s
## Write timeout.
# write: 2s
## Idle timeout.
# idle: 30s
##
## TOTP Configuration
##

View File

@ -40,12 +40,13 @@ For example this YAML configuration:
log:
level: info
server:
read_buffer_size: 4096
buffers:
read: 4096
```
Can be replaced by this environment variable configuration:
```bash
AUTHELIA_LOG_LEVEL=info
AUTHELIA_SERVER_READ_BUFFER_SIZE=4096
AUTHELIA_SERVER_BUFFERS_READ=4096
```

View File

@ -22,8 +22,6 @@ server:
host: 0.0.0.0
port: 9091
path: ""
read_buffer_size: 4096
write_buffer_size: 4096
enable_pprof: false
enable_expvars: false
disable_healthcheck: false
@ -33,6 +31,13 @@ server:
client_certificates: []
headers:
csp_template: ""
buffers:
read: 4096
write: 4096
timeouts:
read: 10s
write: 10s
idle: 10s
```
## Options
@ -95,18 +100,6 @@ assets that can be overridden must be placed in the `asset_path`. The structure
can be overriden is documented in the
[Sever Asset Overrides Reference Guide](../../reference/guides/server-asset-overrides.md).
### read_buffer_size
{{< confkey type="integer " default="4096" required="no" >}}
Configures the maximum request size. The default of 4096 is generally sufficient for most use cases.
### write_buffer_size
{{< confkey type="integer " default="4096" required="no" >}}
Configures the maximum response size. The default of 4096 is generally sufficient for most use cases.
### enable_pprof
{{< confkey type="boolean" default="false" required="no" >}}
@ -174,6 +167,16 @@ about how browsers utilize and understand this header before attempting to custo
For example, the default CSP template is `default-src 'self'; object-src 'none'; style-src 'self' 'nonce-${NONCE}'`.
### buffers
Configures the server buffers. See the [Server Buffers](../prologue/common.md#server-buffers) documentation for more
information.
### timeouts
Configures the server timeouts. See the [Server Timeouts](../prologue/common.md#server-timeouts) documentation for more
information.
## Additional Notes
### Buffer Sizes

View File

@ -133,3 +133,46 @@ The key `minimum_version` controls the minimum TLS version Authelia will use whe
The possible values are `TLS1.3`, `TLS1.2`, `TLS1.1`, `TLS1.0`. Anything other than `TLS1.3` or `TLS1.2`
are very old and deprecated. You should avoid using these and upgrade your backend service instead of decreasing
this value.
## Server Buffers
### read
{{< confkey type="integer" default="4096" required="no" >}}
Configures the maximum request size. The default of 4096 is generally sufficient for most use cases.
### write
{{< confkey type="integer" default="4096" required="no" >}}
Configures the maximum response size. The default of 4096 is generally sufficient for most use cases.
## Server Timeouts
### read
{{< confkey type="duration" default="2s" required="no" >}}
*__Note:__ This setting uses the [duration notation format](#duration-notation-format). Please see the
[common options](#duration-notation-format) documentation for information on this format.*
Configures the server read timeout.
### write
{{< confkey type="duration" default="2s" required="no" >}}
*__Note:__ This setting uses the [duration notation format](#duration-notation-format). Please see the
[common options](#duration-notation-format) documentation for information on this format.*
Configures the server write timeout.
### idle
{{< confkey type="duration" default="30s" required="no" >}}
*__Note:__ This setting uses the [duration notation format](#duration-notation-format). Please see the
[common options](#duration-notation-format) documentation for information on this format.*
Configures the server write timeout.

View File

@ -31,13 +31,13 @@ regulation:
### max_retries
{{< confkey type="integer " default="3" required="no" >}}
{{< confkey type="integer" default="3" required="no" >}}
The number of failed login attempts before a user may be banned. Setting this option to 0 disables regulation entirely.
### find_time
{{< confkey type="duration " default="2m" required="no" >}}
{{< confkey type="duration" default="2m" required="no" >}}
*__Note:__ This setting uses the [duration notation format](../prologue/common.md#duration-notation-format). Please see
the [common options](../prologue/common.md#duration-notation-format) documentation for information on this format.*

View File

@ -21,6 +21,13 @@ telemetry:
metrics:
enabled: false
address: "tcp://0.0.0.0:9959"
buffers:
read: 4096
write: 4096
timeouts:
read: 10s
write: 10s
idle: 10s
```
## Options
@ -38,6 +45,16 @@ Determines if the [Prometheus] HTTP Metrics Exporter is enabled.
Configures the listener address for the [Prometheus] HTTP Metrics Exporter. This configuration key uses the
[Address](../prologue/common.md#address) format. The scheme must be `tcp://` or empty.
### buffers
Configures the server buffers. See the [Server Buffers](../prologue/common.md#server-buffers) documentation for more
information.
### timeouts
Configures the server timeouts. See the [Server Timeouts](../prologue/common.md#server-timeouts) documentation for more
information.
## See More
- [Telemetry Reference Documentation](../../reference/guides/metrics.md)

View File

@ -15,15 +15,15 @@ toc: true
## Request Header Too Large
The `request header too large` error with a status code of `431` indicates the HTTP request made to *Authelia* had
headers exceeding the server [read_buffer_size](../../configuration/miscellaneous/server.md#read_buffer_size) parameter.
headers exceeding the server [read buffer](../../configuration/miscellaneous/server.md#buffers) parameter.
Usually the defaults are sufficient however some applications cause fairly large headers to be added to requests.
It's suggested you increase the [read_buffer_size](../../configuration/miscellaneous/server.md#read_buffer_size)
It's suggested you increase the [read buffer](../../configuration/miscellaneous/server.md#buffers)
configuration option (by either doubling or quadrupling it) in order to alleviate this issue or use the reverse proxy to
remove the excessive headers which are causing this issue.
It's generally recommended the [write_buffer_size](../../configuration/miscellaneous/server.md#write_buffer_size) is
It's generally recommended the [write buffer](../../configuration/miscellaneous/server.md#buffers) is
also increased.
## User Has Been Inactive Too Long

1
go.mod
View File

@ -36,6 +36,7 @@ require (
github.com/stretchr/testify v1.8.0
github.com/trustelem/zxcvbn v1.0.1
github.com/valyala/fasthttp v1.38.0
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
golang.org/x/text v0.3.7
gopkg.in/square/go-jose.v2 v2.6.0
gopkg.in/yaml.v3 v3.0.1

3
go.sum
View File

@ -1587,7 +1587,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@ -1,13 +1,18 @@
package commands
import (
"context"
"fmt"
"net"
"os"
"os/signal"
"strings"
"sync"
"syscall"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/valyala/fasthttp"
"golang.org/x/sync/errgroup"
"github.com/authelia/authelia/v4/internal/configuration/schema"
"github.com/authelia/authelia/v4/internal/logging"
@ -74,99 +79,127 @@ func cmdRootRun(_ *cobra.Command, _ []string) {
logger.Fatalf("Errors occurred provisioning providers.")
}
doStartupChecks(config, &providers)
doStartupChecks(config, &providers, logger)
runServers(config, providers, logger)
}
func runServers(config *schema.Configuration, providers middlewares.Providers, logger *logrus.Logger) {
wg := new(sync.WaitGroup)
func runServers(config *schema.Configuration, providers middlewares.Providers, log *logrus.Logger) {
ctx := context.Background()
wg.Add(2)
ctx, cancel := context.WithCancel(ctx)
go func() {
err := startDefaultServer(config, providers)
if err != nil {
logger.Fatal(err)
defer cancel()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(quit)
g, ctx := errgroup.WithContext(ctx)
var (
mainServer, metricsServer *fasthttp.Server
mainListener, metricsListener net.Listener
)
g.Go(func() (err error) {
defer func() {
if rec := recover(); rec != nil {
log.WithError(recoverErr(rec)).Errorf("Critical error in server caught (recovered)")
}
}()
if mainServer, mainListener, err = server.CreateDefaultServer(*config, providers); err != nil {
return err
}
wg.Done()
}()
go func() {
err := startMetricsServer(config, providers)
if err != nil {
logger.Fatal(err)
if err = mainServer.Serve(mainListener); err != nil {
return err
}
}()
wg.Wait()
}
func startDefaultServer(config *schema.Configuration, providers middlewares.Providers) (err error) {
svr, listener, err := server.CreateDefaultServer(*config, providers)
switch err {
case nil:
if err = svr.Serve(listener); err != nil {
return fmt.Errorf("error occurred during default server operation: %w", err)
}
default:
return fmt.Errorf("error occurred during default server startup: %w", err)
}
return nil
}
func startMetricsServer(config *schema.Configuration, providers middlewares.Providers) (err error) {
if providers.Metrics == nil {
return nil
}
})
svr, listener, err := server.CreateMetricsServer(config.Telemetry.Metrics)
switch err {
case nil:
if err = svr.Serve(listener); err != nil {
return fmt.Errorf("error occurred during metrics server operation: %w", err)
g.Go(func() (err error) {
if providers.Metrics == nil {
return nil
}
default:
return fmt.Errorf("error occurred during metrics server startup: %w", err)
defer func() {
if rec := recover(); rec != nil {
log.WithError(recoverErr(rec)).Errorf("Critical error in metrics server caught (recovered)")
}
}()
if metricsServer, metricsListener, err = server.CreateMetricsServer(config.Telemetry.Metrics); err != nil {
return err
}
if err = metricsServer.Serve(metricsListener); err != nil {
return err
}
return nil
})
select {
case <-quit:
break
case <-ctx.Done():
break
}
return nil
cancel()
log.Infof("Shutting down")
var err error
if err = mainServer.Shutdown(); err != nil {
log.WithError(err).Errorf("Error occurred shutting down the server")
}
if metricsServer != nil {
if err = metricsServer.Shutdown(); err != nil {
log.WithError(err).Errorf("Error occurred shutting down the metrics server")
}
}
if err = g.Wait(); err != nil {
log.WithError(err).Errorf("Error occurred waiting for shutdown")
}
}
func doStartupChecks(config *schema.Configuration, providers *middlewares.Providers) {
logger := logging.Logger()
func doStartupChecks(config *schema.Configuration, providers *middlewares.Providers, log *logrus.Logger) {
var (
failures []string
err error
)
if err = doStartupCheck(logger, "storage", providers.StorageProvider, false); err != nil {
logger.Errorf("Failure running the storage provider startup check: %+v", err)
if err = doStartupCheck(log, "storage", providers.StorageProvider, false); err != nil {
log.Errorf("Failure running the storage provider startup check: %+v", err)
failures = append(failures, "storage")
}
if err = doStartupCheck(logger, "user", providers.UserProvider, false); err != nil {
logger.Errorf("Failure running the user provider startup check: %+v", err)
if err = doStartupCheck(log, "user", providers.UserProvider, false); err != nil {
log.Errorf("Failure running the user provider startup check: %+v", err)
failures = append(failures, "user")
}
if err = doStartupCheck(logger, "notification", providers.Notifier, config.Notifier.DisableStartupCheck); err != nil {
logger.Errorf("Failure running the notification provider startup check: %+v", err)
if err = doStartupCheck(log, "notification", providers.Notifier, config.Notifier.DisableStartupCheck); err != nil {
log.Errorf("Failure running the notification provider startup check: %+v", err)
failures = append(failures, "notification")
}
if !config.NTP.DisableStartupCheck && !providers.Authorizer.IsSecondFactorEnabled() {
logger.Debug("The NTP startup check was skipped due to there being no configured 2FA access control rules")
} else if err = doStartupCheck(logger, "ntp", providers.NTP, config.NTP.DisableStartupCheck); err != nil {
logger.Errorf("Failure running the ntp provider startup check: %+v", err)
log.Debug("The NTP startup check was skipped due to there being no configured 2FA access control rules")
} else if err = doStartupCheck(log, "ntp", providers.NTP, config.NTP.DisableStartupCheck); err != nil {
log.Errorf("Failure running the ntp provider startup check: %+v", err)
if !config.NTP.DisableFailure {
failures = append(failures, "ntp")
@ -174,7 +207,7 @@ func doStartupChecks(config *schema.Configuration, providers *middlewares.Provid
}
if len(failures) != 0 {
logger.Fatalf("The following providers had fatal failures during startup: %s", strings.Join(failures, ", "))
log.Fatalf("The following providers had fatal failures during startup: %s", strings.Join(failures, ", "))
}
}

View File

@ -0,0 +1,18 @@
package commands
import (
"fmt"
)
func recoverErr(i interface{}) error {
switch v := i.(type) {
case nil:
return nil
case string:
return fmt.Errorf("recovered panic: %s", v)
case error:
return fmt.Errorf("recovered panic: %w", v)
default:
return fmt.Errorf("recovered panic with unknown type: %v", v)
}
}

View File

@ -51,13 +51,6 @@ server:
## Useful to allow overriding of specific static assets.
# asset_path: /config/assets/
## Buffers usually should be configured to be the same value.
## Explanation at https://www.authelia.com/c/server#buffer-sizes
## Read buffer size adjusts the server's max incoming request size in bytes.
## Write buffer size does the same for outgoing responses.
read_buffer_size: 4096
write_buffer_size: 4096
## Enables the pprof endpoint.
enable_pprof: false
@ -85,6 +78,32 @@ server:
## The CSP Template. Read the docs.
csp_template: ""
## Server Buffers configuration.
# buffers:
## Buffers usually should be configured to be the same value.
## Explanation at https://www.authelia.com/c/server#buffer-sizes
## Read buffer size adjusts the server's max incoming request size in bytes.
## Write buffer size does the same for outgoing responses.
## Read buffer.
# read: 4096
## Write buffer.
# write: 4096
## Server Timeouts configuration.
# timeouts:
## Read timeout.
# read: 2s
## Write timeout.
# write: 2s
## Idle timeout.
# idle: 30s
##
## Log Configuration
##
@ -116,6 +135,27 @@ telemetry:
## The address to listen on for metrics. This should be on a different port to the main server.port value.
address: tcp://0.0.0.0:9959
## Metrics Server Buffers configuration.
# buffers:
## Read buffer.
# read: 4096
## Write buffer.
# write: 4096
## Metrics Server Timeouts configuration.
# timeouts:
## Read timeout.
# read: 2s
## Write timeout.
# write: 2s
## Idle timeout.
# idle: 30s
##
## TOTP Configuration
##

View File

@ -106,6 +106,13 @@ var deprecations = map[string]Deprecation{
AutoMap: true,
MapFunc: nil,
},
"storage.postgres.sslmode": {
Version: model.SemanticVersion{Major: 4, Minor: 36},
Key: "storage.postgres.sslmode",
NewKey: "storage.postgres.ssl.mode",
AutoMap: true,
MapFunc: nil,
},
"authentication_backend.disable_reset_password": {
Version: model.SemanticVersion{Major: 4, Minor: 36},
Key: "authentication_backend.disable_reset_password",
@ -113,4 +120,18 @@ var deprecations = map[string]Deprecation{
AutoMap: true,
MapFunc: nil,
},
"server.read_buffer_size": {
Version: model.SemanticVersion{Major: 4, Minor: 37},
Key: "server.read_buffer_size",
NewKey: "server.buffers.read",
AutoMap: true,
MapFunc: nil,
},
"server.write_buffer_size": {
Version: model.SemanticVersion{Major: 4, Minor: 37},
Key: "server.write_buffer_size",
NewKey: "server.buffers.write",
AutoMap: true,
MapFunc: nil,
},
}

View File

@ -150,7 +150,6 @@ var Keys = []string{
"storage.postgres.ssl.root_certificate",
"storage.postgres.ssl.certificate",
"storage.postgres.ssl.key",
"storage.postgres.sslmode",
"storage.encryption_key",
"notifier.disable_startup_check",
"notifier.filesystem.filename",
@ -173,8 +172,6 @@ var Keys = []string{
"server.port",
"server.path",
"server.asset_path",
"server.read_buffer_size",
"server.write_buffer_size",
"server.enable_pprof",
"server.enable_expvars",
"server.disable_healthcheck",
@ -182,8 +179,18 @@ var Keys = []string{
"server.tls.key",
"server.tls.client_certificates",
"server.headers.csp_template",
"server.buffers.read",
"server.buffers.write",
"server.timeouts.read",
"server.timeouts.write",
"server.timeouts.idle",
"telemetry.metrics.enabled",
"telemetry.metrics.address",
"telemetry.metrics.buffers.read",
"telemetry.metrics.buffers.write",
"telemetry.metrics.timeouts.read",
"telemetry.metrics.timeouts.write",
"telemetry.metrics.timeouts.idle",
"webauthn.disable",
"webauthn.display_name",
"webauthn.attestation_conveyance_preference",

View File

@ -1,19 +1,24 @@
package schema
import (
"time"
)
// ServerConfiguration represents the configuration of the http server.
type ServerConfiguration struct {
Host string `koanf:"host"`
Port int `koanf:"port"`
Path string `koanf:"path"`
AssetPath string `koanf:"asset_path"`
ReadBufferSize int `koanf:"read_buffer_size"`
WriteBufferSize int `koanf:"write_buffer_size"`
EnablePprof bool `koanf:"enable_pprof"`
EnableExpvars bool `koanf:"enable_expvars"`
DisableHealthcheck bool `koanf:"disable_healthcheck"`
TLS ServerTLSConfiguration `koanf:"tls"`
Headers ServerHeadersConfiguration `koanf:"headers"`
Buffers ServerBuffers `koanf:"buffers"`
Timeouts ServerTimeouts `koanf:"timeouts"`
}
// ServerTLSConfiguration represents the configuration of the http servers TLS options.
@ -30,8 +35,15 @@ type ServerHeadersConfiguration struct {
// DefaultServerConfiguration represents the default values of the ServerConfiguration.
var DefaultServerConfiguration = ServerConfiguration{
Host: "0.0.0.0",
Port: 9091,
ReadBufferSize: 4096,
WriteBufferSize: 4096,
Host: "0.0.0.0",
Port: 9091,
Buffers: ServerBuffers{
Read: 4096,
Write: 4096,
},
Timeouts: ServerTimeouts{
Read: time.Second * 2,
Write: time.Second * 2,
Idle: time.Second * 30,
},
}

View File

@ -1,8 +1,25 @@
package schema
import (
"time"
)
// TLSConfig is a representation of the TLS configuration.
type TLSConfig struct {
MinimumVersion string `koanf:"minimum_version"`
SkipVerify bool `koanf:"skip_verify"`
ServerName string `koanf:"server_name"`
}
// ServerTimeouts represents server timeout configurations.
type ServerTimeouts struct {
Read time.Duration `koanf:"read"`
Write time.Duration `koanf:"write"`
Idle time.Duration `koanf:"idle"`
}
// ServerBuffers represents server buffer configurations.
type ServerBuffers struct {
Read int `koanf:"read"`
Write int `koanf:"write"`
}

View File

@ -28,9 +28,6 @@ type PostgreSQLStorageConfiguration struct {
Schema string `koanf:"schema"`
SSL PostgreSQLSSLStorageConfiguration `koanf:"ssl"`
// Deprecated. TODO: Remove in v4.36.0.
SSLMode string `koanf:"sslmode"`
}
// PostgreSQLSSLStorageConfiguration represents the SSL configuration of a PostgreSQL database.

View File

@ -2,6 +2,7 @@ package schema
import (
"net"
"time"
)
// TelemetryConfig represents the telemetry config.
@ -13,11 +14,23 @@ type TelemetryConfig struct {
type TelemetryMetricsConfig struct {
Enabled bool `koanf:"enabled"`
Address *Address `koanf:"address"`
Buffers ServerBuffers `koanf:"buffers"`
Timeouts ServerTimeouts `koanf:"timeouts"`
}
// DefaultTelemetryConfig is the default telemetry configuration.
var DefaultTelemetryConfig = TelemetryConfig{
Metrics: TelemetryMetricsConfig{
Address: &Address{true, "tcp", net.ParseIP("0.0.0.0"), 9959},
Buffers: ServerBuffers{
Read: 4096,
Write: 4096,
},
Timeouts: ServerTimeouts{
Read: time.Second * 2,
Write: time.Second * 2,
Idle: time.Second * 30,
},
},
}

View File

@ -247,7 +247,6 @@ const (
errFmtServerPathNoForwardSlashes = "server: option 'path' must not contain any forward slashes"
errFmtServerPathAlphaNum = "server: option 'path' must only contain alpha numeric characters"
errFmtServerBufferSize = "server: option '%s_buffer_size' must be above 0 but it is configured as '%d'"
)
const (

View File

@ -70,15 +70,23 @@ func ValidateServer(config *schema.Configuration, validator *schema.StructValida
config.Server.Path = path.Clean("/" + config.Server.Path)
}
if config.Server.ReadBufferSize == 0 {
config.Server.ReadBufferSize = schema.DefaultServerConfiguration.ReadBufferSize
} else if config.Server.ReadBufferSize < 0 {
validator.Push(fmt.Errorf(errFmtServerBufferSize, "read", config.Server.ReadBufferSize))
if config.Server.Buffers.Read <= 0 {
config.Server.Buffers.Read = schema.DefaultServerConfiguration.Buffers.Read
}
if config.Server.WriteBufferSize == 0 {
config.Server.WriteBufferSize = schema.DefaultServerConfiguration.WriteBufferSize
} else if config.Server.WriteBufferSize < 0 {
validator.Push(fmt.Errorf(errFmtServerBufferSize, "write", config.Server.WriteBufferSize))
if config.Server.Buffers.Write <= 0 {
config.Server.Buffers.Write = schema.DefaultServerConfiguration.Buffers.Write
}
if config.Server.Timeouts.Read <= 0 {
config.Server.Timeouts.Read = schema.DefaultServerConfiguration.Timeouts.Read
}
if config.Server.Timeouts.Write <= 0 {
config.Server.Timeouts.Write = schema.DefaultServerConfiguration.Timeouts.Write
}
if config.Server.Timeouts.Idle <= 0 {
config.Server.Timeouts.Idle = schema.DefaultServerConfiguration.Timeouts.Idle
}
}

View File

@ -3,6 +3,7 @@ package validator
import (
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -23,8 +24,8 @@ func TestShouldSetDefaultServerValues(t *testing.T) {
assert.Equal(t, schema.DefaultServerConfiguration.Host, config.Server.Host)
assert.Equal(t, schema.DefaultServerConfiguration.Port, config.Server.Port)
assert.Equal(t, schema.DefaultServerConfiguration.ReadBufferSize, config.Server.ReadBufferSize)
assert.Equal(t, schema.DefaultServerConfiguration.WriteBufferSize, config.Server.WriteBufferSize)
assert.Equal(t, schema.DefaultServerConfiguration.Buffers.Read, config.Server.Buffers.Read)
assert.Equal(t, schema.DefaultServerConfiguration.Buffers.Write, config.Server.Buffers.Write)
assert.Equal(t, schema.DefaultServerConfiguration.TLS.Key, config.Server.TLS.Key)
assert.Equal(t, schema.DefaultServerConfiguration.TLS.Certificate, config.Server.TLS.Certificate)
assert.Equal(t, schema.DefaultServerConfiguration.Path, config.Server.Path)
@ -41,8 +42,8 @@ func TestShouldSetDefaultConfig(t *testing.T) {
assert.Len(t, validator.Errors(), 0)
assert.Len(t, validator.Warnings(), 0)
assert.Equal(t, schema.DefaultServerConfiguration.ReadBufferSize, config.Server.ReadBufferSize)
assert.Equal(t, schema.DefaultServerConfiguration.WriteBufferSize, config.Server.WriteBufferSize)
assert.Equal(t, schema.DefaultServerConfiguration.Buffers.Read, config.Server.Buffers.Read)
assert.Equal(t, schema.DefaultServerConfiguration.Buffers.Write, config.Server.Buffers.Write)
}
func TestShouldParsePathCorrectly(t *testing.T) {
@ -61,21 +62,32 @@ func TestShouldParsePathCorrectly(t *testing.T) {
assert.Equal(t, "/apple", config.Server.Path)
}
func TestShouldRaiseOnNegativeValues(t *testing.T) {
func TestShouldDefaultOnNegativeValues(t *testing.T) {
validator := schema.NewStructValidator()
config := &schema.Configuration{
Server: schema.ServerConfiguration{
ReadBufferSize: -1,
WriteBufferSize: -1,
Buffers: schema.ServerBuffers{
Read: -1,
Write: -1,
},
Timeouts: schema.ServerTimeouts{
Read: time.Second * -1,
Write: time.Second * -1,
Idle: time.Second * -1,
},
},
}
ValidateServer(config, validator)
require.Len(t, validator.Errors(), 2)
require.Len(t, validator.Errors(), 0)
assert.EqualError(t, validator.Errors()[0], "server: option 'read_buffer_size' must be above 0 but it is configured as '-1'")
assert.EqualError(t, validator.Errors()[1], "server: option 'write_buffer_size' must be above 0 but it is configured as '-1'")
assert.Equal(t, schema.DefaultServerConfiguration.Buffers.Read, config.Server.Buffers.Read)
assert.Equal(t, schema.DefaultServerConfiguration.Buffers.Write, config.Server.Buffers.Write)
assert.Equal(t, schema.DefaultServerConfiguration.Timeouts.Read, config.Server.Timeouts.Read)
assert.Equal(t, schema.DefaultServerConfiguration.Timeouts.Write, config.Server.Timeouts.Write)
assert.Equal(t, schema.DefaultServerConfiguration.Timeouts.Idle, config.Server.Timeouts.Idle)
}
func TestShouldRaiseOnNonAlphanumericCharsInPath(t *testing.T) {

View File

@ -56,11 +56,6 @@ func validatePostgreSQLConfiguration(config *schema.PostgreSQLStorageConfigurati
config.Schema = schema.DefaultPostgreSQLStorageConfiguration.Schema
}
// Deprecated. TODO: Remove in v4.36.0.
if config.SSLMode != "" && config.SSL.Mode == "" {
config.SSL.Mode = config.SSLMode
}
if config.SSL.Mode == "" {
config.SSL.Mode = schema.DefaultPostgreSQLStorageConfiguration.SSL.Mode
} else if !utils.IsStringInSlice(config.SSL.Mode, validStoragePostgreSQLSSLModes) {

View File

@ -166,26 +166,6 @@ func (suite *StorageSuite) TestShouldValidatePostgresSSLModeMustBeValid() {
suite.Assert().EqualError(suite.validator.Errors()[0], "storage: postgres: ssl: option 'mode' must be one of 'disable', 'require', 'verify-ca', 'verify-full' but it is configured as 'unknown'")
}
// Deprecated. TODO: Remove in v4.36.0.
func (suite *StorageSuite) TestShouldValidatePostgresSSLModeMustBeMappedForDeprecations() {
suite.config.PostgreSQL = &schema.PostgreSQLStorageConfiguration{
SQLStorageConfiguration: schema.SQLStorageConfiguration{
Host: "pg",
Username: "myuser",
Password: "pass",
Database: "database",
},
SSLMode: "require",
}
ValidateStorage(suite.config, suite.validator)
suite.Assert().Len(suite.validator.Warnings(), 0)
suite.Assert().Len(suite.validator.Errors(), 0)
suite.Assert().Equal(suite.config.PostgreSQL.SSL.Mode, "require")
}
func (suite *StorageSuite) TestShouldRaiseErrorOnNoEncryptionKey() {
suite.config.EncryptionKey = ""
suite.config.Local = &schema.LocalStorageConfiguration{

View File

@ -22,4 +22,24 @@ func ValidateTelemetry(config *schema.Configuration, validator *schema.StructVal
if config.Telemetry.Metrics.Address.Port == 0 {
config.Telemetry.Metrics.Address.Port = schema.DefaultTelemetryConfig.Metrics.Address.Port
}
if config.Telemetry.Metrics.Buffers.Read <= 0 {
config.Telemetry.Metrics.Buffers.Read = schema.DefaultTelemetryConfig.Metrics.Buffers.Read
}
if config.Telemetry.Metrics.Buffers.Write <= 0 {
config.Telemetry.Metrics.Buffers.Write = schema.DefaultTelemetryConfig.Metrics.Buffers.Write
}
if config.Telemetry.Metrics.Timeouts.Read <= 0 {
config.Telemetry.Metrics.Timeouts.Read = schema.DefaultTelemetryConfig.Metrics.Timeouts.Read
}
if config.Telemetry.Metrics.Timeouts.Write <= 0 {
config.Telemetry.Metrics.Timeouts.Write = schema.DefaultTelemetryConfig.Metrics.Timeouts.Write
}
if config.Telemetry.Metrics.Timeouts.Idle <= 0 {
config.Telemetry.Metrics.Timeouts.Idle = schema.DefaultTelemetryConfig.Metrics.Timeouts.Idle
}
}

View File

@ -74,3 +74,12 @@ const (
cspDefaultTemplate = "default-src 'self'%s; frame-src 'none'; object-src 'none'; style-src 'self' 'nonce-%s'; frame-ancestors 'none'; base-uri 'self'"
cspNoncePlaceholder = "${NONCE}"
)
const (
connNonTLS = "non-TLS"
connTLS = "TLS"
)
const (
fmtLogServerInit = "Initializing %s for %s connections on '%s' path '%s'"
)

View File

@ -7,6 +7,7 @@ import (
"net"
"os"
"strconv"
"strings"
"github.com/valyala/fasthttp"
@ -21,8 +22,11 @@ func CreateDefaultServer(config schema.Configuration, providers middlewares.Prov
ErrorHandler: handleError(),
Handler: handleRouter(config, providers),
NoDefaultServerHeader: true,
ReadBufferSize: config.Server.ReadBufferSize,
WriteBufferSize: config.Server.WriteBufferSize,
ReadBufferSize: config.Server.Buffers.Read,
WriteBufferSize: config.Server.Buffers.Write,
ReadTimeout: config.Server.Timeouts.Read,
WriteTimeout: config.Server.Timeouts.Write,
IdleTimeout: config.Server.Timeouts.Idle,
}
address := net.JoinHostPort(config.Server.Host, strconv.Itoa(config.Server.Port))
@ -33,7 +37,7 @@ func CreateDefaultServer(config schema.Configuration, providers middlewares.Prov
)
if config.Server.TLS.Certificate != "" && config.Server.TLS.Key != "" {
connectionType, connectionScheme = "TLS", schemeHTTPS
connectionType, connectionScheme = connTLS, schemeHTTPS
if err = server.AppendCert(config.Server.TLS.Certificate, config.Server.TLS.Key); err != nil {
return nil, nil, fmt.Errorf("unable to load tls server certificate '%s' or private key '%s': %w", config.Server.TLS.Certificate, config.Server.TLS.Key, err)
@ -62,7 +66,7 @@ func CreateDefaultServer(config schema.Configuration, providers middlewares.Prov
return nil, nil, fmt.Errorf("unable to initialize tcp listener: %w", err)
}
} else {
connectionType, connectionScheme = "non-TLS", schemeHTTP
connectionType, connectionScheme = connNonTLS, schemeHTTP
if listener, err = net.Listen("tcp", address); err != nil {
return nil, nil, fmt.Errorf("unable to initialize tcp listener: %w", err)
@ -74,14 +78,14 @@ func CreateDefaultServer(config schema.Configuration, providers middlewares.Prov
return nil, nil, fmt.Errorf("unable to configure healthcheck: %w", err)
}
logger := logging.Logger()
paths := []string{"/"}
if config.Server.Path == "" {
logger.Infof("Initializing server for %s connections on '%s' path '/'", connectionType, listener.Addr().String())
} else {
logger.Infof("Initializing server for %s connections on '%s' paths '/' and '%s'", connectionType, listener.Addr().String(), config.Server.Path)
if config.Server.Path != "" {
paths = append(paths, config.Server.Path)
}
logging.Logger().Infof(fmtLogServerInit, "server", connectionType, listener.Addr().String(), strings.Join(paths, "' and '"))
return server, listener, nil
}
@ -95,7 +99,14 @@ func CreateMetricsServer(config schema.TelemetryMetricsConfig) (server *fasthttp
ErrorHandler: handleError(),
NoDefaultServerHeader: true,
Handler: handleMetrics(),
ReadBufferSize: config.Buffers.Read,
WriteBufferSize: config.Buffers.Write,
ReadTimeout: config.Timeouts.Read,
WriteTimeout: config.Timeouts.Write,
IdleTimeout: config.Timeouts.Idle,
}
logging.Logger().Infof(fmtLogServerInit, "server (metrics)", connNonTLS, listener.Addr().String(), "/metrics")
return server, listener, nil
}

View File

@ -192,7 +192,6 @@ func TestShouldRaiseErrorWhenClientDoesNotSkipVerify(t *testing.T) {
defer tlsServerContext.Close()
fmt.Println(tlsServerContext.Port())
req, err := http.NewRequest("GET", fmt.Sprintf("https://local.example.com:%d", tlsServerContext.Port()), nil)
require.NoError(t, err)