mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-19 22:44:24 +00:00
rpc: Strip down the base RPC client interface. (#6971)
* rpc: Strip down the base RPC client interface. Prior to this change, the RPC client interface requires implementing the entire Service interface, but most of the methods of Service are not needed by the concrete clients. Dissociate the Client interface from the Service interface. - Extract only those methods of Service that are necessary to make the existing clients work. - Update the clients to combine Start/Onstart and Stop/OnStop. This does not change what the clients do to start or stop. Only the websocket clients make use of this functionality anyway. The websocket implementation uses some plumbing from the BaseService helper. We should be able to excising that entirely, but the current interface dependencies among the clients would require a much larger change, and one that leaks into other (non-RPC) packages. As a less-invasive intermediate step, preserve the existing client behaviour (and tests) by extracting the necessary subset of the BaseService functionality to an analogous RunState helper for clients. I plan to obsolete that type in a future PR, but for now this makes a useful waypoint. Related: - Clean up client implementations. - Update mocks.
This commit is contained in:
@@ -4,8 +4,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -82,3 +84,86 @@ func WaitForOneEvent(c EventsClient, eventValue string, timeout time.Duration) (
|
|||||||
return nil, errors.New("timed out waiting for event")
|
return nil, errors.New("timed out waiting for event")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrClientRunning is returned by Start when the client is already running.
|
||||||
|
ErrClientRunning = errors.New("client already running")
|
||||||
|
|
||||||
|
// ErrClientNotRunning is returned by Stop when the client is not running.
|
||||||
|
ErrClientNotRunning = errors.New("client is not running")
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunState is a helper that a client implementation can embed to implement
|
||||||
|
// common plumbing for keeping track of run state and logging.
|
||||||
|
//
|
||||||
|
// TODO(creachadair): This type is a temporary measure, and will be removed.
|
||||||
|
// See the discussion on #6971.
|
||||||
|
type RunState struct {
|
||||||
|
Logger log.Logger
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
name string
|
||||||
|
isRunning bool
|
||||||
|
quit chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRunState returns a new unstarted run state tracker with the given logging
|
||||||
|
// label and log sink. If logger == nil, a no-op logger is provided by default.
|
||||||
|
func NewRunState(name string, logger log.Logger) *RunState {
|
||||||
|
if logger == nil {
|
||||||
|
logger = log.NewNopLogger()
|
||||||
|
}
|
||||||
|
return &RunState{
|
||||||
|
name: name,
|
||||||
|
Logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start sets the state to running, or reports an error.
|
||||||
|
func (r *RunState) Start() error {
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
if r.isRunning {
|
||||||
|
r.Logger.Error("not starting client, it is already started", "client", r.name)
|
||||||
|
return ErrClientRunning
|
||||||
|
}
|
||||||
|
r.Logger.Info("starting client", "client", r.name)
|
||||||
|
r.isRunning = true
|
||||||
|
r.quit = make(chan struct{})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop sets the state to not running, or reports an error.
|
||||||
|
func (r *RunState) Stop() error {
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
if !r.isRunning {
|
||||||
|
r.Logger.Error("not stopping client; it is already stopped", "client", r.name)
|
||||||
|
return ErrClientNotRunning
|
||||||
|
}
|
||||||
|
r.Logger.Info("stopping client", "client", r.name)
|
||||||
|
r.isRunning = false
|
||||||
|
close(r.quit)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLogger updates the log sink.
|
||||||
|
func (r *RunState) SetLogger(logger log.Logger) {
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
r.Logger = logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsRunning reports whether the state is running.
|
||||||
|
func (r *RunState) IsRunning() bool {
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
return r.isRunning
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quit returns a channel that is closed when a call to Stop succeeds.
|
||||||
|
func (r *RunState) Quit() <-chan struct{} {
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
return r.quit
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/libs/bytes"
|
"github.com/tendermint/tendermint/libs/bytes"
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
|
||||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/coretypes"
|
ctypes "github.com/tendermint/tendermint/rpc/coretypes"
|
||||||
jsonrpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client"
|
jsonrpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client"
|
||||||
@@ -158,11 +157,6 @@ func NewWithClientAndWSOptions(remote string, c *http.Client, wso WSOptions) (*H
|
|||||||
|
|
||||||
var _ rpcclient.Client = (*HTTP)(nil)
|
var _ rpcclient.Client = (*HTTP)(nil)
|
||||||
|
|
||||||
// SetLogger sets a logger.
|
|
||||||
func (c *HTTP) SetLogger(l log.Logger) {
|
|
||||||
c.wsEvents.SetLogger(l)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remote returns the remote network address in a string form.
|
// Remote returns the remote network address in a string form.
|
||||||
func (c *HTTP) Remote() string {
|
func (c *HTTP) Remote() string {
|
||||||
return c.remote
|
return c.remote
|
||||||
|
|||||||
+12
-18
@@ -10,14 +10,11 @@ import (
|
|||||||
tmsync "github.com/tendermint/tendermint/internal/libs/sync"
|
tmsync "github.com/tendermint/tendermint/internal/libs/sync"
|
||||||
tmjson "github.com/tendermint/tendermint/libs/json"
|
tmjson "github.com/tendermint/tendermint/libs/json"
|
||||||
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
|
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
|
||||||
"github.com/tendermint/tendermint/libs/service"
|
|
||||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/coretypes"
|
ctypes "github.com/tendermint/tendermint/rpc/coretypes"
|
||||||
jsonrpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client"
|
jsonrpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errNotRunning = errors.New("client is not running. Use .Start() method to start")
|
|
||||||
|
|
||||||
// WSOptions for the WS part of the HTTP client.
|
// WSOptions for the WS part of the HTTP client.
|
||||||
type WSOptions struct {
|
type WSOptions struct {
|
||||||
Path string // path (e.g. "/ws")
|
Path string // path (e.g. "/ws")
|
||||||
@@ -48,7 +45,7 @@ func (wso WSOptions) Validate() error {
|
|||||||
|
|
||||||
// wsEvents is a wrapper around WSClient, which implements EventsClient.
|
// wsEvents is a wrapper around WSClient, which implements EventsClient.
|
||||||
type wsEvents struct {
|
type wsEvents struct {
|
||||||
service.BaseService
|
*rpcclient.RunState
|
||||||
ws *jsonrpcclient.WSClient
|
ws *jsonrpcclient.WSClient
|
||||||
|
|
||||||
mtx tmsync.RWMutex
|
mtx tmsync.RWMutex
|
||||||
@@ -78,7 +75,7 @@ func newWsEvents(remote string, wso WSOptions) (*wsEvents, error) {
|
|||||||
w := &wsEvents{
|
w := &wsEvents{
|
||||||
subscriptions: make(map[string]*wsSubscription),
|
subscriptions: make(map[string]*wsSubscription),
|
||||||
}
|
}
|
||||||
w.BaseService = *service.NewBaseService(nil, "wsEvents", w)
|
w.RunState = rpcclient.NewRunState("wsEvents", nil)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
w.ws, err = jsonrpcclient.NewWSWithOptions(remote, wso.Path, wso.WSOptions)
|
w.ws, err = jsonrpcclient.NewWSWithOptions(remote, wso.Path, wso.WSOptions)
|
||||||
@@ -94,23 +91,20 @@ func newWsEvents(remote string, wso WSOptions) (*wsEvents, error) {
|
|||||||
return w, nil
|
return w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnStart implements service.Service by starting WSClient and event loop.
|
// Start starts the websocket client and the event loop.
|
||||||
func (w *wsEvents) OnStart() error {
|
func (w *wsEvents) Start() error {
|
||||||
if err := w.ws.Start(); err != nil {
|
if err := w.ws.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
go w.eventListener()
|
go w.eventListener()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnStop implements service.Service by stopping WSClient.
|
// IsRunning reports whether the websocket client is running.
|
||||||
func (w *wsEvents) OnStop() {
|
func (w *wsEvents) IsRunning() bool { return w.ws.IsRunning() }
|
||||||
if err := w.ws.Stop(); err != nil {
|
|
||||||
w.Logger.Error("Can't stop ws client", "err", err)
|
// Stop shuts down the websocket client.
|
||||||
}
|
func (w *wsEvents) Stop() error { return w.ws.Stop() }
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe implements EventsClient by using WSClient to subscribe given
|
// Subscribe implements EventsClient by using WSClient to subscribe given
|
||||||
// subscriber to query. By default, it returns a channel with cap=1. Error is
|
// subscriber to query. By default, it returns a channel with cap=1. Error is
|
||||||
@@ -128,7 +122,7 @@ func (w *wsEvents) Subscribe(ctx context.Context, subscriber, query string,
|
|||||||
outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) {
|
outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) {
|
||||||
|
|
||||||
if !w.IsRunning() {
|
if !w.IsRunning() {
|
||||||
return nil, errNotRunning
|
return nil, rpcclient.ErrClientNotRunning
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := w.ws.Subscribe(ctx, query); err != nil {
|
if err := w.ws.Subscribe(ctx, query); err != nil {
|
||||||
@@ -156,7 +150,7 @@ func (w *wsEvents) Subscribe(ctx context.Context, subscriber, query string,
|
|||||||
// It returns an error if wsEvents is not running.
|
// It returns an error if wsEvents is not running.
|
||||||
func (w *wsEvents) Unsubscribe(ctx context.Context, subscriber, query string) error {
|
func (w *wsEvents) Unsubscribe(ctx context.Context, subscriber, query string) error {
|
||||||
if !w.IsRunning() {
|
if !w.IsRunning() {
|
||||||
return errNotRunning
|
return rpcclient.ErrClientNotRunning
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := w.ws.Unsubscribe(ctx, query); err != nil {
|
if err := w.ws.Unsubscribe(ctx, query); err != nil {
|
||||||
@@ -182,7 +176,7 @@ func (w *wsEvents) Unsubscribe(ctx context.Context, subscriber, query string) er
|
|||||||
// It returns an error if wsEvents is not running.
|
// It returns an error if wsEvents is not running.
|
||||||
func (w *wsEvents) UnsubscribeAll(ctx context.Context, subscriber string) error {
|
func (w *wsEvents) UnsubscribeAll(ctx context.Context, subscriber string) error {
|
||||||
if !w.IsRunning() {
|
if !w.IsRunning() {
|
||||||
return errNotRunning
|
return rpcclient.ErrClientNotRunning
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := w.ws.UnsubscribeAll(ctx); err != nil {
|
if err := w.ws.UnsubscribeAll(ctx); err != nil {
|
||||||
|
|||||||
+13
-4
@@ -24,17 +24,26 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/libs/bytes"
|
"github.com/tendermint/tendermint/libs/bytes"
|
||||||
"github.com/tendermint/tendermint/libs/service"
|
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/coretypes"
|
ctypes "github.com/tendermint/tendermint/rpc/coretypes"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate ../../scripts/mockery_generate.sh Client
|
//go:generate ../../scripts/mockery_generate.sh Client
|
||||||
|
|
||||||
// Client wraps most important rpc calls a client would make if you want to
|
// Client describes the interface of Tendermint RPC client implementations.
|
||||||
// listen for events, test if it also implements events.EventSwitch.
|
|
||||||
type Client interface {
|
type Client interface {
|
||||||
service.Service
|
// These methods define the operational structure of the client.
|
||||||
|
|
||||||
|
// Start the client. Start must report an error if the client is running.
|
||||||
|
Start() error
|
||||||
|
|
||||||
|
// Stop the client. Stop must report an error if the client is not running.
|
||||||
|
Stop() error
|
||||||
|
|
||||||
|
// IsRunning reports whether the client is running.
|
||||||
|
IsRunning() bool
|
||||||
|
|
||||||
|
// These embedded interfaces define the callable methods of the service.
|
||||||
ABCIClient
|
ABCIClient
|
||||||
EventsClient
|
EventsClient
|
||||||
HistoryClient
|
HistoryClient
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
|
|
||||||
"github.com/tendermint/tendermint/internal/rpc/core"
|
"github.com/tendermint/tendermint/internal/rpc/core"
|
||||||
"github.com/tendermint/tendermint/libs/bytes"
|
"github.com/tendermint/tendermint/libs/bytes"
|
||||||
"github.com/tendermint/tendermint/libs/service"
|
|
||||||
"github.com/tendermint/tendermint/rpc/client"
|
"github.com/tendermint/tendermint/rpc/client"
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/coretypes"
|
ctypes "github.com/tendermint/tendermint/rpc/coretypes"
|
||||||
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||||
@@ -29,15 +28,7 @@ import (
|
|||||||
|
|
||||||
// Client wraps arbitrary implementations of the various interfaces.
|
// Client wraps arbitrary implementations of the various interfaces.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
client.ABCIClient
|
client.Client
|
||||||
client.SignClient
|
|
||||||
client.HistoryClient
|
|
||||||
client.StatusClient
|
|
||||||
client.EventsClient
|
|
||||||
client.EvidenceClient
|
|
||||||
client.MempoolClient
|
|
||||||
service.Service
|
|
||||||
|
|
||||||
env *core.Environment
|
env *core.Environment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ import (
|
|||||||
|
|
||||||
coretypes "github.com/tendermint/tendermint/rpc/coretypes"
|
coretypes "github.com/tendermint/tendermint/rpc/coretypes"
|
||||||
|
|
||||||
log "github.com/tendermint/tendermint/libs/log"
|
|
||||||
|
|
||||||
mock "github.com/stretchr/testify/mock"
|
mock "github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
types "github.com/tendermint/tendermint/types"
|
types "github.com/tendermint/tendermint/types"
|
||||||
@@ -542,74 +540,6 @@ func (_m *Client) NumUnconfirmedTxs(_a0 context.Context) (*coretypes.ResultUncon
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnReset provides a mock function with given fields:
|
|
||||||
func (_m *Client) OnReset() error {
|
|
||||||
ret := _m.Called()
|
|
||||||
|
|
||||||
var r0 error
|
|
||||||
if rf, ok := ret.Get(0).(func() error); ok {
|
|
||||||
r0 = rf()
|
|
||||||
} else {
|
|
||||||
r0 = ret.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnStart provides a mock function with given fields:
|
|
||||||
func (_m *Client) OnStart() error {
|
|
||||||
ret := _m.Called()
|
|
||||||
|
|
||||||
var r0 error
|
|
||||||
if rf, ok := ret.Get(0).(func() error); ok {
|
|
||||||
r0 = rf()
|
|
||||||
} else {
|
|
||||||
r0 = ret.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnStop provides a mock function with given fields:
|
|
||||||
func (_m *Client) OnStop() {
|
|
||||||
_m.Called()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quit provides a mock function with given fields:
|
|
||||||
func (_m *Client) Quit() <-chan struct{} {
|
|
||||||
ret := _m.Called()
|
|
||||||
|
|
||||||
var r0 <-chan struct{}
|
|
||||||
if rf, ok := ret.Get(0).(func() <-chan struct{}); ok {
|
|
||||||
r0 = rf()
|
|
||||||
} else {
|
|
||||||
if ret.Get(0) != nil {
|
|
||||||
r0 = ret.Get(0).(<-chan struct{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset provides a mock function with given fields:
|
|
||||||
func (_m *Client) Reset() error {
|
|
||||||
ret := _m.Called()
|
|
||||||
|
|
||||||
var r0 error
|
|
||||||
if rf, ok := ret.Get(0).(func() error); ok {
|
|
||||||
r0 = rf()
|
|
||||||
} else {
|
|
||||||
r0 = ret.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLogger provides a mock function with given fields: _a0
|
|
||||||
func (_m *Client) SetLogger(_a0 log.Logger) {
|
|
||||||
_m.Called(_a0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start provides a mock function with given fields:
|
// Start provides a mock function with given fields:
|
||||||
func (_m *Client) Start() error {
|
func (_m *Client) Start() error {
|
||||||
ret := _m.Called()
|
ret := _m.Called()
|
||||||
@@ -661,20 +591,6 @@ func (_m *Client) Stop() error {
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// String provides a mock function with given fields:
|
|
||||||
func (_m *Client) String() string {
|
|
||||||
ret := _m.Called()
|
|
||||||
|
|
||||||
var r0 string
|
|
||||||
if rf, ok := ret.Get(0).(func() string); ok {
|
|
||||||
r0 = rf()
|
|
||||||
} else {
|
|
||||||
r0 = ret.Get(0).(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe provides a mock function with given fields: ctx, subscriber, query, outCapacity
|
// Subscribe provides a mock function with given fields: ctx, subscriber, query, outCapacity
|
||||||
func (_m *Client) Subscribe(ctx context.Context, subscriber string, query string, outCapacity ...int) (<-chan coretypes.ResultEvent, error) {
|
func (_m *Client) Subscribe(ctx context.Context, subscriber string, query string, outCapacity ...int) (<-chan coretypes.ResultEvent, error) {
|
||||||
_va := make([]interface{}, len(outCapacity))
|
_va := make([]interface{}, len(outCapacity))
|
||||||
@@ -824,8 +740,3 @@ func (_m *Client) Validators(ctx context.Context, height *int64, page *int, perP
|
|||||||
|
|
||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait provides a mock function with given fields:
|
|
||||||
func (_m *Client) Wait() {
|
|
||||||
_m.Called()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
metrics "github.com/rcrowley/go-metrics"
|
metrics "github.com/rcrowley/go-metrics"
|
||||||
|
|
||||||
tmsync "github.com/tendermint/tendermint/internal/libs/sync"
|
tmsync "github.com/tendermint/tendermint/internal/libs/sync"
|
||||||
"github.com/tendermint/tendermint/libs/service"
|
tmclient "github.com/tendermint/tendermint/rpc/client"
|
||||||
types "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
types "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,6 +41,7 @@ func DefaultWSOptions() WSOptions {
|
|||||||
//
|
//
|
||||||
// WSClient is safe for concurrent use by multiple goroutines.
|
// WSClient is safe for concurrent use by multiple goroutines.
|
||||||
type WSClient struct { // nolint: maligned
|
type WSClient struct { // nolint: maligned
|
||||||
|
*tmclient.RunState
|
||||||
conn *websocket.Conn
|
conn *websocket.Conn
|
||||||
|
|
||||||
Address string // IP:PORT or /path/to/socket
|
Address string // IP:PORT or /path/to/socket
|
||||||
@@ -83,8 +84,6 @@ type WSClient struct { // nolint: maligned
|
|||||||
// Send pings to server with this period. Must be less than readWait. If 0, no pings will be sent.
|
// Send pings to server with this period. Must be less than readWait. If 0, no pings will be sent.
|
||||||
pingPeriod time.Duration
|
pingPeriod time.Duration
|
||||||
|
|
||||||
service.BaseService
|
|
||||||
|
|
||||||
// Time between sending a ping and receiving a pong. See
|
// Time between sending a ping and receiving a pong. See
|
||||||
// https://godoc.org/github.com/rcrowley/go-metrics#Timer.
|
// https://godoc.org/github.com/rcrowley/go-metrics#Timer.
|
||||||
PingPongLatencyTimer metrics.Timer
|
PingPongLatencyTimer metrics.Timer
|
||||||
@@ -114,6 +113,7 @@ func NewWSWithOptions(remoteAddr, endpoint string, opts WSOptions) (*WSClient, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
c := &WSClient{
|
c := &WSClient{
|
||||||
|
RunState: tmclient.NewRunState("WSClient", nil),
|
||||||
Address: parsedURL.GetTrimmedHostWithPath(),
|
Address: parsedURL.GetTrimmedHostWithPath(),
|
||||||
Dialer: dialFn,
|
Dialer: dialFn,
|
||||||
Endpoint: endpoint,
|
Endpoint: endpoint,
|
||||||
@@ -127,7 +127,6 @@ func NewWSWithOptions(remoteAddr, endpoint string, opts WSOptions) (*WSClient, e
|
|||||||
|
|
||||||
// sentIDs: make(map[types.JSONRPCIntID]bool),
|
// sentIDs: make(map[types.JSONRPCIntID]bool),
|
||||||
}
|
}
|
||||||
c.BaseService = *service.NewBaseService(nil, "WSClient", c)
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,9 +142,11 @@ func (c *WSClient) String() string {
|
|||||||
return fmt.Sprintf("WSClient{%s (%s)}", c.Address, c.Endpoint)
|
return fmt.Sprintf("WSClient{%s (%s)}", c.Address, c.Endpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnStart implements service.Service by dialing a server and creating read and
|
// Start dials the specified service address and starts the I/O routines.
|
||||||
// write routines.
|
func (c *WSClient) Start() error {
|
||||||
func (c *WSClient) OnStart() error {
|
if err := c.RunState.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
err := c.dial()
|
err := c.dial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -167,10 +168,9 @@ func (c *WSClient) OnStart() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop overrides service.Service#Stop. There is no other way to wait until Quit
|
// Stop shuts down the client.
|
||||||
// channel is closed.
|
|
||||||
func (c *WSClient) Stop() error {
|
func (c *WSClient) Stop() error {
|
||||||
if err := c.BaseService.Stop(); err != nil {
|
if err := c.RunState.Stop(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// only close user-facing channels when we can't write to them
|
// only close user-facing channels when we can't write to them
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
"github.com/tendermint/tendermint/libs/service"
|
"github.com/tendermint/tendermint/rpc/client"
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/coretypes"
|
ctypes "github.com/tendermint/tendermint/rpc/coretypes"
|
||||||
types "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
types "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||||
)
|
)
|
||||||
@@ -86,8 +86,8 @@ func (wm *WebsocketManager) WebsocketHandler(w http.ResponseWriter, r *http.Requ
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// register connection
|
// register connection
|
||||||
con := newWSConnection(wsConn, wm.funcMap, wm.wsConnOptions...)
|
logger := wm.logger.With("remote", wsConn.RemoteAddr())
|
||||||
con.SetLogger(wm.logger.With("remote", wsConn.RemoteAddr()))
|
con := newWSConnection(wsConn, wm.funcMap, logger, wm.wsConnOptions...)
|
||||||
wm.logger.Info("New websocket connection", "remote", con.remoteAddr)
|
wm.logger.Info("New websocket connection", "remote", con.remoteAddr)
|
||||||
err = con.Start() // BLOCKING
|
err = con.Start() // BLOCKING
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -106,7 +106,7 @@ func (wm *WebsocketManager) WebsocketHandler(w http.ResponseWriter, r *http.Requ
|
|||||||
//
|
//
|
||||||
// In case of an error, the connection is stopped.
|
// In case of an error, the connection is stopped.
|
||||||
type wsConnection struct {
|
type wsConnection struct {
|
||||||
service.BaseService
|
*client.RunState
|
||||||
|
|
||||||
remoteAddr string
|
remoteAddr string
|
||||||
baseConn *websocket.Conn
|
baseConn *websocket.Conn
|
||||||
@@ -150,9 +150,11 @@ type wsConnection struct {
|
|||||||
func newWSConnection(
|
func newWSConnection(
|
||||||
baseConn *websocket.Conn,
|
baseConn *websocket.Conn,
|
||||||
funcMap map[string]*RPCFunc,
|
funcMap map[string]*RPCFunc,
|
||||||
|
logger log.Logger,
|
||||||
options ...func(*wsConnection),
|
options ...func(*wsConnection),
|
||||||
) *wsConnection {
|
) *wsConnection {
|
||||||
wsc := &wsConnection{
|
wsc := &wsConnection{
|
||||||
|
RunState: client.NewRunState("wsConnection", logger),
|
||||||
remoteAddr: baseConn.RemoteAddr().String(),
|
remoteAddr: baseConn.RemoteAddr().String(),
|
||||||
baseConn: baseConn,
|
baseConn: baseConn,
|
||||||
funcMap: funcMap,
|
funcMap: funcMap,
|
||||||
@@ -166,7 +168,6 @@ func newWSConnection(
|
|||||||
option(wsc)
|
option(wsc)
|
||||||
}
|
}
|
||||||
wsc.baseConn.SetReadLimit(wsc.readLimit)
|
wsc.baseConn.SetReadLimit(wsc.readLimit)
|
||||||
wsc.BaseService = *service.NewBaseService(nil, "wsConnection", wsc)
|
|
||||||
return wsc
|
return wsc
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,9 +219,11 @@ func ReadLimit(readLimit int64) func(*wsConnection) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnStart implements service.Service by starting the read and write routines. It
|
// Start starts the client service routines and blocks until there is an error.
|
||||||
// blocks until there's some error.
|
func (wsc *wsConnection) Start() error {
|
||||||
func (wsc *wsConnection) OnStart() error {
|
if err := wsc.RunState.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
wsc.writeChan = make(chan types.RPCResponse, wsc.writeChanCapacity)
|
wsc.writeChan = make(chan types.RPCResponse, wsc.writeChanCapacity)
|
||||||
|
|
||||||
// Read subscriptions/unsubscriptions to events
|
// Read subscriptions/unsubscriptions to events
|
||||||
@@ -231,16 +234,18 @@ func (wsc *wsConnection) OnStart() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnStop implements service.Service by unsubscribing remoteAddr from all
|
// Stop unsubscribes the remote from all subscriptions.
|
||||||
// subscriptions.
|
func (wsc *wsConnection) Stop() error {
|
||||||
func (wsc *wsConnection) OnStop() {
|
if err := wsc.RunState.Stop(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if wsc.onDisconnect != nil {
|
if wsc.onDisconnect != nil {
|
||||||
wsc.onDisconnect(wsc.remoteAddr)
|
wsc.onDisconnect(wsc.remoteAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if wsc.ctx != nil {
|
if wsc.ctx != nil {
|
||||||
wsc.cancel()
|
wsc.cancel()
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRemoteAddr returns the remote address of the underlying connection.
|
// GetRemoteAddr returns the remote address of the underlying connection.
|
||||||
|
|||||||
Reference in New Issue
Block a user