serve http, webdav, restic: ensure rclone exits if the port is in use

This commit is contained in:
Nick Craig-Wood 2018-11-01 17:16:31 +00:00
parent 1f05d5bf4a
commit bb5637d46a
7 changed files with 65 additions and 40 deletions

View file

@ -46,7 +46,11 @@ control the stats printing.
f := cmd.NewFsSrc(args) f := cmd.NewFsSrc(args)
cmd.Run(false, true, command, func() error { cmd.Run(false, true, command, func() error {
s := newServer(f, &httpflags.Opt) s := newServer(f, &httpflags.Opt)
s.serve() err := s.Serve()
if err != nil {
return err
}
s.Wait()
return nil return nil
}) })
}, },
@ -54,30 +58,32 @@ control the stats printing.
// server contains everything to run the server // server contains everything to run the server
type server struct { type server struct {
*httplib.Server
f fs.Fs f fs.Fs
vfs *vfs.VFS vfs *vfs.VFS
srv *httplib.Server
} }
func newServer(f fs.Fs, opt *httplib.Options) *server { func newServer(f fs.Fs, opt *httplib.Options) *server {
mux := http.NewServeMux() mux := http.NewServeMux()
s := &server{ s := &server{
Server: httplib.NewServer(mux, opt),
f: f, f: f,
vfs: vfs.New(f, &vfsflags.Opt), vfs: vfs.New(f, &vfsflags.Opt),
srv: httplib.NewServer(mux, opt),
} }
mux.HandleFunc("/", s.handler) mux.HandleFunc("/", s.handler)
return s return s
} }
// serve runs the http server - doesn't return // Serve runs the http server in the background.
func (s *server) serve() { //
err := s.srv.Serve() // Use s.Close() and s.Wait() to shutdown server
func (s *server) Serve() error {
err := s.Server.Serve()
if err != nil { if err != nil {
fs.Errorf(s.f, "Opening listener: %v", err) return err
} }
fs.Logf(s.f, "Serving on %s", s.srv.URL()) fs.Logf(s.f, "Serving on %s", s.URL())
s.srv.Wait() return nil
} }
// handler reads incoming requests and dispatches them // handler reads incoming requests and dispatches them

View file

@ -35,7 +35,7 @@ func startServer(t *testing.T, f fs.Fs) {
opt := httplib.DefaultOpt opt := httplib.DefaultOpt
opt.ListenAddr = testBindAddress opt.ListenAddr = testBindAddress
httpServer = newServer(f, &opt) httpServer = newServer(f, &opt)
go httpServer.serve() assert.NoError(t, httpServer.Serve())
// try to connect to the test server // try to connect to the test server
pause := time.Millisecond pause := time.Millisecond
@ -233,5 +233,6 @@ func TestAddEntry(t *testing.T) {
} }
func TestFinalise(t *testing.T) { func TestFinalise(t *testing.T) {
httpServer.srv.Close() httpServer.Close()
httpServer.Wait()
} }

View file

