github.com/bcnmy/go-ethereum@v1.10.27/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.ReadHeaderTimeout = h.timeouts.ReadHeaderTimeout
   138  		h.server.WriteTimeout = h.timeouts.WriteTimeout
   139  		h.server.IdleTimeout = h.timeouts.IdleTimeout
   140  	}
   141  
   142  	// Start the server.
   143  	listener, err := net.Listen("tcp", h.endpoint)
   144  	if err != nil {
   145  		// If the server fails to start, we need to clear out the RPC and WS
   146  		// configuration so they can be configured another time.
   147  		h.disableRPC()
   148  		h.disableWS()
   149  		return err
   150  	}
   151  	h.listener = listener
   152  	go h.server.Serve(listener)
   153  
   154  	if h.wsAllowed() {
   155  		url := fmt.Sprintf("ws://%v", listener.Addr())
   156  		if h.wsConfig.prefix != "" {
   157  			url += h.wsConfig.prefix
   158  		}
   159  		h.log.Info("WebSocket enabled", "url", url)
   160  	}
   161  	// if server is websocket only, return after logging
   162  	if !h.rpcAllowed() {
   163  		return nil
   164  	}
   165  	// Log http endpoint.
   166  	h.log.Info("HTTP server started",
   167  		"endpoint", listener.Addr(), "auth", (h.httpConfig.jwtSecret != nil),
   168  		"prefix", h.httpConfig.prefix,
   169  		"cors", strings.Join(h.httpConfig.CorsAllowedOrigins, ","),
   170  		"vhosts", strings.Join(h.httpConfig.Vhosts, ","),
   171  	)
   172  
   173  	// Log all handlers mounted on server.
   174  	var paths []string
   175  	for path := range h.handlerNames {
   176  		paths = append(paths, path)
   177  	}
   178  	sort.Strings(paths)
   179  	logged := make(map[string]bool, len(paths))
   180  	for _, path := range paths {
   181  		name := h.handlerNames[path]
   182  		if !logged[name] {
   183  			log.Info(name+" enabled", "url", "http://"+listener.Addr().String()+path)
   184  			logged[name] = true
   185  		}
   186  	}
   187  	return nil
   188  }
   189  
   190  func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   191  	// check if ws request and serve if ws enabled
   192  	ws := h.wsHandler.Load().(*rpcHandler)
   193  	if ws != nil && isWebsocket(r) {
   194  		if checkPath(r, h.wsConfig.prefix) {
   195  			ws.ServeHTTP(w, r)
   196  		}
   197  		return
   198  	}
   199  	// if http-rpc is enabled, try to serve request
   200  	rpc := h.httpHandler.Load().(*rpcHandler)
   201  	if rpc != nil {
   202  		// First try to route in the mux.
   203  		// Requests to a path below root are handled by the mux,
   204  		// which has all the handlers registered via Node.RegisterHandler.
   205  		// These are made available when RPC is enabled.
   206  		muxHandler, pattern := h.mux.Handler(r)
   207  		if pattern != "" {
   208  			muxHandler.ServeHTTP(w, r)
   209  			return
   210  		}
   211  
   212  		if checkPath(r, h.httpConfig.prefix) {
   213  			rpc.ServeHTTP(w, r)
   214  			return
   215  		}
   216  	}
   217  	w.WriteHeader(http.StatusNotFound)
   218  }
   219  
   220  // checkPath checks whether a given request URL matches a given path prefix.
   221  func checkPath(r *http.Request, path string) bool {
   222  	// if no prefix has been specified, request URL must be on root
   223  	if path == "" {
   224  		return r.URL.Path == "/"
   225  	}
   226  	// otherwise, check to make sure prefix matches
   227  	return len(r.URL.Path) >= len(path) && r.URL.Path[:len(path)] == path
   228  }
   229  
   230  // validatePrefix checks if 'path' is a valid configuration value for the RPC prefix option.
   231  func validatePrefix(what, path string) error {
   232  	if path == "" {
   233  		return nil
   234  	}
   235  	if path[0] != '/' {
   236  		return fmt.Errorf(`%s RPC path prefix %q does not contain leading "/"`, what, path)
   237  	}
   238  	if strings.ContainsAny(path, "?#") {
   239  		// This is just to avoid confusion. While these would match correctly (i.e. they'd
   240  		// match if URL-escaped into path), it's not easy to understand for users when
   241  		// setting that on the command line.
   242  		return fmt.Errorf("%s RPC path prefix %q contains URL meta-characters", what, path)
   243  	}
   244  	return nil
   245  }
   246  
   247  // stop shuts down the HTTP server.
   248  func (h *httpServer) stop() {
   249  	h.mu.Lock()
   250  	defer h.mu.Unlock()
   251  	h.doStop()
   252  }
   253  
   254  func (h *httpServer) doStop() {
   255  	if h.listener == nil {
   256  		return // not running
   257  	}
   258  
   259  	// Shut down the server.
   260  	httpHandler := h.httpHandler.Load().(*rpcHandler)
   261  	wsHandler := h.wsHandler.Load().(*rpcHandler)
   262  	if httpHandler != nil {
   263  		h.httpHandler.Store((*rpcHandler)(nil))
   264  		httpHandler.server.Stop()
   265  	}
   266  	if wsHandler != nil {
   267  		h.wsHandler.Store((*rpcHandler)(nil))
   268  		wsHandler.server.Stop()
   269  	}
   270  	ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
   271  	defer cancel()
   272  	err := h.server.Shutdown(ctx)
   273  	if err == ctx.Err() {
   274  		h.log.Warn("HTTP server graceful shutdown timed out")
   275  		h.server.Close()
   276  	}
   277  	h.listener.Close()
   278  	h.log.Info("HTTP server stopped", "endpoint", h.listener.Addr())
   279  
   280  	// Clear out everything to allow re-configuring it later.
   281  	h.host, h.port, h.endpoint = "", 0, ""
   282  	h.server, h.listener = nil, nil
   283  }
   284  
   285  // enableRPC turns on JSON-RPC over HTTP on the server.
   286  func (h *httpServer) enableRPC(apis []rpc.API, config httpConfig) error {
   287  	h.mu.Lock()
   288  	defer h.mu.Unlock()
   289  
   290  	if h.rpcAllowed() {
   291  		return fmt.Errorf("JSON-RPC over HTTP is already enabled")
   292  	}
   293  
   294  	// Create RPC server and handler.
   295  	srv := rpc.NewServer()
   296  	if err := RegisterApis(apis, config.Modules, srv); err != nil {
   297  		return err
   298  	}
   299  	h.httpConfig = config
   300  	h.httpHandler.Store(&rpcHandler{
   301  		Handler: NewHTTPHandlerStack(srv, config.CorsAllowedOrigins, config.Vhosts, config.jwtSecret),
   302  		server:  srv,
   303  	})
   304  	return nil
   305  }
   306  
   307  // disableRPC stops the HTTP RPC handler. This is internal, the caller must hold h.mu.
   308  func (h *httpServer) disableRPC() bool {
   309  	handler := h.httpHandler.Load().(*rpcHandler)
   310  	if handler != nil {
   311  		h.httpHandler.Store((*rpcHandler)(nil))
   312  		handler.server.Stop()
   313  	}
   314  	return handler != nil
   315  }
   316  
   317  // enableWS turns on JSON-RPC over WebSocket on the server.
   318  func (h *httpServer) enableWS(apis []rpc.API, config wsConfig) error {
   319  	h.mu.Lock()
   320  	defer h.mu.Unlock()
   321  
   322  	if h.wsAllowed() {
   323  		return fmt.Errorf("JSON-RPC over WebSocket is already enabled")
   324  	}
   325  	// Create RPC server and handler.
   326  	srv := rpc.NewServer()
   327  	if err := RegisterApis(apis, config.Modules, srv); err != nil {
   328  		return err
   329  	}
   330  	h.wsConfig = config
   331  	h.wsHandler.Store(&rpcHandler{
   332  		Handler: NewWSHandlerStack(srv.WebsocketHandler(config.Origins), config.jwtSecret),
   333  		server:  srv,
   334  	})
   335  	return nil
   336  }
   337  
   338  // stopWS disables JSON-RPC over WebSocket and also stops the server if it only serves WebSocket.
   339  func (h *httpServer) stopWS() {
   340  	h.mu.Lock()
   341  	defer h.mu.Unlock()
   342  
   343  	if h.disableWS() {
   344  		if !h.rpcAllowed() {
   345  			h.doStop()
   346  		}
   347  	}
   348  }
   349  
   350  // disableWS disables the WebSocket handler. This is internal, the caller must hold h.mu.
   351  func (h *httpServer) disableWS() bool {
   352  	ws := h.wsHandler.Load().(*rpcHandler)
   353  	if ws != nil {
   354  		h.wsHandler.Store((*rpcHandler)(nil))
   355  		ws.server.Stop()
   356  	}
   357  	return ws != nil
   358  }
   359  
   360  // rpcAllowed returns true when JSON-RPC over HTTP is enabled.
   361  func (h *httpServer) rpcAllowed() bool {
   362  	return h.httpHandler.Load().(*rpcHandler) != nil
   363  }
   364  
   365  // wsAllowed returns true when JSON-RPC over WebSocket is enabled.
   366  func (h *httpServer) wsAllowed() bool {
   367  	return h.wsHandler.Load().(*rpcHandler) != nil
   368  }
   369  
   370  // isWebsocket checks the header of an http request for a websocket upgrade request.
   371  func isWebsocket(r *http.Request) bool {
   372  	return strings.EqualFold(r.Header.Get("Upgrade"), "websocket") &&
   373  		strings.Contains(strings.ToLower(r.Header.Get("Connection")), "upgrade")
   374  }
   375  
   376  // NewHTTPHandlerStack returns wrapped http-related handlers
   377  func NewHTTPHandlerStack(srv http.Handler, cors []string, vhosts []string, jwtSecret []byte) http.Handler {
   378  	// Wrap the CORS-handler within a host-handler
   379  	handler := newCorsHandler(srv, cors)
   380  	handler = newVHostHandler(vhosts, handler)
   381  	if len(jwtSecret) != 0 {
   382  		handler = newJWTHandler(jwtSecret, handler)
   383  	}
   384  	return newGzipHandler(handler)
   385  }
   386  
   387  // NewWSHandlerStack returns a wrapped ws-related handler.
   388  func NewWSHandlerStack(srv http.Handler, jwtSecret []byte) http.Handler {
   389  	if len(jwtSecret) != 0 {
   390  		return newJWTHandler(jwtSecret, srv)
   391  	}
   392  	return srv
   393  }
   394  
   395  func newCorsHandler(srv http.Handler, allowedOrigins []string) http.Handler {
   396  	// disable CORS support if user has not specified a custom CORS configuration
   397  	if len(allowedOrigins) == 0 {
   398  		return srv
   399  	}
   400  	c := cors.New(cors.Options{
   401  		AllowedOrigins: allowedOrigins,
   402  		AllowedMethods: []string{http.MethodPost, http.MethodGet},
   403  		AllowedHeaders: []string{"*"},
   404  		MaxAge:         600,
   405  	})
   406  	return c.Handler(srv)
   407  }
   408  
   409  // virtualHostHandler is a handler which validates the Host-header of incoming requests.
   410  // Using virtual hosts can help prevent DNS rebinding attacks, where a 'random' domain name points to
   411  // the service ip address (but without CORS headers). By verifying the targeted virtual host, we can
   412  // ensure that it's a destination that the node operator has defined.
   413  type virtualHostHandler struct {
   414  	vhosts map[string]struct{}
   415  	next   http.Handler
   416  }
   417  
   418  func newVHostHandler(vhosts []string, next http.Handler) http.Handler {
   419  	vhostMap := make(map[string]struct{})
   420  	for _, allowedHost := range vhosts {
   421  		vhostMap[strings.ToLower(allowedHost)] = struct{}{}
   422  	}
   423  	return &virtualHostHandler{vhostMap, next}
   424  }
   425  
   426  // ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler
   427  func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   428  	// if r.Host is not set, we can continue serving since a browser would set the Host header
   429  	if r.Host == "" {
   430  		h.next.ServeHTTP(w, r)
   431  		return
   432  	}
   433  	host, _, err := net.SplitHostPort(r.Host)
   434  	if err != nil {
   435  		// Either invalid (too many colons) or no port specified
   436  		host = r.Host
   437  	}
   438  	if ipAddr := net.ParseIP(host); ipAddr != nil {
   439  		// It's an IP address, we can serve that
   440  		h.next.ServeHTTP(w, r)
   441  		return
   442  	}
   443  	// Not an IP address, but a hostname. Need to validate
   444  	if _, exist := h.vhosts["*"]; exist {
   445  		h.next.ServeHTTP(w, r)
   446  		return
   447  	}
   448  	if _, exist := h.vhosts[host]; exist {
   449  		h.next.ServeHTTP(w, r)
   450  		return
   451  	}
   452  	http.Error(w, "invalid host specified", http.StatusForbidden)
   453  }
   454  
   455  var gzPool = sync.Pool{
   456  	New: func() interface{} {
   457  		w := gzip.NewWriter(io.Discard)
   458  		return w
   459  	},
   460  }
   461  
   462  type gzipResponseWriter struct {
   463  	io.Writer
   464  	http.ResponseWriter
   465  }
   466  
   467  func (w *gzipResponseWriter) WriteHeader(status int) {
   468  	w.Header().Del("Content-Length")
   469  	w.ResponseWriter.WriteHeader(status)
   470  }
   471  
   472  func (w *gzipResponseWriter) Write(b []byte) (int, error) {
   473  	return w.Writer.Write(b)
   474  }
   475  
   476  func newGzipHandler(next http.Handler) http.Handler {
   477  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   478  		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
   479  			next.ServeHTTP(w, r)
   480  			return
   481  		}
   482  
   483  		w.Header().Set("Content-Encoding", "gzip")
   484  
   485  		gz := gzPool.Get().(*gzip.Writer)
   486  		defer gzPool.Put(gz)
   487  
   488  		gz.Reset(w)
   489  		defer gz.Close()
   490  
   491  		next.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, Writer: gz}, r)
   492  	})
   493  }
   494  
   495  type ipcServer struct {
   496  	log      log.Logger
   497  	endpoint string
   498  
   499  	mu       sync.Mutex
   500  	listener net.Listener
   501  	srv      *rpc.Server
   502  }
   503  
   504  func newIPCServer(log log.Logger, endpoint string) *ipcServer {
   505  	return &ipcServer{log: log, endpoint: endpoint}
   506  }
   507  
   508  // Start starts the httpServer's http.Server
   509  func (is *ipcServer) start(apis []rpc.API) error {
   510  	is.mu.Lock()
   511  	defer is.mu.Unlock()
   512  
   513  	if is.listener != nil {
   514  		return nil // already running
   515  	}
   516  	listener, srv, err := rpc.StartIPCEndpoint(is.endpoint, apis)
   517  	if err != nil {
   518  		is.log.Warn("IPC opening failed", "url", is.endpoint, "error", err)
   519  		return err
   520  	}
   521  	is.log.Info("IPC endpoint opened", "url", is.endpoint)
   522  	is.listener, is.srv = listener, srv
   523  	return nil
   524  }
   525  
   526  func (is *ipcServer) stop() error {
   527  	is.mu.Lock()
   528  	defer is.mu.Unlock()
   529  
   530  	if is.listener == nil {
   531  		return nil // not running
   532  	}
   533  	err := is.listener.Close()
   534  	is.srv.Stop()
   535  	is.listener, is.srv = nil, nil
   536  	is.log.Info("IPC endpoint closed", "url", is.endpoint)
   537  	return err
   538  }
   539  
   540  // RegisterApis checks the given modules' availability, generates an allowlist based on the allowed modules,
   541  // and then registers all of the APIs exposed by the services.
   542  func RegisterApis(apis []rpc.API, modules []string, srv *rpc.Server) error {
   543  	if bad, available := checkModuleAvailability(modules, apis); len(bad) > 0 {
   544  		log.Error("Unavailable modules in HTTP API list", "unavailable", bad, "available", available)
   545  	}
   546  	// Generate the allow list based on the allowed modules
   547  	allowList := make(map[string]bool)
   548  	for _, module := range modules {
   549  		allowList[module] = true
   550  	}
   551  	// Register all the APIs exposed by the services
   552  	for _, api := range apis {
   553  		if allowList[api.Namespace] || len(allowList) == 0 {
   554  			if err := srv.RegisterName(api.Namespace, api.Service); err != nil {
   555  				return err
   556  			}
   557  		}
   558  	}
   559  	return nil
   560  }