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 }