github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/light/proxy/proxy.go (about)

     1  package proxy
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net"
     7  	"net/http"
     8  
     9  	tmpubsub "github.com/ari-anchor/sei-tendermint/internal/pubsub"
    10  	rpccore "github.com/ari-anchor/sei-tendermint/internal/rpc/core"
    11  	"github.com/ari-anchor/sei-tendermint/libs/log"
    12  	"github.com/ari-anchor/sei-tendermint/light"
    13  	lrpc "github.com/ari-anchor/sei-tendermint/light/rpc"
    14  	rpchttp "github.com/ari-anchor/sei-tendermint/rpc/client/http"
    15  	rpcserver "github.com/ari-anchor/sei-tendermint/rpc/jsonrpc/server"
    16  )
    17  
    18  // A Proxy defines parameters for running an HTTP server proxy.
    19  type Proxy struct {
    20  	Addr     string // TCP address to listen on, ":http" if empty
    21  	Config   *rpcserver.Config
    22  	Client   *lrpc.Client
    23  	Logger   log.Logger
    24  	Listener net.Listener
    25  }
    26  
    27  // NewProxy creates the struct used to run an HTTP server for serving light
    28  // client rpc requests.
    29  func NewProxy(
    30  	lightClient *light.Client,
    31  	listenAddr, providerAddr string,
    32  	config *rpcserver.Config,
    33  	logger log.Logger,
    34  	opts ...lrpc.Option,
    35  ) (*Proxy, error) {
    36  	rpcClient, err := rpchttp.NewWithTimeout(providerAddr, config.WriteTimeout)
    37  	if err != nil {
    38  		return nil, fmt.Errorf("failed to create http client for %s: %w", providerAddr, err)
    39  	}
    40  
    41  	return &Proxy{
    42  		Addr:   listenAddr,
    43  		Config: config,
    44  		Client: lrpc.NewClient(logger, rpcClient, lightClient, opts...),
    45  		Logger: logger,
    46  	}, nil
    47  }
    48  
    49  // ListenAndServe configures the rpcserver.WebsocketManager, sets up the RPC
    50  // routes to proxy via Client, and starts up an HTTP server on the TCP network
    51  // address p.Addr.
    52  // See http#Server#ListenAndServe.
    53  func (p *Proxy) ListenAndServe(ctx context.Context) error {
    54  	listener, mux, err := p.listen(ctx)
    55  	if err != nil {
    56  		return err
    57  	}
    58  	p.Listener = listener
    59  
    60  	return rpcserver.Serve(
    61  		ctx,
    62  		listener,
    63  		mux,
    64  		p.Logger,
    65  		p.Config,
    66  	)
    67  }
    68  
    69  // ListenAndServeTLS acts identically to ListenAndServe, except that it expects
    70  // HTTPS connections.
    71  // See http#Server#ListenAndServeTLS.
    72  func (p *Proxy) ListenAndServeTLS(ctx context.Context, certFile, keyFile string) error {
    73  	listener, mux, err := p.listen(ctx)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	p.Listener = listener
    78  
    79  	return rpcserver.ServeTLS(
    80  		ctx,
    81  		listener,
    82  		mux,
    83  		certFile,
    84  		keyFile,
    85  		p.Logger,
    86  		p.Config,
    87  	)
    88  }
    89  
    90  func (p *Proxy) listen(ctx context.Context) (net.Listener, *http.ServeMux, error) {
    91  	mux := http.NewServeMux()
    92  
    93  	// 1) Register regular routes.
    94  	r := rpccore.NewRoutesMap(proxyService{Client: p.Client}, nil)
    95  	rpcserver.RegisterRPCFuncs(mux, r, p.Logger)
    96  
    97  	// 2) Allow websocket connections.
    98  	wmLogger := p.Logger.With("protocol", "websocket")
    99  	wm := rpcserver.NewWebsocketManager(wmLogger, r,
   100  		rpcserver.OnDisconnect(func(remoteAddr string) {
   101  			err := p.Client.UnsubscribeAll(context.Background(), remoteAddr)
   102  			if err != nil && err != tmpubsub.ErrSubscriptionNotFound {
   103  				wmLogger.Error("Failed to unsubscribe addr from events", "addr", remoteAddr, "err", err)
   104  			}
   105  		}),
   106  		rpcserver.ReadLimit(p.Config.MaxBodyBytes),
   107  	)
   108  
   109  	mux.HandleFunc("/websocket", wm.WebsocketHandler)
   110  
   111  	// 3) Start a client.
   112  	if !p.Client.IsRunning() {
   113  		if err := p.Client.Start(ctx); err != nil {
   114  			return nil, mux, fmt.Errorf("can't start client: %w", err)
   115  		}
   116  	}
   117  
   118  	// 4) Start listening for new connections.
   119  	listener, err := rpcserver.Listen(p.Addr, p.Config.MaxOpenConnections)
   120  	if err != nil {
   121  		return nil, mux, err
   122  	}
   123  
   124  	return listener, mux, nil
   125  }