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