@ -13,6 +13,7 @@ import (
auth "github.com/abbot/go-http-auth" auth "github.com/abbot/go-http-auth"
"github.com/ncw/rclone/fs" "github.com/ncw/rclone/fs"
"github.com/pkg/errors"
) )
// Globals // Globals
@ -188,7 +189,7 @@ func NewServer(handler http.Handler, opt *Options) *Server {
func (s *Server) Serve() error { func (s *Server) Serve() error {
ln, err := net.Listen("tcp", s.httpServer.Addr) ln, err := net.Listen("tcp", s.httpServer.Addr)
if err != nil { if err != nil {
return err return errors.Wrapf(err, "start server failed")
} }
s.listener = ln s.listener = ln
s.waitChan = make(chan struct{}) s.waitChan = make(chan struct{})

View file

@ -138,8 +138,11 @@ these **must** end with /. Eg
httpSrv.ServeConn(conn, opts) httpSrv.ServeConn(conn, opts)
return nil return nil
} }
err := s.Serve()
s.serve() if err != nil {
return err
}
s.Wait()
return nil return nil
}) })
}, },
@ -151,28 +154,30 @@ const (
// server contains everything to run the server // server contains everything to run the server
type server struct { type server struct {
*httplib.Server
f fs.Fs f fs.Fs
srv *httplib.Server
} }
func newServer(f fs.Fs, opt *httplib.Options) *server { func newServer(f fs.Fs, opt *httplib.Options) *server {
mux := http.NewServeMux() mux := http.NewServeMux()
s := &server{ s := &server{
Server: httplib.NewServer(mux, opt),
f: f, f: f,
srv: httplib.NewServer(mux, opt),
} }
mux.HandleFunc("/", s.handler) mux.HandleFunc("/", s.handler)
return s return s
} }
// serve runs the http server - doesn't return // Serve runs the http server in the background.
func (s *server) serve() { //
err := s.srv.Serve() // Use s.Close() and s.Wait() to shutdown server
func (s *server) Serve() error {
err := s.Server.Serve()
if err != nil { if err != nil {
fs.Errorf(s.f, "Opening listener: %v", err) return err
} }
fs.Logf(s.f, "Serving restic REST API on %s", s.srv.URL()) fs.Logf(s.f, "Serving restic REST API on %s", s.URL())
s.srv.Wait() return nil
} }
var matchData = regexp.MustCompile("(?:^|/)data/([^/]{2,})$") var matchData = regexp.MustCompile("(?:^|/)data/([^/]{2,})$")

View file

@ -41,8 +41,11 @@ func TestRestic(t *testing.T) {
// Start the server // Start the server
w := newServer(fremote, &opt) w := newServer(fremote, &opt)
go w.serve() assert.NoError(t, w.Serve())
defer w.srv.Close() defer func() {
w.Close()
w.Wait()
}()
// Change directory to run the tests // Change directory to run the tests
err = os.Chdir(resticSource) err = os.Chdir(resticSource)

View file

@ -68,8 +68,12 @@ Use "rclone hashsum" to see the full list.
fs.Debugf(f, "Using hash %v for ETag", hashType) fs.Debugf(f, "Using hash %v for ETag", hashType)
} }
cmd.Run(false, false, command, func() error { cmd.Run(false, false, command, func() error {
w := newWebDAV(f, &httpflags.Opt) s := newWebDAV(f, &httpflags.Opt)
w.serve() err := s.serve()
if err != nil {
return err
}
s.Wait()
return nil return nil
}) })
return nil return nil
@ -89,9 +93,9 @@ Use "rclone hashsum" to see the full list.
// might apply". In particular, whether or not renaming a file or directory // might apply". In particular, whether or not renaming a file or directory
// overwriting another existing file or directory is an error is OS-dependent. // overwriting another existing file or directory is an error is OS-dependent.
type WebDAV struct { type WebDAV struct {
*httplib.Server
f fs.Fs f fs.Fs
vfs *vfs.VFS vfs *vfs.VFS
srv *httplib.Server
} }
// check interface // check interface
@ -110,18 +114,20 @@ func newWebDAV(f fs.Fs, opt *httplib.Options) *WebDAV {
Logger: w.logRequest, // FIXME Logger: w.logRequest, // FIXME
} }
w.srv = httplib.NewServer(handler, opt) w.Server = httplib.NewServer(handler, opt)
return w return w
} }
// serve runs the http server - doesn't return // serve runs the http server in the background.
func (w *WebDAV) serve() { //
err := w.srv.Serve() // Use s.Close() and s.Wait() to shutdown server
func (w *WebDAV) serve() error {
err := w.Serve()
if err != nil { if err != nil {
fs.Errorf(w.f, "Opening listener: %v", err) return err
} }
fs.Logf(w.f, "WebDav Server started on %s", w.srv.URL()) fs.Logf(w.f, "WebDav Server started on %s", w.URL())
w.srv.Wait() return nil
} }
// logRequest is called by the webdav module on every request // logRequest is called by the webdav module on every request

View file

@ -48,8 +48,11 @@ func TestWebDav(t *testing.T) {
// Start the server // Start the server
w := newWebDAV(fremote, &opt) w := newWebDAV(fremote, &opt)
go w.serve() assert.NoError(t, w.serve())
defer w.srv.Close() defer func() {
w.Close()
w.Wait()
}()
// Change directory to run the tests // Change directory to run the tests
err = os.Chdir("../../../backend/webdav") err = os.Chdir("../../../backend/webdav")