github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/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  	"io/ioutil"
    25  	"net"
    26  	"net/http"
    27  	"sort"
    28  	"strings"
    29  	"sync"
    30  	"sync/atomic"
    31  
    32  	"github.com/kisexp/xdchain/log"
    33  	"github.com/kisexp/xdchain/plugin/security"
    34  	"github.com/kisexp/xdchain/rpc"
    35  	"github.com/rs/cors"
    36  )
    37  
    38  // httpConfig is the JSON-RPC/HTTP configuration.
    39  type httpConfig struct {
    40  	Modules            []string
    41  	CorsAllowedOrigins []string
    42  	Vhosts             []string
    43  }
    44  
    45  // wsConfig is the JSON-RPC/Websocket configuration
    46  type wsConfig struct {
    47  	Origins []string
    48  	Modules []string
    49  }
    50  
    51  type rpcHandler struct {
    52  	http.Handler
    53  	server *rpc.Server
    54  }
    55  
    56  type httpServer struct {
    57  	log      log.Logger
    58  	timeouts rpc.HTTPTimeouts
    59  	mux      http.ServeMux // registered handlers go here
    60  
    61  	mu       sync.Mutex
    62  	server   *http.Server
    63  	listener net.Listener // non-nil when server is running
    64  
    65  	// HTTP RPC handler things.
    66  	httpConfig  httpConfig
    67  	httpHandler atomic.Value // *rpcHandler
    68  
    69  	// WebSocket handler things.
    70  	wsConfig  wsConfig
    71  	wsHandler atomic.Value // *rpcHandler
    72  
    73  	// These are set by setListenAddr.
    74  	endpoint string
    75  	host     string
    76  	port     int
    77  
    78  	handlerNames map[string]string
    79  
    80  	// Quorum
    81  	// isMultitenant determines if the server supports mutlitenancy
    82  	isMultitenant bool
    83  }
    84  
    85  func newHTTPServer(log log.Logger, timeouts rpc.HTTPTimeouts) *httpServer {
    86  	h := &httpServer{log: log, timeouts: timeouts, handlerNames: make(map[string]string)}
    87  	h.httpHandler.Store((*rpcHandler)(nil))
    88  	h.wsHandler.Store((*rpcHandler)(nil))
    89  	return h
    90  }
    91  
    92  // Quorum
    93  // withMultitenancy indicates if this server supports multitenancy
    94  func (h *httpServer) withMultitenancy(b bool) *httpServer {
    95  	h.isMultitenant = b
    96  	return h
    97  }
    98  
    99  // setListenAddr configures the listening address of the server.
   100  // The address can only be set while the server isn't running.
   101  func (h *httpServer) setListenAddr(host string, port int) error {
   102  	h.mu.Lock()
   103  	defer h.mu.Unlock()
   104  
   105  	if h.listener != nil && (host != h.host || port != h.port) {
   106  		return fmt.Errorf("HTTP server already running on %s", h.endpoint)
   107  	}
   108  
   109  	h.host, h.port = host, port
   110  	h.endpoint = fmt.Sprintf("%s:%d", host, port)
   111  	return nil
   112  }
   113  
   114  // listenAddr returns the listening address of the server.
   115  func (h *httpServer) listenAddr() string {
   116  	h.mu.Lock()
   117  	defer h.mu.Unlock()
   118  
   119  	if h.listener != nil {
   120  		return h.listener.Addr().String()
   121  	}
   122  	return h.endpoint
   123  }
   124  
   125  // Quorum - add tlsConfigSource used to start a listener with tls
   126  // start starts the HTTP server if it is enabled and not already running.
   127  func (h *httpServer) start(tlsConfigSource security.TLSConfigurationSource) error {
   128  	h.mu.Lock()
   129  	defer h.mu.Unlock()
   130  
   131  	if h.endpoint == "" || h.listener != nil {
   132  		return nil // already running or not configured
   133  	}
   134  
   135  	// Initialize the server.
   136  	h.server = &http.Server{Handler: h}
   137  	if h.timeouts != (rpc.HTTPTimeouts{}) {
   138  		CheckTimeouts(&h.timeouts)
   139  		h.server.ReadTimeout = h.timeouts.ReadTimeout
   140  		h.server.WriteTimeout = h.timeouts.WriteTimeout
   141  		h.server.IdleTimeout = h.timeouts.IdleTimeout
   142  	}
   143  
   144  	// Start the server.
   145  	isTls, listener, err := startListener(h.endpoint, tlsConfigSource)
   146  	if err != nil {
   147  		// If the server fails to start, we need to clear out the RPC and WS
   148  		// configuration so they can be configured another time.
   149  		h.disableRPC()
   150  		h.disableWS()
   151  		return err
   152  	}
   153  	h.listener = listener
   154  	go h.server.Serve(listener)
   155  
   156  	// if server is websocket only, return after logging
   157  	if h.wsAllowed() && !h.rpcAllowed() {
   158  		h.log.Info("WebSocket enabled", "url", fmt.Sprintf("ws://%v", listener.Addr()))
   159  		return nil
   160  	}
   161  	// Log http endpoint.
   162  	h.log.Info("HTTP server started",
   163  		"endpoint", listener.Addr(),
   164  		"cors", strings.Join(h.httpConfig.CorsAllowedOrigins, ","),
   165  		"vhosts", strings.Join(h.httpConfig.Vhosts, ","),
   166  		"isTls", isTls,
   167  	)
   168  
   169  	// Log all handlers mounted on server.
   170  	var paths []string
   171  	for path := range h.handlerNames {
   172  		paths = append(paths, path)
   173  	}
   174  	sort.Strings(paths)
   175  	logged := make(map[string]bool, len(paths))
   176  	for _, path := range paths {
   177  		name := h.handlerNames[path]
   178  		if !logged[name] {
   179  			log.Info(name+" enabled", "url", "http://"+listener.Addr().String()+path)
   180  			logged[name] = true
   181  		}
   182  	}
   183  	return nil
   184  }
   185  
   186  func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   187  	rpc := h.httpHandler.Load().(*rpcHandler)
   188  	// Quorum - replaced r.RequestURI with r.URL.Path in the check below in order to allow URL parameters (for PSI - mps)
   189  	if r.URL.Path == "/" {
   190  		// Serve JSON-RPC on the root path.
   191  		ws := h.wsHandler.Load().(*rpcHandler)
   192  		if ws != nil && isWebsocket(r) {
   193  			ws.ServeHTTP(w, r)
   194  			return
   195  		}
   196  		if rpc != nil {
   197  			rpc.ServeHTTP(w, r)
   198  			return
   199  		}
   200  	} else if rpc != nil {
   201  		// Requests to a path below root are handled by the mux,
   202  		// which has all the handlers registered via Node.RegisterHandler.
   203  		// These are made available when RPC is enabled.
   204  		h.mux.ServeHTTP(w, r)
   205  		return
   206  	}
   207  	w.WriteHeader(404)
   208  }
   209  
   210  // stop shuts down the HTTP server.
   211  func (h *httpServer) stop() {
   212  	h.mu.Lock()
   213  	defer h.mu.Unlock()
   214  	h.doStop()
   215  }
   216  
   217  func (h *httpServer) doStop() {
   218  	if h.listener == nil {
   219  		return // not running
   220  	}
   221  
   222  	// Shut down the server.
   223  	httpHandler := h.httpHandler.Load().(*rpcHandler)
   224  	wsHandler := h.httpHandler.Load().(*rpcHandler)
   225  	if httpHandler != nil {
   226  		h.httpHandler.Store((*rpcHandler)(nil))
   227  		httpHandler.server.Stop()
   228  	}
   229  	if wsHandler != nil {
   230  		h.wsHandler.Store((*rpcHandler)(nil))
   231  		wsHandler.server.Stop()
   232  	}
   233  	h.server.Shutdown(context.Background())
   234  	h.listener.Close()
   235  	h.log.Info("HTTP server stopped", "endpoint", h.listener.Addr())
   236  
   237  	// Clear out everything to allow re-configuring it later.
   238  	h.host, h.port, h.endpoint = "", 0, ""
   239  	h.server, h.listener = nil, nil
   240  }
   241  
   242  // Quorum - added argument `authManager` used to create protected server
   243  // enableRPC turns on JSON-RPC over HTTP on the server.
   244  func (h *httpServer) enableRPC(apis []rpc.API, config httpConfig, authManager security.AuthenticationManager) error {
   245  	h.mu.Lock()
   246  	defer h.mu.Unlock()
   247  
   248  	if h.rpcAllowed() {
   249  		return fmt.Errorf("JSON-RPC over HTTP is already enabled")
   250  	}
   251  
   252  	// Create RPC server and handler.
   253  	srv := rpc.NewProtectedServer(authManager, h.isMultitenant)
   254  	if err := RegisterApisFromWhitelist(apis, config.Modules, srv, false); err != nil {
   255  		return err
   256  	}
   257  	h.httpConfig = config
   258  	h.httpHandler.Store(&rpcHandler{
   259  		Handler: NewHTTPHandlerStack(srv, config.CorsAllowedOrigins, config.Vhosts),
   260  		server:  srv,
   261  	})
   262  	return nil
   263  }
   264  
   265  // disableRPC stops the HTTP RPC handler. This is internal, the caller must hold h.mu.
   266  func (h *httpServer) disableRPC() bool {
   267  	handler := h.httpHandler.Load().(*rpcHandler)
   268  	if handler != nil {
   269  		h.httpHandler.Store((*rpcHandler)(nil))
   270  		handler.server.Stop()
   271  	}
   272  	return handler != nil
   273  }
   274  
   275  // Quorum - added argument `authManager` used to create protected server
   276  // enableWS turns on JSON-RPC over WebSocket on the server.
   277  func (h *httpServer) enableWS(apis []rpc.API, config wsConfig, authManager security.AuthenticationManager) error {
   278  	h.mu.Lock()
   279  	defer h.mu.Unlock()
   280  
   281  	if h.wsAllowed() {
   282  		return fmt.Errorf("JSON-RPC over WebSocket is already enabled")
   283  	}
   284  
   285  	// Create RPC server and handler.
   286  	srv := rpc.NewProtectedServer(authManager, h.isMultitenant)
   287  	if err := RegisterApisFromWhitelist(apis, config.Modules, srv, false); err != nil {
   288  		return err
   289  	}
   290  	h.wsConfig = config
   291  	h.wsHandler.Store(&rpcHandler{
   292  		Handler: srv.WebsocketHandler(config.Origins),
   293  		server:  srv,
   294  	})
   295  	return nil
   296  }
   297  
   298  // stopWS disables JSON-RPC over WebSocket and also stops the server if it only serves WebSocket.
   299  func (h *httpServer) stopWS() {
   300  	h.mu.Lock()
   301  	defer h.mu.Unlock()
   302  
   303  	if h.disableWS() {
   304  		if !h.rpcAllowed() {
   305  			h.doStop()
   306  		}
   307  	}
   308  }
   309  
   310  // disableWS disables the WebSocket handler. This is internal, the caller must hold h.mu.
   311  func (h *httpServer) disableWS() bool {
   312  	ws := h.wsHandler.Load().(*rpcHandler)
   313  	if ws != nil {
   314  		h.wsHandler.Store((*rpcHandler)(nil))
   315  		ws.server.Stop()
   316  	}
   317  	return ws != nil
   318  }
   319  
   320  // rpcAllowed returns true when JSON-RPC over HTTP is enabled.
   321  func (h *httpServer) rpcAllowed() bool {
   322  	return h.httpHandler.Load().(*rpcHandler) != nil
   323  }
   324  
   325  // wsAllowed returns true when JSON-RPC over WebSocket is enabled.
   326  func (h *httpServer) wsAllowed() bool {
   327  	return h.wsHandler.Load().(*rpcHandler) != nil
   328  }
   329  
   330  // isWebsocket checks the header of an http request for a websocket upgrade request.
   331  func isWebsocket(r *http.Request) bool {
   332  	return strings.ToLower(r.Header.Get("Upgrade")) == "websocket" &&
   333  		strings.Contains(strings.ToLower(r.Header.Get("Connection")), "upgrade")
   334  }
   335  
   336  // NewHTTPHandlerStack returns wrapped http-related handlers
   337  func NewHTTPHandlerStack(srv http.Handler, cors []string, vhosts []string) http.Handler {
   338  	// Wrap the CORS-handler within a host-handler
   339  	handler := newCorsHandler(srv, cors)
   340  	handler = newVHostHandler(vhosts, handler)
   341  	return newGzipHandler(handler)
   342  }
   343  
   344  func newCorsHandler(srv http.Handler, allowedOrigins []string) http.Handler {
   345  	// disable CORS support if user has not specified a custom CORS configuration
   346  	if len(allowedOrigins) == 0 {
   347  		return srv
   348  	}
   349  	c := cors.New(cors.Options{
   350  		AllowedOrigins: allowedOrigins,
   351  		AllowedMethods: []string{http.MethodPost, http.MethodGet},
   352  		AllowedHeaders: []string{"*"},
   353  		MaxAge:         600,
   354  	})
   355  	return c.Handler(srv)
   356  }
   357  
   358  // virtualHostHandler is a handler which validates the Host-header of incoming requests.
   359  // Using virtual hosts can help prevent DNS rebinding attacks, where a 'random' domain name points to
   360  // the service ip address (but without CORS headers). By verifying the targeted virtual host, we can
   361  // ensure that it's a destination that the node operator has defined.
   362  type virtualHostHandler struct {
   363  	vhosts map[string]struct{}
   364  	next   http.Handler
   365  }
   366  
   367  func newVHostHandler(vhosts []string, next http.Handler) http.Handler {
   368  	vhostMap := make(map[string]struct{})
   369  	for _, allowedHost := range vhosts {
   370  		vhostMap[strings.ToLower(allowedHost)] = struct{}{}
   371  	}
   372  	return &virtualHostHandler{vhostMap, next}
   373  }
   374  
   375  // ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler
   376  func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   377  	// if r.Host is not set, we can continue serving since a browser would set the Host header
   378  	if r.Host == "" {
   379  		h.next.ServeHTTP(w, r)
   380  		return
   381  	}
   382  	host, _, err := net.SplitHostPort(r.Host)
   383  	if err != nil {
   384  		// Either invalid (too many colons) or no port specified
   385  		host = r.Host
   386  	}
   387  	if ipAddr := net.ParseIP(host); ipAddr != nil {
   388  		// It's an IP address, we can serve that
   389  		h.next.ServeHTTP(w, r)
   390  		return
   391  
   392  	}
   393  	// Not an IP address, but a hostname. Need to validate
   394  	if _, exist := h.vhosts["*"]; exist {
   395  		h.next.ServeHTTP(w, r)
   396  		return
   397  	}
   398  	if _, exist := h.vhosts[host]; exist {
   399  		h.next.ServeHTTP(w, r)
   400  		return
   401  	}
   402  	http.Error(w, "invalid host specified", http.StatusForbidden)
   403  }
   404  
   405  var gzPool = sync.Pool{
   406  	New: func() interface{} {
   407  		w := gzip.NewWriter(ioutil.Discard)
   408  		return w
   409  	},
   410  }
   411  
   412  type gzipResponseWriter struct {
   413  	io.Writer
   414  	http.ResponseWriter
   415  }
   416  
   417  func (w *gzipResponseWriter) WriteHeader(status int) {
   418  	w.Header().Del("Content-Length")
   419  	w.ResponseWriter.WriteHeader(status)
   420  }
   421  
   422  func (w *gzipResponseWriter) Write(b []byte) (int, error) {
   423  	return w.Writer.Write(b)
   424  }
   425  
   426  func newGzipHandler(next http.Handler) http.Handler {
   427  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   428  		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
   429  			next.ServeHTTP(w, r)
   430  			return
   431  		}
   432  
   433  		w.Header().Set("Content-Encoding", "gzip")
   434  
   435  		gz := gzPool.Get().(*gzip.Writer)
   436  		defer gzPool.Put(gz)
   437  
   438  		gz.Reset(w)
   439  		defer gz.Close()
   440  
   441  		next.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, Writer: gz}, r)
   442  	})
   443  }
   444  
   445  type ipcServer struct {
   446  	log      log.Logger
   447  	endpoint string
   448  
   449  	mu       sync.Mutex
   450  	listener net.Listener
   451  	srv      *rpc.Server
   452  
   453  	// Quorum
   454  	// isMultitenant determines if the server supports mutlitenancy
   455  	isMultitenant bool
   456  }
   457  
   458  func newIPCServer(log log.Logger, endpoint string) *ipcServer {
   459  	return &ipcServer{log: log, endpoint: endpoint, isMultitenant: false}
   460  }
   461  
   462  // Quorum
   463  // withMultitenancy indicates if this server supports multitenancy
   464  func (is *ipcServer) withMultitenancy(b bool) *ipcServer {
   465  	is.isMultitenant = b
   466  	return is
   467  }
   468  
   469  // Start starts the httpServer's http.Server
   470  func (is *ipcServer) start(apis []rpc.API) error {
   471  	is.mu.Lock()
   472  	defer is.mu.Unlock()
   473  
   474  	if is.listener != nil {
   475  		return nil // already running
   476  	}
   477  	listener, srv, err := rpc.StartIPCEndpoint(is.endpoint, apis)
   478  	if err != nil {
   479  		is.log.Warn("IPC opening failed", "url", is.endpoint, "error", err)
   480  		return err
   481  	}
   482  	srv.EnableMultitenancy(is.isMultitenant)
   483  	is.log.Info("IPC endpoint opened", "url", is.endpoint, "isMultitenant", is.isMultitenant)
   484  	is.listener, is.srv = listener, srv
   485  	return nil
   486  }
   487  
   488  func (is *ipcServer) stop() error {
   489  	is.mu.Lock()
   490  	defer is.mu.Unlock()
   491  
   492  	if is.listener == nil {
   493  		return nil // not running
   494  	}
   495  	err := is.listener.Close()
   496  	is.srv.Stop()
   497  	is.listener, is.srv = nil, nil
   498  	is.log.Info("IPC endpoint closed", "url", is.endpoint)
   499  	return err
   500  }
   501  
   502  // RegisterApisFromWhitelist checks the given modules' availability, generates a whitelist based on the allowed modules,
   503  // and then registers all of the APIs exposed by the services.
   504  func RegisterApisFromWhitelist(apis []rpc.API, modules []string, srv *rpc.Server, exposeAll bool) error {
   505  	if bad, available := checkModuleAvailability(modules, apis); len(bad) > 0 {
   506  		log.Error("Unavailable modules in HTTP API list", "unavailable", bad, "available", available)
   507  	}
   508  	// Generate the whitelist based on the allowed modules
   509  	whitelist := make(map[string]bool)
   510  	for _, module := range modules {
   511  		whitelist[module] = true
   512  	}
   513  	// Register all the APIs exposed by the services
   514  	for _, api := range apis {
   515  		if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) {
   516  			if err := srv.RegisterName(api.Namespace, api.Service); err != nil {
   517  				return err
   518  			}
   519  		}
   520  	}
   521  	return nil
   522  }