github.com/fiagdao/tendermint@v0.32.11-0.20220824195748-2087fcc480c1/rpc/client/http/http.go (about)

     1  package http
     2  
     3  import (
     4  	"context"
     5  	"net/http"
     6  	"strings"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/pkg/errors"
    11  
    12  	amino "github.com/tendermint/go-amino"
    13  
    14  	"github.com/tendermint/tendermint/libs/bytes"
    15  	"github.com/tendermint/tendermint/libs/log"
    16  	tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
    17  	"github.com/tendermint/tendermint/libs/service"
    18  	rpcclient "github.com/tendermint/tendermint/rpc/client"
    19  	ctypes "github.com/tendermint/tendermint/rpc/core/types"
    20  	jsonrpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client"
    21  	"github.com/tendermint/tendermint/types"
    22  )
    23  
    24  /*
    25  HTTP is a Client implementation that communicates with a Tendermint node over
    26  JSON RPC and WebSockets.
    27  
    28  This is the main implementation you probably want to use in production code.
    29  There are other implementations when calling the Tendermint node in-process
    30  (Local), or when you want to mock out the server for test code (mock).
    31  
    32  You can subscribe for any event published by Tendermint using Subscribe method.
    33  Note delivery is best-effort. If you don't read events fast enough or network is
    34  slow, Tendermint might cancel the subscription. The client will attempt to
    35  resubscribe (you don't need to do anything). It will keep trying every second
    36  indefinitely until successful.
    37  
    38  Request batching is available for JSON RPC requests over HTTP, which conforms to
    39  the JSON RPC specification (https://www.jsonrpc.org/specification#batch). See
    40  the example for more details.
    41  
    42  Example:
    43  
    44  		c, err := New("http://192.168.1.10:26657", "/websocket")
    45  		if err != nil {
    46  			// handle error
    47  		}
    48  
    49  		// call Start/Stop if you're subscribing to events
    50  		err = c.Start()
    51  		if err != nil {
    52  			// handle error
    53  		}
    54  		defer c.Stop()
    55  
    56  		res, err := c.Status()
    57  		if err != nil {
    58  			// handle error
    59  		}
    60  
    61  		// handle result
    62  */
    63  type HTTP struct {
    64  	remote string
    65  	rpc    *jsonrpcclient.Client
    66  
    67  	*baseRPCClient
    68  	*WSEvents
    69  }
    70  
    71  // BatchHTTP provides the same interface as `HTTP`, but allows for batching of
    72  // requests (as per https://www.jsonrpc.org/specification#batch). Do not
    73  // instantiate directly - rather use the HTTP.NewBatch() method to create an
    74  // instance of this struct.
    75  //
    76  // Batching of HTTP requests is thread-safe in the sense that multiple
    77  // goroutines can each create their own batches and send them using the same
    78  // HTTP client. Multiple goroutines could also enqueue transactions in a single
    79  // batch, but ordering of transactions in the batch cannot be guaranteed in such
    80  // an example.
    81  type BatchHTTP struct {
    82  	rpcBatch *jsonrpcclient.RequestBatch
    83  	*baseRPCClient
    84  }
    85  
    86  // rpcClient is an internal interface to which our RPC clients (batch and
    87  // non-batch) must conform. Acts as an additional code-level sanity check to
    88  // make sure the implementations stay coherent.
    89  type rpcClient interface {
    90  	rpcclient.ABCIClient
    91  	rpcclient.HistoryClient
    92  	rpcclient.NetworkClient
    93  	rpcclient.SignClient
    94  	rpcclient.StatusClient
    95  }
    96  
    97  // baseRPCClient implements the basic RPC method logic without the actual
    98  // underlying RPC call functionality, which is provided by `caller`.
    99  type baseRPCClient struct {
   100  	caller jsonrpcclient.Caller
   101  }
   102  
   103  var _ rpcClient = (*HTTP)(nil)
   104  var _ rpcClient = (*BatchHTTP)(nil)
   105  var _ rpcClient = (*baseRPCClient)(nil)
   106  
   107  //-----------------------------------------------------------------------------
   108  // HTTP
   109  
   110  // New takes a remote endpoint in the form <protocol>://<host>:<port> and
   111  // the websocket path (which always seems to be "/websocket")
   112  // An error is returned on invalid remote. The function panics when remote is nil.
   113  func New(remote, wsEndpoint string) (*HTTP, error) {
   114  	httpClient, err := jsonrpcclient.DefaultHTTPClient(remote)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	return NewWithClient(remote, wsEndpoint, httpClient)
   119  }
   120  
   121  // Create timeout enabled http client
   122  func NewWithTimeout(remote, wsEndpoint string, timeout uint) (*HTTP, error) {
   123  	httpClient, err := jsonrpcclient.DefaultHTTPClient(remote)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	httpClient.Timeout = time.Duration(timeout) * time.Second
   128  	return NewWithClient(remote, wsEndpoint, httpClient)
   129  }
   130  
   131  // NewWithClient allows for setting a custom http client (See New).
   132  // An error is returned on invalid remote. The function panics when remote is nil.
   133  func NewWithClient(remote, wsEndpoint string, client *http.Client) (*HTTP, error) {
   134  	if client == nil {
   135  		panic("nil http.Client provided")
   136  	}
   137  
   138  	rc, err := jsonrpcclient.NewWithHTTPClient(remote, client)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  	cdc := rc.Codec()
   143  	ctypes.RegisterAmino(cdc)
   144  	rc.SetCodec(cdc)
   145  
   146  	wsEvents, err := newWSEvents(cdc, remote, wsEndpoint)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	httpClient := &HTTP{
   152  		rpc:           rc,
   153  		remote:        remote,
   154  		baseRPCClient: &baseRPCClient{caller: rc},
   155  		WSEvents:      wsEvents,
   156  	}
   157  	return httpClient, nil
   158  }
   159  
   160  var _ rpcclient.Client = (*HTTP)(nil)
   161  
   162  // SetLogger sets a logger.
   163  func (c *HTTP) SetLogger(l log.Logger) {
   164  	c.WSEvents.SetLogger(l)
   165  }
   166  
   167  // Remote returns the remote network address in a string form.
   168  func (c *HTTP) Remote() string {
   169  	return c.remote
   170  }
   171  
   172  // NewBatch creates a new batch client for this HTTP client.
   173  func (c *HTTP) NewBatch() *BatchHTTP {
   174  	rpcBatch := c.rpc.NewRequestBatch()
   175  	return &BatchHTTP{
   176  		rpcBatch: rpcBatch,
   177  		baseRPCClient: &baseRPCClient{
   178  			caller: rpcBatch,
   179  		},
   180  	}
   181  }
   182  
   183  //-----------------------------------------------------------------------------
   184  // BatchHTTP
   185  
   186  // Send is a convenience function for an HTTP batch that will trigger the
   187  // compilation of the batched requests and send them off using the client as a
   188  // single request. On success, this returns a list of the deserialized results
   189  // from each request in the sent batch.
   190  func (b *BatchHTTP) Send() ([]interface{}, error) {
   191  	return b.rpcBatch.Send()
   192  }
   193  
   194  // Clear will empty out this batch of requests and return the number of requests
   195  // that were cleared out.
   196  func (b *BatchHTTP) Clear() int {
   197  	return b.rpcBatch.Clear()
   198  }
   199  
   200  // Count returns the number of enqueued requests waiting to be sent.
   201  func (b *BatchHTTP) Count() int {
   202  	return b.rpcBatch.Count()
   203  }
   204  
   205  //-----------------------------------------------------------------------------
   206  // baseRPCClient
   207  
   208  func (c *baseRPCClient) ConsensusReactorStatus() (*ctypes.ResultConsensusReactorStatus, error) {
   209  	result := new(ctypes.ResultConsensusReactorStatus)
   210  	_, err := c.caller.Call("consensus_reactor_status", map[string]interface{}{}, result)
   211  	if err != nil {
   212  		return nil, errors.Wrap(err, "ConsensusReactorStatus")
   213  	}
   214  	return result, nil
   215  }
   216  
   217  func (c *baseRPCClient) Status() (*ctypes.ResultStatus, error) {
   218  	result := new(ctypes.ResultStatus)
   219  	_, err := c.caller.Call("status", map[string]interface{}{}, result)
   220  	if err != nil {
   221  		return nil, errors.Wrap(err, "Status")
   222  	}
   223  	return result, nil
   224  }
   225  
   226  func (c *baseRPCClient) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
   227  	result := new(ctypes.ResultABCIInfo)
   228  	_, err := c.caller.Call("abci_info", map[string]interface{}{}, result)
   229  	if err != nil {
   230  		return nil, errors.Wrap(err, "ABCIInfo")
   231  	}
   232  	return result, nil
   233  }
   234  
   235  func (c *baseRPCClient) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
   236  	return c.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions)
   237  }
   238  
   239  func (c *baseRPCClient) ABCIQueryWithOptions(
   240  	path string,
   241  	data bytes.HexBytes,
   242  	opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
   243  	result := new(ctypes.ResultABCIQuery)
   244  	_, err := c.caller.Call("abci_query",
   245  		map[string]interface{}{"path": path, "data": data, "height": opts.Height, "prove": opts.Prove},
   246  		result)
   247  	if err != nil {
   248  		return nil, errors.Wrap(err, "ABCIQuery")
   249  	}
   250  	return result, nil
   251  }
   252  
   253  func (c *baseRPCClient) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
   254  	result := new(ctypes.ResultBroadcastTxCommit)
   255  	_, err := c.caller.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result)
   256  	if err != nil {
   257  		return nil, errors.Wrap(err, "broadcast_tx_commit")
   258  	}
   259  	return result, nil
   260  }
   261  
   262  func (c *baseRPCClient) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
   263  	return c.broadcastTX("broadcast_tx_async", tx)
   264  }
   265  
   266  func (c *baseRPCClient) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
   267  	return c.broadcastTX("broadcast_tx_sync", tx)
   268  }
   269  
   270  func (c *baseRPCClient) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
   271  	result := new(ctypes.ResultBroadcastTx)
   272  	_, err := c.caller.Call(route, map[string]interface{}{"tx": tx}, result)
   273  	if err != nil {
   274  		return nil, errors.Wrap(err, route)
   275  	}
   276  	return result, nil
   277  }
   278  
   279  func (c *baseRPCClient) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) {
   280  	result := new(ctypes.ResultUnconfirmedTxs)
   281  	_, err := c.caller.Call("unconfirmed_txs", map[string]interface{}{"limit": limit}, result)
   282  	if err != nil {
   283  		return nil, errors.Wrap(err, "unconfirmed_txs")
   284  	}
   285  	return result, nil
   286  }
   287  
   288  func (c *baseRPCClient) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) {
   289  	result := new(ctypes.ResultUnconfirmedTxs)
   290  	_, err := c.caller.Call("num_unconfirmed_txs", map[string]interface{}{}, result)
   291  	if err != nil {
   292  		return nil, errors.Wrap(err, "num_unconfirmed_txs")
   293  	}
   294  	return result, nil
   295  }
   296  
   297  func (c *baseRPCClient) NetInfo() (*ctypes.ResultNetInfo, error) {
   298  	result := new(ctypes.ResultNetInfo)
   299  	_, err := c.caller.Call("net_info", map[string]interface{}{}, result)
   300  	if err != nil {
   301  		return nil, errors.Wrap(err, "NetInfo")
   302  	}
   303  	return result, nil
   304  }
   305  
   306  func (c *baseRPCClient) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
   307  	result := new(ctypes.ResultDumpConsensusState)
   308  	_, err := c.caller.Call("dump_consensus_state", map[string]interface{}{}, result)
   309  	if err != nil {
   310  		return nil, errors.Wrap(err, "DumpConsensusState")
   311  	}
   312  	return result, nil
   313  }
   314  
   315  func (c *baseRPCClient) ConsensusState() (*ctypes.ResultConsensusState, error) {
   316  	result := new(ctypes.ResultConsensusState)
   317  	_, err := c.caller.Call("consensus_state", map[string]interface{}{}, result)
   318  	if err != nil {
   319  		return nil, errors.Wrap(err, "ConsensusState")
   320  	}
   321  	return result, nil
   322  }
   323  
   324  func (c *baseRPCClient) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams, error) {
   325  	result := new(ctypes.ResultConsensusParams)
   326  	_, err := c.caller.Call("consensus_params", map[string]interface{}{"height": height}, result)
   327  	if err != nil {
   328  		return nil, errors.Wrap(err, "ConsensusParams")
   329  	}
   330  	return result, nil
   331  }
   332  
   333  func (c *baseRPCClient) Health() (*ctypes.ResultHealth, error) {
   334  	result := new(ctypes.ResultHealth)
   335  	_, err := c.caller.Call("health", map[string]interface{}{}, result)
   336  	if err != nil {
   337  		return nil, errors.Wrap(err, "Health")
   338  	}
   339  	return result, nil
   340  }
   341  
   342  func (c *baseRPCClient) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
   343  	result := new(ctypes.ResultBlockchainInfo)
   344  	_, err := c.caller.Call("blockchain",
   345  		map[string]interface{}{"minHeight": minHeight, "maxHeight": maxHeight},
   346  		result)
   347  	if err != nil {
   348  		return nil, errors.Wrap(err, "BlockchainInfo")
   349  	}
   350  	return result, nil
   351  }
   352  
   353  func (c *baseRPCClient) Genesis() (*ctypes.ResultGenesis, error) {
   354  	result := new(ctypes.ResultGenesis)
   355  	_, err := c.caller.Call("genesis", map[string]interface{}{}, result)
   356  	if err != nil {
   357  		return nil, errors.Wrap(err, "Genesis")
   358  	}
   359  	return result, nil
   360  }
   361  
   362  func (c *baseRPCClient) Block(height *int64) (*ctypes.ResultBlock, error) {
   363  	result := new(ctypes.ResultBlock)
   364  	_, err := c.caller.Call("block", map[string]interface{}{"height": height}, result)
   365  	if err != nil {
   366  		return nil, errors.Wrap(err, "Block")
   367  	}
   368  	return result, nil
   369  }
   370  
   371  func (c *baseRPCClient) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) {
   372  	result := new(ctypes.ResultBlockResults)
   373  	_, err := c.caller.Call("block_results", map[string]interface{}{"height": height}, result)
   374  	if err != nil {
   375  		return nil, errors.Wrap(err, "Block Result")
   376  	}
   377  	return result, nil
   378  }
   379  
   380  func (c *baseRPCClient) Commit(height *int64) (*ctypes.ResultCommit, error) {
   381  	result := new(ctypes.ResultCommit)
   382  	_, err := c.caller.Call("commit", map[string]interface{}{"height": height}, result)
   383  	if err != nil {
   384  		return nil, errors.Wrap(err, "Commit")
   385  	}
   386  	return result, nil
   387  }
   388  
   389  func (c *baseRPCClient) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
   390  	result := new(ctypes.ResultTx)
   391  	params := map[string]interface{}{
   392  		"hash":  hash,
   393  		"prove": prove,
   394  	}
   395  	_, err := c.caller.Call("tx", params, result)
   396  	if err != nil {
   397  		return nil, errors.Wrap(err, "Tx")
   398  	}
   399  	return result, nil
   400  }
   401  
   402  func (c *baseRPCClient) TxSearch(query string, prove bool, page, perPage int, orderBy string) (
   403  	*ctypes.ResultTxSearch, error) {
   404  	result := new(ctypes.ResultTxSearch)
   405  	params := map[string]interface{}{
   406  		"query":    query,
   407  		"prove":    prove,
   408  		"page":     page,
   409  		"per_page": perPage,
   410  		"order_by": orderBy,
   411  	}
   412  	_, err := c.caller.Call("tx_search", params, result)
   413  	if err != nil {
   414  		return nil, errors.Wrap(err, "TxSearch")
   415  	}
   416  	return result, nil
   417  }
   418  
   419  func (c *baseRPCClient) Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error) {
   420  	result := new(ctypes.ResultValidators)
   421  	_, err := c.caller.Call("validators", map[string]interface{}{
   422  		"height":   height,
   423  		"page":     page,
   424  		"per_page": perPage,
   425  	}, result)
   426  	if err != nil {
   427  		return nil, errors.Wrap(err, "Validators")
   428  	}
   429  	return result, nil
   430  }
   431  
   432  func (c *baseRPCClient) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
   433  	result := new(ctypes.ResultBroadcastEvidence)
   434  	_, err := c.caller.Call("broadcast_evidence", map[string]interface{}{"evidence": ev}, result)
   435  	if err != nil {
   436  		return nil, errors.Wrap(err, "BroadcastEvidence")
   437  	}
   438  	return result, nil
   439  }
   440  
   441  //-----------------------------------------------------------------------------
   442  // WSEvents
   443  
   444  var errNotRunning = errors.New("client is not running. Use .Start() method to start")
   445  
   446  // WSEvents is a wrapper around WSClient, which implements EventsClient.
   447  type WSEvents struct {
   448  	service.BaseService
   449  	cdc      *amino.Codec
   450  	remote   string
   451  	endpoint string
   452  	ws       *jsonrpcclient.WSClient
   453  
   454  	mtx           sync.RWMutex
   455  	subscriptions map[string]chan ctypes.ResultEvent // query -> chan
   456  }
   457  
   458  func newWSEvents(cdc *amino.Codec, remote, endpoint string) (*WSEvents, error) {
   459  	w := &WSEvents{
   460  		cdc:           cdc,
   461  		endpoint:      endpoint,
   462  		remote:        remote,
   463  		subscriptions: make(map[string]chan ctypes.ResultEvent),
   464  	}
   465  	w.BaseService = *service.NewBaseService(nil, "WSEvents", w)
   466  
   467  	var err error
   468  	w.ws, err = jsonrpcclient.NewWS(w.remote, w.endpoint, jsonrpcclient.OnReconnect(func() {
   469  		// resubscribe immediately
   470  		w.redoSubscriptionsAfter(0 * time.Second)
   471  	}))
   472  	if err != nil {
   473  		return nil, err
   474  	}
   475  	w.ws.SetCodec(w.cdc)
   476  	w.ws.SetLogger(w.Logger)
   477  
   478  	return w, nil
   479  }
   480  
   481  // OnStart implements service.Service by starting WSClient and event loop.
   482  func (w *WSEvents) OnStart() error {
   483  	if err := w.ws.Start(); err != nil {
   484  		return err
   485  	}
   486  
   487  	go w.eventListener()
   488  
   489  	return nil
   490  }
   491  
   492  // OnStop implements service.Service by stopping WSClient.
   493  func (w *WSEvents) OnStop() {
   494  	_ = w.ws.Stop()
   495  }
   496  
   497  // Subscribe implements EventsClient by using WSClient to subscribe given
   498  // subscriber to query. By default, returns a channel with cap=1. Error is
   499  // returned if it fails to subscribe.
   500  //
   501  // Channel is never closed to prevent clients from seeing an erroneous event.
   502  //
   503  // It returns an error if WSEvents is not running.
   504  func (w *WSEvents) Subscribe(ctx context.Context, subscriber, query string,
   505  	outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) {
   506  
   507  	if !w.IsRunning() {
   508  		return nil, errNotRunning
   509  	}
   510  
   511  	if err := w.ws.Subscribe(ctx, query); err != nil {
   512  		return nil, err
   513  	}
   514  
   515  	outCap := 1
   516  	if len(outCapacity) > 0 {
   517  		outCap = outCapacity[0]
   518  	}
   519  
   520  	outc := make(chan ctypes.ResultEvent, outCap)
   521  	w.mtx.Lock()
   522  	// subscriber param is ignored because Tendermint will override it with
   523  	// remote IP anyway.
   524  	w.subscriptions[query] = outc
   525  	w.mtx.Unlock()
   526  
   527  	return outc, nil
   528  }
   529  
   530  // Unsubscribe implements EventsClient by using WSClient to unsubscribe given
   531  // subscriber from query.
   532  //
   533  // It returns an error if WSEvents is not running.
   534  func (w *WSEvents) Unsubscribe(ctx context.Context, subscriber, query string) error {
   535  	if !w.IsRunning() {
   536  		return errNotRunning
   537  	}
   538  
   539  	if err := w.ws.Unsubscribe(ctx, query); err != nil {
   540  		return err
   541  	}
   542  
   543  	w.mtx.Lock()
   544  	_, ok := w.subscriptions[query]
   545  	if ok {
   546  		delete(w.subscriptions, query)
   547  	}
   548  	w.mtx.Unlock()
   549  
   550  	return nil
   551  }
   552  
   553  // UnsubscribeAll implements EventsClient by using WSClient to unsubscribe
   554  // given subscriber from all the queries.
   555  //
   556  // It returns an error if WSEvents is not running.
   557  func (w *WSEvents) UnsubscribeAll(ctx context.Context, subscriber string) error {
   558  	if !w.IsRunning() {
   559  		return errNotRunning
   560  	}
   561  
   562  	if err := w.ws.UnsubscribeAll(ctx); err != nil {
   563  		return err
   564  	}
   565  
   566  	w.mtx.Lock()
   567  	w.subscriptions = make(map[string]chan ctypes.ResultEvent)
   568  	w.mtx.Unlock()
   569  
   570  	return nil
   571  }
   572  
   573  // After being reconnected, it is necessary to redo subscription to server
   574  // otherwise no data will be automatically received.
   575  func (w *WSEvents) redoSubscriptionsAfter(d time.Duration) {
   576  	time.Sleep(d)
   577  
   578  	w.mtx.RLock()
   579  	defer w.mtx.RUnlock()
   580  	for q := range w.subscriptions {
   581  		err := w.ws.Subscribe(context.Background(), q)
   582  		if err != nil {
   583  			w.Logger.Error("Failed to resubscribe", "err", err)
   584  		}
   585  	}
   586  }
   587  
   588  func isErrAlreadySubscribed(err error) bool {
   589  	return strings.Contains(err.Error(), tmpubsub.ErrAlreadySubscribed.Error())
   590  }
   591  
   592  func (w *WSEvents) eventListener() {
   593  	for {
   594  		select {
   595  		case resp, ok := <-w.ws.ResponsesCh:
   596  			if !ok {
   597  				return
   598  			}
   599  
   600  			if resp.Error != nil {
   601  				w.Logger.Error("WS error", "err", resp.Error.Error())
   602  				// Error can be ErrAlreadySubscribed or max client (subscriptions per
   603  				// client) reached or Tendermint exited.
   604  				// We can ignore ErrAlreadySubscribed, but need to retry in other
   605  				// cases.
   606  				if !isErrAlreadySubscribed(resp.Error) {
   607  					// Resubscribe after 1 second to give Tendermint time to restart (if
   608  					// crashed).
   609  					w.redoSubscriptionsAfter(1 * time.Second)
   610  				}
   611  				continue
   612  			}
   613  
   614  			result := new(ctypes.ResultEvent)
   615  			err := w.cdc.UnmarshalJSON(resp.Result, result)
   616  			if err != nil {
   617  				w.Logger.Error("failed to unmarshal response", "err", err)
   618  				continue
   619  			}
   620  
   621  			w.mtx.RLock()
   622  			if out, ok := w.subscriptions[result.Query]; ok {
   623  				if cap(out) == 0 {
   624  					out <- *result
   625  				} else {
   626  					select {
   627  					case out <- *result:
   628  					default:
   629  						w.Logger.Error("wanted to publish ResultEvent, but out channel is full", "result", result, "query", result.Query)
   630  					}
   631  				}
   632  			}
   633  			w.mtx.RUnlock()
   634  		case <-w.Quit():
   635  			return
   636  		}
   637  	}
   638  }