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