Skip to content

Commit 40283d0

Browse files
authored
node: shut down all node-related HTTP servers gracefully (#20956)
Rather than just closing the underlying network listener to stop our HTTP servers, use the graceful shutdown procedure, waiting for any in-process requests to finish.
1 parent a070e23 commit 40283d0

File tree

4 files changed

+48
-42
lines changed

4 files changed

+48
-42
lines changed

cmd/clef/main.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -592,15 +592,16 @@ func signer(c *cli.Context) error {
592592

593593
// start http server
594594
httpEndpoint := fmt.Sprintf("%s:%d", c.GlobalString(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name))
595-
listener, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler)
595+
httpServer, addr, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler)
596596
if err != nil {
597597
utils.Fatalf("Could not start RPC api: %v", err)
598598
}
599-
extapiURL = fmt.Sprintf("http://%v/", listener.Addr())
599+
extapiURL = fmt.Sprintf("http://%v/", addr)
600600
log.Info("HTTP endpoint opened", "url", extapiURL)
601601

602602
defer func() {
603-
listener.Close()
603+
// Don't bother imposing a timeout here.
604+
httpServer.Shutdown(context.Background())
604605
log.Info("HTTP endpoint closed", "url", extapiURL)
605606
}()
606607
}

cmd/geth/retesteth.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -905,15 +905,16 @@ func retesteth(ctx *cli.Context) error {
905905
IdleTimeout: 120 * time.Second,
906906
}
907907
httpEndpoint := fmt.Sprintf("%s:%d", ctx.GlobalString(utils.RPCListenAddrFlag.Name), ctx.Int(rpcPortFlag.Name))
908-
listener, err := node.StartHTTPEndpoint(httpEndpoint, RetestethHTTPTimeouts, handler)
908+
httpServer, _, err := node.StartHTTPEndpoint(httpEndpoint, RetestethHTTPTimeouts, handler)
909909
if err != nil {
910910
utils.Fatalf("Could not start RPC api: %v", err)
911911
}
912912
extapiURL = fmt.Sprintf("http://%s", httpEndpoint)
913913
log.Info("HTTP endpoint opened", "url", extapiURL)
914914

915915
defer func() {
916-
listener.Close()
916+
// Don't bother imposing a timeout here.
917+
httpServer.Shutdown(context.Background())
917918
log.Info("HTTP endpoint closed", "url", httpEndpoint)
918919
}()
919920

node/endpoints.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ import (
2626
)
2727

