- package proxy
-
- import (
- "context"
- "fmt"
- "net"
- "net/http"
-
- tmpubsub "github.com/tendermint/tendermint/internal/pubsub"
- rpccore "github.com/tendermint/tendermint/internal/rpc/core"
- "github.com/tendermint/tendermint/libs/log"
- "github.com/tendermint/tendermint/light"
- lrpc "github.com/tendermint/tendermint/light/rpc"
- rpchttp "github.com/tendermint/tendermint/rpc/client/http"
- rpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server"
- )
-
- // A Proxy defines parameters for running an HTTP server proxy.
- type Proxy struct {
- Addr string // TCP address to listen on, ":http" if empty
- Config *rpcserver.Config
- Client *lrpc.Client
- Logger log.Logger
- Listener net.Listener
- }
-
- // NewProxy creates the struct used to run an HTTP server for serving light
- // client rpc requests.
- func NewProxy(
- lightClient *light.Client,
- listenAddr, providerAddr string,
- config *rpcserver.Config,
- logger log.Logger,
- opts ...lrpc.Option,
- ) (*Proxy, error) {
- rpcClient, err := rpchttp.NewWithTimeout(providerAddr, config.WriteTimeout)
- if err != nil {
- return nil, fmt.Errorf("failed to create http client for %s: %w", providerAddr, err)
- }
-
- return &Proxy{
- Addr: listenAddr,
- Config: config,
- Client: lrpc.NewClient(logger, rpcClient, lightClient, opts...),
- Logger: logger,
- }, nil
- }
-
- // ListenAndServe configures the rpcserver.WebsocketManager, sets up the RPC
- // routes to proxy via Client, and starts up an HTTP server on the TCP network
- // address p.Addr.
- // See http#Server#ListenAndServe.
- func (p *Proxy) ListenAndServe(ctx context.Context) error {
- listener, mux, err := p.listen(ctx)
- if err != nil {
- return err
- }
- p.Listener = listener
-
- return rpcserver.Serve(
- ctx,
- listener,
- mux,
- p.Logger,
- p.Config,
- )
- }
-
- // ListenAndServeTLS acts identically to ListenAndServe, except that it expects
- // HTTPS connections.
- // See http#Server#ListenAndServeTLS.
- func (p *Proxy) ListenAndServeTLS(ctx context.Context, certFile, keyFile string) error {
- listener, mux, err := p.listen(ctx)
- if err != nil {
- return err
- }
- p.Listener = listener
-
- return rpcserver.ServeTLS(
- ctx,
- listener,
- mux,
- certFile,
- keyFile,
- p.Logger,
- p.Config,
- )
- }
-
- func (p *Proxy) listen(ctx context.Context) (net.Listener, *http.ServeMux, error) {
- mux := http.NewServeMux()
-
- // 1) Register regular routes.
- r := rpccore.NewRoutesMap(proxyService{Client: p.Client}, nil)
- rpcserver.RegisterRPCFuncs(mux, r, p.Logger)
-
- // 2) Allow websocket connections.
- wmLogger := p.Logger.With("protocol", "websocket")
- wm := rpcserver.NewWebsocketManager(wmLogger, r,
- rpcserver.OnDisconnect(func(remoteAddr string) {
- err := p.Client.UnsubscribeAll(context.Background(), remoteAddr)
- if err != nil && err != tmpubsub.ErrSubscriptionNotFound {
- wmLogger.Error("Failed to unsubscribe addr from events", "addr", remoteAddr, "err", err)
- }
- }),
- rpcserver.ReadLimit(p.Config.MaxBodyBytes),
- )
-
- mux.HandleFunc("/websocket", wm.WebsocketHandler)
-
- // 3) Start a client.
- if !p.Client.IsRunning() {
- if err := p.Client.Start(ctx); err != nil {
- return nil, mux, fmt.Errorf("can't start client: %w", err)
- }
- }
-
- // 4) Start listening for new connections.
- listener, err := rpcserver.Listen(p.Addr, p.Config.MaxOpenConnections)
- if err != nil {
- return nil, mux, err
- }
-
- return listener, mux, nil
- }
|