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