2828
// StartHTTPEndpoint starts the HTTP RPC endpoint.
29-
func StartHTTPEndpoint(endpoint string, timeouts rpc.HTTPTimeouts, handler http.Handler) (net.Listener, error) {
29+
func StartHTTPEndpoint(endpoint string, timeouts rpc.HTTPTimeouts, handler http.Handler) (*http.Server, net.Addr, error) {
3030
// start the HTTP listener
3131
var (
3232
listener net.Listener
3333
err error
3434
)
3535
if listener, err = net.Listen("tcp", endpoint); err != nil {
36-
return nil, err
36+
return nil, nil, err
3737
}
3838
// make sure timeout values are meaningful
3939
CheckTimeouts(&timeouts)
@@ -45,22 +45,22 @@ func StartHTTPEndpoint(endpoint string, timeouts rpc.HTTPTimeouts, handler http.
4545
IdleTimeout: timeouts.IdleTimeout,
4646
}
4747
go httpSrv.Serve(listener)
48-
return listener, err
48+
return httpSrv, listener.Addr(), err
4949
}
5050

5151
// startWSEndpoint starts a websocket endpoint.
52-
func startWSEndpoint(endpoint string, handler http.Handler) (net.Listener, error) {
52+
func startWSEndpoint(endpoint string, handler http.Handler) (*http.Server, net.Addr, error) {
5353
// start the HTTP listener
5454
var (
5555
listener net.Listener
5656
err error
5757
)
5858
if listener, err = net.Listen("tcp", endpoint); err != nil {
59-
return nil, err
59+
return nil, nil, err
6060
}
6161
wsSrv := &http.Server{Handler: handler}
6262
go wsSrv.Serve(listener)
63-
return listener, err
63+
return wsSrv, listener.Addr(), err
6464
}
6565

6666
// checkModuleAvailability checks that all names given in modules are actually

node/node.go

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
package node
1818

1919
import (
20+
"context"
2021
"errors"
2122
"fmt"
2223
"net"
24+
"net/http"
2325
"os"
2426
"path/filepath"
2527
"reflect"
@@ -59,14 +61,16 @@ type Node struct {
5961
ipcListener net.Listener // IPC RPC listener socket to serve API requests
6062
ipcHandler *rpc.Server // IPC RPC request handler to process the API requests
6163

62-
httpEndpoint string // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled)
63-
httpWhitelist []string // HTTP RPC modules to allow through this endpoint
64-
httpListener net.Listener // HTTP RPC listener socket to server API requests
65-
httpHandler *rpc.Server // HTTP RPC request handler to process the API requests
64+
httpEndpoint string // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled)
65+
httpWhitelist []string // HTTP RPC modules to allow through this endpoint
66+
httpListenerAddr net.Addr // Address of HTTP RPC listener socket serving API requests
67+
httpServer *http.Server // HTTP RPC HTTP server
68+
httpHandler *rpc.Server // HTTP RPC request handler to process the API requests
6669

67-
wsEndpoint string // Websocket endpoint (interface + port) to listen at (empty = websocket disabled)
68-
wsListener net.Listener // Websocket RPC listener socket to server API requests
69-
wsHandler *rpc.Server // Websocket RPC request handler to process the API requests
70+
wsEndpoint string // WebSocket endpoint (interface + port) to listen at (empty = WebSocket disabled)
71+
wsListenerAddr net.Addr // Address of WebSocket RPC listener socket serving API requests
72+
wsHTTPServer *http.Server // WebSocket RPC HTTP server
73+
wsHandler *rpc.Server // WebSocket RPC request handler to process the API requests
7074

7175
stop chan struct{} // Channel to wait for termination notifications
7276
lock sync.RWMutex
@@ -375,43 +379,43 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors
375379
return err
376380
}
377381
handler := NewHTTPHandlerStack(srv, cors, vhosts)
378-
// wrap handler in websocket handler only if websocket port is the same as http rpc
382+
// wrap handler in WebSocket handler only if WebSocket port is the same as http rpc
379383
if n.httpEndpoint == n.wsEndpoint {
380384
handler = NewWebsocketUpgradeHandler(handler, srv.WebsocketHandler(wsOrigins))
381385
}
382-
listener, err := StartHTTPEndpoint(endpoint, timeouts, handler)
386+
httpServer, addr, err := StartHTTPEndpoint(endpoint, timeouts, handler)
383387
if err != nil {
384388
return err
385389
}
386-
n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%v/", listener.Addr()),
390+
n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%v/", addr),
387391
"cors", strings.Join(cors, ","),
388392
"vhosts", strings.Join(vhosts, ","))
389393
if n.httpEndpoint == n.wsEndpoint {
390-
n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%v", listener.Addr()))
394+
n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%v", addr))
391395
}
392396
// All listeners booted successfully
393397
n.httpEndpoint = endpoint
394-
n.httpListener = listener
398+
n.httpListenerAddr = addr
399+
n.httpServer = httpServer
395400
n.httpHandler = srv
396401

397402
return nil
398403
}
399404

400405
// stopHTTP terminates the HTTP RPC endpoint.
401406
func (n *Node) stopHTTP() {
402-
if n.httpListener != nil {
403-
url := fmt.Sprintf("http://%v/", n.httpListener.Addr())
404-
n.httpListener.Close()
405-
n.httpListener = nil
406-
n.log.Info("HTTP endpoint closed", "url", url)
407+
if n.httpServer != nil {
408+
// Don't bother imposing a timeout here.
409+
n.httpServer.Shutdown(context.Background())
410+
n.log.Info("HTTP endpoint closed", "url", fmt.Sprintf("http://%v/", n.httpListenerAddr))
407411
}
408412
if n.httpHandler != nil {
409413
n.httpHandler.Stop()
410414
n.httpHandler = nil
411415
}
412416
}
413417

414-
// startWS initializes and starts the websocket RPC endpoint.
418+
// startWS initializes and starts the WebSocket RPC endpoint.
415419
func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool) error {
416420
// Short circuit if the WS endpoint isn't being exposed
417421
if endpoint == "" {
@@ -424,26 +428,26 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrig
424428
if err != nil {
425429
return err
426430
}
427-
listener, err := startWSEndpoint(endpoint, handler)
431+
httpServer, addr, err := startWSEndpoint(endpoint, handler)
428432
if err != nil {
429433
return err
430434
}
431-
n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%s", listener.Addr()))
435+
n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%v", addr))
432436
// All listeners booted successfully
433437
n.wsEndpoint = endpoint
434-
n.wsListener = listener
438+
n.wsListenerAddr = addr
439+
n.wsHTTPServer = httpServer
435440
n.wsHandler = srv
436441

437442
return nil
438443
}
439444

440-
// stopWS terminates the websocket RPC endpoint.
445+
// stopWS terminates the WebSocket RPC endpoint.
441446
func (n *Node) stopWS() {
442-
if n.wsListener != nil {
443-
n.wsListener.Close()
444-
n.wsListener = nil
445-
446-
n.log.Info("WebSocket endpoint closed", "url", fmt.Sprintf("ws://%s", n.wsEndpoint))
447+
if n.wsHTTPServer != nil {
448+
// Don't bother imposing a timeout here.
449+
n.wsHTTPServer.Shutdown(context.Background())
450+
n.log.Info("WebSocket endpoint closed", "url", fmt.Sprintf("ws://%v", n.wsListenerAddr))
447451
}
448452
if n.wsHandler != nil {
449453
n.wsHandler.Stop()
@@ -607,8 +611,8 @@ func (n *Node) HTTPEndpoint() string {
607611
n.lock.Lock()
608612
defer n.lock.Unlock()
609613

610-
if n.httpListener != nil {
611-
return n.httpListener.Addr().String()
614+
if n.httpListenerAddr != nil {
615+
return n.httpListenerAddr.String()
612616
}
613617
return n.httpEndpoint
614618
}
@@ -618,8 +622,8 @@ func (n *Node) WSEndpoint() string {
618622
n.lock.Lock()
619623
defer n.lock.Unlock()
620624

621-
if n.wsListener != nil {
622-
return n.wsListener.Addr().String()
625+
if n.wsListenerAddr != nil {
626+
return n.wsListenerAddr.String()
623627
}
624628
return n.wsEndpoint
625629
}

0 commit comments

Comments
 (0)