serve webdav: convert options to new style

This commit is contained in:
Nick Craig-Wood 2025-03-28 14:54:28 +00:00
parent 6054c4e49d
commit 3c981e6c2c
2 changed files with 47 additions and 46 deletions

View File

@ -33,41 +33,42 @@ import (
"golang.org/x/net/webdav" "golang.org/x/net/webdav"
) )
// OptionsInfo describes the Options in use
var OptionsInfo = fs.Options{{
Name: "etag_hash",
Default: "",
Help: "Which hash to use for the ETag, or auto or blank for off",
}, {
Name: "disable_dir_list",
Default: false,
Help: "Disable HTML directory list on GET request for a directory",
}}.
Add(libhttp.ConfigInfo).
Add(libhttp.AuthConfigInfo).
Add(libhttp.TemplateConfigInfo)
// Options required for http server // Options required for http server
type Options struct { type Options struct {
Auth libhttp.AuthConfig Auth libhttp.AuthConfig
HTTP libhttp.Config HTTP libhttp.Config
Template libhttp.TemplateConfig Template libhttp.TemplateConfig
HashName string EtagHash string `config:"etag_hash"`
HashType hash.Type DisableDirList bool `config:"disable_dir_list"`
DisableGETDir bool
}
// DefaultOpt is the default values used for Options
var DefaultOpt = Options{
Auth: libhttp.DefaultAuthCfg(),
HTTP: libhttp.DefaultCfg(),
Template: libhttp.DefaultTemplateCfg(),
HashType: hash.None,
DisableGETDir: false,
} }
// Opt is options set by command line flags // Opt is options set by command line flags
var Opt = DefaultOpt var Opt Options
// flagPrefix is the prefix used to uniquely identify command line flags. // flagPrefix is the prefix used to uniquely identify command line flags.
// It is intentionally empty for this package. // It is intentionally empty for this package.
const flagPrefix = "" const flagPrefix = ""
func init() { func init() {
fs.RegisterGlobalOptions(fs.OptionsInfo{Name: "webdav", Opt: &Opt, Options: OptionsInfo})
flagSet := Command.Flags() flagSet := Command.Flags()
libhttp.AddAuthFlagsPrefix(flagSet, flagPrefix, &Opt.Auth) flags.AddFlagsFromOptions(flagSet, "", OptionsInfo)
libhttp.AddHTTPFlagsPrefix(flagSet, flagPrefix, &Opt.HTTP)
libhttp.AddTemplateFlagsPrefix(flagSet, "", &Opt.Template)
vfsflags.AddFlags(flagSet) vfsflags.AddFlags(flagSet)
proxyflags.AddFlags(flagSet) proxyflags.AddFlags(flagSet)
flags.StringVarP(flagSet, &Opt.HashName, "etag-hash", "", "", "Which hash to use for the ETag, or auto or blank for off", "")
flags.BoolVarP(flagSet, &Opt.DisableGETDir, "disable-dir-list", "", false, "Disable HTML directory list on GET request for a directory", "")
cmdserve.Command.AddCommand(Command) cmdserve.Command.AddCommand(Command)
} }
@ -143,18 +144,6 @@ done by the permissions on the socket.
} else { } else {
cmd.CheckArgs(0, 0, command, args) cmd.CheckArgs(0, 0, command, args)
} }
Opt.HashType = hash.None
if Opt.HashName == "auto" {
Opt.HashType = f.Hashes().GetOne()
} else if Opt.HashName != "" {
err := Opt.HashType.Set(Opt.HashName)
if err != nil {
return err
}
}
if Opt.HashType != hash.None {
fs.Debugf(f, "Using hash %v for ETag", Opt.HashType)
}
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)
if err != nil { if err != nil {
@ -192,6 +181,7 @@ type WebDAV struct {
webdavhandler *webdav.Handler webdavhandler *webdav.Handler
proxy *proxy.Proxy proxy *proxy.Proxy
ctx context.Context // for global config ctx context.Context // for global config
etagHashType hash.Type
} }
// check interface // check interface
@ -200,9 +190,21 @@ 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) (w *WebDAV, err error) {
w = &WebDAV{ w = &WebDAV{
f: f, f: f,
ctx: ctx, ctx: ctx,
opt: *opt, opt: *opt,
etagHashType: hash.None,
}
if opt.EtagHash == "auto" {
w.etagHashType = f.Hashes().GetOne()
} else if opt.EtagHash != "" {
err := w.etagHashType.Set(opt.EtagHash)
if err != nil {
return nil, err
}
}
if w.etagHashType != hash.None {
fs.Debugf(f, "Using hash %v for ETag", w.etagHashType)
} }
if proxy.Opt.AuthProxy != "" { if proxy.Opt.AuthProxy != "" {
w.proxy = proxy.New(ctx, &proxy.Opt, &vfscommon.Opt) w.proxy = proxy.New(ctx, &proxy.Opt, &vfscommon.Opt)
@ -333,7 +335,7 @@ func (w *WebDAV) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
urlPath := r.URL.Path urlPath := r.URL.Path
isDir := strings.HasSuffix(urlPath, "/") isDir := strings.HasSuffix(urlPath, "/")
remote := strings.Trim(urlPath, "/") remote := strings.Trim(urlPath, "/")
if !w.opt.DisableGETDir && (r.Method == "GET" || r.Method == "HEAD") && isDir { if !w.opt.DisableDirList && (r.Method == "GET" || r.Method == "HEAD") && isDir {
w.serveDir(rw, r, remote) w.serveDir(rw, r, remote)
return return
} }
@ -517,16 +519,16 @@ func (h Handle) DeadProps() (map[xml.Name]webdav.Property, error) {
property webdav.Property property webdav.Property
properties = make(map[xml.Name]webdav.Property) properties = make(map[xml.Name]webdav.Property)
) )
if h.w.opt.HashType != hash.None { if h.w.etagHashType != hash.None {
entry := h.Handle.Node().DirEntry() entry := h.Handle.Node().DirEntry()
if o, ok := entry.(fs.Object); ok { if o, ok := entry.(fs.Object); ok {
hash, err := o.Hash(h.ctx, h.w.opt.HashType) hash, err := o.Hash(h.ctx, h.w.etagHashType)
if err == nil { if err == nil {
xmlName.Space = "http://owncloud.org/ns" xmlName.Space = "http://owncloud.org/ns"
xmlName.Local = "checksums" xmlName.Local = "checksums"
property.XMLName = xmlName property.XMLName = xmlName
property.InnerXML = append(property.InnerXML, "<checksum xmlns=\"http://owncloud.org/ns\">"...) property.InnerXML = append(property.InnerXML, "<checksum xmlns=\"http://owncloud.org/ns\">"...)
property.InnerXML = append(property.InnerXML, strings.ToUpper(h.w.opt.HashType.String())...) property.InnerXML = append(property.InnerXML, strings.ToUpper(h.w.etagHashType.String())...)
property.InnerXML = append(property.InnerXML, ':') property.InnerXML = append(property.InnerXML, ':')
property.InnerXML = append(property.InnerXML, hash...) property.InnerXML = append(property.InnerXML, hash...)
property.InnerXML = append(property.InnerXML, "</checksum>"...) property.InnerXML = append(property.InnerXML, "</checksum>"...)
@ -579,7 +581,7 @@ type FileInfo struct {
// ETag returns an ETag for the FileInfo // ETag returns an ETag for the FileInfo
func (fi FileInfo) ETag(ctx context.Context) (etag string, err error) { func (fi FileInfo) ETag(ctx context.Context) (etag string, err error) {
// defer log.Trace(fi, "")("etag=%q, err=%v", &etag, &err) // defer log.Trace(fi, "")("etag=%q, err=%v", &etag, &err)
if fi.w.opt.HashType == hash.None { if fi.w.etagHashType == hash.None {
return "", webdav.ErrNotImplemented return "", webdav.ErrNotImplemented
} }
node, ok := (fi.FileInfo).(vfs.Node) node, ok := (fi.FileInfo).(vfs.Node)
@ -592,7 +594,7 @@ func (fi FileInfo) ETag(ctx context.Context) (etag string, err error) {
if !ok { if !ok {
return "", webdav.ErrNotImplemented return "", webdav.ErrNotImplemented
} }
hash, err := o.Hash(ctx, fi.w.opt.HashType) hash, err := o.Hash(ctx, fi.w.etagHashType)
if err != nil || hash == "" { if err != nil || hash == "" {
return "", webdav.ErrNotImplemented return "", webdav.ErrNotImplemented
} }

View File

@ -23,7 +23,6 @@ import (
"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/hash"
"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"
@ -48,13 +47,13 @@ var (
func TestWebDav(t *testing.T) { func TestWebDav(t *testing.T) {
// Configure and start the server // Configure and start the server
start := func(f fs.Fs) (configmap.Simple, func()) { start := func(f fs.Fs) (configmap.Simple, func()) {
opt := DefaultOpt opt := Opt
opt.HTTP.ListenAddr = []string{testBindAddress} opt.HTTP.ListenAddr = []string{testBindAddress}
opt.HTTP.BaseURL = "/prefix" opt.HTTP.BaseURL = "/prefix"
opt.Auth.BasicUser = testUser opt.Auth.BasicUser = testUser
opt.Auth.BasicPass = testPass opt.Auth.BasicPass = testPass
opt.Template.Path = testTemplate opt.Template.Path = testTemplate
opt.HashType = hash.MD5 opt.EtagHash = "MD5"
// Start the server // Start the server
w, err := newWebDAV(context.Background(), f, &opt) w, err := newWebDAV(context.Background(), f, &opt)
@ -98,7 +97,7 @@ func TestHTTPFunction(t *testing.T) {
f, err := fs.NewFs(context.Background(), "../http/testdata/files") f, err := fs.NewFs(context.Background(), "../http/testdata/files")
assert.NoError(t, err) assert.NoError(t, err)
opt := DefaultOpt opt := Opt
opt.HTTP.ListenAddr = []string{testBindAddress} opt.HTTP.ListenAddr = []string{testBindAddress}
opt.Template.Path = testTemplate opt.Template.Path = testTemplate