github.com/authcall/reference-optimistic-geth@v0.0.0-20220816224302-06313bfeb8d2/node/rpcstack.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package node
    18  
    19  import (
    20  	"compress/gzip"
    21  	"context"
    22  	"fmt"
    23  	"io"
    24  	"net"
    25  	"net/http"
    26  	"sort"
    27  	"strings"
    28  	"sync"
    29  	"sync/atomic"
    30  	"time"
    31  
    32  	"github.com/ethereum/go-ethereum/log"
    33  	"github.com/ethereum/go-ethereum/rpc"
    34  	"github.com/rs/cors"
    35  )
    36  
    37  // httpConfig is the JSON-RPC/HTTP configuration.
    38  type httpConfig struct {
    39  	Modules            []string
    40  	CorsAllowedOrigins []string
    41  	Vhosts             []string
    42  	prefix             string // path prefix on which to mount http handler
    43  	jwtSecret          []byte // optional JWT secret
    44  }
    45  
    46  // wsConfig is the JSON-RPC/Websocket configuration
    47  type wsConfig struct {
    48  	Origins   []string
    49  	Modules   []string
    50  	prefix    string // path prefix on which to mount ws handler
    51  	jwtSecret []byte // optional JWT secret
    52  }
    53  
    54  type rpcHandler struct {
    55  	http.Handler
    56  	server *rpc.Server
    57  }
    58  
    59  type httpServer struct {
    60  	log      log.Logger
    61  	timeouts rpc.HTTPTimeouts
    62  	mux      http.ServeMux // registered handlers go here
    63  
    64  	mu       sync.Mutex
    65  	server   *http.Server
    66  	listener net.Listener // non-nil when server is running
    67  
    68  	// HTTP RPC handler things.
    69  
    70  	httpConfig  httpConfig
    71  	httpHandler atomic.Value // *rpcHandler
    72  
    73  	// WebSocket handler things.
    74  	wsConfig  wsConfig
    75  	wsHandler atomic.Value // *rpcHandler
    76  
    77  	// These are set by setListenAddr.
    78  	endpoint string
    79  	host     string
    80  	port     int
    81  
    82  	handlerNames map[string]string
    83  }
    84  
    85  const (
    86  	shutdownTimeout = 5 * time.Second
    87  )
    88  
    89  func newHTTPServer(log log.Logger, timeouts rpc.HTTPTimeouts) *httpServer {
    90  	h := &httpServer{log: log, timeouts: timeouts, handlerNames: make(map[string]string)}
    91  
    92  	h.httpHandler.Store((*rpcHandler)(nil))
    93  	h.wsHandler.Store((*rpcHandler)(nil))
    94  	return h
    95  }
    96  
    97  // setListenAddr configures the listening address of the server.
    98  // The address can only be set while the server isn't running.
    99  func (h *httpServer) setListenAddr(host string, port int) error {
   100  	h.mu.Lock()
   101  	defer h.mu.Unlock()
   102  
   103  	if h.listener != nil && (host != h.host || port != h.port) {
   104  		return fmt.Errorf("HTTP server already running on %s", h.endpoint)
   105  	}
   106  
   107  	h.host, h.port = host, port
   108  	h.endpoint = fmt.Sprintf("%s:%d", host, port)
   109  	return nil
   110  }
   111  
   112  // listenAddr returns the listening address of the server.
   113  func (h *httpServer) listenAddr() string {
   114  	h.mu.Lock()
   115  	defer h.mu.Unlock()
   116  
   117  	if h.listener != nil {
   118  		return h.listener.Addr().String()
   119  	}
   120  	return h.endpoint
   121  }
   122  
   123  // start starts the HTTP server if it is enabled and not already running.
   124  func (h *httpServer) start() error {
   125  	h.mu.Lock()
   126  	defer h.mu.Unlock()
   127  
   128  	if h.endpoint == "" || h.listener != nil {
   129  		return nil // already running or not configured
   130  	}
   131  
   132  	// Initialize the server.
   133  	h.server = &http.Server{Handler: h}
   134  	if h.timeouts != (rpc.HTTPTimeouts{}) {
   135  		CheckTimeouts(&h.timeouts)
   136  		h.server.ReadTimeout = h.timeouts.ReadTimeout
   137  		h.server.WriteTimeout = h.timeouts.WriteTimeout
   138  		h.server.IdleTimeout = h.timeouts.IdleTimeout
   139  	}
   140  
   141  	// Start the server.
   142  	listener, err := net.Listen("tcp", h.endpoint)
   143  	if err != nil {
   144  		// If the server fails to start, we need to clear out the RPC and WS
   145  		// configuration so they can be configured another time.
   146  		h.disableRPC()
   147  		h.disableWS()
   148  		return err
   149  	}
   150  	h.listener = listener
   151  	go h.server.Serve(listener)
   152  
   153  	if h.wsAllowed() {
   154  		url := fmt.Sprintf("ws://%v", listener.Addr())
   155  		if h.wsConfig.prefix != "" {
   156  			url += h.wsConfig.prefix
   157  		}
   158  		h.log.Info("WebSocket enabled", "url", url)
   159  	}
   160  	// if server is websocket only, return after logging
   161  	if !h.rpcAllowed() {
   162  		return nil
   163  	}
   164  	// Log http endpoint.
   165  	h.log.Info("HTTP server started",
   166  		"endpoint", listener.Addr(), "auth", (h.httpConfig.jwtSecret != nil),
   167  		"prefix", h.httpConfig.prefix,
   168  		"cors", strings.Join(h.httpConfig.CorsAllowedOrigins, ","),
   169  		"vhosts", strings.Join(h.httpConfig.Vhosts, ","),
   170  	)
   171  
   172  	// Log all handlers mounted on server.
   173  	var paths []string
   174  	for path := range h.handlerNames {
   175  		paths = append(paths, path)
   176  	}
   177  	sort.Strings(paths)
   178  	logged := make(map[string]bool, len(paths))
   179  	for _, path := range paths {
   180  		name := h.handlerNames[path]
   181  		if !logged[name] {
   182  			log.Info(name+" enabled", "url", "http://"+listener.Addr().String()+path)
   183  			logged[name] = true
   184  		}
   185  	}
   186  	return nil
   187  }
   188  
   189  func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   190  	// check if ws request and serve if ws enabled
   191  	ws := h.wsHandler.Load().(*rpcHandler)
   192  	if ws != nil && isWebsocket(r) {
   193  		if checkPath(r, h.wsConfig.prefix) {
   194  			ws.ServeHTTP(w, r)
   195  		}
   196  		return
   197  	}
   198  	// if http-rpc is enabled, try to serve request
   199  	rpc := h.httpHandler.Load().(*rpcHandler)
   200  	if rpc != nil {
   201  		// First try to route in the mux.
   202  		// Requests to a path below root are handled by the mux,
   203  		// which has all the handlers registered via Node.RegisterHandler.
   204  		// These are made available when RPC is enabled.
   205  		muxHandler, pattern := h.mux.Handler(r)
   206  		if pattern != "" {
   207  			muxHandler.ServeHTTP(w, r)
   208  			return
   209  		}
   210  
   211  		if checkPath(r, h.httpConfig.prefix) {
   212  			rpc.ServeHTTP(w, r)
   213  			return
   214  		}
   215  	}
   216  	w.WriteHeader(http.StatusNotFound)
   217  }
   218  
   219  // checkPath checks whether a given request URL matches a given path prefix.
   220  func checkPath(r *http.Request, path string) bool {
   221  	// if no prefix has been specified, request URL must be on root
   222  	if path == "" {
   223  		return r.URL.Path == "/"
   224  	}
   225  	// otherwise, check to make sure prefix matches
   226  	return len(r.URL.Path) >= len(path) && r.URL.Path[:len(path)] == path
   227  }
   228  
   229  // validatePrefix checks if 'path' is a valid configuration value for the RPC prefix option.
   230  func validatePrefix(what, path string) error {
   231  	if path == "" {
   232  		return nil
   233  	}
   234  	if path[0] != '/' {
   235  		return fmt.Errorf(`%s RPC path prefix %q does not contain leading "/"`, what, path)
   236  	}
   237  	if strings.ContainsAny(path, "?#") {
   238  		// This is just to avoid confusion. While these would match correctly (i.e. they'd
   239  		// match if URL-escaped into path), it's not easy to understand for users when
   240  		// setting that on the command line.
   241  		return fmt.Errorf("%s RPC path prefix %q contains URL meta-characters", what, path)
   242  	}
   243  	return nil
   244  }
   245  
   246  // stop shuts down the HTTP server.
   247  func (h *httpServer) stop() {
   248  	h.mu.Lock()
   249  	defer h.mu.Unlock()
   250  	h.doStop()
   251  }
   252  
   253  func (h *httpServer) doStop() {
   254  	if h.listener == nil {
   255  		return // not running
   256  	}
   257  
   258  	// Shut down the server.
   259  	httpHandler := h.httpHandler.Load().(*rpcHandler)
   260  	wsHandler := h.wsHandler.Load().(*rpcHandler)
   261  	if httpHandler != nil {
   262  		h.httpHandler.Store((*rpcHandler)(nil))
   263  		httpHandler.server.Stop()
   264  	}
   265  	if wsHandler != nil {
   266  		h.wsHandler.Store((*rpcHandler)(nil))
   267  		wsHandler.server.Stop()
   268  	}
   269  	ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
   270  	defer cancel()
   271  	err := h.server.Shutdown(ctx)
   272  	if err == ctx.Err() {
   273  		h.log.Warn("HTTP server graceful shutdown timed out")
   274  		h.server.Close()
   275  	}
   276  	h.listener.Close()
   277  	h.log.Info("HTTP server stopped", "endpoint", h.listener.Addr())
   278  
   279  	// Clear out everything to allow re-configuring it later.
   280  	h.host, h.port, h.endpoint = "", 0, ""
   281  	h.server, h.listener = nil, nil
   282  }
   283  
   284  // enableRPC turns on JSON-RPC over HTTP on the server.
   285  func (h *httpServer) enableRPC(apis []rpc.API, config httpConfig) error {
   286  	h.mu.Lock()
   287  	defer h.mu.Unlock()
   288  
   289  	if h.rpcAllowed() {
   290  		return fmt.Errorf("JSON-RPC over HTTP is already enabled")
   291  	}
   292  
   293  	// Create RPC server and handler.
   294  	srv := rpc.NewServer()
   295  	if err := RegisterApis(apis, config.Modules, srv); err != nil {
   296  		return err
   297  	}
   298  	h.httpConfig = config
   299  	h.httpHandler.Store(&rpcHandler{
   300  		Handler: NewHTTPHandlerStack(srv, config.CorsAllowedOrigins, config.Vhosts, config.jwtSecret),
   301  		server:  srv,
   302  	})
   303  	return nil
   304  }
   305  
   306  // disableRPC stops the HTTP RPC handler. This is internal, the caller must hold h.mu.
   307  func (h *httpServer) disableRPC() bool {
   308  	handler := h.httpHandler.Load().(*rpcHandler)
   309  	if handler != nil {
   310  		h.httpHandler.Store((*rpcHandler)(nil))
   311  		handler.server.Stop()
   312  	}
   313  	return handler != nil
   314  }
   315  
   316  // enableWS turns on JSON-RPC over WebSocket on the server.
   317  func (h *httpServer) enableWS(apis []rpc.API, config wsConfig) error {
   318  	h.mu.Lock()
   319  	defer h.mu.Unlock()
   320  
   321  	if h.wsAllowed() {
   322  		return fmt.Errorf("JSON-RPC over WebSocket is already enabled")
   323  	}
   324  	// Create RPC server and handler.
   325  	srv := rpc.NewServer()
   326  	if err := RegisterApis(apis, config.Modules, srv); err != nil {
   327  		return err
   328  	}
   329  	h.wsConfig = config
   330  	h.wsHandler.Store(&rpcHandler{
   331  		Handler: NewWSHandlerStack(srv.WebsocketHandler(config.Origins), config.jwtSecret),
   332  		server:  srv,
   333  	})
   334  	return nil
   335  }
   336  
   337  // stopWS disables JSON-RPC over WebSocket and also stops the server if it only serves WebSocket.
   338  func (h *httpServer) stopWS() {
   339  	h.mu.Lock()
   340  	defer h.mu.Unlock()
   341  
   342  	if h.disableWS() {
   343  		if !h.rpcAllowed() {
   344  			h.doStop()
   345  		}
   346  	}
   347  }
   348  
   349  // disableWS disables the WebSocket handler. This is internal, the caller must hold h.mu.
   350  func (h *httpServer) disableWS() bool {
   351  	ws := h.wsHandler.Load().(*rpcHandler)
   352  	if ws != nil {
   353  		h.wsHandler.Store((*rpcHandler)(nil))
   354  		ws.server.Stop()
   355  	}
   356  	return ws != nil
   357  }
   358  
   359  // rpcAllowed returns true when JSON-RPC over HTTP is enabled.
   360  func (h *httpServer) rpcAllowed() bool {
   361  	return h.httpHandler.Load().(*rpcHandler) != nil
   362  }
   363  
   364  // wsAllowed returns true when JSON-RPC over WebSocket is enabled.
   365  func (h *httpServer) wsAllowed() bool {
   366  	return h.wsHandler.Load().(*rpcHandler) != nil
   367  }
   368  
   369  // isWebsocket checks the header of an http request for a websocket upgrade request.
   370  func isWebsocket(r *http.Request) bool {
   371  	return strings.EqualFold(r.Header.Get("Upgrade"), "websocket") &&
   372  		strings.Contains(strings.ToLower(r.Header.Get("Connection")), "upgrade")
   373  }
   374  
   375  // NewHTTPHandlerStack returns wrapped http-related handlers
   376  func NewHTTPHandlerStack(srv http.Handler, cors []string, vhosts []string, jwtSecret []byte) http.Handler {
   377  	// Wrap the CORS-handler within a host-handler
   378  	handler := newCorsHandler(srv, cors)
   379  	handler = newVHostHandler(vhosts, handler)
   380  	if len(jwtSecret) != 0 {
   381  		handler = newJWTHandler(jwtSecret, handler)
   382  	}
   383  	return newGzipHandler(handler)
   384  }
   385  
   386  // NewWSHandlerStack returns a wrapped ws-related handler.
   387  func NewWSHandlerStack(srv http.Handler, jwtSecret []byte) http.Handler {
   388  	if len(jwtSecret) != 0 {
   389  		return newJWTHandler(jwtSecret, srv)
   390  	}
   391  	return srv
   392  }
   393  
   394  func newCorsHandler(srv http.Handler, allowedOrigins []string) http.Handler {
   395  	// disable CORS support if user has not specified a custom CORS configuration
   396  	if len(allowedOrigins) == 0 {
   397  		return srv
   398  	}
   399  	c := cors.New(cors.Options{
   400  		AllowedOrigins: allowedOrigins,
   401  		AllowedMethods: []string{http.MethodPost, http.MethodGet},
   402  		AllowedHeaders: []string{"*"},
   403  		MaxAge:         600,
   404  	})
   405  	return c.Handler(srv)
   406  }
   407  
   408  // virtualHostHandler is a handler which validates the Host-header of incoming requests.
   409  // Using virtual hosts can help prevent DNS rebinding attacks, where a 'random' domain name points to
   410  // the service ip address (but without CORS headers). By verifying the targeted virtual host, we can
   411  // ensure that it's a destination that the node operator has defined.
   412  type virtualHostHandler struct {
   413  	vhosts map[string]struct{}
   414  	next   http.Handler
   415  }
   416  
   417  func newVHostHandler(vhosts []string, next http.Handler) http.Handler {
   418  	vhostMap := make(map[string]struct{})
   419  	for _, allowedHost := range vhosts {
   420  		vhostMap[strings.ToLower(allowedHost)] = struct{}{}
   421  	}
   422  	return &virtualHostHandler{vhostMap, next}
   423  }
   424  
   425  // ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler
   426  func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   427  	// if r.Host is not set, we can continue serving since a browser would set the Host header
   428  	if r.Host == "" {
   429  		h.next.ServeHTTP(w, r)
   430  		return
   431  	}
   432  	host, _, err := net.SplitHostPort(r.Host)
   433  	if err != nil {
   434  		// Either invalid (too many colons) or no port specified
   435  		host = r.Host
   436  	}
   437  	if ipAddr := net.ParseIP(host); ipAddr != nil {
   438  		// It's an IP address, we can serve that
   439  		h.next.ServeHTTP(w, r)
   440  		return
   441  	}
   442  	// Not an IP address, but a hostname. Need to validate
   443  	if _, exist := h.vhosts["*"]; exist {
   444  		h.next.ServeHTTP(w, r)
   445  		return
   446  	}
   447  	if _, exist := h.vhosts[host]; exist {
   448  		h.next.ServeHTTP(w, r)
   449  		return
   450  	}
   451  	http.Error(w, "invalid host specified", http.StatusForbidden)
   452  }
   453  
   454  var gzPool = sync.Pool{
   455  	New: func() interface{} {
   456  		w := gzip.NewWriter(io.Discard)
   457  		return w
   458  	},
   459  }
   460  
   461  type gzipResponseWriter struct {
   462  	io.Writer
   463  	http.ResponseWriter
   464  }
   465  
   466  func (w *gzipResponseWriter) WriteHeader(status int) {
   467  	w.Header().Del("Content-Length")
   468  	w.ResponseWriter.WriteHeader(status)
   469  }
   470  
   471  func (w *gzipResponseWriter) Write(b []byte) (int, error) {
   472  	return w.Writer.Write(b)
   473  }
   474  
   475  func newGzipHandler(next http.Handler) http.Handler {
   476  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   477  		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
   478  			next.ServeHTTP(w, r)
   479  			return
   480  		}
   481  
   482  		w.Header().Set("Content-Encoding", "gzip")
   483  
   484  		gz := gzPool.Get().(*gzip.Writer)
   485  		defer gzPool.Put(gz)
   486  
   487  		gz.Reset(w)
   488  		defer gz.Close()
   489  
   490  		next.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, Writer: gz}, r)
   491  	})
   492  }
   493  
   494  type ipcServer struct {
   495  	log      log.Logger
   496  	endpoint string
   497  
   498  	mu       sync.Mutex
   499  	listener net.Listener
   500  	srv      *rpc.Server
   501  }
   502  
   503  func newIPCServer(log log.Logger, endpoint string) *ipcServer {
   504  	return &ipcServer{log: log, endpoint: endpoint}
   505  }
   506  
   507  // Start starts the httpServer's http.Server
   508  func (is *ipcServer) start(apis []rpc.API) error {
   509  	is.mu.Lock()
   510  	defer is.mu.Unlock()
   511  
   512  	if is.listener != nil {
   513  		return nil // already running
   514  	}
   515  	listener, srv, err := rpc.StartIPCEndpoint(is.endpoint, apis)
   516  	if err != nil {
   517  		is.log.Warn("IPC opening failed", "url", is.endpoint, "error", err)
   518  		return err
   519  	}
   520  	is.log.Info("IPC endpoint opened", "url", is.endpoint)
   521  	is.listener, is.srv = listener, srv
   522  	return nil
   523  }
   524  
   525  func (is *ipcServer) stop() error {
   526  	is.mu.Lock()
   527  	defer is.mu.Unlock()
   528  
   529  	if is.listener == nil {
   530  		return nil // not running
   531  	}
   532  	err := is.listener.Close()
   533  	is.srv.Stop()
   534  	is.listener, is.srv = nil, nil
   535  	is.log.Info("IPC endpoint closed", "url", is.endpoint)
   536  	return err
   537  }
   538  
   539  // RegisterApis checks the given modules' availability, generates an allowlist based on the allowed modules,
   540  // and then registers all of the APIs exposed by the services.
   541  func RegisterApis(apis []rpc.API, modules []string, srv *rpc.Server) error {
   542  	if bad, available := checkModuleAvailability(modules, apis); len(bad) > 0 {
   543  		log.Error("Unavailable modules in HTTP API list", "unavailable", bad, "available", available)
   544  	}
   545  	// Generate the allow list based on the allowed modules
   546  	allowList := make(map[string]bool)
   547  	for _, module := range modules {
   548  		allowList[module] = true
   549  	}
   550  	// Register all the APIs exposed by the services
   551  	for _, api := range apis {
   552  		if allowList[api.Namespace] || len(allowList) == 0 {
   553  			if err := srv.RegisterName(api.Namespace, api.Service); err != nil {
   554  				return err
   555  			}
   556  		}
   557  	}
   558  	return nil
   559  }