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