Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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