您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

idletimer.go 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // Copyright (c) 2017 Shivaram Lingamneni <slingamn@cs.stanford.edu>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "fmt"
  6. "sync"
  7. "time"
  8. "github.com/oragono/oragono/irc/caps"
  9. )
  10. const (
  11. // RegisterTimeout is how long clients have to register before we disconnect them
  12. RegisterTimeout = time.Minute
  13. // DefaultIdleTimeout is how long without traffic before we send the client a PING
  14. DefaultIdleTimeout = time.Minute + 30*time.Second
  15. // For Tor clients, we send a PING at least every 30 seconds, as a workaround for this bug
  16. // (single-onion circuits will close unless the client sends data once every 60 seconds):
  17. // https://bugs.torproject.org/29665
  18. TorIdleTimeout = time.Second * 30
  19. // This is how long a client gets without sending any message, including the PONG to our
  20. // PING, before we disconnect them:
  21. DefaultTotalTimeout = 2*time.Minute + 30*time.Second
  22. // Resumeable clients (clients who have negotiated caps.Resume) get longer:
  23. ResumeableTotalTimeout = 3*time.Minute + 30*time.Second
  24. )
  25. // client idleness state machine
  26. type TimerState uint
  27. const (
  28. TimerUnregistered TimerState = iota // client is unregistered
  29. TimerActive // client is actively sending commands
  30. TimerIdle // client is idle, we sent PING and are waiting for PONG
  31. TimerDead // client was terminated
  32. )
  33. type IdleTimer struct {
  34. sync.Mutex // tier 1
  35. // immutable after construction
  36. registerTimeout time.Duration
  37. session *Session
  38. // mutable
  39. idleTimeout time.Duration
  40. quitTimeout time.Duration
  41. state TimerState
  42. timer *time.Timer
  43. }
  44. // Initialize sets up an IdleTimer and starts counting idle time;
  45. // if there is no activity from the client, it will eventually be stopped.
  46. func (it *IdleTimer) Initialize(session *Session) {
  47. it.session = session
  48. it.registerTimeout = RegisterTimeout
  49. it.idleTimeout, it.quitTimeout = it.recomputeDurations()
  50. registered := session.client.Registered()
  51. it.Lock()
  52. defer it.Unlock()
  53. if registered {
  54. it.state = TimerActive
  55. } else {
  56. it.state = TimerUnregistered
  57. }
  58. it.resetTimeout()
  59. }
  60. // recomputeDurations recomputes the idle and quit durations, given the client's caps.
  61. func (it *IdleTimer) recomputeDurations() (idleTimeout, quitTimeout time.Duration) {
  62. totalTimeout := DefaultTotalTimeout
  63. // if they have the resume cap, wait longer before pinging them out
  64. // to give them a chance to resume their connection
  65. if it.session.capabilities.Has(caps.Resume) {
  66. totalTimeout = ResumeableTotalTimeout
  67. }
  68. idleTimeout = DefaultIdleTimeout
  69. if it.session.isTor {
  70. idleTimeout = TorIdleTimeout
  71. }
  72. quitTimeout = totalTimeout - idleTimeout
  73. return
  74. }
  75. func (it *IdleTimer) Touch() {
  76. idleTimeout, quitTimeout := it.recomputeDurations()
  77. it.Lock()
  78. defer it.Unlock()
  79. it.idleTimeout, it.quitTimeout = idleTimeout, quitTimeout
  80. // a touch transitions TimerUnregistered or TimerIdle into TimerActive
  81. if it.state != TimerDead {
  82. it.state = TimerActive
  83. it.resetTimeout()
  84. }
  85. }
  86. func (it *IdleTimer) processTimeout() {
  87. idleTimeout, quitTimeout := it.recomputeDurations()
  88. var previousState TimerState
  89. func() {
  90. it.Lock()
  91. defer it.Unlock()
  92. it.idleTimeout, it.quitTimeout = idleTimeout, quitTimeout
  93. previousState = it.state
  94. // TimerActive transitions to TimerIdle, all others to TimerDead
  95. if it.state == TimerActive {
  96. // send them a ping, give them time to respond
  97. it.state = TimerIdle
  98. it.resetTimeout()
  99. } else {
  100. it.state = TimerDead
  101. }
  102. }()
  103. if previousState == TimerActive {
  104. it.session.Ping()
  105. } else {
  106. it.session.client.Quit(it.quitMessage(previousState), it.session)
  107. it.session.client.destroy(it.session)
  108. }
  109. }
  110. // Stop stops counting idle time.
  111. func (it *IdleTimer) Stop() {
  112. if it == nil {
  113. return
  114. }
  115. it.Lock()
  116. defer it.Unlock()
  117. it.state = TimerDead
  118. it.resetTimeout()
  119. }
  120. func (it *IdleTimer) resetTimeout() {
  121. if it.timer != nil {
  122. it.timer.Stop()
  123. }
  124. var nextTimeout time.Duration
  125. switch it.state {
  126. case TimerUnregistered:
  127. nextTimeout = it.registerTimeout
  128. case TimerActive:
  129. nextTimeout = it.idleTimeout
  130. case TimerIdle:
  131. nextTimeout = it.quitTimeout
  132. case TimerDead:
  133. return
  134. }
  135. if it.timer != nil {
  136. it.timer.Reset(nextTimeout)
  137. } else {
  138. it.timer = time.AfterFunc(nextTimeout, it.processTimeout)
  139. }
  140. }
  141. func (it *IdleTimer) quitMessage(state TimerState) string {
  142. switch state {
  143. case TimerUnregistered:
  144. return fmt.Sprintf("Registration timeout: %v", it.registerTimeout)
  145. case TimerIdle:
  146. // how many seconds before registered clients are timed out (IdleTimeout plus QuitTimeout).
  147. it.Lock()
  148. defer it.Unlock()
  149. return fmt.Sprintf("Ping timeout: %v", (it.idleTimeout + it.quitTimeout))
  150. default:
  151. // shouldn't happen
  152. return ""
  153. }
  154. }
  155. // BrbTimer is a timer on the client as a whole (not an individual session) for implementing
  156. // the BRB command and related functionality (where a client can remain online without
  157. // having any connected sessions).
  158. type BrbState uint
  159. const (
  160. // BrbDisabled is the default state; the client will be disconnected if it has no sessions
  161. BrbDisabled BrbState = iota
  162. // BrbEnabled allows the client to remain online without sessions; if a timeout is
  163. // reached, it will be removed
  164. BrbEnabled
  165. // BrbDead is the state of a client after its timeout has expired; it will be removed
  166. // and therefore new sessions cannot be attached to it
  167. BrbDead
  168. )
  169. type BrbTimer struct {
  170. // XXX we use client.stateMutex for synchronization, so we can atomically test
  171. // conditions that use both brbTimer.state and client.sessions. This code
  172. // is tightly coupled with the rest of Client.
  173. client *Client
  174. state BrbState
  175. brbAt time.Time
  176. duration time.Duration
  177. timer *time.Timer
  178. }
  179. func (bt *BrbTimer) Initialize(client *Client) {
  180. bt.client = client
  181. }
  182. // attempts to enable BRB for a client, returns whether it succeeded
  183. func (bt *BrbTimer) Enable() (success bool, duration time.Duration) {
  184. // TODO make this configurable
  185. duration = ResumeableTotalTimeout
  186. bt.client.stateMutex.Lock()
  187. defer bt.client.stateMutex.Unlock()
  188. if !bt.client.registered || bt.client.alwaysOn || bt.client.resumeID == "" {
  189. return
  190. }
  191. switch bt.state {
  192. case BrbDisabled, BrbEnabled:
  193. bt.state = BrbEnabled
  194. bt.duration = duration
  195. bt.resetTimeout()
  196. // only track the earliest BRB, if multiple sessions are BRB'ing at once
  197. // TODO(#524) this is inaccurate in case of an auto-BRB
  198. if bt.brbAt.IsZero() {
  199. bt.brbAt = time.Now().UTC()
  200. }
  201. success = true
  202. default:
  203. // BrbDead
  204. success = false
  205. }
  206. return
  207. }
  208. // turns off BRB for a client and stops the timer; used on resume and during
  209. // client teardown
  210. func (bt *BrbTimer) Disable() (brbAt time.Time) {
  211. bt.client.stateMutex.Lock()
  212. defer bt.client.stateMutex.Unlock()
  213. if bt.state == BrbEnabled {
  214. bt.state = BrbDisabled
  215. brbAt = bt.brbAt
  216. bt.brbAt = time.Time{}
  217. }
  218. bt.resetTimeout()
  219. return
  220. }
  221. func (bt *BrbTimer) resetTimeout() {
  222. if bt.timer != nil {
  223. bt.timer.Stop()
  224. }
  225. if bt.state != BrbEnabled {
  226. return
  227. }
  228. if bt.timer == nil {
  229. bt.timer = time.AfterFunc(bt.duration, bt.processTimeout)
  230. } else {
  231. bt.timer.Reset(bt.duration)
  232. }
  233. }
  234. func (bt *BrbTimer) processTimeout() {
  235. dead := false
  236. defer func() {
  237. if dead {
  238. bt.client.Quit(bt.client.AwayMessage(), nil)
  239. bt.client.destroy(nil)
  240. }
  241. }()
  242. bt.client.stateMutex.Lock()
  243. defer bt.client.stateMutex.Unlock()
  244. if bt.client.alwaysOn {
  245. return
  246. }
  247. switch bt.state {
  248. case BrbDisabled, BrbEnabled:
  249. if len(bt.client.sessions) == 0 {
  250. // client never returned, quit them
  251. bt.state = BrbDead
  252. dead = true
  253. } else {
  254. // client resumed, reattached, or has another active session
  255. bt.state = BrbDisabled
  256. bt.brbAt = time.Time{}
  257. }
  258. case BrbDead:
  259. dead = true // shouldn't be possible but whatever
  260. }
  261. bt.resetTimeout()
  262. }