提交 00158c4b 作者: Steven Allen

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: 's avatarSteven Allen <steven@stebalien.com>
上级 8bcf6f48
...@@ -5,6 +5,7 @@ high-level HTTP interfaces to IPFS. ...@@ -5,6 +5,7 @@ high-level HTTP interfaces to IPFS.
package corehttp package corehttp
import ( import (
"context"
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
...@@ -12,6 +13,7 @@ import ( ...@@ -12,6 +13,7 @@ import (
core "github.com/ipfs/go-ipfs/core" core "github.com/ipfs/go-ipfs/core"
"gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess"
periodicproc "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/periodic"
manet "gx/ipfs/QmV6FjemM1K8oXjrvuq3wuVWWoU2TLDPmNnKrxHzY3v6Ai/go-multiaddr-net" manet "gx/ipfs/QmV6FjemM1K8oXjrvuq3wuVWWoU2TLDPmNnKrxHzY3v6Ai/go-multiaddr-net"
ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr"
logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log"
...@@ -19,6 +21,10 @@ import ( ...@@ -19,6 +21,10 @@ import (
var log = logging.Logger("core/server") var log = logging.Logger("core/server")
// shutdownTimeout is the timeout after which we'll stop waiting for hung
// commands to return on shutdown.
const shutdownTimeout = 30 * time.Second
// ServeOption registers any HTTP handlers it provides on the given mux. // ServeOption registers any HTTP handlers it provides on the given mux.
// It returns the mux to expose to future options, which may be a new mux if it // It returns the mux to expose to future options, which may be a new mux if it
// is interested in mediating requests to future options, or the same mux // 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 ...@@ -65,6 +71,9 @@ func ListenAndServe(n *core.IpfsNode, listeningMultiAddr string, options ...Serv
} }
func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error { func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error {
// make sure we close this no matter what.
defer lis.Close()
handler, err := makeHandler(node, lis, options...) handler, err := makeHandler(node, lis, options...)
if err != nil { if err != nil {
return err return err
...@@ -75,43 +84,44 @@ func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error ...@@ -75,43 +84,44 @@ func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error
return err return err
} }
// if the server exits beforehand
var serverError error
serverExited := make(chan struct{})
select { select {
case <-node.Process().Closing(): case <-node.Process().Closing():
return fmt.Errorf("failed to start server, process closing") return fmt.Errorf("failed to start server, process closing")
default: default:
} }
node.Process().Go(func(p goprocess.Process) { server := &http.Server{
serverError = http.Serve(lis, handler) Handler: handler,
close(serverExited) }
var serverError error
serverProc := node.Process().Go(func(p goprocess.Process) {
serverError = server.Serve(lis)
}) })
// wait for server to exit. // wait for server to exit.
select { select {
case <-serverExited: case <-serverProc.Closed():
// if node being closed before server exits, close server // if node being closed before server exits, close server
case <-node.Process().Closing(): case <-node.Process().Closing():
log.Infof("server at %s terminating...", addr) log.Infof("server at %s terminating...", addr)
lis.Close() warnProc := periodicproc.Tick(5*time.Second, func(_ goprocess.Process) {
outer:
for {
// wait until server exits
select {
case <-serverExited:
// if the server exited as we are closing, we really dont care about errors
serverError = nil
break outer
case <-time.After(5 * time.Second):
log.Infof("waiting for server at %s to terminate...", addr) log.Infof("waiting for server at %s to terminate...", addr)
} })
}
// This timeout shouldn't be necessary if all of our commands
// are obeying their contexts but we should have *some* timeout.
ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
defer cancel()
err := server.Shutdown(ctx)
// Should have already closed but we still need to wait for it
// to set the error.
<-serverProc.Closed()
serverError = err
warnProc.Close()
} }
log.Infof("server at %s terminated", addr) log.Infof("server at %s terminated", addr)
......
...@@ -13,7 +13,7 @@ test_init_ipfs ...@@ -13,7 +13,7 @@ test_init_ipfs
test_launch_ipfs_daemon test_launch_ipfs_daemon
test_expect_success "shutdown succeeds" ' test_expect_success "shutdown succeeds" '
ipfs shutdown || true # bug: https://github.com/ipfs/go-ipfs/issues/4055 ipfs shutdown
' '
test_expect_success "daemon no longer running" ' test_expect_success "daemon no longer running" '
...@@ -27,7 +27,7 @@ test_expect_success "daemon no longer running" ' ...@@ -27,7 +27,7 @@ test_expect_success "daemon no longer running" '
test_launch_ipfs_daemon --offline test_launch_ipfs_daemon --offline
test_expect_success "shutdown succeeds" ' test_expect_success "shutdown succeeds" '
ipfs shutdown || true # bug: https://github.com/ipfs/go-ipfs/issues/4055 ipfs shutdown
' '
test_expect_success "daemon no longer running" ' test_expect_success "daemon no longer running" '
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论