cosmos-sdk/client/lcd/root.go

129 lines
3.7 KiB
Go

package lcd
import (
"fmt"
"net"
"net/http"
"os"
"time"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/rakyll/statik/fs"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/log"
tmrpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
// unnamed import of statik for swagger UI support
_ "github.com/cosmos/cosmos-sdk/client/lcd/statik"
)
// RestServer represents the Light Client Rest server
type RestServer struct {
Mux *mux.Router
ClientCtx client.Context
log log.Logger
listener net.Listener
}
// NewRestServer creates a new rest server instance
func NewRestServer(cdc *codec.Codec) *RestServer {
r := mux.NewRouter()
clientCtx := client.NewContext().WithCodec(cdc)
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server")
return &RestServer{
Mux: r,
ClientCtx: clientCtx,
log: logger,
}
}
// StartWithConfig starts the REST server that listens on the provided listenAddr.
// It will use the provided RPC configuration.
func (rs *RestServer) StartWithConfig(listenAddr string, cors bool, cfg *tmrpcserver.Config) error {
server.TrapSignal(func() {
err := rs.listener.Close()
rs.log.Error("error closing listener", "err", err)
})
listener, err := tmrpcserver.Listen(listenAddr, cfg)
if err != nil {
return err
}
rs.listener = listener
rs.log.Info(
fmt.Sprintf("Starting application REST service (chain-id: %q)...", viper.GetString(flags.FlagChainID)),
)
var h http.Handler = rs.Mux
if cors {
return tmrpcserver.Serve(rs.listener, handlers.CORS()(h), rs.log, cfg)
}
return tmrpcserver.Serve(rs.listener, rs.Mux, rs.log, cfg)
}
// Start starts the REST server that listens on the provided listenAddr. The REST
// service will use Tendermint's default RPC configuration, where the R/W timeout
// and max open connections are overridden.
func (rs *RestServer) Start(listenAddr string, maxOpen int, readTimeout, writeTimeout uint, cors bool) error {
cfg := tmrpcserver.DefaultConfig()
cfg.MaxOpenConnections = maxOpen
cfg.ReadTimeout = time.Duration(readTimeout) * time.Second
cfg.WriteTimeout = time.Duration(writeTimeout) * time.Second
return rs.StartWithConfig(listenAddr, cors, cfg)
}
// ServeCommand will start the application REST service as a blocking process. It
// takes a codec to create a RestServer object and a function to register all
// necessary routes.
func ServeCommand(cdc *codec.Codec, registerRoutesFn func(*RestServer)) *cobra.Command {
cmd := &cobra.Command{
Use: "rest-server",
Short: "Start LCD (light-client daemon), a local REST server",
RunE: func(cmd *cobra.Command, args []string) (err error) {
rs := NewRestServer(cdc)
registerRoutesFn(rs)
rs.registerSwaggerUI()
cfg := tmrpcserver.DefaultConfig()
cfg.MaxOpenConnections = viper.GetInt(flags.FlagMaxOpenConnections)
cfg.ReadTimeout = time.Duration(viper.GetInt64(flags.FlagRPCReadTimeout)) * time.Second
cfg.WriteTimeout = time.Duration(viper.GetInt64(flags.FlagRPCWriteTimeout)) * time.Second
cfg.MaxBodyBytes = viper.GetInt64(flags.FlagRPCMaxBodyBytes)
// start the rest server and return error if one exists
return rs.StartWithConfig(
viper.GetString(flags.FlagListenAddr),
viper.GetBool(flags.FlagUnsafeCORS),
cfg,
)
},
}
return flags.RegisterRestServerFlags(cmd)
}
func (rs *RestServer) registerSwaggerUI() {
statikFS, err := fs.New()
if err != nil {
panic(err)
}
staticServer := http.FileServer(statikFS)
rs.Mux.PathPrefix("/").Handler(staticServer)
}