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.

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