You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

idletimer.go 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  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. // IdleTimeout is how long without traffic before a registered client is considered idle.
  14. IdleTimeout = time.Minute + time.Second*30
  15. // IdleTimeoutWithResumeCap is how long without traffic before a registered client is considered idle, when they have the resume capability.
  16. IdleTimeoutWithResumeCap = time.Minute*2 + time.Second*30
  17. // QuitTimeout is how long without traffic before an idle client is disconnected
  18. QuitTimeout = time.Minute
  19. )
  20. // client idleness state machine
  21. type TimerState uint
  22. const (
  23. TimerUnregistered TimerState = iota // client is unregistered
  24. TimerActive // client is actively sending commands
  25. TimerIdle // client is idle, we sent PING and are waiting for PONG
  26. TimerDead // client was terminated
  27. )
  28. type IdleTimer struct {
  29. sync.Mutex // tier 1
  30. // immutable after construction
  31. registerTimeout time.Duration
  32. quitTimeout time.Duration
  33. client *Client
  34. // mutable
  35. idleTimeout time.Duration
  36. state TimerState
  37. timer *time.Timer
  38. }
  39. // NewIdleTimer sets up a new IdleTimer using constant timeouts.
  40. func NewIdleTimer(client *Client) *IdleTimer {
  41. it := IdleTimer{
  42. registerTimeout: RegisterTimeout,
  43. idleTimeout: IdleTimeout,
  44. quitTimeout: QuitTimeout,
  45. client: client,
  46. }
  47. return &it
  48. }
  49. // updateIdleDuration updates the idle duration, given the client's caps.
  50. func (it *IdleTimer) updateIdleDuration() {
  51. newIdleTime := IdleTimeout
  52. // if they have the resume cap, wait longer before pinging them out
  53. // to give them a chance to resume their connection
  54. if it.client.capabilities.Has(caps.Resume) {
  55. newIdleTime = IdleTimeoutWithResumeCap
  56. }
  57. it.Lock()
  58. defer it.Unlock()
  59. it.idleTimeout = newIdleTime
  60. }
  61. // Start starts counting idle time; if there is no activity from the client,
  62. // it will eventually be stopped.
  63. func (it *IdleTimer) Start() {
  64. it.Lock()
  65. defer it.Unlock()
  66. it.state = TimerUnregistered
  67. it.resetTimeout()
  68. }
  69. func (it *IdleTimer) Touch() {
  70. // ignore touches from unregistered clients
  71. if !it.client.Registered() {
  72. return
  73. }
  74. it.updateIdleDuration()
  75. it.Lock()
  76. defer it.Unlock()
  77. // a touch transitions TimerUnregistered or TimerIdle into TimerActive
  78. if it.state != TimerDead {
  79. it.state = TimerActive
  80. it.resetTimeout()
  81. }
  82. }
  83. func (it *IdleTimer) processTimeout() {
  84. it.updateIdleDuration()
  85. var previousState TimerState
  86. func() {
  87. it.Lock()
  88. defer it.Unlock()
  89. previousState = it.state
  90. // TimerActive transitions to TimerIdle, all others to TimerDead
  91. if it.state == TimerActive {
  92. // send them a ping, give them time to respond
  93. it.state = TimerIdle
  94. it.resetTimeout()
  95. } else {
  96. it.state = TimerDead
  97. }
  98. }()
  99. if previousState == TimerActive {
  100. it.client.Ping()
  101. } else {
  102. it.client.Quit(it.quitMessage(previousState))
  103. it.client.destroy(false)
  104. }
  105. }
  106. // Stop stops counting idle time.
  107. func (it *IdleTimer) Stop() {
  108. it.Lock()
  109. defer it.Unlock()
  110. it.state = TimerDead
  111. it.resetTimeout()
  112. }
  113. func (it *IdleTimer) resetTimeout() {
  114. if it.timer != nil {
  115. it.timer.Stop()
  116. }
  117. var nextTimeout time.Duration
  118. switch it.state {
  119. case TimerUnregistered:
  120. nextTimeout = it.registerTimeout
  121. case TimerActive:
  122. nextTimeout = it.idleTimeout
  123. case TimerIdle:
  124. nextTimeout = it.quitTimeout
  125. case TimerDead:
  126. return
  127. }
  128. it.timer = time.AfterFunc(nextTimeout, it.processTimeout)
  129. }
  130. func (it *IdleTimer) quitMessage(state TimerState) string {
  131. switch state {
  132. case TimerUnregistered:
  133. return fmt.Sprintf("Registration timeout: %v", it.registerTimeout)
  134. case TimerIdle:
  135. // how many seconds before registered clients are timed out (IdleTimeout plus QuitTimeout).
  136. it.Lock()
  137. defer it.Unlock()
  138. return fmt.Sprintf("Ping timeout: %v", (it.idleTimeout + it.quitTimeout))
  139. default:
  140. // shouldn't happen
  141. return ""
  142. }
  143. }