mirror of
https://github.com/rclone/rclone.git
synced 2025-06-24 14:01:31 +02:00
serve webdav: add serve rc interface - fixes #4505
This commit is contained in:
parent
780f4040ea
commit
e37775bb41
@ -7,6 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mime"
|
"mime"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
@ -21,8 +22,10 @@ import (
|
|||||||
"github.com/rclone/rclone/cmd/serve/proxy"
|
"github.com/rclone/rclone/cmd/serve/proxy"
|
||||||
"github.com/rclone/rclone/cmd/serve/proxy/proxyflags"
|
"github.com/rclone/rclone/cmd/serve/proxy/proxyflags"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/config/configstruct"
|
||||||
"github.com/rclone/rclone/fs/config/flags"
|
"github.com/rclone/rclone/fs/config/flags"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
|
"github.com/rclone/rclone/fs/rc"
|
||||||
libhttp "github.com/rclone/rclone/lib/http"
|
libhttp "github.com/rclone/rclone/lib/http"
|
||||||
"github.com/rclone/rclone/lib/http/serve"
|
"github.com/rclone/rclone/lib/http/serve"
|
||||||
"github.com/rclone/rclone/lib/systemd"
|
"github.com/rclone/rclone/lib/systemd"
|
||||||
@ -70,6 +73,28 @@ func init() {
|
|||||||
vfsflags.AddFlags(flagSet)
|
vfsflags.AddFlags(flagSet)
|
||||||
proxyflags.AddFlags(flagSet)
|
proxyflags.AddFlags(flagSet)
|
||||||
cmdserve.Command.AddCommand(Command)
|
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
|
// Command definition for cobra
|
||||||
@ -145,17 +170,12 @@ done by the permissions on the socket.
|
|||||||
cmd.CheckArgs(0, 0, command, args)
|
cmd.CheckArgs(0, 0, command, args)
|
||||||
}
|
}
|
||||||
cmd.Run(false, false, command, func() error {
|
cmd.Run(false, false, command, func() error {
|
||||||
s, err := newWebDAV(context.Background(), f, &Opt)
|
s, err := newWebDAV(context.Background(), f, &Opt, &vfscommon.Opt, &proxy.Opt)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = s.serve()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer systemd.Notify()()
|
defer systemd.Notify()()
|
||||||
s.Wait()
|
return s.Serve()
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
return nil
|
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
|
// 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 {
|
||||||
*libhttp.Server
|
server *libhttp.Server
|
||||||
opt Options
|
opt Options
|
||||||
f fs.Fs
|
f fs.Fs
|
||||||
_vfs *vfs.VFS // don't use directly, use getVFS
|
_vfs *vfs.VFS // don't use directly, use getVFS
|
||||||
@ -188,7 +208,7 @@ type WebDAV struct {
|
|||||||
var _ webdav.FileSystem = (*WebDAV)(nil)
|
var _ webdav.FileSystem = (*WebDAV)(nil)
|
||||||
|
|
||||||
// Make a new WebDAV to serve the remote
|
// 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{
|
w = &WebDAV{
|
||||||
f: f,
|
f: f,
|
||||||
ctx: ctx,
|
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 {
|
if w.etagHashType != hash.None {
|
||||||
fs.Debugf(f, "Using hash %v for ETag", w.etagHashType)
|
fs.Debugf(f, "Using hash %v for ETag", w.etagHashType)
|
||||||
}
|
}
|
||||||
if proxy.Opt.AuthProxy != "" {
|
if proxyOpt.AuthProxy != "" {
|
||||||
w.proxy = proxy.New(ctx, &proxy.Opt, &vfscommon.Opt)
|
w.proxy = proxy.New(ctx, proxyOpt, vfsOpt)
|
||||||
// override auth
|
// override auth
|
||||||
w.opt.Auth.CustomAuthFn = w.auth
|
w.opt.Auth.CustomAuthFn = w.auth
|
||||||
} else {
|
} 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.WithConfig(w.opt.HTTP),
|
||||||
libhttp.WithAuth(w.opt.Auth),
|
libhttp.WithAuth(w.opt.Auth),
|
||||||
libhttp.WithTemplate(w.opt.Template),
|
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
|
w.webdavhandler = webdavHandler
|
||||||
|
|
||||||
router := w.Server.Router()
|
router := w.server.Router()
|
||||||
router.Use(
|
router.Use(
|
||||||
middleware.SetHeader("Accept-Ranges", "bytes"),
|
middleware.SetHeader("Accept-Ranges", "bytes"),
|
||||||
middleware.SetHeader("Server", "rclone/"+fs.Version),
|
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
|
// Make the entries for display
|
||||||
directory := serve.NewDirectory(dirRemote, w.Server.HTMLTemplate())
|
directory := serve.NewDirectory(dirRemote, w.server.HTMLTemplate())
|
||||||
for _, node := range dirEntries {
|
for _, node := range dirEntries {
|
||||||
if vfscommon.Opt.NoModTime {
|
if vfscommon.Opt.NoModTime {
|
||||||
directory.AddHTMLEntry(node.Path(), node.IsDir(), node.Size(), time.Time{})
|
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)
|
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
|
// Use s.Close() and s.Wait() to shutdown server
|
||||||
func (w *WebDAV) serve() error {
|
func (w *WebDAV) Serve() error {
|
||||||
w.Serve()
|
w.server.Serve()
|
||||||
fs.Logf(w.f, "WebDav Server started on %s", w.URLs())
|
fs.Logf(w.f, "WebDav Server started on %s", w.server.URLs())
|
||||||
|
w.server.Wait()
|
||||||
return nil
|
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
|
// logRequest is called by the webdav module on every request
|
||||||
func (w *WebDAV) logRequest(r *http.Request, err error) {
|
func (w *WebDAV) logRequest(r *http.Request, err error) {
|
||||||
fs.Infof(r.URL.Path, "%s from %s", r.Method, r.RemoteAddr)
|
fs.Infof(r.URL.Path, "%s from %s", r.Method, r.RemoteAddr)
|
||||||
|
@ -18,11 +18,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
_ "github.com/rclone/rclone/backend/local"
|
_ "github.com/rclone/rclone/backend/local"
|
||||||
|
"github.com/rclone/rclone/cmd/serve/proxy"
|
||||||
"github.com/rclone/rclone/cmd/serve/servetest"
|
"github.com/rclone/rclone/cmd/serve/servetest"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
"github.com/rclone/rclone/fs/config/configmap"
|
"github.com/rclone/rclone/fs/config/configmap"
|
||||||
"github.com/rclone/rclone/fs/config/obscure"
|
"github.com/rclone/rclone/fs/config/obscure"
|
||||||
"github.com/rclone/rclone/fs/filter"
|
"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/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/net/webdav"
|
"golang.org/x/net/webdav"
|
||||||
@ -56,22 +59,23 @@ func TestWebDav(t *testing.T) {
|
|||||||
opt.EtagHash = "MD5"
|
opt.EtagHash = "MD5"
|
||||||
|
|
||||||
// Start the server
|
// 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, 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 for the backend we'll use to connect to the server
|
||||||
config := configmap.Simple{
|
config := configmap.Simple{
|
||||||
"type": "webdav",
|
"type": "webdav",
|
||||||
"vendor": "rclone",
|
"vendor": "rclone",
|
||||||
"url": w.Server.URLs()[0],
|
"url": w.server.URLs()[0],
|
||||||
"user": testUser,
|
"user": testUser,
|
||||||
"pass": obscure.MustObscure(testPass),
|
"pass": obscure.MustObscure(testPass),
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, func() {
|
return config, func() {
|
||||||
assert.NoError(t, w.Shutdown())
|
assert.NoError(t, w.Shutdown())
|
||||||
w.Wait()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,14 +106,15 @@ func TestHTTPFunction(t *testing.T) {
|
|||||||
opt.Template.Path = testTemplate
|
opt.Template.Path = testTemplate
|
||||||
|
|
||||||
// Start the server
|
// 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)
|
assert.NoError(t, err)
|
||||||
require.NoError(t, w.serve())
|
go func() {
|
||||||
|
require.NoError(t, w.Serve())
|
||||||
|
}()
|
||||||
defer func() {
|
defer func() {
|
||||||
assert.NoError(t, w.Shutdown())
|
assert.NoError(t, w.Shutdown())
|
||||||
w.Wait()
|
|
||||||
}()
|
}()
|
||||||
testURL := w.Server.URLs()[0]
|
testURL := w.server.URLs()[0]
|
||||||
pause := time.Millisecond
|
pause := time.Millisecond
|
||||||
i := 0
|
i := 0
|
||||||
for ; i < 10; i++ {
|
for ; i < 10; i++ {
|
||||||
@ -259,3 +264,10 @@ func HelpTestGET(t *testing.T, testURL string) {
|
|||||||
checkGolden(t, test.Golden, body)
|
checkGolden(t, test.Golden, body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRc(t *testing.T) {
|
||||||
|
servetest.TestRc(t, rc.Params{
|
||||||
|
"type": "webdav",
|
||||||
|
"vfs_cache_mode": "off",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user