diff --git a/cmd/serve/webdav/webdav.go b/cmd/serve/webdav/webdav.go index 535d999f8..c334ccc85 100644 --- a/cmd/serve/webdav/webdav.go +++ b/cmd/serve/webdav/webdav.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "mime" + "net" "net/http" "os" "path" @@ -21,8 +22,10 @@ import ( "github.com/rclone/rclone/cmd/serve/proxy" "github.com/rclone/rclone/cmd/serve/proxy/proxyflags" "github.com/rclone/rclone/fs" + "github.com/rclone/rclone/fs/config/configstruct" "github.com/rclone/rclone/fs/config/flags" "github.com/rclone/rclone/fs/hash" + "github.com/rclone/rclone/fs/rc" libhttp "github.com/rclone/rclone/lib/http" "github.com/rclone/rclone/lib/http/serve" "github.com/rclone/rclone/lib/systemd" @@ -70,6 +73,28 @@ func init() { vfsflags.AddFlags(flagSet) proxyflags.AddFlags(flagSet) cmdserve.Command.AddCommand(Command) + cmdserve.AddRc("webdav", func(ctx context.Context, f fs.Fs, in rc.Params) (cmdserve.Handle, error) { + // Read VFS Opts + var vfsOpt = vfscommon.Opt // set default opts + err := configstruct.SetAny(in, &vfsOpt) + if err != nil { + return nil, err + } + // Read Proxy Opts + var proxyOpt = proxy.Opt // set default opts + err = configstruct.SetAny(in, &proxyOpt) + if err != nil { + return nil, err + } + // Read opts + var opt = Opt // set default opts + err = configstruct.SetAny(in, &opt) + if err != nil { + return nil, err + } + // Create server + return newWebDAV(ctx, f, &opt, &vfsOpt, &proxyOpt) + }) } // Command definition for cobra @@ -145,17 +170,12 @@ done by the permissions on the socket. cmd.CheckArgs(0, 0, command, args) } cmd.Run(false, false, command, func() error { - s, err := newWebDAV(context.Background(), f, &Opt) - if err != nil { - return err - } - err = s.serve() + s, err := newWebDAV(context.Background(), f, &Opt, &vfscommon.Opt, &proxy.Opt) if err != nil { return err } defer systemd.Notify()() - s.Wait() - return nil + return s.Serve() }) return nil }, @@ -174,7 +194,7 @@ done by the permissions on the socket. // might apply". In particular, whether or not renaming a file or directory // overwriting another existing file or directory is an error is OS-dependent. type WebDAV struct { - *libhttp.Server + server *libhttp.Server opt Options f fs.Fs _vfs *vfs.VFS // don't use directly, use getVFS @@ -188,7 +208,7 @@ type WebDAV struct { var _ webdav.FileSystem = (*WebDAV)(nil) // Make a new WebDAV to serve the remote -func newWebDAV(ctx context.Context, f fs.Fs, opt *Options) (w *WebDAV, err error) { +func newWebDAV(ctx context.Context, f fs.Fs, opt *Options, vfsOpt *vfscommon.Options, proxyOpt *proxy.Options) (w *WebDAV, err error) { w = &WebDAV{ f: f, ctx: ctx, @@ -206,15 +226,15 @@ func newWebDAV(ctx context.Context, f fs.Fs, opt *Options) (w *WebDAV, err error if w.etagHashType != hash.None { fs.Debugf(f, "Using hash %v for ETag", w.etagHashType) } - if proxy.Opt.AuthProxy != "" { - w.proxy = proxy.New(ctx, &proxy.Opt, &vfscommon.Opt) + if proxyOpt.AuthProxy != "" { + w.proxy = proxy.New(ctx, proxyOpt, vfsOpt) // override auth w.opt.Auth.CustomAuthFn = w.auth } else { - w._vfs = vfs.New(f, &vfscommon.Opt) + w._vfs = vfs.New(f, vfsOpt) } - w.Server, err = libhttp.NewServer(ctx, + w.server, err = libhttp.NewServer(ctx, libhttp.WithConfig(w.opt.HTTP), libhttp.WithAuth(w.opt.Auth), libhttp.WithTemplate(w.opt.Template), @@ -234,7 +254,7 @@ func newWebDAV(ctx context.Context, f fs.Fs, opt *Options) (w *WebDAV, err error } w.webdavhandler = webdavHandler - router := w.Server.Router() + router := w.server.Router() router.Use( middleware.SetHeader("Accept-Ranges", "bytes"), middleware.SetHeader("Server", "rclone/"+fs.Version), @@ -382,7 +402,7 @@ func (w *WebDAV) serveDir(rw http.ResponseWriter, r *http.Request, dirRemote str } // Make the entries for display - directory := serve.NewDirectory(dirRemote, w.Server.HTMLTemplate()) + directory := serve.NewDirectory(dirRemote, w.server.HTMLTemplate()) for _, node := range dirEntries { if vfscommon.Opt.NoModTime { directory.AddHTMLEntry(node.Path(), node.IsDir(), node.Size(), time.Time{}) @@ -398,15 +418,26 @@ func (w *WebDAV) serveDir(rw http.ResponseWriter, r *http.Request, dirRemote str directory.Serve(rw, r) } -// serve runs the http server in the background. +// Serve HTTP until the server is shutdown // // Use s.Close() and s.Wait() to shutdown server -func (w *WebDAV) serve() error { - w.Serve() - fs.Logf(w.f, "WebDav Server started on %s", w.URLs()) +func (w *WebDAV) Serve() error { + w.server.Serve() + fs.Logf(w.f, "WebDav Server started on %s", w.server.URLs()) + w.server.Wait() return nil } +// Addr returns the first address of the server +func (w *WebDAV) Addr() net.Addr { + return w.server.Addr() +} + +// Shutdown the server +func (w *WebDAV) Shutdown() error { + return w.server.Shutdown() +} + // logRequest is called by the webdav module on every request func (w *WebDAV) logRequest(r *http.Request, err error) { fs.Infof(r.URL.Path, "%s from %s", r.Method, r.RemoteAddr) diff --git a/cmd/serve/webdav/webdav_test.go b/cmd/serve/webdav/webdav_test.go index 9dbd7354c..4066f6b76 100644 --- a/cmd/serve/webdav/webdav_test.go +++ b/cmd/serve/webdav/webdav_test.go @@ -18,11 +18,14 @@ import ( "time" _ "github.com/rclone/rclone/backend/local" + "github.com/rclone/rclone/cmd/serve/proxy" "github.com/rclone/rclone/cmd/serve/servetest" "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/config/configmap" "github.com/rclone/rclone/fs/config/obscure" "github.com/rclone/rclone/fs/filter" + "github.com/rclone/rclone/fs/rc" + "github.com/rclone/rclone/vfs/vfscommon" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/net/webdav" @@ -56,22 +59,23 @@ func TestWebDav(t *testing.T) { opt.EtagHash = "MD5" // Start the server - w, err := newWebDAV(context.Background(), f, &opt) + w, err := newWebDAV(context.Background(), f, &opt, &vfscommon.Opt, &proxy.Opt) require.NoError(t, err) - require.NoError(t, w.serve()) + go func() { + require.NoError(t, w.Serve()) + }() // Config for the backend we'll use to connect to the server config := configmap.Simple{ "type": "webdav", "vendor": "rclone", - "url": w.Server.URLs()[0], + "url": w.server.URLs()[0], "user": testUser, "pass": obscure.MustObscure(testPass), } return config, func() { assert.NoError(t, w.Shutdown()) - w.Wait() } } @@ -102,14 +106,15 @@ func TestHTTPFunction(t *testing.T) { opt.Template.Path = testTemplate // Start the server - w, err := newWebDAV(context.Background(), f, &opt) + w, err := newWebDAV(context.Background(), f, &opt, &vfscommon.Opt, &proxy.Opt) assert.NoError(t, err) - require.NoError(t, w.serve()) + go func() { + require.NoError(t, w.Serve()) + }() defer func() { assert.NoError(t, w.Shutdown()) - w.Wait() }() - testURL := w.Server.URLs()[0] + testURL := w.server.URLs()[0] pause := time.Millisecond i := 0 for ; i < 10; i++ { @@ -259,3 +264,10 @@ func HelpTestGET(t *testing.T, testURL string) { checkGolden(t, test.Golden, body) } } + +func TestRc(t *testing.T) { + servetest.TestRc(t, rc.Params{ + "type": "webdav", + "vfs_cache_mode": "off", + }) +}