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 }