|
@@ -40,6 +40,22 @@ const (
|
40
|
40
|
lastSeenWriteInterval = time.Hour
|
41
|
41
|
)
|
42
|
42
|
|
|
43
|
+const (
|
|
44
|
+ // RegisterTimeout is how long clients have to register before we disconnect them
|
|
45
|
+ RegisterTimeout = time.Minute
|
|
46
|
+ // DefaultIdleTimeout is how long without traffic before we send the client a PING
|
|
47
|
+ DefaultIdleTimeout = time.Minute + 30*time.Second
|
|
48
|
+ // For Tor clients, we send a PING at least every 30 seconds, as a workaround for this bug
|
|
49
|
+ // (single-onion circuits will close unless the client sends data once every 60 seconds):
|
|
50
|
+ // https://bugs.torproject.org/29665
|
|
51
|
+ TorIdleTimeout = time.Second * 30
|
|
52
|
+ // This is how long a client gets without sending any message, including the PONG to our
|
|
53
|
+ // PING, before we disconnect them:
|
|
54
|
+ DefaultTotalTimeout = 2*time.Minute + 30*time.Second
|
|
55
|
+ // Resumeable clients (clients who have negotiated caps.Resume) get longer:
|
|
56
|
+ ResumeableTotalTimeout = 3*time.Minute + 30*time.Second
|
|
57
|
+)
|
|
58
|
+
|
43
|
59
|
// ResumeDetails is a place to stash data at various stages of
|
44
|
60
|
// the resume process: when handling the RESUME command itself,
|
45
|
61
|
// when completing the registration, and when rejoining channels.
|
|
@@ -83,6 +99,7 @@ type Client struct {
|
83
|
99
|
realname string
|
84
|
100
|
realIP net.IP
|
85
|
101
|
registered bool
|
|
102
|
+ registrationTimer *time.Timer
|
86
|
103
|
resumeID string
|
87
|
104
|
server *Server
|
88
|
105
|
skeleton string
|
|
@@ -123,7 +140,10 @@ type Session struct {
|
123
|
140
|
deviceID string
|
124
|
141
|
|
125
|
142
|
ctime time.Time
|
126
|
|
- lastActive time.Time
|
|
143
|
+ lastActive time.Time // last non-CTCP PRIVMSG sent; updates publicly visible idle time
|
|
144
|
+ lastTouch time.Time // last line sent; updates timer for idle timeouts
|
|
145
|
+ idleTimer *time.Timer
|
|
146
|
+ pingSent bool // we sent PING to a putatively idle connection and we're waiting for PONG
|
127
|
147
|
|
128
|
148
|
socket *Socket
|
129
|
149
|
realIP net.IP
|
|
@@ -131,7 +151,6 @@ type Session struct {
|
131
|
151
|
rawHostname string
|
132
|
152
|
isTor bool
|
133
|
153
|
|
134
|
|
- idletimer IdleTimer
|
135
|
154
|
fakelag Fakelag
|
136
|
155
|
deferredFakelagCount int
|
137
|
156
|
destroyed uint32
|
|
@@ -342,7 +361,6 @@ func (server *Server) RunClient(conn IRCConn) {
|
342
|
361
|
}
|
343
|
362
|
client.sessions = []*Session{session}
|
344
|
363
|
|
345
|
|
- session.idletimer.Initialize(session)
|
346
|
364
|
session.resetFakelag()
|
347
|
365
|
|
348
|
366
|
if wConn.Secure {
|
|
@@ -363,6 +381,7 @@ func (server *Server) RunClient(conn IRCConn) {
|
363
|
381
|
}
|
364
|
382
|
}
|
365
|
383
|
|
|
384
|
+ client.registrationTimer = time.AfterFunc(RegisterTimeout, client.handleRegisterTimeout)
|
366
|
385
|
server.stats.Add()
|
367
|
386
|
client.run(session)
|
368
|
387
|
}
|
|
@@ -605,7 +624,7 @@ func (client *Client) run(session *Session) {
|
605
|
624
|
|
606
|
625
|
isReattach := client.Registered()
|
607
|
626
|
if isReattach {
|
608
|
|
- session.idletimer.Touch()
|
|
627
|
+ client.Touch(session)
|
609
|
628
|
if session.resumeDetails != nil {
|
610
|
629
|
session.playResume()
|
611
|
630
|
session.resumeDetails = nil
|
|
@@ -739,6 +758,7 @@ func (client *Client) Touch(session *Session) {
|
739
|
758
|
client.lastSeenLastWrite = now
|
740
|
759
|
}
|
741
|
760
|
}
|
|
761
|
+ client.updateIdleTimer(session, now)
|
742
|
762
|
client.stateMutex.Unlock()
|
743
|
763
|
if markDirty {
|
744
|
764
|
client.markDirty(IncludeLastSeen)
|
|
@@ -763,6 +783,71 @@ func (client *Client) setLastSeen(now time.Time, deviceID string) {
|
763
|
783
|
}
|
764
|
784
|
}
|
765
|
785
|
|
|
786
|
+func (client *Client) updateIdleTimer(session *Session, now time.Time) {
|
|
787
|
+ session.lastTouch = now
|
|
788
|
+ session.pingSent = false
|
|
789
|
+
|
|
790
|
+ if session.idleTimer == nil {
|
|
791
|
+ pingTimeout := DefaultIdleTimeout
|
|
792
|
+ if session.isTor {
|
|
793
|
+ pingTimeout = TorIdleTimeout
|
|
794
|
+ }
|
|
795
|
+ session.idleTimer = time.AfterFunc(pingTimeout, session.handleIdleTimeout)
|
|
796
|
+ }
|
|
797
|
+}
|
|
798
|
+
|
|
799
|
+func (session *Session) handleIdleTimeout() {
|
|
800
|
+ totalTimeout := DefaultTotalTimeout
|
|
801
|
+ if session.capabilities.Has(caps.Resume) {
|
|
802
|
+ totalTimeout = ResumeableTotalTimeout
|
|
803
|
+ }
|
|
804
|
+ pingTimeout := DefaultIdleTimeout
|
|
805
|
+ if session.isTor {
|
|
806
|
+ pingTimeout = TorIdleTimeout
|
|
807
|
+ }
|
|
808
|
+
|
|
809
|
+ session.client.stateMutex.Lock()
|
|
810
|
+ now := time.Now()
|
|
811
|
+ timeUntilDestroy := session.lastTouch.Add(totalTimeout).Sub(now)
|
|
812
|
+ timeUntilPing := session.lastTouch.Add(pingTimeout).Sub(now)
|
|
813
|
+ shouldDestroy := session.pingSent && timeUntilDestroy <= 0
|
|
814
|
+ shouldSendPing := !session.pingSent && timeUntilPing <= 0
|
|
815
|
+ if !shouldDestroy {
|
|
816
|
+ if shouldSendPing {
|
|
817
|
+ session.pingSent = true
|
|
818
|
+ }
|
|
819
|
+ // check in again at the minimum of these 3 possible intervals:
|
|
820
|
+ // 1. the ping timeout (assuming we PING and they reply immediately with PONG)
|
|
821
|
+ // 2. the next time we would send PING (if they don't send any more lines)
|
|
822
|
+ // 3. the next time we would destroy (if they don't send any more lines)
|
|
823
|
+ nextTimeout := pingTimeout
|
|
824
|
+ if 0 < timeUntilPing && timeUntilPing < nextTimeout {
|
|
825
|
+ nextTimeout = timeUntilPing
|
|
826
|
+ }
|
|
827
|
+ if 0 < timeUntilDestroy && timeUntilDestroy < nextTimeout {
|
|
828
|
+ nextTimeout = timeUntilDestroy
|
|
829
|
+ }
|
|
830
|
+ session.idleTimer.Stop()
|
|
831
|
+ session.idleTimer.Reset(nextTimeout)
|
|
832
|
+ }
|
|
833
|
+ session.client.stateMutex.Unlock()
|
|
834
|
+
|
|
835
|
+ if shouldDestroy {
|
|
836
|
+ session.client.Quit(fmt.Sprintf("Ping timeout: %v", totalTimeout), session)
|
|
837
|
+ session.client.destroy(session)
|
|
838
|
+ } else if shouldSendPing {
|
|
839
|
+ session.Ping()
|
|
840
|
+ }
|
|
841
|
+}
|
|
842
|
+
|
|
843
|
+func (session *Session) stopIdleTimer() {
|
|
844
|
+ session.client.stateMutex.Lock()
|
|
845
|
+ defer session.client.stateMutex.Unlock()
|
|
846
|
+ if session.idleTimer != nil {
|
|
847
|
+ session.idleTimer.Stop()
|
|
848
|
+ }
|
|
849
|
+}
|
|
850
|
+
|
766
|
851
|
// Ping sends the client a PING message.
|
767
|
852
|
func (session *Session) Ping() {
|
768
|
853
|
session.Send(nil, "", "PING", session.client.Nick())
|
|
@@ -1119,6 +1204,10 @@ func (client *Client) SetNick(nick, nickCasefolded, skeleton string) (success bo
|
1119
|
1204
|
} else if !client.registered {
|
1120
|
1205
|
// XXX test this before setting it to avoid annoying the race detector
|
1121
|
1206
|
client.registered = true
|
|
1207
|
+ if client.registrationTimer != nil {
|
|
1208
|
+ client.registrationTimer.Stop()
|
|
1209
|
+ client.registrationTimer = nil
|
|
1210
|
+ }
|
1122
|
1211
|
}
|
1123
|
1212
|
client.nick = nick
|
1124
|
1213
|
client.nickCasefolded = nickCasefolded
|
|
@@ -1294,6 +1383,11 @@ func (client *Client) destroy(session *Session) {
|
1294
|
1383
|
client.awayMessage = awayMessage
|
1295
|
1384
|
}
|
1296
|
1385
|
|
|
1386
|
+ if client.registrationTimer != nil {
|
|
1387
|
+ // unconditionally stop; if the client is still unregistered it must be destroyed
|
|
1388
|
+ client.registrationTimer.Stop()
|
|
1389
|
+ }
|
|
1390
|
+
|
1297
|
1391
|
client.stateMutex.Unlock()
|
1298
|
1392
|
|
1299
|
1393
|
// XXX there is no particular reason to persist this state here rather than
|
|
@@ -1311,7 +1405,7 @@ func (client *Client) destroy(session *Session) {
|
1311
|
1405
|
// session has been attached to a new client; do not destroy it
|
1312
|
1406
|
continue
|
1313
|
1407
|
}
|
1314
|
|
- session.idletimer.Stop()
|
|
1408
|
+ session.stopIdleTimer()
|
1315
|
1409
|
// send quit/error message to client if they haven't been sent already
|
1316
|
1410
|
client.Quit("", session)
|
1317
|
1411
|
quitMessage = session.quitMessage
|
|
@@ -1693,6 +1787,11 @@ func (client *Client) historyStatus(config *Config) (status HistoryStatus, targe
|
1693
|
1787
|
return
|
1694
|
1788
|
}
|
1695
|
1789
|
|
|
1790
|
+func (client *Client) handleRegisterTimeout() {
|
|
1791
|
+ client.Quit(fmt.Sprintf("Registration timeout: %v", RegisterTimeout), nil)
|
|
1792
|
+ client.destroy(nil)
|
|
1793
|
+}
|
|
1794
|
+
|
1696
|
1795
|
func (client *Client) copyLastSeen() (result map[string]time.Time) {
|
1697
|
1796
|
client.stateMutex.RLock()
|
1698
|
1797
|
defer client.stateMutex.RUnlock()
|