mirror of
https://github.com/rclone/rclone.git
synced 2024-11-23 07:41:36 +08:00
serve webdav: this implements a webdav server for any rclone remote.
This commit is contained in:
parent
c4ad3ac94c
commit
68d0b5adbb
|
@ -5,11 +5,13 @@ import (
|
||||||
|
|
||||||
"github.com/ncw/rclone/cmd"
|
"github.com/ncw/rclone/cmd"
|
||||||
"github.com/ncw/rclone/cmd/serve/http"
|
"github.com/ncw/rclone/cmd/serve/http"
|
||||||
|
"github.com/ncw/rclone/cmd/serve/webdav"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Command.AddCommand(http.Command)
|
Command.AddCommand(http.Command)
|
||||||
|
Command.AddCommand(webdav.Command)
|
||||||
cmd.Root.AddCommand(Command)
|
cmd.Root.AddCommand(Command)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
140
cmd/serve/webdav/webdav.go
Normal file
140
cmd/serve/webdav/webdav.go
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
package webdav
|
||||||
|
|
||||||
|
// FIXME need to fix directory listings reading each file - make an
|
||||||
|
// override for getcontenttype property?
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ncw/rclone/cmd"
|
||||||
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/ncw/rclone/vfs"
|
||||||
|
"github.com/ncw/rclone/vfs/vfsflags"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"golang.org/x/net/webdav"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Globals
|
||||||
|
var (
|
||||||
|
bindAddress = "localhost:8081"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Command.Flags().StringVarP(&bindAddress, "addr", "", bindAddress, "IPaddress:Port to bind server to.")
|
||||||
|
vfsflags.AddFlags(Command.Flags())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command definition for cobra
|
||||||
|
var Command = &cobra.Command{
|
||||||
|
Use: "webdav remote:path",
|
||||||
|
Short: `Serve remote:path over webdav.`,
|
||||||
|
Long: `
|
||||||
|
rclone serve webdav implements a basic webdav server to serve the
|
||||||
|
remote over HTTP via the webdav protocol. This can be viewed with a
|
||||||
|
webdav client or you can make a remote of type webdav to read and
|
||||||
|
write it.
|
||||||
|
|
||||||
|
FIXME at the moment each directory listing reads the start of each
|
||||||
|
file which is undesirable
|
||||||
|
`,
|
||||||
|
Run: func(command *cobra.Command, args []string) {
|
||||||
|
cmd.CheckArgs(1, 1, command, args)
|
||||||
|
fsrc := cmd.NewFsSrc(args)
|
||||||
|
cmd.Run(false, false, command, func() error {
|
||||||
|
return serveWebDav(fsrc)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// serve the remote
|
||||||
|
func serveWebDav(f fs.Fs) error {
|
||||||
|
fs.Logf(f, "WebDav Server started on %v", bindAddress)
|
||||||
|
|
||||||
|
webdavFS := &WebDAV{
|
||||||
|
f: f,
|
||||||
|
vfs: vfs.New(f, &vfsflags.Opt),
|
||||||
|
}
|
||||||
|
|
||||||
|
handler := &webdav.Handler{
|
||||||
|
FileSystem: webdavFS,
|
||||||
|
LockSystem: webdav.NewMemLS(),
|
||||||
|
Logger: webdavFS.logRequest, // FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME use our HTTP transport
|
||||||
|
http.Handle("/", handler)
|
||||||
|
return http.ListenAndServe(bindAddress, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebDAV is a webdav.FileSystem interface
|
||||||
|
//
|
||||||
|
// A FileSystem implements access to a collection of named files. The elements
|
||||||
|
// in a file path are separated by slash ('/', U+002F) characters, regardless
|
||||||
|
// of host operating system convention.
|
||||||
|
//
|
||||||
|
// Each method has the same semantics as the os package's function of the same
|
||||||
|
// name.
|
||||||
|
//
|
||||||
|
// Note that the os.Rename documentation says that "OS-specific restrictions
|
||||||
|
// 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 {
|
||||||
|
f fs.Fs
|
||||||
|
vfs *vfs.VFS
|
||||||
|
}
|
||||||
|
|
||||||
|
// check interface
|
||||||
|
var _ webdav.FileSystem = (*WebDAV)(nil)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mkdir creates a directory
|
||||||
|
func (w *WebDAV) Mkdir(ctx context.Context, name string, perm os.FileMode) (err error) {
|
||||||
|
defer fs.Trace(name, "perm=%v", perm)("err = %v", &err)
|
||||||
|
dir, leaf, err := w.vfs.StatParent(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = dir.Mkdir(leaf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenFile opens a file or a directory
|
||||||
|
func (w *WebDAV) OpenFile(ctx context.Context, name string, flags int, perm os.FileMode) (file webdav.File, err error) {
|
||||||
|
defer fs.Trace(name, "flags=%v, perm=%v", flags, perm)("err = %v", &err)
|
||||||
|
return w.vfs.OpenFile(name, flags, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAll removes a file or a directory and its contents
|
||||||
|
func (w *WebDAV) RemoveAll(ctx context.Context, name string) (err error) {
|
||||||
|
defer fs.Trace(name, "")("err = %v", &err)
|
||||||
|
node, err := w.vfs.Stat(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = node.RemoveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename a file or a directory
|
||||||
|
func (w *WebDAV) Rename(ctx context.Context, oldName, newName string) (err error) {
|
||||||
|
defer fs.Trace(oldName, "newName=%q", newName)("err = %v", &err)
|
||||||
|
return w.vfs.Rename(oldName, newName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat returns info about the file or directory
|
||||||
|
func (w *WebDAV) Stat(ctx context.Context, name string) (fi os.FileInfo, err error) {
|
||||||
|
defer fs.Trace(name, "")("fi=%+v, err = %v", &fi, &err)
|
||||||
|
return w.vfs.Stat(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check interface
|
||||||
|
var _ os.FileInfo = vfs.Node(nil)
|
63
cmd/serve/webdav/webdav_test.go
Normal file
63
cmd/serve/webdav/webdav_test.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// Serve webdav tests set up a server and run the integration tests
|
||||||
|
// for the webdav remote against it.
|
||||||
|
//
|
||||||
|
// We skip tests on platforms with troublesome character mappings
|
||||||
|
|
||||||
|
//+build !windows,!darwin
|
||||||
|
|
||||||
|
package webdav
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ncw/rclone/fstest"
|
||||||
|
_ "github.com/ncw/rclone/local"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestWebDav runs the webdav server then runs the unit tests for the
|
||||||
|
// webdav remote against it.
|
||||||
|
func TestWebDav(t *testing.T) {
|
||||||
|
fstest.Initialise()
|
||||||
|
|
||||||
|
fremote, _, clean, err := fstest.RandomRemote(*fstest.RemoteName, *fstest.SubDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer clean()
|
||||||
|
|
||||||
|
err = fremote.Mkdir("")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Start the server
|
||||||
|
go func() {
|
||||||
|
err := serveWebDav(fremote)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}()
|
||||||
|
// FIXME shut it down somehow?
|
||||||
|
|
||||||
|
// Change directory to run the tests
|
||||||
|
err = os.Chdir("../../../webdav")
|
||||||
|
assert.NoError(t, err, "failed to cd to webdav remote")
|
||||||
|
|
||||||
|
// Run the webdav tests with an on the fly remote
|
||||||
|
args := []string{"test"}
|
||||||
|
if testing.Verbose() {
|
||||||
|
args = append(args, "-v")
|
||||||
|
}
|
||||||
|
if *fstest.Verbose {
|
||||||
|
args = append(args, "-verbose")
|
||||||
|
}
|
||||||
|
args = append(args, "-remote", "webdavtest:")
|
||||||
|
cmd := exec.Command("go", args...)
|
||||||
|
cmd.Env = append(os.Environ(),
|
||||||
|
"RCLONE_CONFIG_WEBDAVTEST_TYPE=webdav",
|
||||||
|
"RCLONE_CONFIG_WEBDAVTEST_URL=http://localhost:8081/",
|
||||||
|
"RCLONE_CONFIG_WEBDAVTEST_VENDOR=other",
|
||||||
|
)
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if len(out) != 0 {
|
||||||
|
t.Logf("\n----------\n%s----------\n", string(out))
|
||||||
|
}
|
||||||
|
assert.NoError(t, err, "Running webdav integration tests")
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user