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