aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/golang.org/x/net/webdav
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/net/webdav')
-rw-r--r--vendor/golang.org/x/net/webdav/prop.go54
-rw-r--r--vendor/golang.org/x/net/webdav/prop_test.go106
2 files changed, 158 insertions, 2 deletions
diff --git a/vendor/golang.org/x/net/webdav/prop.go b/vendor/golang.org/x/net/webdav/prop.go
index e36a3b3..4d7be34 100644
--- a/vendor/golang.org/x/net/webdav/prop.go
+++ b/vendor/golang.org/x/net/webdav/prop.go
@@ -7,6 +7,7 @@ package webdav
import (
"bytes"
"encoding/xml"
+ "errors"
"fmt"
"io"
"mime"
@@ -376,10 +377,37 @@ func findContentLength(ctx context.Context, fs FileSystem, ls LockSystem, name s
}
func findLastModified(ctx context.Context, fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
- return fi.ModTime().Format(http.TimeFormat), nil
+ return fi.ModTime().UTC().Format(http.TimeFormat), nil
+}
+
+// ErrNotImplemented should be returned by optional interfaces if they
+// want the original implementation to be used.
+var ErrNotImplemented = errors.New("not implemented")
+
+// ContentTyper is an optional interface for the os.FileInfo
+// objects returned by the FileSystem.
+//
+// If this interface is defined then it will be used to read the
+// content type from the object.
+//
+// If this interface is not defined the file will be opened and the
+// content type will be guessed from the initial contents of the file.
+type ContentTyper interface {
+ // ContentType returns the content type for the file.
+ //
+ // If this returns error ErrNotImplemented then the error will
+ // be ignored and the base implementation will be used
+ // instead.
+ ContentType(ctx context.Context) (string, error)
}
func findContentType(ctx context.Context, fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
+ if do, ok := fi.(ContentTyper); ok {
+ ctype, err := do.ContentType(ctx)
+ if err != ErrNotImplemented {
+ return ctype, err
+ }
+ }
f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0)
if err != nil {
return "", err
@@ -402,7 +430,31 @@ func findContentType(ctx context.Context, fs FileSystem, ls LockSystem, name str
return ctype, err
}
+// ETager is an optional interface for the os.FileInfo objects
+// returned by the FileSystem.
+//
+// If this interface is defined then it will be used to read the ETag
+// for the object.
+//
+// If this interface is not defined an ETag will be computed using the
+// ModTime() and the Size() methods of the os.FileInfo object.
+type ETager interface {
+ // ETag returns an ETag for the file. This should be of the
+ // form "value" or W/"value"
+ //
+ // If this returns error ErrNotImplemented then the error will
+ // be ignored and the base implementation will be used
+ // instead.
+ ETag(ctx context.Context) (string, error)
+}
+
func findETag(ctx context.Context, fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
+ if do, ok := fi.(ETager); ok {
+ etag, err := do.ETag(ctx)
+ if err != ErrNotImplemented {
+ return etag, err
+ }
+ }
// The Apache http 2.4 web server by default concatenates the
// modification time and size of a file. We replicate the heuristic
// with nanosecond granularity.
diff --git a/vendor/golang.org/x/net/webdav/prop_test.go b/vendor/golang.org/x/net/webdav/prop_test.go
index 57d0e82..bc42b91 100644
--- a/vendor/golang.org/x/net/webdav/prop_test.go
+++ b/vendor/golang.org/x/net/webdav/prop_test.go
@@ -10,6 +10,7 @@ import (
"net/http"
"os"
"reflect"
+ "regexp"
"sort"
"testing"
@@ -29,7 +30,7 @@ func TestMemPS(t *testing.T) {
for i, p := range pst.Props {
switch p.XMLName {
case xml.Name{Space: "DAV:", Local: "getlastmodified"}:
- p.InnerXML = []byte(fi.ModTime().Format(http.TimeFormat))
+ p.InnerXML = []byte(fi.ModTime().UTC().Format(http.TimeFormat))
pst.Props[i] = p
case xml.Name{Space: "DAV:", Local: "getetag"}:
if fi.IsDir() {
@@ -611,3 +612,106 @@ func (f noDeadPropsFile) Readdir(count int) ([]os.FileInfo, error) { return f.f
func (f noDeadPropsFile) Seek(off int64, whence int) (int64, error) { return f.f.Seek(off, whence) }
func (f noDeadPropsFile) Stat() (os.FileInfo, error) { return f.f.Stat() }
func (f noDeadPropsFile) Write(p []byte) (int, error) { return f.f.Write(p) }
+
+type overrideContentType struct {
+ os.FileInfo
+ contentType string
+ err error
+}
+
+func (o *overrideContentType) ContentType(ctx context.Context) (string, error) {
+ return o.contentType, o.err
+}
+
+func TestFindContentTypeOverride(t *testing.T) {
+ fs, err := buildTestFS([]string{"touch /file"})
+ if err != nil {
+ t.Fatalf("cannot create test filesystem: %v", err)
+ }
+ ctx := context.Background()
+ fi, err := fs.Stat(ctx, "/file")
+ if err != nil {
+ t.Fatalf("cannot Stat /file: %v", err)
+ }
+
+ // Check non overridden case
+ originalContentType, err := findContentType(ctx, fs, nil, "/file", fi)
+ if err != nil {
+ t.Fatalf("findContentType /file failed: %v", err)
+ }
+ if originalContentType != "text/plain; charset=utf-8" {
+ t.Fatalf("ContentType wrong want %q got %q", "text/plain; charset=utf-8", originalContentType)
+ }
+
+ // Now try overriding the ContentType
+ o := &overrideContentType{fi, "OverriddenContentType", nil}
+ ContentType, err := findContentType(ctx, fs, nil, "/file", o)
+ if err != nil {
+ t.Fatalf("findContentType /file failed: %v", err)
+ }
+ if ContentType != o.contentType {
+ t.Fatalf("ContentType wrong want %q got %q", o.contentType, ContentType)
+ }
+
+ // Now return ErrNotImplemented and check we get the original content type
+ o = &overrideContentType{fi, "OverriddenContentType", ErrNotImplemented}
+ ContentType, err = findContentType(ctx, fs, nil, "/file", o)
+ if err != nil {
+ t.Fatalf("findContentType /file failed: %v", err)
+ }
+ if ContentType != originalContentType {
+ t.Fatalf("ContentType wrong want %q got %q", originalContentType, ContentType)
+ }
+}
+
+type overrideETag struct {
+ os.FileInfo
+ eTag string
+ err error
+}
+
+func (o *overrideETag) ETag(ctx context.Context) (string, error) {
+ return o.eTag, o.err
+}
+
+func TestFindETagOverride(t *testing.T) {
+ fs, err := buildTestFS([]string{"touch /file"})
+ if err != nil {
+ t.Fatalf("cannot create test filesystem: %v", err)
+ }
+ ctx := context.Background()
+ fi, err := fs.Stat(ctx, "/file")
+ if err != nil {
+ t.Fatalf("cannot Stat /file: %v", err)
+ }
+
+ // Check non overridden case
+ originalETag, err := findETag(ctx, fs, nil, "/file", fi)
+ if err != nil {
+ t.Fatalf("findETag /file failed: %v", err)
+ }
+ matchETag := regexp.MustCompile(`^"-?[0-9a-f]{6,}"$`)
+ if !matchETag.MatchString(originalETag) {
+ t.Fatalf("ETag wrong, wanted something matching %v got %q", matchETag, originalETag)
+ }
+
+ // Now try overriding the ETag
+ o := &overrideETag{fi, `"OverriddenETag"`, nil}
+ ETag, err := findETag(ctx, fs, nil, "/file", o)
+ if err != nil {
+ t.Fatalf("findETag /file failed: %v", err)
+ }
+ if ETag != o.eTag {
+ t.Fatalf("ETag wrong want %q got %q", o.eTag, ETag)
+ }
+
+ // Now return ErrNotImplemented and check we get the original Etag
+ o = &overrideETag{fi, `"OverriddenETag"`, ErrNotImplemented}
+ ETag, err = findETag(ctx, fs, nil, "/file", o)
+ if err != nil {
+ t.Fatalf("findETag /file failed: %v", err)
+ }
+ if ETag != originalETag {
+ t.Fatalf("ETag wrong want %q got %q", originalETag, ETag)
+ }
+}