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