@cryptotaxi247 / kubo / commits / 00158c4b9

wait for all connections to close before exiting on shutdown.

Using httpServer.Shutdown will: 1. Close the listener (preventing new connections). 2. Close each connection as outstanding requests finish. This prevent us from shutting down before outstanding requests get a chance to respond. fixes #4055 License: MIT Signed-off-by: Steven Allen <steven@stebalien.com>

Steven Allen committed Jul 31, 2018 at 12:45 UTC 00158c4b9240fa62743220f288a8c40fd4e850d6
2 files changed +35 -25
core/corehttp/corehttp.go
+33 -23
@@ -5,6 +5,7 @@ high-level HTTP interfaces to IPFS.
5 package corehttp
6
7 import (
8 + "context"
9 "fmt"
10 "net"
11 "net/http"
@@ -12,6 +13,7 @@ import (
13
14 core "github.com/ipfs/go-ipfs/core"
15 "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess"
16 + periodicproc "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/periodic"
17 manet "gx/ipfs/QmV6FjemM1K8oXjrvuq3wuVWWoU2TLDPmNnKrxHzY3v6Ai/go-multiaddr-net"
18 ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr"
19 logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log"
@@ -19,6 +21,10 @@ import (
21
22 var log = logging.Logger("core/server")
23
24 +// shutdownTimeout is the timeout after which we'll stop waiting for hung
25 +// commands to return on shutdown.
26 +const shutdownTimeout = 30 * time.Second
27 +
28 // ServeOption registers any HTTP handlers it provides on the given mux.
29 // It returns the mux to expose to future options, which may be a new mux if it
30 // is interested in mediating requests to future options, or the same mux
@@ -65,6 +71,9 @@ func ListenAndServe(n *core.IpfsNode, listeningMultiAddr string, options ...Serv
71 }
72
73 func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error {
74 + // make sure we close this no matter what.
75 + defer lis.Close()
76 +
77 handler, err := makeHandler(node, lis, options...)
78 if err != nil {
79 return err
@@ -75,43 +84,44 @@ func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error
84 return err
85 }
86
78 - // if the server exits beforehand
79 - var serverError error
80 - serverExited := make(chan struct{})
81 -
87 select {
88 case <-node.Process().Closing():
89 return fmt.Errorf("failed to start server, process closing")
90 default:
91 }
92
88 - node.Process().Go(func(p goprocess.Process) {
89 - serverError = http.Serve(lis, handler)
90 - close(serverExited)
93 + server := &http.Server{
94 + Handler: handler,
95 + }
96 +
97 + var serverError error
98 + serverProc := node.Process().Go(func(p goprocess.Process) {
99 + serverError = server.Serve(lis)
100 })
101
102 // wait for server to exit.
103 select {
95 - case <-serverExited:
96 -
104 + case <-serverProc.Closed():
105 // if node being closed before server exits, close server
106 case <-node.Process().Closing():
107 log.Infof("server at %s terminating...", addr)
108
101 - lis.Close()
102 -
103 - outer:
104 - for {
105 - // wait until server exits
106 - select {
107 - case <-serverExited:
108 - // if the server exited as we are closing, we really dont care about errors
109 - serverError = nil
110 - break outer
111 - case <-time.After(5 * time.Second):
112 - log.Infof("waiting for server at %s to terminate...", addr)
113 - }
114 - }
109 + warnProc := periodicproc.Tick(5*time.Second, func(_ goprocess.Process) {
110 + log.Infof("waiting for server at %s to terminate...", addr)
111 + })
112 +
113 + // This timeout shouldn't be necessary if all of our commands
114 + // are obeying their contexts but we should have *some* timeout.
115 + ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
116 + defer cancel()
117 + err := server.Shutdown(ctx)
118 +
119 + // Should have already closed but we still need to wait for it
120 + // to set the error.
121 + <-serverProc.Closed()
122 + serverError = err
123 +
124 + warnProc.Close()
125 }
126
127 log.Infof("server at %s terminated", addr)
test/sharness/t0023-shutdown.sh
+2 -2
@@ -13,7 +13,7 @@ test_init_ipfs
13 test_launch_ipfs_daemon
14
15 test_expect_success "shutdown succeeds" '
16 - ipfs shutdown || true # bug: https://github.com/ipfs/go-ipfs/issues/4055
16 + ipfs shutdown
17 '
18
19 test_expect_success "daemon no longer running" '
@@ -27,7 +27,7 @@ test_expect_success "daemon no longer running" '
27 test_launch_ipfs_daemon --offline
28
29 test_expect_success "shutdown succeeds" '
30 - ipfs shutdown || true # bug: https://github.com/ipfs/go-ipfs/issues/4055
30 + ipfs shutdown
31 '
32
33 test_expect_success "daemon no longer running" '