You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
5.1 KiB

  1. package rpc
  2. import (
  3. "context"
  4. "net/http"
  5. "time"
  6. "github.com/rs/cors"
  7. "github.com/tendermint/tendermint/config"
  8. "github.com/tendermint/tendermint/internal/consensus"
  9. "github.com/tendermint/tendermint/internal/rpc/core"
  10. "github.com/tendermint/tendermint/internal/state"
  11. "github.com/tendermint/tendermint/internal/state/indexer"
  12. "github.com/tendermint/tendermint/libs/log"
  13. "github.com/tendermint/tendermint/libs/pubsub"
  14. "github.com/tendermint/tendermint/rpc/jsonrpc/server"
  15. "github.com/tendermint/tendermint/types"
  16. )
  17. // Server defines parameters for running an Inspector rpc server.
  18. type Server struct {
  19. Addr string // TCP address to listen on, ":http" if empty
  20. Handler http.Handler
  21. Logger log.Logger
  22. Config *config.RPCConfig
  23. }
  24. type eventBusUnsubscriber interface {
  25. UnsubscribeAll(ctx context.Context, subscriber string) error
  26. }
  27. // Routes returns the set of routes used by the Inspector server.
  28. func Routes(cfg config.RPCConfig, s state.Store, bs state.BlockStore, es []indexer.EventSink, logger log.Logger) core.RoutesMap {
  29. env := &core.Environment{
  30. Config: cfg,
  31. EventSinks: es,
  32. StateStore: s,
  33. BlockStore: bs,
  34. ConsensusReactor: waitSyncCheckerImpl{},
  35. Logger: logger,
  36. }
  37. return core.RoutesMap{
  38. "blockchain": server.NewRPCFunc(env.BlockchainInfo, "minHeight,maxHeight", true),
  39. "consensus_params": server.NewRPCFunc(env.ConsensusParams, "height", true),
  40. "block": server.NewRPCFunc(env.Block, "height", true),
  41. "block_by_hash": server.NewRPCFunc(env.BlockByHash, "hash", true),
  42. "block_results": server.NewRPCFunc(env.BlockResults, "height", true),
  43. "commit": server.NewRPCFunc(env.Commit, "height", true),
  44. "validators": server.NewRPCFunc(env.Validators, "height,page,per_page", true),
  45. "tx": server.NewRPCFunc(env.Tx, "hash,prove", true),
  46. "tx_search": server.NewRPCFunc(env.TxSearch, "query,prove,page,per_page,order_by", false),
  47. "block_search": server.NewRPCFunc(env.BlockSearch, "query,page,per_page,order_by", false),
  48. }
  49. }
  50. // Handler returns the http.Handler configured for use with an Inspector server. Handler
  51. // registers the routes on the http.Handler and also registers the websocket handler
  52. // and the CORS handler if specified by the configuration options.
  53. func Handler(rpcConfig *config.RPCConfig, routes core.RoutesMap, logger log.Logger) http.Handler {
  54. mux := http.NewServeMux()
  55. wmLogger := logger.With("protocol", "websocket")
  56. var eventBus eventBusUnsubscriber
  57. websocketDisconnectFn := func(remoteAddr string) {
  58. err := eventBus.UnsubscribeAll(context.Background(), remoteAddr)
  59. if err != nil && err != pubsub.ErrSubscriptionNotFound {
  60. wmLogger.Error("Failed to unsubscribe addr from events", "addr", remoteAddr, "err", err)
  61. }
  62. }
  63. wm := server.NewWebsocketManager(routes,
  64. server.OnDisconnect(websocketDisconnectFn),
  65. server.ReadLimit(rpcConfig.MaxBodyBytes))
  66. wm.SetLogger(wmLogger)
  67. mux.HandleFunc("/websocket", wm.WebsocketHandler)
  68. server.RegisterRPCFuncs(mux, routes, logger)
  69. var rootHandler http.Handler = mux
  70. if rpcConfig.IsCorsEnabled() {
  71. rootHandler = addCORSHandler(rpcConfig, mux)
  72. }
  73. return rootHandler
  74. }
  75. func addCORSHandler(rpcConfig *config.RPCConfig, h http.Handler) http.Handler {
  76. corsMiddleware := cors.New(cors.Options{
  77. AllowedOrigins: rpcConfig.CORSAllowedOrigins,
  78. AllowedMethods: rpcConfig.CORSAllowedMethods,
  79. AllowedHeaders: rpcConfig.CORSAllowedHeaders,
  80. })
  81. h = corsMiddleware.Handler(h)
  82. return h
  83. }
  84. type waitSyncCheckerImpl struct{}
  85. func (waitSyncCheckerImpl) WaitSync() bool {
  86. return false
  87. }
  88. func (waitSyncCheckerImpl) GetPeerState(peerID types.NodeID) (*consensus.PeerState, bool) {
  89. return nil, false
  90. }
  91. // ListenAndServe listens on the address specified in srv.Addr and handles any
  92. // incoming requests over HTTP using the Inspector rpc handler specified on the server.
  93. func (srv *Server) ListenAndServe(ctx context.Context) error {
  94. listener, err := server.Listen(srv.Addr, srv.Config.MaxOpenConnections)
  95. if err != nil {
  96. return err
  97. }
  98. go func() {
  99. <-ctx.Done()
  100. listener.Close()
  101. }()
  102. return server.Serve(listener, srv.Handler, srv.Logger, serverRPCConfig(srv.Config))
  103. }
  104. // ListenAndServeTLS listens on the address specified in srv.Addr. ListenAndServeTLS handles
  105. // incoming requests over HTTPS using the Inspector rpc handler specified on the server.
  106. func (srv *Server) ListenAndServeTLS(ctx context.Context, certFile, keyFile string) error {
  107. listener, err := server.Listen(srv.Addr, srv.Config.MaxOpenConnections)
  108. if err != nil {
  109. return err
  110. }
  111. go func() {
  112. <-ctx.Done()
  113. listener.Close()
  114. }()
  115. return server.ServeTLS(listener, srv.Handler, certFile, keyFile, srv.Logger, serverRPCConfig(srv.Config))
  116. }
  117. func serverRPCConfig(r *config.RPCConfig) *server.Config {
  118. cfg := server.DefaultConfig()
  119. cfg.MaxBodyBytes = r.MaxBodyBytes
  120. cfg.MaxHeaderBytes = r.MaxHeaderBytes
  121. // If necessary adjust global WriteTimeout to ensure it's greater than
  122. // TimeoutBroadcastTxCommit.
  123. // See https://github.com/tendermint/tendermint/issues/3435
  124. if cfg.WriteTimeout <= r.TimeoutBroadcastTxCommit {
  125. cfg.WriteTimeout = r.TimeoutBroadcastTxCommit + 1*time.Second
  126. }
  127. return cfg
  128. }