Repository navigation
Expand file tree
/
Copy pathkeepalive.go
More file actions
127 lines (104 loc) · 3.1 KB
/
Copy pathkeepalive.go
File metadata and controls
127 lines (104 loc) · 3.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package ssh
import (
"sync"
"time"
)
// SessionKeepAlive tracks the keep-alive state for an SSH connection.
type SessionKeepAlive struct {
clientAliveInterval time.Duration
clientAliveCountMax int
ticker *time.Ticker
tickerCh <-chan time.Time
lastReceived time.Time
metrics KeepAliveMetrics
m sync.Mutex
closed bool
}
// NewSessionKeepAlive creates a new SessionKeepAlive.
func NewSessionKeepAlive(
clientAliveInterval time.Duration,
clientAliveCountMax int,
) *SessionKeepAlive {
var t *time.Ticker
var tickerCh <-chan time.Time
if clientAliveInterval > 0 {
t = time.NewTicker(clientAliveInterval)
tickerCh = t.C
}
return &SessionKeepAlive{
clientAliveInterval: clientAliveInterval,
clientAliveCountMax: clientAliveCountMax,
ticker: t,
tickerCh: tickerCh,
lastReceived: time.Now(),
}
}
// RequestHandlerCallback is called when a client-initiated keep-alive is received.
func (ska *SessionKeepAlive) RequestHandlerCallback() {
ska.m.Lock()
ska.metrics.RequestHandlerCalled++
ska.m.Unlock()
ska.Reset()
}
// ServerRequestedKeepAliveCallback is called when the server sends a keep-alive.
func (ska *SessionKeepAlive) ServerRequestedKeepAliveCallback() {
ska.m.Lock()
defer ska.m.Unlock()
ska.metrics.ServerRequestedKeepAlive++
}
// Reset resets the keep-alive timer.
func (ska *SessionKeepAlive) Reset() {
ska.m.Lock()
defer ska.m.Unlock()
ska.metrics.KeepAliveReplyReceived++
if ska.ticker != nil && !ska.closed {
ska.lastReceived = time.Now()
ska.ticker.Reset(ska.clientAliveInterval)
}
}
// notePeerActivity records inbound traffic from the peer. It bumps
// lastReceived (clearing the dead-peer deadline used by TimeIsUp) and
// resets the ticker so the next probe fires `interval` after the most
// recent activity. Matches OpenSSH sshd, which clears keep_alive_timeouts
// on every successfully-received packet and defers the next probe on
// inbound traffic. Internal — driven by the package's request loops.
func (ska *SessionKeepAlive) notePeerActivity() {
ska.m.Lock()
defer ska.m.Unlock()
if ska.ticker != nil && !ska.closed {
ska.lastReceived = time.Now()
ska.ticker.Reset(ska.clientAliveInterval)
}
}
// Ticks returns the channel that fires on each keep-alive interval.
func (ska *SessionKeepAlive) Ticks() <-chan time.Time {
return ska.tickerCh
}
// TimeIsUp returns true if the keep-alive deadline has passed.
func (ska *SessionKeepAlive) TimeIsUp() bool {
ska.m.Lock()
defer ska.m.Unlock()
return ska.lastReceived.Add(time.Duration(ska.clientAliveCountMax) * ska.clientAliveInterval).
Before(time.Now())
}
// Close stops the keep-alive ticker.
func (ska *SessionKeepAlive) Close() {
ska.m.Lock()
defer ska.m.Unlock()
if ska.ticker != nil {
ska.ticker.Stop()
}
ska.closed = true
}
// Metrics returns the current keep-alive metrics.
func (ska *SessionKeepAlive) Metrics() KeepAliveMetrics {
ska.m.Lock()
defer ska.m.Unlock()
return ska.metrics
}
// KeepAliveMetrics tracks keep-alive statistics.
type KeepAliveMetrics struct {
RequestHandlerCalled int
KeepAliveReplyReceived int
ServerRequestedKeepAlive int
}