2020-10-27 03:28:25 +00:00
|
|
|
package healthcheck
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2022-03-21 19:57:35 +00:00
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"net"
|
2020-12-30 19:34:11 +00:00
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
2021-07-23 19:22:41 +00:00
|
|
|
func (s *Server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) {
|
2021-05-11 22:24:32 +00:00
|
|
|
defer close(done)
|
2021-07-18 03:17:48 +00:00
|
|
|
|
2021-08-18 22:01:04 +00:00
|
|
|
s.vpn.healthyTimer = time.NewTimer(s.vpn.healthyWait)
|
2021-07-18 03:17:48 +00:00
|
|
|
|
2020-12-30 19:34:11 +00:00
|
|
|
for {
|
2021-01-02 18:36:15 +00:00
|
|
|
previousErr := s.handler.getErr()
|
|
|
|
|
|
2022-03-21 19:57:35 +00:00
|
|
|
const healthcheckTimeout = 3 * time.Second
|
|
|
|
|
healthcheckCtx, healthcheckCancel := context.WithTimeout(
|
|
|
|
|
ctx, healthcheckTimeout)
|
|
|
|
|
err := s.healthCheck(healthcheckCtx)
|
|
|
|
|
healthcheckCancel()
|
|
|
|
|
|
2020-12-30 19:34:11 +00:00
|
|
|
s.handler.setErr(err)
|
2021-01-02 18:36:15 +00:00
|
|
|
|
|
|
|
|
if previousErr != nil && err == nil {
|
2021-02-09 02:45:50 +00:00
|
|
|
s.logger.Info("healthy!")
|
2021-08-18 22:01:04 +00:00
|
|
|
s.vpn.healthyTimer.Stop()
|
2022-01-06 06:40:23 -05:00
|
|
|
s.vpn.healthyWait = *s.config.VPN.Initial
|
2021-02-09 02:45:50 +00:00
|
|
|
} else if previousErr == nil && err != nil {
|
2022-11-11 09:43:07 +00:00
|
|
|
s.logger.Info("unhealthy: " + err.Error() + "(see https://github.com/qdm12/gluetun/wiki/Healthcheck)")
|
2021-08-18 22:01:04 +00:00
|
|
|
s.vpn.healthyTimer.Stop()
|
|
|
|
|
s.vpn.healthyTimer = time.NewTimer(s.vpn.healthyWait)
|
2021-01-02 18:36:15 +00:00
|
|
|
}
|
|
|
|
|
|
2020-12-30 19:34:11 +00:00
|
|
|
if err != nil { // try again after 1 second
|
|
|
|
|
timer := time.NewTimer(time.Second)
|
|
|
|
|
select {
|
|
|
|
|
case <-ctx.Done():
|
|
|
|
|
if !timer.Stop() {
|
|
|
|
|
<-timer.C
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
case <-timer.C:
|
2021-08-18 22:01:04 +00:00
|
|
|
case <-s.vpn.healthyTimer.C:
|
2021-09-11 21:04:21 +00:00
|
|
|
s.onUnhealthyVPN(ctx)
|
2020-12-30 19:34:11 +00:00
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
2021-07-18 03:17:48 +00:00
|
|
|
|
2021-05-04 15:36:12 -04:00
|
|
|
// Success, check again in 5 seconds
|
|
|
|
|
const period = 5 * time.Second
|
2020-12-30 19:34:11 +00:00
|
|
|
timer := time.NewTimer(period)
|
|
|
|
|
select {
|
|
|
|
|
case <-ctx.Done():
|
|
|
|
|
if !timer.Stop() {
|
|
|
|
|
<-timer.C
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
case <-timer.C:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-21 19:57:35 +00:00
|
|
|
func (s *Server) healthCheck(ctx context.Context) (err error) {
|
2020-10-27 03:28:25 +00:00
|
|
|
// TODO use mullvad API if current provider is Mullvad
|
2021-09-11 21:49:46 +00:00
|
|
|
|
2022-03-21 19:57:35 +00:00
|
|
|
address, err := makeAddressToDial(s.config.TargetAddress)
|
|
|
|
|
if err != nil {
|
2020-12-30 19:34:11 +00:00
|
|
|
return err
|
2020-10-27 03:28:25 +00:00
|
|
|
}
|
2022-03-21 19:57:35 +00:00
|
|
|
|
|
|
|
|
const dialNetwork = "tcp4"
|
|
|
|
|
connection, err := s.dialer.DialContext(ctx, dialNetwork, address)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("cannot dial: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = connection.Close()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("cannot close connection: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func makeAddressToDial(address string) (addressToDial string, err error) {
|
|
|
|
|
host, port, err := net.SplitHostPort(address)
|
|
|
|
|
if err != nil {
|
|
|
|
|
addrErr := new(net.AddrError)
|
|
|
|
|
ok := errors.As(err, &addrErr)
|
|
|
|
|
if !ok || addrErr.Err != "missing port in address" {
|
|
|
|
|
return "", fmt.Errorf("cannot split host and port from address: %w", err)
|
|
|
|
|
}
|
|
|
|
|
host = address
|
|
|
|
|
const defaultPort = "443"
|
|
|
|
|
port = defaultPort
|
|
|
|
|
}
|
|
|
|
|
address = net.JoinHostPort(host, port)
|
|
|
|
|
return address, nil
|
2020-10-27 03:28:25 +00:00
|
|
|
}
|