Official cwtch.im peer implementation. https://cwtch.im
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.

144 lines
3.5 KiB

  1. package plugins
  2. import (
  3. "cwtch.im/cwtch/event"
  4. "cwtch.im/cwtch/protocol/connections"
  5. "git.openprivacy.ca/openprivacy/log"
  6. "sync"
  7. "time"
  8. )
  9. const tickTime = 10 * time.Second
  10. const maxBakoff int = 32 // 320 seconds or ~5 min
  11. type connectionType int
  12. const (
  13. peerConn connectionType = iota
  14. serverConn
  15. )
  16. type contact struct {
  17. id string
  18. state connections.ConnectionState
  19. ctype connectionType
  20. ticks int
  21. backoff int
  22. }
  23. type contactRetry struct {
  24. bus event.Manager
  25. queue event.Queue
  26. networkUp bool
  27. running bool
  28. breakChan chan bool
  29. onion string
  30. connections sync.Map //[string]*contact
  31. }
  32. // NewConnectionRetry returns a Plugin that when started will retry connecting to contacts with a backoff timing
  33. func NewConnectionRetry(bus event.Manager, onion string) Plugin {
  34. cr := &contactRetry{bus: bus, queue: event.NewQueue(), breakChan: make(chan bool), connections: sync.Map{}, networkUp: false, onion: onion}
  35. return cr
  36. }
  37. func (cr *contactRetry) Start() {
  38. if !cr.running {
  39. go cr.run()
  40. } else {
  41. log.Errorf("Attempted to start Contact Retry plugin twice for %v", cr.onion)
  42. }
  43. }
  44. func (cr *contactRetry) run() {
  45. cr.running = true
  46. cr.bus.Subscribe(event.PeerStateChange, cr.queue)
  47. cr.bus.Subscribe(event.ACNStatus, cr.queue)
  48. cr.bus.Subscribe(event.ServerStateChange, cr.queue)
  49. for {
  50. select {
  51. case e := <-cr.queue.OutChan():
  52. switch e.EventType {
  53. case event.PeerStateChange:
  54. state := connections.ConnectionStateToType()[e.Data[event.ConnectionState]]
  55. peer := e.Data[event.RemotePeer]
  56. cr.handleEvent(peer, state, peerConn)
  57. case event.ServerStateChange:
  58. state := connections.ConnectionStateToType()[e.Data[event.ConnectionState]]
  59. server := e.Data[event.GroupServer]
  60. cr.handleEvent(server, state, serverConn)
  61. case event.ACNStatus:
  62. prog := e.Data[event.Progreess]
  63. if prog == "100" && cr.networkUp == false {
  64. cr.networkUp = true
  65. cr.connections.Range(func(k, v interface{}) bool {
  66. p := v.(*contact)
  67. p.ticks = 0
  68. p.backoff = 1
  69. if p.ctype == peerConn {
  70. cr.bus.Publish(event.NewEvent(event.RetryPeerRequest, map[event.Field]string{event.RemotePeer: p.id}))
  71. }
  72. return true
  73. })
  74. } else if prog != "100" {
  75. cr.networkUp = false
  76. }
  77. }
  78. case <-time.After(tickTime):
  79. cr.connections.Range(func(k, v interface{}) bool {
  80. p := v.(*contact)
  81. if p.state == connections.DISCONNECTED {
  82. p.ticks++
  83. if p.ticks == p.backoff {
  84. p.ticks = 0
  85. if cr.networkUp {
  86. if p.ctype == peerConn {
  87. cr.bus.Publish(event.NewEvent(event.RetryPeerRequest, map[event.Field]string{event.RemotePeer: p.id}))
  88. }
  89. }
  90. }
  91. }
  92. return true
  93. })
  94. case <-cr.breakChan:
  95. cr.running = false
  96. return
  97. }
  98. }
  99. }
  100. func (cr *contactRetry) handleEvent(id string, state connections.ConnectionState, ctype connectionType) {
  101. if _, exists := cr.connections.Load(id); !exists {
  102. p := &contact{id: id, state: connections.DISCONNECTED, backoff: 1, ticks: 0, ctype: ctype}
  103. cr.connections.Store(id, p)
  104. return
  105. }
  106. pinf, _ := cr.connections.Load(id)
  107. p := pinf.(*contact)
  108. if state == connections.DISCONNECTED || state == connections.FAILED || state == connections.KILLED {
  109. p.state = connections.DISCONNECTED
  110. if p.backoff < maxBakoff {
  111. p.backoff *= 2
  112. }
  113. p.ticks = 0
  114. } else if state == connections.CONNECTING || state == connections.CONNECTED {
  115. p.state = state
  116. } else if state == connections.AUTHENTICATED {
  117. p.state = state
  118. p.backoff = 1
  119. }
  120. }
  121. func (cr *contactRetry) Shutdown() {
  122. cr.breakChan <- true
  123. }