3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-14 07:59:31 +01:00

Merge pull request #441 from slingamn/tor_timeout

work around a Tor bug
This commit is contained in:
Daniel Oaks 2019-03-06 06:08:09 +00:00 committed by GitHub
commit acd9eeeb15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -15,12 +15,17 @@ import (
const ( const (
// RegisterTimeout is how long clients have to register before we disconnect them // RegisterTimeout is how long clients have to register before we disconnect them
RegisterTimeout = time.Minute RegisterTimeout = time.Minute
// IdleTimeout is how long without traffic before a registered client is considered idle. // DefaultIdleTimeout is how long without traffic before we send the client a PING
IdleTimeout = time.Minute + time.Second*30 DefaultIdleTimeout = time.Minute + 30*time.Second
// IdleTimeoutWithResumeCap is how long without traffic before a registered client is considered idle, when they have the resume capability. // For Tor clients, we send a PING at least every 30 seconds, as a workaround for this bug
IdleTimeoutWithResumeCap = time.Minute*2 + time.Second*30 // (single-onion circuits will close unless the client sends data once every 60 seconds):
// QuitTimeout is how long without traffic before an idle client is disconnected // https://bugs.torproject.org/29665
QuitTimeout = time.Minute TorIdleTimeout = time.Second * 30
// This is how long a client gets without sending any message, including the PONG to our
// PING, before we disconnect them:
DefaultTotalTimeout = 2*time.Minute + 30*time.Second
// Resumeable clients (clients who have negotiated caps.Resume) get longer:
ResumeableTotalTimeout = 3*time.Minute + 30*time.Second
) )
// client idleness state machine // client idleness state machine
@ -39,11 +44,11 @@ type IdleTimer struct {
// immutable after construction // immutable after construction
registerTimeout time.Duration registerTimeout time.Duration
quitTimeout time.Duration
client *Client client *Client
// mutable // mutable
idleTimeout time.Duration idleTimeout time.Duration
quitTimeout time.Duration
state TimerState state TimerState
timer *time.Timer timer *time.Timer
} }
@ -52,26 +57,28 @@ type IdleTimer struct {
func NewIdleTimer(client *Client) *IdleTimer { func NewIdleTimer(client *Client) *IdleTimer {
it := IdleTimer{ it := IdleTimer{
registerTimeout: RegisterTimeout, registerTimeout: RegisterTimeout,
idleTimeout: IdleTimeout,
quitTimeout: QuitTimeout,
client: client, client: client,
} }
it.idleTimeout, it.quitTimeout = it.recomputeDurations()
return &it return &it
} }
// updateIdleDuration updates the idle duration, given the client's caps. // recomputeDurations recomputes the idle and quit durations, given the client's caps.
func (it *IdleTimer) updateIdleDuration() { func (it *IdleTimer) recomputeDurations() (idleTimeout, quitTimeout time.Duration) {
newIdleTime := IdleTimeout totalTimeout := DefaultTotalTimeout
// if they have the resume cap, wait longer before pinging them out // if they have the resume cap, wait longer before pinging them out
// to give them a chance to resume their connection // to give them a chance to resume their connection
if it.client.capabilities.Has(caps.Resume) { if it.client.capabilities.Has(caps.Resume) {
newIdleTime = IdleTimeoutWithResumeCap totalTimeout = ResumeableTotalTimeout
} }
it.Lock() idleTimeout = DefaultIdleTimeout
defer it.Unlock() if it.client.isTor {
it.idleTimeout = newIdleTime idleTimeout = TorIdleTimeout
}
quitTimeout = totalTimeout - idleTimeout
return
} }
// Start starts counting idle time; if there is no activity from the client, // Start starts counting idle time; if there is no activity from the client,
@ -84,10 +91,11 @@ func (it *IdleTimer) Start() {
} }
func (it *IdleTimer) Touch() { func (it *IdleTimer) Touch() {
it.updateIdleDuration() idleTimeout, quitTimeout := it.recomputeDurations()
it.Lock() it.Lock()
defer it.Unlock() defer it.Unlock()
it.idleTimeout, it.quitTimeout = idleTimeout, quitTimeout
// a touch transitions TimerUnregistered or TimerIdle into TimerActive // a touch transitions TimerUnregistered or TimerIdle into TimerActive
if it.state != TimerDead { if it.state != TimerDead {
it.state = TimerActive it.state = TimerActive
@ -96,12 +104,13 @@ func (it *IdleTimer) Touch() {
} }
func (it *IdleTimer) processTimeout() { func (it *IdleTimer) processTimeout() {
it.updateIdleDuration() idleTimeout, quitTimeout := it.recomputeDurations()
var previousState TimerState var previousState TimerState
func() { func() {
it.Lock() it.Lock()
defer it.Unlock() defer it.Unlock()
it.idleTimeout, it.quitTimeout = idleTimeout, quitTimeout
previousState = it.state previousState = it.state
// TimerActive transitions to TimerIdle, all others to TimerDead // TimerActive transitions to TimerIdle, all others to TimerDead
if it.state == TimerActive { if it.state == TimerActive {