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.

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