github.com/okex/exchain@v1.8.0/libs/tendermint/rpc/core/events.go (about)

     1  package core
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/pkg/errors"
     8  
     9  	"github.com/okex/exchain/libs/tendermint/config"
    10  	tmpubsub "github.com/okex/exchain/libs/tendermint/libs/pubsub"
    11  	tmquery "github.com/okex/exchain/libs/tendermint/libs/pubsub/query"
    12  	ctypes "github.com/okex/exchain/libs/tendermint/rpc/core/types"
    13  	rpctypes "github.com/okex/exchain/libs/tendermint/rpc/jsonrpc/types"
    14  )
    15  
    16  const (
    17  	// Buffer on the Tendermint (server) side to allow some slowness in clients.
    18  	subBufferSize = 100
    19  )
    20  
    21  // Subscribe for events via WebSocket.
    22  // More: https://docs.tendermint.com/master/rpc/#/Websocket/subscribe
    23  func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) {
    24  	addr := ctx.RemoteAddr()
    25  
    26  	if env.EventBus.NumClients() >= config.DynamicConfig.GetMaxSubscriptionClients() {
    27  		return nil, fmt.Errorf("max_subscription_clients %d reached", config.DynamicConfig.GetMaxSubscriptionClients())
    28  	} else if env.EventBus.NumClientSubscriptions(addr) >= env.Config.MaxSubscriptionsPerClient {
    29  		return nil, fmt.Errorf("max_subscriptions_per_client %d reached", env.Config.MaxSubscriptionsPerClient)
    30  	}
    31  
    32  	env.Logger.Info("Subscribe to query", "remote", addr, "query", query)
    33  
    34  	q, err := tmquery.New(query)
    35  	if err != nil {
    36  		return nil, errors.Wrap(err, "failed to parse query")
    37  	}
    38  
    39  	subCtx, cancel := context.WithTimeout(ctx.Context(), SubscribeTimeout)
    40  	defer cancel()
    41  
    42  	sub, err := env.EventBus.Subscribe(subCtx, addr, q, subBufferSize)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	// Capture the current ID, since it can change in the future.
    48  	subscriptionID := ctx.JSONReq.ID
    49  	go func() {
    50  		for {
    51  			select {
    52  			case msg := <-sub.Out():
    53  				resultEvent := &ctypes.ResultEvent{Query: query, Data: msg.Data(), Events: msg.Events()}
    54  				ctx.WSConn.TryWriteRPCResponse(
    55  					rpctypes.NewRPCSuccessResponse(
    56  						ctx.WSConn.Codec(),
    57  						subscriptionID,
    58  						resultEvent,
    59  					))
    60  			case <-sub.Cancelled():
    61  				if sub.Err() != tmpubsub.ErrUnsubscribed {
    62  					var reason string
    63  					if sub.Err() == nil {
    64  						reason = "Tendermint exited"
    65  					} else {
    66  						reason = sub.Err().Error()
    67  					}
    68  					ctx.WSConn.TryWriteRPCResponse(
    69  						rpctypes.RPCServerError(
    70  							subscriptionID,
    71  							fmt.Errorf("subscription was cancelled (reason: %s)", reason),
    72  						))
    73  				}
    74  				return
    75  			}
    76  		}
    77  	}()
    78  
    79  	return &ctypes.ResultSubscribe{}, nil
    80  }
    81  
    82  // Unsubscribe from events via WebSocket.
    83  // More: https://docs.tendermint.com/master/rpc/#/Websocket/unsubscribe
    84  func Unsubscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error) {
    85  	addr := ctx.RemoteAddr()
    86  	env.Logger.Info("Unsubscribe from query", "remote", addr, "query", query)
    87  	q, err := tmquery.New(query)
    88  	if err != nil {
    89  		return nil, errors.Wrap(err, "failed to parse query")
    90  	}
    91  	err = env.EventBus.Unsubscribe(context.Background(), addr, q)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	return &ctypes.ResultUnsubscribe{}, nil
    96  }
    97  
    98  // UnsubscribeAll from all events via WebSocket.
    99  // More: https://docs.tendermint.com/master/rpc/#/Websocket/unsubscribe_all
   100  func UnsubscribeAll(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error) {
   101  	addr := ctx.RemoteAddr()
   102  	env.Logger.Info("Unsubscribe from all", "remote", addr)
   103  	err := env.EventBus.UnsubscribeAll(context.Background(), addr)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	return &ctypes.ResultUnsubscribe{}, nil
   108  }