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