diff --git a/Dockerfile b/Dockerfile index 71de3704..f324cf55 100644 --- a/Dockerfile +++ b/Dockerfile @@ -141,6 +141,7 @@ ENV VPN_SERVICE_PROVIDER=pia \ # Health HEALTH_SERVER_ADDRESS=127.0.0.1:9999 \ HEALTH_TARGET_ADDRESS=cloudflare.com:443 \ + HEALTH_SUCCESS_WAIT_DURATION=5s \ HEALTH_VPN_DURATION_INITIAL=6s \ HEALTH_VPN_DURATION_ADDITION=5s \ # DNS over TLS diff --git a/internal/configuration/settings/health.go b/internal/configuration/settings/health.go index 468f3865..86c84d73 100644 --- a/internal/configuration/settings/health.go +++ b/internal/configuration/settings/health.go @@ -20,13 +20,19 @@ type Health struct { // 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. + // 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. TargetAddress string - VPN HealthyWait + // SuccessWait is the duration to wait to re-run the + // healthcheck after a successful healthcheck. + // It defaults to 5 seconds and cannot be zero in + // the internal state. + SuccessWait time.Duration + // VPN has health settings specific to the VPN loop. + VPN HealthyWait } func (h Health) Validate() (err error) { @@ -51,6 +57,7 @@ func (h *Health) copy() (copied Health) { ReadHeaderTimeout: h.ReadHeaderTimeout, ReadTimeout: h.ReadTimeout, TargetAddress: h.TargetAddress, + SuccessWait: h.SuccessWait, VPN: h.VPN.copy(), } } @@ -62,6 +69,7 @@ func (h *Health) MergeWith(other Health) { 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.SuccessWait = helpers.MergeWithDuration(h.SuccessWait, other.SuccessWait) h.VPN.mergeWith(other.VPN) } @@ -73,6 +81,7 @@ func (h *Health) OverrideWith(other Health) { 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.SuccessWait = helpers.OverrideWithDuration(h.SuccessWait, other.SuccessWait) h.VPN.overrideWith(other.VPN) } @@ -83,6 +92,8 @@ func (h *Health) SetDefaults() { const defaultReadTimeout = 500 * time.Millisecond h.ReadTimeout = helpers.DefaultDuration(h.ReadTimeout, defaultReadTimeout) h.TargetAddress = helpers.DefaultString(h.TargetAddress, "cloudflare.com:443") + const defaultSuccessWait = 5 * time.Second + h.SuccessWait = helpers.DefaultDuration(h.SuccessWait, defaultSuccessWait) h.VPN.setDefaults() } @@ -94,6 +105,7 @@ 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("Duration to wait after success: %s", h.SuccessWait) node.Appendf("Read header timeout: %s", h.ReadHeaderTimeout) node.Appendf("Read timeout: %s", h.ReadTimeout) node.AppendNode(h.VPN.toLinesNode("VPN")) diff --git a/internal/configuration/settings/settings_test.go b/internal/configuration/settings/settings_test.go index dce92558..1fc62241 100644 --- a/internal/configuration/settings/settings_test.go +++ b/internal/configuration/settings/settings_test.go @@ -66,6 +66,7 @@ func Test_Settings_String(t *testing.T) { ├── Health settings: | ├── Server listening address: 127.0.0.1:9999 | ├── Target address: cloudflare.com:443 +| ├── Duration to wait after success: 5s | ├── Read header timeout: 100ms | ├── Read timeout: 500ms | └── VPN wait durations: diff --git a/internal/configuration/sources/env/health.go b/internal/configuration/sources/env/health.go index bad583a0..a7d28762 100644 --- a/internal/configuration/sources/env/health.go +++ b/internal/configuration/sources/env/health.go @@ -11,6 +11,13 @@ func (s *Source) ReadHealth() (health settings.Health, err error) { health.ServerAddress = getCleanedEnv("HEALTH_SERVER_ADDRESS") _, health.TargetAddress = s.getEnvWithRetro("HEALTH_TARGET_ADDRESS", "HEALTH_ADDRESS_TO_PING") + successWaitPtr, err := envToDurationPtr("HEALTH_SUCCESS_WAIT_DURATION") + if err != nil { + return health, fmt.Errorf("environment variable HEALTH_SUCCESS_WAIT_DURATION: %w", err) + } else if successWaitPtr != nil { + health.SuccessWait = *successWaitPtr + } + health.VPN.Initial, err = s.readDurationWithRetro( "HEALTH_VPN_DURATION_INITIAL", "HEALTH_OPENVPN_DURATION_INITIAL") diff --git a/internal/healthcheck/health.go b/internal/healthcheck/health.go index 4ce82ea6..2cd61426 100644 --- a/internal/healthcheck/health.go +++ b/internal/healthcheck/health.go @@ -49,9 +49,8 @@ func (s *Server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) { continue } - // Success, check again in 5 seconds - const period = 5 * time.Second - timer := time.NewTimer(period) + // Success, check again after the success wait duration + timer := time.NewTimer(s.config.SuccessWait) select { case <-ctx.Done(): if !timer.Stop() {