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.

229 lines
5.6 KiB

cleanup: Reduce and normalize import path aliasing. (#6975) The code in the Tendermint repository makes heavy use of import aliasing. This is made necessary by our extensive reuse of common base package names, and by repetition of similar names across different subdirectories. Unfortunately we have not been very consistent about which packages we alias in various circumstances, and the aliases we use vary. In the spirit of the advice in the style guide and https://github.com/golang/go/wiki/CodeReviewComments#imports, his change makes an effort to clean up and normalize import aliasing. This change makes no API or behavioral changes. It is a pure cleanup intended o help make the code more readable to developers (including myself) trying to understand what is being imported where. Only unexported names have been modified, and the changes were generated and applied mechanically with gofmt -r and comby, respecting the lexical and syntactic rules of Go. Even so, I did not fix every inconsistency. Where the changes would be too disruptive, I left it alone. The principles I followed in this cleanup are: - Remove aliases that restate the package name. - Remove aliases where the base package name is unambiguous. - Move overly-terse abbreviations from the import to the usage site. - Fix lexical issues (remove underscores, remove capitalization). - Fix import groupings to more closely match the style guide. - Group blank (side-effecting) imports and ensure they are commented. - Add aliases to multiple imports with the same base package name.
3 years ago
cleanup: Reduce and normalize import path aliasing. (#6975) The code in the Tendermint repository makes heavy use of import aliasing. This is made necessary by our extensive reuse of common base package names, and by repetition of similar names across different subdirectories. Unfortunately we have not been very consistent about which packages we alias in various circumstances, and the aliases we use vary. In the spirit of the advice in the style guide and https://github.com/golang/go/wiki/CodeReviewComments#imports, his change makes an effort to clean up and normalize import aliasing. This change makes no API or behavioral changes. It is a pure cleanup intended o help make the code more readable to developers (including myself) trying to understand what is being imported where. Only unexported names have been modified, and the changes were generated and applied mechanically with gofmt -r and comby, respecting the lexical and syntactic rules of Go. Even so, I did not fix every inconsistency. Where the changes would be too disruptive, I left it alone. The principles I followed in this cleanup are: - Remove aliases that restate the package name. - Remove aliases where the base package name is unambiguous. - Move overly-terse abbreviations from the import to the usage site. - Fix lexical issues (remove underscores, remove capitalization). - Fix import groupings to more closely match the style guide. - Group blank (side-effecting) imports and ensure they are commented. - Add aliases to multiple imports with the same base package name.
3 years ago
cleanup: Reduce and normalize import path aliasing. (#6975) The code in the Tendermint repository makes heavy use of import aliasing. This is made necessary by our extensive reuse of common base package names, and by repetition of similar names across different subdirectories. Unfortunately we have not been very consistent about which packages we alias in various circumstances, and the aliases we use vary. In the spirit of the advice in the style guide and https://github.com/golang/go/wiki/CodeReviewComments#imports, his change makes an effort to clean up and normalize import aliasing. This change makes no API or behavioral changes. It is a pure cleanup intended o help make the code more readable to developers (including myself) trying to understand what is being imported where. Only unexported names have been modified, and the changes were generated and applied mechanically with gofmt -r and comby, respecting the lexical and syntactic rules of Go. Even so, I did not fix every inconsistency. Where the changes would be too disruptive, I left it alone. The principles I followed in this cleanup are: - Remove aliases that restate the package name. - Remove aliases where the base package name is unambiguous. - Move overly-terse abbreviations from the import to the usage site. - Fix lexical issues (remove underscores, remove capitalization). - Fix import groupings to more closely match the style guide. - Group blank (side-effecting) imports and ensure they are commented. - Add aliases to multiple imports with the same base package name.
3 years ago
cleanup: Reduce and normalize import path aliasing. (#6975) The code in the Tendermint repository makes heavy use of import aliasing. This is made necessary by our extensive reuse of common base package names, and by repetition of similar names across different subdirectories. Unfortunately we have not been very consistent about which packages we alias in various circumstances, and the aliases we use vary. In the spirit of the advice in the style guide and https://github.com/golang/go/wiki/CodeReviewComments#imports, his change makes an effort to clean up and normalize import aliasing. This change makes no API or behavioral changes. It is a pure cleanup intended o help make the code more readable to developers (including myself) trying to understand what is being imported where. Only unexported names have been modified, and the changes were generated and applied mechanically with gofmt -r and comby, respecting the lexical and syntactic rules of Go. Even so, I did not fix every inconsistency. Where the changes would be too disruptive, I left it alone. The principles I followed in this cleanup are: - Remove aliases that restate the package name. - Remove aliases where the base package name is unambiguous. - Move overly-terse abbreviations from the import to the usage site. - Fix lexical issues (remove underscores, remove capitalization). - Fix import groupings to more closely match the style guide. - Group blank (side-effecting) imports and ensure they are commented. - Add aliases to multiple imports with the same base package name.
3 years ago
cleanup: Reduce and normalize import path aliasing. (#6975) The code in the Tendermint repository makes heavy use of import aliasing. This is made necessary by our extensive reuse of common base package names, and by repetition of similar names across different subdirectories. Unfortunately we have not been very consistent about which packages we alias in various circumstances, and the aliases we use vary. In the spirit of the advice in the style guide and https://github.com/golang/go/wiki/CodeReviewComments#imports, his change makes an effort to clean up and normalize import aliasing. This change makes no API or behavioral changes. It is a pure cleanup intended o help make the code more readable to developers (including myself) trying to understand what is being imported where. Only unexported names have been modified, and the changes were generated and applied mechanically with gofmt -r and comby, respecting the lexical and syntactic rules of Go. Even so, I did not fix every inconsistency. Where the changes would be too disruptive, I left it alone. The principles I followed in this cleanup are: - Remove aliases that restate the package name. - Remove aliases where the base package name is unambiguous. - Move overly-terse abbreviations from the import to the usage site. - Fix lexical issues (remove underscores, remove capitalization). - Fix import groupings to more closely match the style guide. - Group blank (side-effecting) imports and ensure they are commented. - Add aliases to multiple imports with the same base package name.
3 years ago
cleanup: Reduce and normalize import path aliasing. (#6975) The code in the Tendermint repository makes heavy use of import aliasing. This is made necessary by our extensive reuse of common base package names, and by repetition of similar names across different subdirectories. Unfortunately we have not been very consistent about which packages we alias in various circumstances, and the aliases we use vary. In the spirit of the advice in the style guide and https://github.com/golang/go/wiki/CodeReviewComments#imports, his change makes an effort to clean up and normalize import aliasing. This change makes no API or behavioral changes. It is a pure cleanup intended o help make the code more readable to developers (including myself) trying to understand what is being imported where. Only unexported names have been modified, and the changes were generated and applied mechanically with gofmt -r and comby, respecting the lexical and syntactic rules of Go. Even so, I did not fix every inconsistency. Where the changes would be too disruptive, I left it alone. The principles I followed in this cleanup are: - Remove aliases that restate the package name. - Remove aliases where the base package name is unambiguous. - Move overly-terse abbreviations from the import to the usage site. - Fix lexical issues (remove underscores, remove capitalization). - Fix import groupings to more closely match the style guide. - Group blank (side-effecting) imports and ensure they are commented. - Add aliases to multiple imports with the same base package name.
3 years ago
  1. package http
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "strings"
  7. "sync"
  8. "time"
  9. "github.com/tendermint/tendermint/internal/pubsub"
  10. rpcclient "github.com/tendermint/tendermint/rpc/client"
  11. "github.com/tendermint/tendermint/rpc/coretypes"
  12. jsonrpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client"
  13. )
  14. // wsEvents is a wrapper around WSClient, which implements EventsClient.
  15. type wsEvents struct {
  16. *rpcclient.RunState
  17. ws *jsonrpcclient.WSClient
  18. mtx sync.RWMutex
  19. subscriptions map[string]*wsSubscription
  20. }
  21. type wsSubscription struct {
  22. res chan coretypes.ResultEvent
  23. id string
  24. query string
  25. }
  26. var _ rpcclient.EventsClient = (*wsEvents)(nil)
  27. func newWsEvents(remote string) (*wsEvents, error) {
  28. w := &wsEvents{
  29. RunState: rpcclient.NewRunState("wsEvents", nil),
  30. subscriptions: make(map[string]*wsSubscription),
  31. }
  32. var err error
  33. w.ws, err = jsonrpcclient.NewWS(strings.TrimSuffix(remote, "/"), "/websocket")
  34. if err != nil {
  35. return nil, fmt.Errorf("can't create WS client: %w", err)
  36. }
  37. w.ws.OnReconnect(func() {
  38. // resubscribe immediately
  39. w.redoSubscriptionsAfter(0 * time.Second)
  40. })
  41. w.ws.Logger = w.Logger
  42. return w, nil
  43. }
  44. // Start starts the websocket client and the event loop.
  45. func (w *wsEvents) Start(ctx context.Context) error {
  46. if err := w.ws.Start(ctx); err != nil {
  47. return err
  48. }
  49. go w.eventListener(ctx)
  50. return nil
  51. }
  52. // IsRunning reports whether the websocket client is running.
  53. func (w *wsEvents) IsRunning() bool { return w.ws.IsRunning() }
  54. // Stop shuts down the websocket client.
  55. func (w *wsEvents) Stop() error { return w.ws.Stop() }
  56. // Subscribe implements EventsClient by using WSClient to subscribe given
  57. // subscriber to query. By default, it returns a channel with cap=1. Error is
  58. // returned if it fails to subscribe.
  59. //
  60. // When reading from the channel, keep in mind there's a single events loop, so
  61. // if you don't read events for this subscription fast enough, other
  62. // subscriptions will slow down in effect.
  63. //
  64. // The channel is never closed to prevent clients from seeing an erroneous
  65. // event.
  66. //
  67. // It returns an error if wsEvents is not running.
  68. func (w *wsEvents) Subscribe(ctx context.Context, subscriber, query string,
  69. outCapacity ...int) (out <-chan coretypes.ResultEvent, err error) {
  70. if !w.IsRunning() {
  71. return nil, rpcclient.ErrClientNotRunning
  72. }
  73. if err := w.ws.Subscribe(ctx, query); err != nil {
  74. return nil, err
  75. }
  76. outCap := 1
  77. if len(outCapacity) > 0 {
  78. outCap = outCapacity[0]
  79. }
  80. outc := make(chan coretypes.ResultEvent, outCap)
  81. w.mtx.Lock()
  82. defer w.mtx.Unlock()
  83. // subscriber param is ignored because Tendermint will override it with
  84. // remote IP anyway.
  85. w.subscriptions[query] = &wsSubscription{res: outc, query: query}
  86. return outc, nil
  87. }
  88. // Unsubscribe implements EventsClient by using WSClient to unsubscribe given
  89. // subscriber from query.
  90. //
  91. // It returns an error if wsEvents is not running.
  92. func (w *wsEvents) Unsubscribe(ctx context.Context, subscriber, query string) error {
  93. if !w.IsRunning() {
  94. return rpcclient.ErrClientNotRunning
  95. }
  96. if err := w.ws.Unsubscribe(ctx, query); err != nil {
  97. return err
  98. }
  99. w.mtx.Lock()
  100. info, ok := w.subscriptions[query]
  101. if ok {
  102. if info.id != "" {
  103. delete(w.subscriptions, info.id)
  104. }
  105. delete(w.subscriptions, info.query)
  106. }
  107. w.mtx.Unlock()
  108. return nil
  109. }
  110. // UnsubscribeAll implements EventsClient by using WSClient to unsubscribe
  111. // given subscriber from all the queries.
  112. //
  113. // It returns an error if wsEvents is not running.
  114. func (w *wsEvents) UnsubscribeAll(ctx context.Context, subscriber string) error {
  115. if !w.IsRunning() {
  116. return rpcclient.ErrClientNotRunning
  117. }
  118. if err := w.ws.UnsubscribeAll(ctx); err != nil {
  119. return err
  120. }
  121. w.mtx.Lock()
  122. w.subscriptions = make(map[string]*wsSubscription)
  123. w.mtx.Unlock()
  124. return nil
  125. }
  126. // After being reconnected, it is necessary to redo subscription to server
  127. // otherwise no data will be automatically received.
  128. func (w *wsEvents) redoSubscriptionsAfter(d time.Duration) {
  129. time.Sleep(d)
  130. ctx := context.Background()
  131. w.mtx.Lock()
  132. defer w.mtx.Unlock()
  133. for q, info := range w.subscriptions {
  134. if q != "" && q == info.id {
  135. continue
  136. }
  137. err := w.ws.Subscribe(ctx, q)
  138. if err != nil {
  139. w.Logger.Error("failed to resubscribe", "query", q, "err", err)
  140. delete(w.subscriptions, q)
  141. }
  142. }
  143. }
  144. func isErrAlreadySubscribed(err error) bool {
  145. return strings.Contains(err.Error(), pubsub.ErrAlreadySubscribed.Error())
  146. }
  147. func (w *wsEvents) eventListener(ctx context.Context) {
  148. for {
  149. select {
  150. case resp, ok := <-w.ws.ResponsesCh:
  151. if !ok {
  152. return
  153. }
  154. if resp.Error != nil {
  155. w.Logger.Error("WS error", "err", resp.Error.Error())
  156. // Error can be ErrAlreadySubscribed or max client (subscriptions per
  157. // client) reached or Tendermint exited.
  158. // We can ignore ErrAlreadySubscribed, but need to retry in other
  159. // cases.
  160. if !isErrAlreadySubscribed(resp.Error) {
  161. // Resubscribe after 1 second to give Tendermint time to restart (if
  162. // crashed).
  163. w.redoSubscriptionsAfter(1 * time.Second)
  164. }
  165. continue
  166. }
  167. result := new(coretypes.ResultEvent)
  168. err := json.Unmarshal(resp.Result, result)
  169. if err != nil {
  170. w.Logger.Error("failed to unmarshal response", "err", err)
  171. continue
  172. }
  173. w.mtx.RLock()
  174. out, ok := w.subscriptions[result.Query]
  175. if ok {
  176. if _, idOk := w.subscriptions[result.SubscriptionID]; !idOk {
  177. out.id = result.SubscriptionID
  178. w.subscriptions[result.SubscriptionID] = out
  179. }
  180. }
  181. w.mtx.RUnlock()
  182. if ok {
  183. select {
  184. case out.res <- *result:
  185. case <-ctx.Done():
  186. return
  187. }
  188. }
  189. case <-ctx.Done():
  190. return
  191. }
  192. }
  193. }