Browse Source

Merge pull request #441 from slingamn/tor_timeout

work around a Tor bug
tags/v1.1.0-rc1
Daniel Oaks 5 years ago
parent
commit
acd9eeeb15
No account linked to committer's email address
1 changed files with 28 additions and 19 deletions
  1. 28
    19
      irc/idletimer.go

+ 28
- 19
irc/idletimer.go View File

@@ -15,12 +15,17 @@ import (
15 15
 const (
16 16
 	// RegisterTimeout is how long clients have to register before we disconnect them
17 17
 	RegisterTimeout = time.Minute
18
-	// IdleTimeout is how long without traffic before a registered client is considered idle.
19
-	IdleTimeout = time.Minute + time.Second*30
20
-	// IdleTimeoutWithResumeCap is how long without traffic before a registered client is considered idle, when they have the resume capability.
21
-	IdleTimeoutWithResumeCap = time.Minute*2 + time.Second*30
22
-	// QuitTimeout is how long without traffic before an idle client is disconnected
23
-	QuitTimeout = time.Minute
18
+	// DefaultIdleTimeout is how long without traffic before we send the client a PING
19
+	DefaultIdleTimeout = time.Minute + 30*time.Second
20
+	// For Tor clients, we send a PING at least every 30 seconds, as a workaround for this bug
21
+	// (single-onion circuits will close unless the client sends data once every 60 seconds):
22
+	// https://bugs.torproject.org/29665
23
+	TorIdleTimeout = time.Second * 30
24
+	// This is how long a client gets without sending any message, including the PONG to our
25
+	// PING, before we disconnect them:
26
+	DefaultTotalTimeout = 2*time.Minute + 30*time.Second
27
+	// Resumeable clients (clients who have negotiated caps.Resume) get longer:
28
+	ResumeableTotalTimeout = 3*time.Minute + 30*time.Second
24 29
 )
25 30
 
26 31
 // client idleness state machine
@@ -39,11 +44,11 @@ type IdleTimer struct {
39 44
 
40 45
 	// immutable after construction
41 46
 	registerTimeout time.Duration
42
-	quitTimeout     time.Duration
43 47
 	client          *Client
44 48
 
45 49
 	// mutable
46 50
 	idleTimeout time.Duration
51
+	quitTimeout time.Duration
47 52
 	state       TimerState
48 53
 	timer       *time.Timer
49 54
 }
@@ -52,26 +57,28 @@ type IdleTimer struct {
52 57
 func NewIdleTimer(client *Client) *IdleTimer {
53 58
 	it := IdleTimer{
54 59
 		registerTimeout: RegisterTimeout,
55
-		idleTimeout:     IdleTimeout,
56
-		quitTimeout:     QuitTimeout,
57 60
 		client:          client,
58 61
 	}
62
+	it.idleTimeout, it.quitTimeout = it.recomputeDurations()
59 63
 	return &it
60 64
 }
61 65
 
62
-// updateIdleDuration updates the idle duration, given the client's caps.
63
-func (it *IdleTimer) updateIdleDuration() {
64
-	newIdleTime := IdleTimeout
65
-
66
+// recomputeDurations recomputes the idle and quit durations, given the client's caps.
67
+func (it *IdleTimer) recomputeDurations() (idleTimeout, quitTimeout time.Duration) {
68
+	totalTimeout := DefaultTotalTimeout
66 69
 	// if they have the resume cap, wait longer before pinging them out
67 70
 	// to give them a chance to resume their connection
68 71
 	if it.client.capabilities.Has(caps.Resume) {
69
-		newIdleTime = IdleTimeoutWithResumeCap
72
+		totalTimeout = ResumeableTotalTimeout
70 73
 	}
71 74
 
72
-	it.Lock()
73
-	defer it.Unlock()
74
-	it.idleTimeout = newIdleTime
75
+	idleTimeout = DefaultIdleTimeout
76
+	if it.client.isTor {
77
+		idleTimeout = TorIdleTimeout
78
+	}
79
+
80
+	quitTimeout = totalTimeout - idleTimeout
81
+	return
75 82
 }
76 83
 
77 84
 // Start starts counting idle time; if there is no activity from the client,
@@ -84,10 +91,11 @@ func (it *IdleTimer) Start() {
84 91
 }
85 92
 
86 93
 func (it *IdleTimer) Touch() {
87
-	it.updateIdleDuration()
94
+	idleTimeout, quitTimeout := it.recomputeDurations()
88 95
 
89 96
 	it.Lock()
90 97
 	defer it.Unlock()
98
+	it.idleTimeout, it.quitTimeout = idleTimeout, quitTimeout
91 99
 	// a touch transitions TimerUnregistered or TimerIdle into TimerActive
92 100
 	if it.state != TimerDead {
93 101
 		it.state = TimerActive
@@ -96,12 +104,13 @@ func (it *IdleTimer) Touch() {
96 104
 }
97 105
 
98 106
 func (it *IdleTimer) processTimeout() {
99
-	it.updateIdleDuration()
107
+	idleTimeout, quitTimeout := it.recomputeDurations()
100 108
 
101 109
 	var previousState TimerState
102 110
 	func() {
103 111
 		it.Lock()
104 112
 		defer it.Unlock()
113
+		it.idleTimeout, it.quitTimeout = idleTimeout, quitTimeout
105 114
 		previousState = it.state
106 115
 		// TimerActive transitions to TimerIdle, all others to TimerDead
107 116
 		if it.state == TimerActive {

Loading…
Cancel
Save