tailcfg, control/controlclient: add MapResponse.PingRequest

So the control server can test whether a client's actually present.

Most clients are over HTTP/2, so these pings (to the same host) are
super cheap.

This mimics the earlier goroutine dump mechanism.

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2021-03-04 20:54:44 -08:00
committed by Brad Fitzpatrick
parent e453c7ca57
commit f45a9e291b
2 changed files with 54 additions and 1 deletions

View File

@ -676,6 +676,10 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm
return err
}
if pr := resp.PingRequest; pr != nil {
go answerPing(c.logf, c.httpc, pr)
}
if resp.KeepAlive {
vlogf("netmap: got keep-alive")
} else {
@ -1204,3 +1208,29 @@ func ipForwardingBroken(routes []netaddr.IPPrefix) bool {
return false
}
func answerPing(logf logger.Logf, c *http.Client, pr *tailcfg.PingRequest) {
if pr.URL == "" {
logf("invalid PingRequest with no URL")
return
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "HEAD", pr.URL, nil)
if err != nil {
logf("http.NewRequestWithContext(%q): %v", pr.URL, err)
return
}
if pr.Log {
logf("answerPing: sending ping to %v ...", pr.URL)
}
t0 := time.Now()
_, err = c.Do(req)
d := time.Since(t0).Round(time.Millisecond)
if err != nil {
logf("answerPing error: %v to %v (after %v)", err, pr.URL, d)
} else if pr.Log {
logf("answerPing complete to %v (after %v)", pr.URL, d)
}
}