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.

214 lines
5.3 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. "github.com/tendermint/tendermint/libs/log"
  11. rpcclient "github.com/tendermint/tendermint/rpc/client"
  12. "github.com/tendermint/tendermint/rpc/coretypes"
  13. jsonrpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client"
  14. )
  15. // wsEvents is a wrapper around WSClient, which implements EventsClient.
  16. type wsEvents struct {
  17. Logger log.Logger
  18. ws *jsonrpcclient.WSClient
  19. mtx sync.RWMutex
  20. subscriptions map[string]*wsSubscription
  21. }
  22. type wsSubscription struct {
  23. res chan coretypes.ResultEvent
  24. id string
  25. query string
  26. }
  27. var _ rpcclient.EventsClient = (*wsEvents)(nil)
  28. func newWsEvents(remote string) (*wsEvents, error) {
  29. w := &wsEvents{
  30. Logger: log.NewNopLogger(),
  31. subscriptions: make(map[string]*wsSubscription),
  32. }
  33. var err error
  34. w.ws, err = jsonrpcclient.NewWS(strings.TrimSuffix(remote, "/"), "/websocket")
  35. if err != nil {
  36. return nil, fmt.Errorf("can't create WS client: %w", err)
  37. }
  38. w.ws.OnReconnect(func() {
  39. // resubscribe immediately
  40. w.redoSubscriptionsAfter(0 * time.Second)
  41. })
  42. w.ws.Logger = w.Logger
  43. return w, nil
  44. }
  45. // Start starts the websocket client and the event loop.
  46. func (w *wsEvents) Start(ctx context.Context) error {
  47. if err := w.ws.Start(ctx); err != nil {
  48. return err
  49. }
  50. go w.eventListener(ctx)
  51. return nil
  52. }
  53. // Stop shuts down the websocket client.
  54. func (w *wsEvents) Stop() error { return w.ws.Stop() }
  55. // Subscribe implements EventsClient by using WSClient to subscribe given
  56. // subscriber to query. By default, it returns a channel with cap=1. Error is
  57. // returned if it fails to subscribe.
  58. //
  59. // When reading from the channel, keep in mind there's a single events loop, so
  60. // if you don't read events for this subscription fast enough, other
  61. // subscriptions will slow down in effect.
  62. //
  63. // The channel is never closed to prevent clients from seeing an erroneous
  64. // event.
  65. //
  66. // It returns an error if wsEvents is not running.
  67. func (w *wsEvents) Subscribe(ctx context.Context, subscriber, query string,
  68. outCapacity ...int) (out <-chan coretypes.ResultEvent, err error) {
  69. if err := w.ws.Subscribe(ctx, query); err != nil {
  70. return nil, err
  71. }
  72. outCap := 1
  73. if len(outCapacity) > 0 {
  74. outCap = outCapacity[0]
  75. }
  76. outc := make(chan coretypes.ResultEvent, outCap)
  77. w.mtx.Lock()
  78. defer w.mtx.Unlock()
  79. // subscriber param is ignored because Tendermint will override it with
  80. // remote IP anyway.
  81. w.subscriptions[query] = &wsSubscription{res: outc, query: query}
  82. return outc, nil
  83. }
  84. // Unsubscribe implements EventsClient by using WSClient to unsubscribe given
  85. // subscriber from query.
  86. //
  87. // It returns an error if wsEvents is not running.
  88. func (w *wsEvents) Unsubscribe(ctx context.Context, subscriber, query string) error {
  89. if err := w.ws.Unsubscribe(ctx, query); err != nil {
  90. return err
  91. }
  92. w.mtx.Lock()
  93. info, ok := w.subscriptions[query]
  94. if ok {
  95. if info.id != "" {
  96. delete(w.subscriptions, info.id)
  97. }
  98. delete(w.subscriptions, info.query)
  99. }
  100. w.mtx.Unlock()
  101. return nil
  102. }
  103. // UnsubscribeAll implements EventsClient by using WSClient to unsubscribe
  104. // given subscriber from all the queries.
  105. //
  106. // It returns an error if wsEvents is not running.
  107. func (w *wsEvents) UnsubscribeAll(ctx context.Context, subscriber string) error {
  108. if err := w.ws.UnsubscribeAll(ctx); err != nil {
  109. return err
  110. }
  111. w.mtx.Lock()
  112. w.subscriptions = make(map[string]*wsSubscription)
  113. w.mtx.Unlock()
  114. return nil
  115. }
  116. // After being reconnected, it is necessary to redo subscription to server
  117. // otherwise no data will be automatically received.
  118. func (w *wsEvents) redoSubscriptionsAfter(d time.Duration) {
  119. time.Sleep(d)
  120. ctx := context.TODO()
  121. w.mtx.Lock()
  122. defer w.mtx.Unlock()
  123. for q, info := range w.subscriptions {
  124. if q != "" && q == info.id {
  125. continue
  126. }
  127. err := w.ws.Subscribe(ctx, q)
  128. if err != nil {
  129. w.Logger.Error("failed to resubscribe", "query", q, "err", err)
  130. delete(w.subscriptions, q)
  131. }
  132. }
  133. }
  134. func isErrAlreadySubscribed(err error) bool {
  135. return strings.Contains(err.Error(), pubsub.ErrAlreadySubscribed.Error())
  136. }
  137. func (w *wsEvents) eventListener(ctx context.Context) {
  138. for {
  139. select {
  140. case resp, ok := <-w.ws.ResponsesCh:
  141. if !ok {
  142. return
  143. }
  144. if resp.Error != nil {
  145. w.Logger.Error("WS error", "err", resp.Error.Error())
  146. // Error can be ErrAlreadySubscribed or max client (subscriptions per
  147. // client) reached or Tendermint exited.
  148. // We can ignore ErrAlreadySubscribed, but need to retry in other
  149. // cases.
  150. if !isErrAlreadySubscribed(resp.Error) {
  151. // Resubscribe after 1 second to give Tendermint time to restart (if
  152. // crashed).
  153. w.redoSubscriptionsAfter(1 * time.Second)
  154. }
  155. continue
  156. }
  157. result := new(coretypes.ResultEvent)
  158. err := json.Unmarshal(resp.Result, result)
  159. if err != nil {
  160. w.Logger.Error("failed to unmarshal response", "err", err)
  161. continue
  162. }
  163. w.mtx.RLock()
  164. out, ok := w.subscriptions[result.Query]
  165. if ok {
  166. if _, idOk := w.subscriptions[result.SubscriptionID]; !idOk {
  167. out.id = result.SubscriptionID
  168. w.subscriptions[result.SubscriptionID] = out
  169. }
  170. }
  171. w.mtx.RUnlock()
  172. if ok {
  173. select {
  174. case out.res <- *result:
  175. case <-ctx.Done():
  176. return
  177. }
  178. }
  179. case <-ctx.Done():
  180. return
  181. }
  182. }
  183. }