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 }