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