github.1485827954.workers.dev/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 }