mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-22 19:07:48 +08:00
Once the server is asked to shut down, we stop accepting new connections, but the 'manners' graceful shutdown will wait for all existing connections closed to close before finishing. For keep-alive connections this will never happen unless the client detects that the server is shutting down through the ipfs API itself, and closes the connection in response. This is a problem e.g. with the webui's connections visualization, which polls the swarm/peers endpoint once a second, and never detects that the API server was shut down. We can mitigate this by telling the server to disable keep-alive, which will add a 'Connection: close' header to the next HTTP response on the connection. A well behaving client should then treat that correspondingly by closing the connection. Unfortunately this doesn't happen immediately in all cases, presumably depending on the keep-alive timeout of the browser that set up the connection, but it's at least a step in the right direction.
103 lines
2.8 KiB
Go
103 lines
2.8 KiB
Go
package corehttp
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
manners "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/braintree/manners"
|
|
ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
|
|
manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
|
|
core "github.com/ipfs/go-ipfs/core"
|
|
eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
|
|
)
|
|
|
|
var log = eventlog.Logger("core/server")
|
|
|
|
// 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
|
|
// is interested in mediating requests to future options, or the same mux
|
|
// initially passed in if not.
|
|
type ServeOption func(*core.IpfsNode, *http.ServeMux) (*http.ServeMux, error)
|
|
|
|
// makeHandler turns a list of ServeOptions into a http.Handler that implements
|
|
// all of the given options, in order.
|
|
func makeHandler(n *core.IpfsNode, options ...ServeOption) (http.Handler, error) {
|
|
topMux := http.NewServeMux()
|
|
mux := topMux
|
|
for _, option := range options {
|
|
var err error
|
|
mux, err = option(n, mux)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return topMux, nil
|
|
}
|
|
|
|
// ListenAndServe runs an HTTP server listening at |listeningMultiAddr| with
|
|
// the given serve options. The address must be provided in multiaddr format.
|
|
//
|
|
// TODO intelligently parse address strings in other formats so long as they
|
|
// unambiguously map to a valid multiaddr. e.g. for convenience, ":8080" should
|
|
// map to "/ip4/0.0.0.0/tcp/8080".
|
|
func ListenAndServe(n *core.IpfsNode, listeningMultiAddr string, options ...ServeOption) error {
|
|
addr, err := ma.NewMultiaddr(listeningMultiAddr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
handler, err := makeHandler(n, options...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return listenAndServe(n, addr, handler)
|
|
}
|
|
|
|
func listenAndServe(node *core.IpfsNode, addr ma.Multiaddr, handler http.Handler) error {
|
|
_, host, err := manet.DialArgs(addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
server := manners.NewServer()
|
|
|
|
// if the server exits beforehand
|
|
var serverError error
|
|
serverExited := make(chan struct{})
|
|
|
|
node.Children().Add(1)
|
|
defer node.Children().Done()
|
|
|
|
go func() {
|
|
serverError = server.ListenAndServe(host, handler)
|
|
close(serverExited)
|
|
}()
|
|
|
|
// wait for server to exit.
|
|
select {
|
|
case <-serverExited:
|
|
|
|
// if node being closed before server exits, close server
|
|
case <-node.Closing():
|
|
log.Infof("server at %s terminating...", addr)
|
|
|
|
// make sure keep-alive connections do not keep the server running
|
|
server.InnerServer.SetKeepAlivesEnabled(false)
|
|
|
|
server.Shutdown <- true
|
|
|
|
outer:
|
|
for {
|
|
// wait until server exits
|
|
select {
|
|
case <-serverExited:
|
|
break outer
|
|
case <-time.After(5 * time.Second):
|
|
log.Infof("waiting for server at %s to terminate...", addr)
|
|
}
|
|
}
|
|
}
|
|
|
|
log.Infof("server at %s terminated", addr)
|
|
return serverError
|
|
}
|