github.com/theQRL/go-zond@v0.1.1/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  	"strconv"
    28  	"strings"
    29  	"sync"
    30  	"sync/atomic"
    31  	"time"
    32  
    33  	"github.com/rs/cors"
    34  	"github.com/theQRL/go-zond/log"
    35  	"github.com/theQRL/go-zond/rpc"
    36  )
    37  
    38  // httpConfig is the JSON-RPC/HTTP configuration.
    39  type httpConfig struct {
    40  	Modules            []string
    41  	CorsAllowedOrigins []string
    42  	Vhosts             []string
    43  	prefix             string // path prefix on which to mount http handler
    44  	rpcEndpointConfig
    45  }
    46  
    47  // wsConfig is the JSON-RPC/Websocket configuration
    48  type wsConfig struct {
    49  	Origins []string
    50  	Modules []string
    51  	prefix  string // path prefix on which to mount ws handler
    52  	rpcEndpointConfig
    53  }
    54  
    55  type rpcEndpointConfig struct {
    56  	jwtSecret              []byte // optional JWT secret
    57  	batchItemLimit         int
    58  	batchResponseSizeLimit int
    59  }
    60  
    61  type rpcHandler struct {
    62  	http.Handler
    63  	server *rpc.Server
    64  }
    65  
    66  type httpServer struct {
    67  	log      log.Logger
    68  	timeouts rpc.HTTPTimeouts
    69  	mux      http.ServeMux // registered handlers go here
    70  
    71  	mu       sync.Mutex
    72  	server   *http.Server
    73  	listener net.Listener // non-nil when server is running
    74  
    75  	// HTTP RPC handler things.
    76  
    77  	httpConfig  httpConfig
    78  	httpHandler atomic.Value // *rpcHandler
    79  
    80  	// WebSocket handler things.
    81  	wsConfig  wsConfig
    82  	wsHandler atomic.Value // *rpcHandler
    83  
    84  	// These are set by setListenAddr.
    85  	endpoint string
    86  	host     string
    87  	port     int
    88  
    89  	handlerNames map[string]string
    90  }
    91  
    92  const (
    93  	shutdownTimeout = 5 * time.Second
    94  )
    95  
    96  func newHTTPServer(log log.Logger, timeouts rpc.HTTPTimeouts) *httpServer {
    97  	h := &httpServer{log: log, timeouts: timeouts, handlerNames: make(map[string]string)}
    98  
    99  	h.httpHandler.Store((*rpcHandler)(nil))
   100  	h.wsHandler.Store((*rpcHandler)(nil))
   101  	return h
   102  }
   103  
   104  // setListenAddr configures the listening address of the server.
   105  // The address can only be set while the server isn't running.
   106  func (h *httpServer) setListenAddr(host string, port int) error {
   107  	h.mu.Lock()
   108  	defer h.mu.Unlock()
   109  
   110  	if h.listener != nil && (host != h.host || port != h.port) {
   111  		return fmt.Errorf("HTTP server already running on %s", h.endpoint)
   112  	}
   113  
   114  	h.host, h.port = host, port
   115  	h.endpoint = net.JoinHostPort(host, fmt.Sprintf("%d", port))
   116  	return nil
   117  }
   118  
   119  // listenAddr returns the listening address of the server.
   120  func (h *httpServer) listenAddr() string {
   121  	h.mu.Lock()
   122  	defer h.mu.Unlock()
   123  
   124  	if h.listener != nil {
   125  		return h.listener.Addr().String()
   126  	}
   127  	return h.endpoint
   128  }
   129  
   130  // start starts the HTTP server if it is enabled and not already running.
   131  func (h *httpServer) start() error {
   132  	h.mu.Lock()
   133  	defer h.mu.Unlock()
   134  
   135  	if h.endpoint == "" || h.listener != nil {
   136  		return nil // already running or not configured
   137  	}
   138  
   139  	// Initialize the server.
   140  	h.server = &http.Server{Handler: h}
   141  	if h.timeouts != (rpc.HTTPTimeouts{}) {
   142  		CheckTimeouts(&h.timeouts)
   143  		h.server.ReadTimeout = h.timeouts.ReadTimeout
   144  		h.server.ReadHeaderTimeout = h.timeouts.ReadHeaderTimeout
   145  		h.server.WriteTimeout = h.timeouts.WriteTimeout
   146  		h.server.IdleTimeout = h.timeouts.IdleTimeout
   147  	}
   148  
   149  	// Start the server.
   150  	listener, err := net.Listen("tcp", h.endpoint)
   151  	if err != nil {
   152  		// If the server fails to start, we need to clear out the RPC and WS
   153  		// configuration so they can be configured another time.
   154  		h.disableRPC()
   155  		h.disableWS()
   156  		return err
   157  	}
   158  	h.listener = listener
   159  	go h.server.Serve(listener)
   160  
   161  	if h.wsAllowed() {
   162  		url := fmt.Sprintf("ws://%v", listener.Addr())
   163  		if h.wsConfig.prefix != "" {
   164  			url += h.wsConfig.prefix
   165  		}
   166  		h.log.Info("WebSocket enabled", "url", url)
   167  	}
   168  	// if server is websocket only, return after logging
   169  	if !h.rpcAllowed() {
   170  		return nil
   171  	}
   172  	// Log http endpoint.
   173  	h.log.Info("HTTP server started",
   174  		"endpoint", listener.Addr(), "auth", (h.httpConfig.jwtSecret != nil),
   175  		"prefix", h.httpConfig.prefix,
   176  		"cors", strings.Join(h.httpConfig.CorsAllowedOrigins, ","),
   177  		"vhosts", strings.Join(h.httpConfig.Vhosts, ","),
   178  	)
   179  
   180  	// Log all handlers mounted on server.
   181  	var paths []string
   182  	for path := range h.handlerNames {
   183  		paths = append(paths, path)
   184  	}
   185  	sort.Strings(paths)
   186  	logged := make(map[string]bool, len(paths))
   187  	for _, path := range paths {
   188  		name := h.handlerNames[path]
   189  		if !logged[name] {
   190  			log.Info(name+" enabled", "url", "http://"+listener.Addr().String()+path)
   191  			logged[name] = true
   192  		}
   193  	}
   194  	return nil
   195  }
   196  
   197  func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   198  	// check if ws request and serve if ws enabled
   199  	ws := h.wsHandler.Load().(*rpcHandler)
   200  	if ws != nil && isWebsocket(r) {
   201  		if checkPath(r, h.wsConfig.prefix) {
   202  			ws.ServeHTTP(w, r)
   203  		}
   204  		return
   205  	}
   206  
   207  	// if http-rpc is enabled, try to serve request
   208  	rpc := h.httpHandler.Load().(*rpcHandler)
   209  	if rpc != nil {
   210  		// First try to route in the mux.
   211  		// Requests to a path below root are handled by the mux,
   212  		// which has all the handlers registered via Node.RegisterHandler.
   213  		// These are made available when RPC is enabled.
   214  		muxHandler, pattern := h.mux.Handler(r)
   215  		if pattern != "" {
   216  			muxHandler.ServeHTTP(w, r)
   217  			return
   218  		}
   219  
   220  		if checkPath(r, h.httpConfig.prefix) {
   221  			rpc.ServeHTTP(w, r)
   222  			return
   223  		}
   224  	}
   225  	w.WriteHeader(http.StatusNotFound)
   226  }
   227  
   228  // checkPath checks whether a given request URL matches a given path prefix.
   229  func checkPath(r *http.Request, path string) bool {
   230  	// if no prefix has been specified, request URL must be on root
   231  	if path == "" {
   232  		return r.URL.Path == "/"
   233  	}
   234  	// otherwise, check to make sure prefix matches
   235  	return len(r.URL.Path) >= len(path) && r.URL.Path[:len(path)] == path
   236  }
   237  
   238  // validatePrefix checks if 'path' is a valid configuration value for the RPC prefix option.
   239  func validatePrefix(what, path string) error {
   240  	if path == "" {
   241  		return nil
   242  	}
   243  	if path[0] != '/' {
   244  		return fmt.Errorf(`%s RPC path prefix %q does not contain leading "/"`, what, path)
   245  	}
   246  	if strings.ContainsAny(path, "?#") {
   247  		// This is just to avoid confusion. While these would match correctly (i.e. they'd
   248  		// match if URL-escaped into path), it's not easy to understand for users when
   249  		// setting that on the command line.
   250  		return fmt.Errorf("%s RPC path prefix %q contains URL meta-characters", what, path)
   251  	}
   252  	return nil
   253  }
   254  
   255  // stop shuts down the HTTP server.
   256  func (h *httpServer) stop() {
   257  	h.mu.Lock()
   258  	defer h.mu.Unlock()
   259  	h.doStop()
   260  }
   261  
   262  func (h *httpServer) doStop() {
   263  	if h.listener == nil {
   264  		return // not running
   265  	}
   266  
   267  	// Shut down the server.
   268  	httpHandler := h.httpHandler.Load().(*rpcHandler)
   269  	wsHandler := h.wsHandler.Load().(*rpcHandler)
   270  	if httpHandler != nil {
   271  		h.httpHandler.Store((*rpcHandler)(nil))
   272  		httpHandler.server.Stop()
   273  	}
   274  	if wsHandler != nil {
   275  		h.wsHandler.Store((*rpcHandler)(nil))
   276  		wsHandler.server.Stop()
   277  	}
   278  
   279  	ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
   280  	defer cancel()
   281  	err := h.server.Shutdown(ctx)
   282  	if err != nil && err == ctx.Err() {
   283  		h.log.Warn("HTTP server graceful shutdown timed out")
   284  		h.server.Close()
   285  	}
   286  
   287  	h.listener.Close()
   288  	h.log.Info("HTTP server stopped", "endpoint", h.listener.Addr())
   289  
   290  	// Clear out everything to allow re-configuring it later.
   291  	h.host, h.port, h.endpoint = "", 0, ""
   292  	h.server, h.listener = nil, nil
   293  }
   294  
   295  // enableRPC turns on JSON-RPC over HTTP on the server.
   296  func (h *httpServer) enableRPC(apis []rpc.API, config httpConfig) error {
   297  	h.mu.Lock()
   298  	defer h.mu.Unlock()
   299  
   300  	if h.rpcAllowed() {
   301  		return fmt.Errorf("JSON-RPC over HTTP is already enabled")
   302  	}
   303  
   304  	// Create RPC server and handler.
   305  	srv := rpc.NewServer()
   306  	srv.SetBatchLimits(config.batchItemLimit, config.batchResponseSizeLimit)
   307  	if err := RegisterApis(apis, config.Modules, srv); err != nil {
   308  		return err
   309  	}
   310  	h.httpConfig = config
   311  	h.httpHandler.Store(&rpcHandler{
   312  		Handler: NewHTTPHandlerStack(srv, config.CorsAllowedOrigins, config.Vhosts, config.jwtSecret),
   313  		server:  srv,
   314  	})
   315  	return nil
   316  }
   317  
   318  // disableRPC stops the HTTP RPC handler. This is internal, the caller must hold h.mu.
   319  func (h *httpServer) disableRPC() bool {
   320  	handler := h.httpHandler.Load().(*rpcHandler)
   321  	if handler != nil {
   322  		h.httpHandler.Store((*rpcHandler)(nil))
   323  		handler.server.Stop()
   324  	}
   325  	return handler != nil
   326  }
   327  
   328  // enableWS turns on JSON-RPC over WebSocket on the server.
   329  func (h *httpServer) enableWS(apis []rpc.API, config wsConfig) error {
   330  	h.mu.Lock()
   331  	defer h.mu.Unlock()
   332  
   333  	if h.wsAllowed() {
   334  		return fmt.Errorf("JSON-RPC over WebSocket is already enabled")
   335  	}
   336  	// Create RPC server and handler.
   337  	srv := rpc.NewServer()
   338  	srv.SetBatchLimits(config.batchItemLimit, config.batchResponseSizeLimit)
   339  	if err := RegisterApis(apis, config.Modules, srv); err != nil {
   340  		return err
   341  	}
   342  	h.wsConfig = config
   343  	h.wsHandler.Store(&rpcHandler{
   344  		Handler: NewWSHandlerStack(srv.WebsocketHandler(config.Origins), config.jwtSecret),
   345  		server:  srv,
   346  	})
   347  	return nil
   348  }
   349  
   350  // stopWS disables JSON-RPC over WebSocket and also stops the server if it only serves WebSocket.
   351  func (h *httpServer) stopWS() {
   352  	h.mu.Lock()
   353  	defer h.mu.Unlock()
   354  
   355  	if h.disableWS() {
   356  		if !h.rpcAllowed() {
   357  			h.doStop()
   358  		}
   359  	}
   360  }
   361  
   362  // disableWS disables the WebSocket handler. This is internal, the caller must hold h.mu.
   363  func (h *httpServer) disableWS() bool {
   364  	ws := h.wsHandler.Load().(*rpcHandler)
   365  	if ws != nil {
   366  		h.wsHandler.Store((*rpcHandler)(nil))
   367  		ws.server.Stop()
   368  	}
   369  	return ws != nil
   370  }
   371  
   372  // rpcAllowed returns true when JSON-RPC over HTTP is enabled.
   373  func (h *httpServer) rpcAllowed() bool {
   374  	return h.httpHandler.Load().(*rpcHandler) != nil
   375  }
   376  
   377  // wsAllowed returns true when JSON-RPC over WebSocket is enabled.
   378  func (h *httpServer) wsAllowed() bool {
   379  	return h.wsHandler.Load().(*rpcHandler) != nil
   380  }
   381  
   382  // isWebsocket checks the header of an http request for a websocket upgrade request.
   383  func isWebsocket(r *http.Request) bool {
   384  	return strings.EqualFold(r.Header.Get("Upgrade"), "websocket") &&
   385  		strings.Contains(strings.ToLower(r.Header.Get("Connection")), "upgrade")
   386  }
   387  
   388  // NewHTTPHandlerStack returns wrapped http-related handlers
   389  func NewHTTPHandlerStack(srv http.Handler, cors []string, vhosts []string, jwtSecret []byte) http.Handler {
   390  	// Wrap the CORS-handler within a host-handler
   391  	handler := newCorsHandler(srv, cors)
   392  	handler = newVHostHandler(vhosts, handler)
   393  	if len(jwtSecret) != 0 {
   394  		handler = newJWTHandler(jwtSecret, handler)
   395  	}
   396  	return newGzipHandler(handler)
   397  }
   398  
   399  // NewWSHandlerStack returns a wrapped ws-related handler.
   400  func NewWSHandlerStack(srv http.Handler, jwtSecret []byte) http.Handler {
   401  	if len(jwtSecret) != 0 {
   402  		return newJWTHandler(jwtSecret, srv)
   403  	}
   404  	return srv
   405  }
   406  
   407  func newCorsHandler(srv http.Handler, allowedOrigins []string) http.Handler {
   408  	// disable CORS support if user has not specified a custom CORS configuration
   409  	if len(allowedOrigins) == 0 {
   410  		return srv
   411  	}
   412  	c := cors.New(cors.Options{
   413  		AllowedOrigins: allowedOrigins,
   414  		AllowedMethods: []string{http.MethodPost, http.MethodGet},
   415  		AllowedHeaders: []string{"*"},
   416  		MaxAge:         600,
   417  	})
   418  	return c.Handler(srv)
   419  }
   420  
   421  // virtualHostHandler is a handler which validates the Host-header of incoming requests.
   422  // Using virtual hosts can help prevent DNS rebinding attacks, where a 'random' domain name points to
   423  // the service ip address (but without CORS headers). By verifying the targeted virtual host, we can
   424  // ensure that it's a destination that the node operator has defined.
   425  type virtualHostHandler struct {
   426  	vhosts map[string]struct{}
   427  	next   http.Handler
   428  }
   429  
   430  func newVHostHandler(vhosts []string, next http.Handler) http.Handler {
   431  	vhostMap := make(map[string]struct{})
   432  	for _, allowedHost := range vhosts {
   433  		vhostMap[strings.ToLower(allowedHost)] = struct{}{}
   434  	}
   435  	return &virtualHostHandler{vhostMap, next}
   436  }
   437  
   438  // ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler
   439  func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   440  	// if r.Host is not set, we can continue serving since a browser would set the Host header
   441  	if r.Host == "" {
   442  		h.next.ServeHTTP(w, r)
   443  		return
   444  	}
   445  	host, _, err := net.SplitHostPort(r.Host)
   446  	if err != nil {
   447  		// Either invalid (too many colons) or no port specified
   448  		host = r.Host
   449  	}
   450  	if ipAddr := net.ParseIP(host); ipAddr != nil {
   451  		// It's an IP address, we can serve that
   452  		h.next.ServeHTTP(w, r)
   453  		return
   454  	}
   455  	// Not an IP address, but a hostname. Need to validate
   456  	if _, exist := h.vhosts["*"]; exist {
   457  		h.next.ServeHTTP(w, r)
   458  		return
   459  	}
   460  	if _, exist := h.vhosts[host]; exist {
   461  		h.next.ServeHTTP(w, r)
   462  		return
   463  	}
   464  	http.Error(w, "invalid host specified", http.StatusForbidden)
   465  }
   466  
   467  var gzPool = sync.Pool{
   468  	New: func() interface{} {
   469  		w := gzip.NewWriter(io.Discard)
   470  		return w
   471  	},
   472  }
   473  
   474  type gzipResponseWriter struct {
   475  	resp http.ResponseWriter
   476  
   477  	gz            *gzip.Writer
   478  	contentLength uint64 // total length of the uncompressed response
   479  	written       uint64 // amount of written bytes from the uncompressed response
   480  	hasLength     bool   // true if uncompressed response had Content-Length
   481  	inited        bool   // true after init was called for the first time
   482  }
   483  
   484  // init runs just before response headers are written. Among other things, this function
   485  // also decides whether compression will be applied at all.
   486  func (w *gzipResponseWriter) init() {
   487  	if w.inited {
   488  		return
   489  	}
   490  	w.inited = true
   491  
   492  	hdr := w.resp.Header()
   493  	length := hdr.Get("content-length")
   494  	if len(length) > 0 {
   495  		if n, err := strconv.ParseUint(length, 10, 64); err != nil {
   496  			w.hasLength = true
   497  			w.contentLength = n
   498  		}
   499  	}
   500  
   501  	// Setting Transfer-Encoding to "identity" explicitly disables compression. net/http
   502  	// also recognizes this header value and uses it to disable "chunked" transfer
   503  	// encoding, trimming the header from the response. This means downstream handlers can
   504  	// set this without harm, even if they aren't wrapped by newGzipHandler.
   505  	//
   506  	// In go-ethereum, we use this signal to disable compression for certain error
   507  	// responses which are flushed out close to the write deadline of the response. For
   508  	// these cases, we want to avoid chunked transfer encoding and compression because
   509  	// they require additional output that may not get written in time.
   510  	passthrough := hdr.Get("transfer-encoding") == "identity"
   511  	if !passthrough {
   512  		w.gz = gzPool.Get().(*gzip.Writer)
   513  		w.gz.Reset(w.resp)
   514  		hdr.Del("content-length")
   515  		hdr.Set("content-encoding", "gzip")
   516  	}
   517  }
   518  
   519  func (w *gzipResponseWriter) Header() http.Header {
   520  	return w.resp.Header()
   521  }
   522  
   523  func (w *gzipResponseWriter) WriteHeader(status int) {
   524  	w.init()
   525  	w.resp.WriteHeader(status)
   526  }
   527  
   528  func (w *gzipResponseWriter) Write(b []byte) (int, error) {
   529  	w.init()
   530  
   531  	if w.gz == nil {
   532  		// Compression is disabled.
   533  		return w.resp.Write(b)
   534  	}
   535  
   536  	n, err := w.gz.Write(b)
   537  	w.written += uint64(n)
   538  	if w.hasLength && w.written >= w.contentLength {
   539  		// The HTTP handler has finished writing the entire uncompressed response. Close
   540  		// the gzip stream to ensure the footer will be seen by the client in case the
   541  		// response is flushed after this call to write.
   542  		err = w.gz.Close()
   543  	}
   544  	return n, err
   545  }
   546  
   547  func (w *gzipResponseWriter) Flush() {
   548  	if w.gz != nil {
   549  		w.gz.Flush()
   550  	}
   551  	if f, ok := w.resp.(http.Flusher); ok {
   552  		f.Flush()
   553  	}
   554  }
   555  
   556  func (w *gzipResponseWriter) close() {
   557  	if w.gz == nil {
   558  		return
   559  	}
   560  	w.gz.Close()
   561  	gzPool.Put(w.gz)
   562  	w.gz = nil
   563  }
   564  
   565  func newGzipHandler(next http.Handler) http.Handler {
   566  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   567  		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
   568  			next.ServeHTTP(w, r)
   569  			return
   570  		}
   571  
   572  		wrapper := &gzipResponseWriter{resp: w}
   573  		defer wrapper.close()
   574  
   575  		next.ServeHTTP(wrapper, r)
   576  	})
   577  }
   578  
   579  type ipcServer struct {
   580  	log      log.Logger
   581  	endpoint string
   582  
   583  	mu       sync.Mutex
   584  	listener net.Listener
   585  	srv      *rpc.Server
   586  }
   587  
   588  func newIPCServer(log log.Logger, endpoint string) *ipcServer {
   589  	return &ipcServer{log: log, endpoint: endpoint}
   590  }
   591  
   592  // Start starts the httpServer's http.Server
   593  func (is *ipcServer) start(apis []rpc.API) error {
   594  	is.mu.Lock()
   595  	defer is.mu.Unlock()
   596  
   597  	if is.listener != nil {
   598  		return nil // already running
   599  	}
   600  	listener, srv, err := rpc.StartIPCEndpoint(is.endpoint, apis)
   601  	if err != nil {
   602  		is.log.Warn("IPC opening failed", "url", is.endpoint, "error", err)
   603  		return err
   604  	}
   605  	is.log.Info("IPC endpoint opened", "url", is.endpoint)
   606  	is.listener, is.srv = listener, srv
   607  	return nil
   608  }
   609  
   610  func (is *ipcServer) stop() error {
   611  	is.mu.Lock()
   612  	defer is.mu.Unlock()
   613  
   614  	if is.listener == nil {
   615  		return nil // not running
   616  	}
   617  	err := is.listener.Close()
   618  	is.srv.Stop()
   619  	is.listener, is.srv = nil, nil
   620  	is.log.Info("IPC endpoint closed", "url", is.endpoint)
   621  	return err
   622  }
   623  
   624  // RegisterApis checks the given modules' availability, generates an allowlist based on the allowed modules,
   625  // and then registers all of the APIs exposed by the services.
   626  func RegisterApis(apis []rpc.API, modules []string, srv *rpc.Server) error {
   627  	if bad, available := checkModuleAvailability(modules, apis); len(bad) > 0 {
   628  		log.Error("Unavailable modules in HTTP API list", "unavailable", bad, "available", available)
   629  	}
   630  	// Generate the allow list based on the allowed modules
   631  	allowList := make(map[string]bool)
   632  	for _, module := range modules {
   633  		allowList[module] = true
   634  	}
   635  	// Register all the APIs exposed by the services
   636  	for _, api := range apis {
   637  		if allowList[api.Namespace] || len(allowList) == 0 {
   638  			if err := srv.RegisterName(api.Namespace, api.Service); err != nil {
   639  				return err
   640  			}
   641  		}
   642  	}
   643  	return nil
   644  }