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.

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