cloudflare/cloudflared

Public

mirrored from https://github.com/cloudflare/cloudflaredAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2018.10.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

h2mux/idletimer.go

81lines · modecode

1package h2mux
2
3import (
4 "math/rand"
5 "sync"
6 "time"
7)
8
9// IdleTimer is a type of Timer designed for managing heartbeats on an idle connection.
10// The timer ticks on an interval with added jitter to avoid accidental synchronisation
11// between two endpoints. It tracks the number of retries/ticks since the connection was
12// last marked active.
13//
14// The methods of IdleTimer must not be called while a goroutine is reading from C.
15type IdleTimer struct {
16 // The channel on which ticks are delivered.
17 C <-chan time.Time
18
19 // A timer used to measure idle connection time. Reset after sending data.
20 idleTimer *time.Timer
21 // The maximum length of time a connection is idle before sending a ping.
22 idleDuration time.Duration
23 // A pseudorandom source used to add jitter to the idle duration.
24 randomSource *rand.Rand
25 // The maximum number of retries allowed.
26 maxRetries uint64
27 // The number of retries since the connection was last marked active.
28 retries uint64
29 // A lock to prevent race condition while checking retries
30 stateLock sync.RWMutex
31}
32
33func NewIdleTimer(idleDuration time.Duration, maxRetries uint64) *IdleTimer {
34 t := &IdleTimer{
35 idleTimer: time.NewTimer(idleDuration),
36 idleDuration: idleDuration,
37 randomSource: rand.New(rand.NewSource(time.Now().Unix())),
38 maxRetries: maxRetries,
39 }
40 t.C = t.idleTimer.C
41 return t
42}
43
44// Retry should be called when retrying the idle timeout. If the maximum number of retries
45// has been met, returns false.
46// After calling this function and sending a heartbeat, call ResetTimer. Since sending the
47// heartbeat could be a blocking operation, we resetting the timer after the write completes
48// to avoid it expiring during the write.
49func (t *IdleTimer) Retry() bool {
50 t.stateLock.Lock()
51 defer t.stateLock.Unlock()
52 if t.retries >= t.maxRetries {
53 return false
54 }
55 t.retries++
56 return true
57}
58
59func (t *IdleTimer) RetryCount() uint64 {
60 t.stateLock.RLock()
61 defer t.stateLock.RUnlock()
62 return t.retries
63}
64
65// MarkActive resets the idle connection timer and suppresses any outstanding idle events.
66func (t *IdleTimer) MarkActive() {
67 if !t.idleTimer.Stop() {
68 // eat the timer event to prevent spurious pings
69 <-t.idleTimer.C
70 }
71 t.stateLock.Lock()
72 t.retries = 0
73 t.stateLock.Unlock()
74 t.ResetTimer()
75}
76
77// Reset the idle timer according to the configured duration, with some added jitter.
78func (t *IdleTimer) ResetTimer() {
79 jitter := time.Duration(t.randomSource.Int63n(int64(t.idleDuration)))
80 t.idleTimer.Reset(t.idleDuration + jitter)
81}
82