webdav: add Context argument to FileSystem interface
Currently there is no way to pass request scoped information from
the handler to the FileSytem interface. This can be important
to pass credentials or timeout parameters to the FileSystem
operations. Pipe context through the request from
http.Request.Context(). With pre-go1.7 use context.Background().
Custom FileSystem implementations will need to change, but it will
only require a new argument in each of the FileSystem methods.
The change will be trivial to update to.
Fixes golang/go#17658
Change-Id: I7491faf3467ad55db793a68081e074a9b3f9624d
Reviewed-on: https://go-review.googlesource.com/32421
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/webdav/file.go b/webdav/file.go
index 3d95c6c..748118d 100644
--- a/webdav/file.go
+++ b/webdav/file.go
@@ -14,6 +14,8 @@
"strings"
"sync"
"time"
+
+ "golang.org/x/net/context"
)
// slashClean is equivalent to but slightly more efficient than
@@ -36,11 +38,11 @@
// 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 FileSystem interface {
- Mkdir(name string, perm os.FileMode) error
- OpenFile(name string, flag int, perm os.FileMode) (File, error)
- RemoveAll(name string) error
- Rename(oldName, newName string) error
- Stat(name string) (os.FileInfo, error)
+ Mkdir(ctx context.Context, name string, perm os.FileMode) error
+ OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (File, error)
+ RemoveAll(ctx context.Context, name string) error
+ Rename(ctx context.Context, oldName, newName string) error
+ Stat(ctx context.Context, name string) (os.FileInfo, error)
}
// A File is returned by a FileSystem's OpenFile method and can be served by a
@@ -76,14 +78,14 @@
return filepath.Join(dir, filepath.FromSlash(slashClean(name)))
}
-func (d Dir) Mkdir(name string, perm os.FileMode) error {
+func (d Dir) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
if name = d.resolve(name); name == "" {
return os.ErrNotExist
}
return os.Mkdir(name, perm)
}
-func (d Dir) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+func (d Dir) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (File, error) {
if name = d.resolve(name); name == "" {
return nil, os.ErrNotExist
}
@@ -94,7 +96,7 @@
return f, nil
}
-func (d Dir) RemoveAll(name string) error {
+func (d Dir) RemoveAll(ctx context.Context, name string) error {
if name = d.resolve(name); name == "" {
return os.ErrNotExist
}
@@ -105,7 +107,7 @@
return os.RemoveAll(name)
}
-func (d Dir) Rename(oldName, newName string) error {
+func (d Dir) Rename(ctx context.Context, oldName, newName string) error {
if oldName = d.resolve(oldName); oldName == "" {
return os.ErrNotExist
}
@@ -119,7 +121,7 @@
return os.Rename(oldName, newName)
}
-func (d Dir) Stat(name string) (os.FileInfo, error) {
+func (d Dir) Stat(ctx context.Context, name string) (os.FileInfo, error) {
if name = d.resolve(name); name == "" {
return nil, os.ErrNotExist
}
@@ -237,7 +239,7 @@
return parent, frag, err
}
-func (fs *memFS) Mkdir(name string, perm os.FileMode) error {
+func (fs *memFS) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
fs.mu.Lock()
defer fs.mu.Unlock()
@@ -260,7 +262,7 @@
return nil
}
-func (fs *memFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+func (fs *memFS) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (File, error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@@ -314,7 +316,7 @@
}, nil
}
-func (fs *memFS) RemoveAll(name string) error {
+func (fs *memFS) RemoveAll(ctx context.Context, name string) error {
fs.mu.Lock()
defer fs.mu.Unlock()
@@ -330,7 +332,7 @@
return nil
}
-func (fs *memFS) Rename(oldName, newName string) error {
+func (fs *memFS) Rename(ctx context.Context, oldName, newName string) error {
fs.mu.Lock()
defer fs.mu.Unlock()
@@ -381,7 +383,7 @@
return nil
}
-func (fs *memFS) Stat(name string) (os.FileInfo, error) {
+func (fs *memFS) Stat(ctx context.Context, name string) (os.FileInfo, error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@@ -599,9 +601,9 @@
// moveFiles moves files and/or directories from src to dst.
//
// See section 9.9.4 for when various HTTP status codes apply.
-func moveFiles(fs FileSystem, src, dst string, overwrite bool) (status int, err error) {
+func moveFiles(ctx context.Context, fs FileSystem, src, dst string, overwrite bool) (status int, err error) {
created := false
- if _, err := fs.Stat(dst); err != nil {
+ if _, err := fs.Stat(ctx, dst); err != nil {
if !os.IsNotExist(err) {
return http.StatusForbidden, err
}
@@ -611,13 +613,13 @@
// and the Overwrite header is "T", then prior to performing the move,
// the server must perform a DELETE with "Depth: infinity" on the
// destination resource.
- if err := fs.RemoveAll(dst); err != nil {
+ if err := fs.RemoveAll(ctx, dst); err != nil {
return http.StatusForbidden, err
}
} else {
return http.StatusPreconditionFailed, os.ErrExist
}
- if err := fs.Rename(src, dst); err != nil {
+ if err := fs.Rename(ctx, src, dst); err != nil {
return http.StatusForbidden, err
}
if created {
@@ -650,7 +652,7 @@
// copyFiles copies files and/or directories from src to dst.
//
// See section 9.8.5 for when various HTTP status codes apply.
-func copyFiles(fs FileSystem, src, dst string, overwrite bool, depth int, recursion int) (status int, err error) {
+func copyFiles(ctx context.Context, fs FileSystem, src, dst string, overwrite bool, depth int, recursion int) (status int, err error) {
if recursion == 1000 {
return http.StatusInternalServerError, errRecursionTooDeep
}
@@ -659,7 +661,7 @@
// TODO: section 9.8.3 says that "Note that an infinite-depth COPY of /A/
// into /A/B/ could lead to infinite recursion if not handled correctly."
- srcFile, err := fs.OpenFile(src, os.O_RDONLY, 0)
+ srcFile, err := fs.OpenFile(ctx, src, os.O_RDONLY, 0)
if err != nil {
if os.IsNotExist(err) {
return http.StatusNotFound, err
@@ -677,7 +679,7 @@
srcPerm := srcStat.Mode() & os.ModePerm
created := false
- if _, err := fs.Stat(dst); err != nil {
+ if _, err := fs.Stat(ctx, dst); err != nil {
if os.IsNotExist(err) {
created = true
} else {
@@ -687,13 +689,13 @@
if !overwrite {
return http.StatusPreconditionFailed, os.ErrExist
}
- if err := fs.RemoveAll(dst); err != nil && !os.IsNotExist(err) {
+ if err := fs.RemoveAll(ctx, dst); err != nil && !os.IsNotExist(err) {
return http.StatusForbidden, err
}
}
if srcStat.IsDir() {
- if err := fs.Mkdir(dst, srcPerm); err != nil {
+ if err := fs.Mkdir(ctx, dst, srcPerm); err != nil {
return http.StatusForbidden, err
}
if depth == infiniteDepth {
@@ -705,7 +707,7 @@
name := c.Name()
s := path.Join(src, name)
d := path.Join(dst, name)
- cStatus, cErr := copyFiles(fs, s, d, overwrite, depth, recursion)
+ cStatus, cErr := copyFiles(ctx, fs, s, d, overwrite, depth, recursion)
if cErr != nil {
// TODO: MultiStatus.
return cStatus, cErr
@@ -714,7 +716,7 @@
}
} else {
- dstFile, err := fs.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, srcPerm)
+ dstFile, err := fs.OpenFile(ctx, dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, srcPerm)
if err != nil {
if os.IsNotExist(err) {
return http.StatusConflict, err
@@ -747,7 +749,7 @@
// Allowed values for depth are 0, 1 or infiniteDepth. For each visited node,
// walkFS calls walkFn. If a visited file system node is a directory and
// walkFn returns filepath.SkipDir, walkFS will skip traversal of this node.
-func walkFS(fs FileSystem, depth int, name string, info os.FileInfo, walkFn filepath.WalkFunc) error {
+func walkFS(ctx context.Context, fs FileSystem, depth int, name string, info os.FileInfo, walkFn filepath.WalkFunc) error {
// This implementation is based on Walk's code in the standard path/filepath package.
err := walkFn(name, info, nil)
if err != nil {
@@ -764,7 +766,7 @@
}
// Read directory names.
- f, err := fs.OpenFile(name, os.O_RDONLY, 0)
+ f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0)
if err != nil {
return walkFn(name, info, err)
}
@@ -776,13 +778,13 @@
for _, fileInfo := range fileInfos {
filename := path.Join(name, fileInfo.Name())
- fileInfo, err := fs.Stat(filename)
+ fileInfo, err := fs.Stat(ctx, filename)
if err != nil {
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
return err
}
} else {
- err = walkFS(fs, depth, filename, fileInfo, walkFn)
+ err = walkFS(ctx, fs, depth, filename, fileInfo, walkFn)
if err != nil {
if !fileInfo.IsDir() || err != filepath.SkipDir {
return err