chore(lint): upgrade golangci-lint to v1.47.2

- Fix Slowloris attacks on HTTP servers
- Force set default of 5 minutes for pprof read timeout
- Change `ShutdownTimeout` to time.Duration since it cannot be set to 0
This commit is contained in:
Quentin McGaw
2022-07-28 21:55:10 +00:00
parent 877617cc53
commit a6f00f2fb2
24 changed files with 348 additions and 158 deletions

View File

@@ -2,7 +2,7 @@ ARG ALPINE_VERSION=3.16
ARG GO_ALPINE_VERSION=3.16
ARG GO_VERSION=1.17
ARG XCPUTRANSLATE_VERSION=v0.6.0
ARG GOLANGCI_LINT_VERSION=v1.46.2
ARG GOLANGCI_LINT_VERSION=v1.47.2
ARG MOCKGEN_VERSION=v1.6.0
ARG BUILDPLATFORM=linux/amd64

View File

@@ -65,7 +65,7 @@ func (d *DoT) copy() (copied DoT) {
// unset field of the receiver settings object.
func (d *DoT) mergeWith(other DoT) {
d.Enabled = helpers.MergeWithBool(d.Enabled, other.Enabled)
d.UpdatePeriod = helpers.MergeWithDuration(d.UpdatePeriod, other.UpdatePeriod)
d.UpdatePeriod = helpers.MergeWithDurationPtr(d.UpdatePeriod, other.UpdatePeriod)
d.Unbound.mergeWith(other.Unbound)
d.Blacklist.mergeWith(other.Blacklist)
}
@@ -75,7 +75,7 @@ func (d *DoT) mergeWith(other DoT) {
// settings.
func (d *DoT) overrideWith(other DoT) {
d.Enabled = helpers.OverrideWithBool(d.Enabled, other.Enabled)
d.UpdatePeriod = helpers.OverrideWithDuration(d.UpdatePeriod, other.UpdatePeriod)
d.UpdatePeriod = helpers.OverrideWithDurationPtr(d.UpdatePeriod, other.UpdatePeriod)
d.Unbound.overrideWith(other.Unbound)
d.Blacklist.overrideWith(other.Blacklist)
}
@@ -83,7 +83,7 @@ func (d *DoT) overrideWith(other DoT) {
func (d *DoT) setDefaults() {
d.Enabled = helpers.DefaultBool(d.Enabled, true)
const defaultUpdatePeriod = 24 * time.Hour
d.UpdatePeriod = helpers.DefaultDuration(d.UpdatePeriod, defaultUpdatePeriod)
d.UpdatePeriod = helpers.DefaultDurationPtr(d.UpdatePeriod, defaultUpdatePeriod)
d.Unbound.setDefaults()
d.Blacklist.setDefaults()
}

View File

@@ -3,6 +3,7 @@ package settings
import (
"fmt"
"os"
"time"
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
"github.com/qdm12/gotree"
@@ -15,6 +16,12 @@ type Health struct {
// for the health check server.
// It cannot be the empty string in the internal state.
ServerAddress string
// ReadHeaderTimeout is the HTTP server header read timeout
// duration of the HTTP server. It defaults to 100 milliseconds.
ReadHeaderTimeout time.Duration
// ReadTimeout is the HTTP read timeout duration of the
// HTTP server. It defaults to 500 milliseconds.
ReadTimeout time.Duration
// TargetAddress is the address (host or host:port)
// to TCP dial to periodically for the health check.
// It cannot be the empty string in the internal state.
@@ -41,6 +48,8 @@ func (h Health) Validate() (err error) {
func (h *Health) copy() (copied Health) {
return Health{
ServerAddress: h.ServerAddress,
ReadHeaderTimeout: h.ReadHeaderTimeout,
ReadTimeout: h.ReadTimeout,
TargetAddress: h.TargetAddress,
VPN: h.VPN.copy(),
}
@@ -50,6 +59,8 @@ func (h *Health) copy() (copied Health) {
// unset field of the receiver settings object.
func (h *Health) MergeWith(other Health) {
h.ServerAddress = helpers.MergeWithString(h.ServerAddress, other.ServerAddress)
h.ReadHeaderTimeout = helpers.MergeWithDuration(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
h.ReadTimeout = helpers.MergeWithDuration(h.ReadTimeout, other.ReadTimeout)
h.TargetAddress = helpers.MergeWithString(h.TargetAddress, other.TargetAddress)
h.VPN.mergeWith(other.VPN)
}
@@ -59,12 +70,18 @@ func (h *Health) MergeWith(other Health) {
// settings.
func (h *Health) OverrideWith(other Health) {
h.ServerAddress = helpers.OverrideWithString(h.ServerAddress, other.ServerAddress)
h.ReadHeaderTimeout = helpers.OverrideWithDuration(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
h.ReadTimeout = helpers.OverrideWithDuration(h.ReadTimeout, other.ReadTimeout)
h.TargetAddress = helpers.OverrideWithString(h.TargetAddress, other.TargetAddress)
h.VPN.overrideWith(other.VPN)
}
func (h *Health) SetDefaults() {
h.ServerAddress = helpers.DefaultString(h.ServerAddress, "127.0.0.1:9999")
const defaultReadHeaderTimeout = 100 * time.Millisecond
h.ReadHeaderTimeout = helpers.DefaultDuration(h.ReadHeaderTimeout, defaultReadHeaderTimeout)
const defaultReadTimeout = 500 * time.Millisecond
h.ReadTimeout = helpers.DefaultDuration(h.ReadTimeout, defaultReadTimeout)
h.TargetAddress = helpers.DefaultString(h.TargetAddress, "cloudflare.com:443")
h.VPN.setDefaults()
}
@@ -77,6 +94,8 @@ func (h Health) toLinesNode() (node *gotree.Node) {
node = gotree.New("Health settings:")
node.Appendf("Server listening address: %s", h.ServerAddress)
node.Appendf("Target address: %s", h.TargetAddress)
node.Appendf("Read header timeout: %s", h.ReadHeaderTimeout)
node.Appendf("Read timeout: %s", h.ReadTimeout)
node.AppendNode(h.VPN.toLinesNode("VPN"))
return node
}

View File

@@ -35,23 +35,23 @@ func (h *HealthyWait) copy() (copied HealthyWait) {
// mergeWith merges the other settings into any
// unset field of the receiver settings object.
func (h *HealthyWait) mergeWith(other HealthyWait) {
h.Initial = helpers.MergeWithDuration(h.Initial, other.Initial)
h.Addition = helpers.MergeWithDuration(h.Addition, other.Addition)
h.Initial = helpers.MergeWithDurationPtr(h.Initial, other.Initial)
h.Addition = helpers.MergeWithDurationPtr(h.Addition, other.Addition)
}
// overrideWith overrides fields of the receiver
// settings object with any field set in the other
// settings.
func (h *HealthyWait) overrideWith(other HealthyWait) {
h.Initial = helpers.OverrideWithDuration(h.Initial, other.Initial)
h.Addition = helpers.OverrideWithDuration(h.Addition, other.Addition)
h.Initial = helpers.OverrideWithDurationPtr(h.Initial, other.Initial)
h.Addition = helpers.OverrideWithDurationPtr(h.Addition, other.Addition)
}
func (h *HealthyWait) setDefaults() {
const initialDurationDefault = 6 * time.Second
const additionDurationDefault = 5 * time.Second
h.Initial = helpers.DefaultDuration(h.Initial, initialDurationDefault)
h.Addition = helpers.DefaultDuration(h.Addition, additionDurationDefault)
h.Initial = helpers.DefaultDurationPtr(h.Initial, initialDurationDefault)
h.Addition = helpers.DefaultDurationPtr(h.Addition, additionDurationDefault)
}
func (h HealthyWait) String() string {

View File

@@ -73,7 +73,15 @@ func DefaultStringPtr(existing *string, defaultValue string) (result *string) {
return result
}
func DefaultDuration(existing *time.Duration,
func DefaultDuration(existing time.Duration,
defaultValue time.Duration) (result time.Duration) {
if existing != 0 {
return existing
}
return defaultValue
}
func DefaultDurationPtr(existing *time.Duration,
defaultValue time.Duration) (result *time.Duration) {
if existing != nil {
return existing

View File

@@ -107,7 +107,14 @@ func MergeWithIP(existing, other net.IP) (result net.IP) {
return result
}
func MergeWithDuration(existing, other *time.Duration) (result *time.Duration) {
func MergeWithDuration(existing, other time.Duration) (result time.Duration) {
if existing != 0 {
return existing
}
return other
}
func MergeWithDurationPtr(existing, other *time.Duration) (result *time.Duration) {
if existing != nil {
return existing
}

View File

@@ -93,7 +93,16 @@ func OverrideWithIP(existing, other net.IP) (result net.IP) {
return result
}
func OverrideWithDuration(existing, other *time.Duration) (result *time.Duration) {
func OverrideWithDuration(existing, other time.Duration) (
result time.Duration) {
if other == 0 {
return existing
}
return other
}
func OverrideWithDurationPtr(existing, other *time.Duration) (
result *time.Duration) {
if other == nil {
return existing
}

View File

@@ -3,6 +3,7 @@ package settings
import (
"fmt"
"os"
"time"
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
"github.com/qdm12/gotree"
@@ -33,6 +34,12 @@ type HTTPProxy struct {
// each request/response. It cannot be nil in the
// internal state.
Log *bool
// ReadHeaderTimeout is the HTTP header read timeout duration
// of the HTTP server. It defaults to 1 second if left unset.
ReadHeaderTimeout time.Duration
// ReadTimeout is the HTTP read timeout duration
// of the HTTP server. It defaults to 3 seconds if left unset.
ReadTimeout time.Duration
}
func (h HTTPProxy) validate() (err error) {
@@ -55,6 +62,8 @@ func (h *HTTPProxy) copy() (copied HTTPProxy) {
Enabled: helpers.CopyBoolPtr(h.Enabled),
Stealth: helpers.CopyBoolPtr(h.Stealth),
Log: helpers.CopyBoolPtr(h.Log),
ReadHeaderTimeout: h.ReadHeaderTimeout,
ReadTimeout: h.ReadTimeout,
}
}
@@ -67,6 +76,8 @@ func (h *HTTPProxy) mergeWith(other HTTPProxy) {
h.Enabled = helpers.MergeWithBool(h.Enabled, other.Enabled)
h.Stealth = helpers.MergeWithBool(h.Stealth, other.Stealth)
h.Log = helpers.MergeWithBool(h.Log, other.Log)
h.ReadHeaderTimeout = helpers.MergeWithDuration(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
h.ReadTimeout = helpers.MergeWithDuration(h.ReadTimeout, other.ReadTimeout)
}
// overrideWith overrides fields of the receiver
@@ -79,6 +90,8 @@ func (h *HTTPProxy) overrideWith(other HTTPProxy) {
h.Enabled = helpers.OverrideWithBool(h.Enabled, other.Enabled)
h.Stealth = helpers.OverrideWithBool(h.Stealth, other.Stealth)
h.Log = helpers.OverrideWithBool(h.Log, other.Log)
h.ReadHeaderTimeout = helpers.OverrideWithDuration(h.ReadHeaderTimeout, other.ReadHeaderTimeout)
h.ReadTimeout = helpers.OverrideWithDuration(h.ReadTimeout, other.ReadTimeout)
}
func (h *HTTPProxy) setDefaults() {
@@ -88,6 +101,10 @@ func (h *HTTPProxy) setDefaults() {
h.Enabled = helpers.DefaultBool(h.Enabled, false)
h.Stealth = helpers.DefaultBool(h.Stealth, false)
h.Log = helpers.DefaultBool(h.Log, false)
const defaultReadHeaderTimeout = time.Second
h.ReadHeaderTimeout = helpers.DefaultDuration(h.ReadHeaderTimeout, defaultReadHeaderTimeout)
const defaultReadTimeout = 3 * time.Second
h.ReadTimeout = helpers.DefaultDuration(h.ReadTimeout, defaultReadTimeout)
}
func (h HTTPProxy) String() string {
@@ -106,6 +123,8 @@ func (h HTTPProxy) toLinesNode() (node *gotree.Node) {
node.Appendf("Password: %s", helpers.ObfuscatePassword(*h.Password))
node.Appendf("Stealth mode: %s", helpers.BoolPtrToYesNo(h.Stealth))
node.Appendf("Log: %s", helpers.BoolPtrToYesNo(h.Log))
node.Appendf("Read header timeout: %s", h.ReadHeaderTimeout)
node.Appendf("Read timeout: %s", h.ReadTimeout)
return node
}

View File

@@ -48,18 +48,18 @@ func (p *PublicIP) copy() (copied PublicIP) {
}
func (p *PublicIP) mergeWith(other PublicIP) {
p.Period = helpers.MergeWithDuration(p.Period, other.Period)
p.Period = helpers.MergeWithDurationPtr(p.Period, other.Period)
p.IPFilepath = helpers.MergeWithStringPtr(p.IPFilepath, other.IPFilepath)
}
func (p *PublicIP) overrideWith(other PublicIP) {
p.Period = helpers.OverrideWithDuration(p.Period, other.Period)
p.Period = helpers.OverrideWithDurationPtr(p.Period, other.Period)
p.IPFilepath = helpers.OverrideWithStringPtr(p.IPFilepath, other.IPFilepath)
}
func (p *PublicIP) setDefaults() {
const defaultPeriod = 12 * time.Hour
p.Period = helpers.DefaultDuration(p.Period, defaultPeriod)
p.Period = helpers.DefaultDurationPtr(p.Period, defaultPeriod)
p.IPFilepath = helpers.DefaultStringPtr(p.IPFilepath, "/tmp/gluetun/ip")
}

View File

@@ -67,6 +67,8 @@ func Test_Settings_String(t *testing.T) {
├── Health settings:
| ├── Server listening address: 127.0.0.1:9999
| ├── Target address: cloudflare.com:443
| ├── Read header timeout: 100ms
| ├── Read timeout: 500ms
| └── VPN wait durations:
| ├── Initial duration: 6s
| └── Additional duration: 5s

View File

@@ -73,7 +73,7 @@ func (u *Updater) copy() (copied Updater) {
// mergeWith merges the other settings into any
// unset field of the receiver settings object.
func (u *Updater) mergeWith(other Updater) {
u.Period = helpers.MergeWithDuration(u.Period, other.Period)
u.Period = helpers.MergeWithDurationPtr(u.Period, other.Period)
u.DNSAddress = helpers.MergeWithString(u.DNSAddress, other.DNSAddress)
u.MinRatio = helpers.MergeWithFloat64(u.MinRatio, other.MinRatio)
u.Providers = helpers.MergeStringSlices(u.Providers, other.Providers)
@@ -83,14 +83,14 @@ func (u *Updater) mergeWith(other Updater) {
// settings object with any field set in the other
// settings.
func (u *Updater) overrideWith(other Updater) {
u.Period = helpers.OverrideWithDuration(u.Period, other.Period)
u.Period = helpers.OverrideWithDurationPtr(u.Period, other.Period)
u.DNSAddress = helpers.OverrideWithString(u.DNSAddress, other.DNSAddress)
u.MinRatio = helpers.OverrideWithFloat64(u.MinRatio, other.MinRatio)
u.Providers = helpers.OverrideWithStringSlice(u.Providers, other.Providers)
}
func (u *Updater) SetDefaults(vpnProvider string) {
u.Period = helpers.DefaultDuration(u.Period, 0)
u.Period = helpers.DefaultDurationPtr(u.Period, 0)
u.DNSAddress = helpers.DefaultString(u.DNSAddress, "1.1.1.1:53")
if u.MinRatio == 0 {

View File

@@ -16,6 +16,8 @@ func (s *Server) Run(ctx context.Context, done chan<- struct{}) {
server := http.Server{
Addr: s.config.ServerAddress,
Handler: s.handler,
ReadHeaderTimeout: s.config.ReadHeaderTimeout,
ReadTimeout: s.config.ReadTimeout,
}
serverDone := make(chan struct{})
go func() {

View File

@@ -23,7 +23,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
settings := l.state.GetSettings()
server := New(runCtx, settings.ListeningAddress, l.logger,
*settings.Stealth, *settings.Log, *settings.User,
*settings.Password)
*settings.Password, settings.ReadHeaderTimeout, settings.ReadTimeout)
errorCh := make(chan error)
go server.Run(runCtx, errorCh)

View File

@@ -12,21 +12,31 @@ type Server struct {
handler http.Handler
logger infoErrorer
internalWG *sync.WaitGroup
readHeaderTimeout time.Duration
readTimeout time.Duration
}
func New(ctx context.Context, address string, logger Logger,
stealth, verbose bool, username, password string) *Server {
stealth, verbose bool, username, password string,
readHeaderTimeout, readTimeout time.Duration) *Server {
wg := &sync.WaitGroup{}
return &Server{
address: address,
handler: newHandler(ctx, wg, logger, stealth, verbose, username, password),
logger: logger,
internalWG: wg,
readHeaderTimeout: readHeaderTimeout,
readTimeout: readTimeout,
}
}
func (s *Server) Run(ctx context.Context, errorCh chan<- error) {
server := http.Server{Addr: s.address, Handler: s.handler}
server := http.Server{
Addr: s.address,
Handler: s.handler,
ReadHeaderTimeout: s.readHeaderTimeout,
ReadTimeout: s.readTimeout,
}
go func() {
<-ctx.Done()
const shutdownGraceDuration = 2 * time.Second

View File

@@ -2,13 +2,10 @@ package httpserver
import (
"regexp"
"time"
gomock "github.com/golang/mock/gomock"
)
func durationPtr(d time.Duration) *time.Duration { return &d }
var _ Logger = (*testLogger)(nil)
type testLogger struct{}

View File

@@ -11,7 +11,12 @@ import (
// The done channel has an error written to when the HTTP server
// is terminated, and can be nil or not nil.
func (s *Server) Run(ctx context.Context, ready chan<- struct{}, done chan<- struct{}) {
server := http.Server{Addr: s.address, Handler: s.handler}
server := http.Server{
Addr: s.address,
Handler: s.handler,
ReadHeaderTimeout: s.readHeaderTimeout,
ReadTimeout: s.readTimeout,
}
crashed := make(chan struct{})
shutdownDone := make(chan struct{})

View File

@@ -13,6 +13,8 @@ type Server struct {
addressSet chan struct{}
handler http.Handler
logger Logger
readHeaderTimeout time.Duration
readTimeout time.Duration
shutdownTimeout time.Duration
}
@@ -30,6 +32,8 @@ func New(settings Settings) (s *Server, err error) {
addressSet: make(chan struct{}),
handler: settings.Handler,
logger: settings.Logger,
shutdownTimeout: *settings.ShutdownTimeout,
readHeaderTimeout: settings.ReadHeaderTimeout,
readTimeout: settings.ReadTimeout,
shutdownTimeout: settings.ShutdownTimeout,
}, nil
}

View File

@@ -32,12 +32,16 @@ func Test_New(t *testing.T) {
Address: ":8001",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
expected: &Server{
address: ":8001",
handler: someHandler,
logger: someLogger,
readHeaderTimeout: time.Second,
readTimeout: time.Second,
shutdownTimeout: time.Second,
},
},

View File

@@ -22,13 +22,22 @@ type Settings struct {
// Logger is the logger to use.
// It must be set and cannot be left to nil.
Logger Logger
// ReadHeaderTimeout is the HTTP header read timeout duration
// of the HTTP server. It defaults to 3 seconds if left unset.
ReadHeaderTimeout time.Duration
// ReadTimeout is the HTTP read timeout duration
// of the HTTP server. It defaults to 3 seconds if left unset.
ReadTimeout time.Duration
// ShutdownTimeout is the shutdown timeout duration
// of the HTTP server. It defaults to 3 seconds.
ShutdownTimeout *time.Duration
// of the HTTP server. It defaults to 3 seconds if left unset.
ShutdownTimeout time.Duration
}
func (s *Settings) SetDefaults() {
s.Address = helpers.DefaultString(s.Address, ":8000")
const defaultReadTimeout = 3 * time.Second
s.ReadHeaderTimeout = helpers.DefaultDuration(s.ReadHeaderTimeout, defaultReadTimeout)
s.ReadTimeout = helpers.DefaultDuration(s.ReadTimeout, defaultReadTimeout)
const defaultShutdownTimeout = 3 * time.Second
s.ShutdownTimeout = helpers.DefaultDuration(s.ShutdownTimeout, defaultShutdownTimeout)
}
@@ -38,7 +47,9 @@ func (s Settings) Copy() Settings {
Address: s.Address,
Handler: s.Handler,
Logger: s.Logger,
ShutdownTimeout: helpers.CopyDurationPtr(s.ShutdownTimeout),
ReadHeaderTimeout: s.ReadHeaderTimeout,
ReadTimeout: s.ReadTimeout,
ShutdownTimeout: s.ShutdownTimeout,
}
}
@@ -48,6 +59,8 @@ func (s *Settings) MergeWith(other Settings) {
if s.Logger == nil {
s.Logger = other.Logger
}
s.ReadHeaderTimeout = helpers.MergeWithDuration(s.ReadHeaderTimeout, other.ReadHeaderTimeout)
s.ReadTimeout = helpers.MergeWithDuration(s.ReadTimeout, other.ReadTimeout)
s.ShutdownTimeout = helpers.MergeWithDuration(s.ShutdownTimeout, other.ShutdownTimeout)
}
@@ -57,12 +70,16 @@ func (s *Settings) OverrideWith(other Settings) {
if other.Logger != nil {
s.Logger = other.Logger
}
s.ReadHeaderTimeout = helpers.OverrideWithDuration(s.ReadHeaderTimeout, other.ReadHeaderTimeout)
s.ReadTimeout = helpers.OverrideWithDuration(s.ReadTimeout, other.ReadTimeout)
s.ShutdownTimeout = helpers.OverrideWithDuration(s.ShutdownTimeout, other.ShutdownTimeout)
}
var (
ErrHandlerIsNotSet = errors.New("HTTP handler cannot be left unset")
ErrLoggerIsNotSet = errors.New("logger cannot be left unset")
ErrReadHeaderTimeoutTooSmall = errors.New("read header timeout is too small")
ErrReadTimeoutTooSmall = errors.New("read timeout is too small")
ErrShutdownTimeoutTooSmall = errors.New("shutdown timeout is too small")
)
@@ -81,11 +98,24 @@ func (s Settings) Validate() (err error) {
return ErrLoggerIsNotSet
}
const minReadTimeout = time.Millisecond
if s.ReadHeaderTimeout < minReadTimeout {
return fmt.Errorf("%w: %s must be at least %s",
ErrReadHeaderTimeoutTooSmall,
s.ReadHeaderTimeout, minReadTimeout)
}
if s.ReadTimeout < minReadTimeout {
return fmt.Errorf("%w: %s must be at least %s",
ErrReadTimeoutTooSmall,
s.ReadTimeout, minReadTimeout)
}
const minShutdownTimeout = 5 * time.Millisecond
if *s.ShutdownTimeout < minShutdownTimeout {
if s.ShutdownTimeout < minShutdownTimeout {
return fmt.Errorf("%w: %s must be at least %s",
ErrShutdownTimeoutTooSmall,
*s.ShutdownTimeout, minShutdownTimeout)
s.ShutdownTimeout, minShutdownTimeout)
}
return nil
@@ -94,7 +124,9 @@ func (s Settings) Validate() (err error) {
func (s Settings) ToLinesNode() (node *gotree.Node) {
node = gotree.New("HTTP server settings:")
node.Appendf("Listening address: %s", s.Address)
node.Appendf("Shutdown timeout: %s", *s.ShutdownTimeout)
node.Appendf("Read header timeout: %s", s.ReadHeaderTimeout)
node.Appendf("Read timeout: %s", s.ReadTimeout)
node.Appendf("Shutdown timeout: %s", s.ShutdownTimeout)
return node
}

View File

@@ -22,17 +22,23 @@ func Test_Settings_SetDefaults(t *testing.T) {
settings: Settings{},
expected: Settings{
Address: ":8000",
ShutdownTimeout: durationPtr(defaultTimeout),
ReadHeaderTimeout: defaultTimeout,
ReadTimeout: defaultTimeout,
ShutdownTimeout: defaultTimeout,
},
},
"filled settings": {
settings: Settings{
Address: ":8001",
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
expected: Settings{
Address: ":8001",
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
},
}
@@ -65,13 +71,17 @@ func Test_Settings_Copy(t *testing.T) {
Address: ":8001",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
expected: Settings{
Address: ":8001",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
},
}
@@ -105,13 +115,17 @@ func Test_Settings_MergeWith(t *testing.T) {
Address: ":8001",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
expected: Settings{
Address: ":8001",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
},
"merge filled with empty": {
@@ -119,13 +133,17 @@ func Test_Settings_MergeWith(t *testing.T) {
Address: ":8001",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
expected: Settings{
Address: ":8001",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
},
}
@@ -159,13 +177,17 @@ func Test_Settings_OverrideWith(t *testing.T) {
Address: ":8001",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
expected: Settings{
Address: ":8001",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
},
"override filled with empty": {
@@ -173,13 +195,17 @@ func Test_Settings_OverrideWith(t *testing.T) {
Address: ":8001",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
expected: Settings{
Address: ":8001",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
},
"override filled with filled": {
@@ -187,17 +213,23 @@ func Test_Settings_OverrideWith(t *testing.T) {
Address: ":8001",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
other: Settings{
Address: ":8002",
ShutdownTimeout: durationPtr(time.Hour),
ReadHeaderTimeout: time.Hour,
ReadTimeout: time.Hour,
ShutdownTimeout: time.Hour,
},
expected: Settings{
Address: ":8002",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Hour),
ReadHeaderTimeout: time.Hour,
ReadTimeout: time.Hour,
ShutdownTimeout: time.Hour,
},
},
}
@@ -247,12 +279,35 @@ func Test_Settings_Validate(t *testing.T) {
errWrapped: ErrLoggerIsNotSet,
errMessage: ErrLoggerIsNotSet.Error(),
},
"read header timeout too small": {
settings: Settings{
Address: ":8000",
Handler: someHandler,
Logger: someLogger,
ReadHeaderTimeout: time.Nanosecond,
},
errWrapped: ErrReadHeaderTimeoutTooSmall,
errMessage: "read header timeout is too small: 1ns must be at least 1ms",
},
"read timeout too small": {
settings: Settings{
Address: ":8000",
Handler: someHandler,
Logger: someLogger,
ReadHeaderTimeout: time.Millisecond,
ReadTimeout: time.Nanosecond,
},
errWrapped: ErrReadTimeoutTooSmall,
errMessage: "read timeout is too small: 1ns must be at least 1ms",
},
"shutdown timeout too small": {
settings: Settings{
Address: ":8000",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Millisecond),
ReadHeaderTimeout: time.Millisecond,
ReadTimeout: time.Millisecond,
ShutdownTimeout: time.Millisecond,
},
errWrapped: ErrShutdownTimeoutTooSmall,
errMessage: "shutdown timeout is too small: 1ms must be at least 5ms",
@@ -262,7 +317,9 @@ func Test_Settings_Validate(t *testing.T) {
Address: ":8000",
Handler: someHandler,
Logger: someLogger,
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Millisecond,
ReadTimeout: time.Millisecond,
ShutdownTimeout: time.Second,
},
},
}
@@ -292,10 +349,14 @@ func Test_Settings_String(t *testing.T) {
"all values": {
settings: Settings{
Address: ":8000",
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Millisecond,
ReadTimeout: time.Millisecond,
ShutdownTimeout: time.Second,
},
s: `HTTP server settings:
├── Listening address: :8000
├── Read header timeout: 1ms
├── Read timeout: 1ms
└── Shutdown timeout: 1s`,
},
}

View File

@@ -2,13 +2,11 @@ package pprof
import (
"regexp"
"time"
gomock "github.com/golang/mock/gomock"
)
func boolPtr(b bool) *bool { return &b }
func durationPtr(d time.Duration) *time.Duration { return &d }
var _ gomock.Matcher = (*regexMatcher)(nil)

View File

@@ -29,7 +29,7 @@ func Test_Server(t *testing.T) {
HTTPServer: httpserver.Settings{
Address: address,
Logger: logger,
ShutdownTimeout: durationPtr(httpServerShutdownTimeout),
ShutdownTimeout: httpServerShutdownTimeout,
},
}

View File

@@ -2,6 +2,7 @@ package pprof
import (
"errors"
"time"
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
"github.com/qdm12/gluetun/internal/httpserver"
@@ -27,6 +28,8 @@ type Settings struct {
func (s *Settings) SetDefaults() {
s.Enabled = helpers.DefaultBool(s.Enabled, false)
s.HTTPServer.Address = helpers.DefaultString(s.HTTPServer.Address, "localhost:6060")
const defaultReadTimeout = 5 * time.Minute // for CPU profiling
s.HTTPServer.ReadTimeout = helpers.DefaultDuration(s.HTTPServer.ReadTimeout, defaultReadTimeout)
s.HTTPServer.SetDefaults()
}

View File

@@ -22,7 +22,9 @@ func Test_Settings_SetDefaults(t *testing.T) {
Enabled: boolPtr(false),
HTTPServer: httpserver.Settings{
Address: "localhost:6060",
ShutdownTimeout: durationPtr(3 * time.Second),
ReadHeaderTimeout: 3 * time.Second,
ReadTimeout: 5 * time.Minute,
ShutdownTimeout: 3 * time.Second,
},
},
},
@@ -33,7 +35,9 @@ func Test_Settings_SetDefaults(t *testing.T) {
MutexProfileRate: 1,
HTTPServer: httpserver.Settings{
Address: ":6061",
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
},
expected: Settings{
@@ -42,7 +46,9 @@ func Test_Settings_SetDefaults(t *testing.T) {
MutexProfileRate: 1,
HTTPServer: httpserver.Settings{
Address: ":6061",
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
},
},
@@ -75,7 +81,7 @@ func Test_Settings_Copy(t *testing.T) {
MutexProfileRate: 1,
HTTPServer: httpserver.Settings{
Address: ":6061",
ShutdownTimeout: durationPtr(time.Second),
ShutdownTimeout: time.Second,
},
},
expected: Settings{
@@ -84,7 +90,7 @@ func Test_Settings_Copy(t *testing.T) {
MutexProfileRate: 1,
HTTPServer: httpserver.Settings{
Address: ":6061",
ShutdownTimeout: durationPtr(time.Second),
ShutdownTimeout: time.Second,
},
},
},
@@ -281,7 +287,9 @@ func Test_Settings_Validate(t *testing.T) {
Address: ":8000",
Handler: http.NewServeMux(),
Logger: &MockLogger{},
ShutdownTimeout: durationPtr(time.Second),
ReadHeaderTimeout: time.Second,
ReadTimeout: time.Second,
ShutdownTimeout: time.Second,
},
},
},
@@ -321,7 +329,7 @@ func Test_Settings_String(t *testing.T) {
MutexProfileRate: 1,
HTTPServer: httpserver.Settings{
Address: ":8000",
ShutdownTimeout: durationPtr(time.Second),
ShutdownTimeout: time.Second,
},
},
s: `Pprof settings:
@@ -329,6 +337,8 @@ func Test_Settings_String(t *testing.T) {
├── Mutex profile rate: 1
└── HTTP server settings:
├── Listening address: :8000
├── Read header timeout: 0s
├── Read timeout: 0s
└── Shutdown timeout: 1s`,
},
}