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