github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/rpc/client/http/http.go (about)

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