mirror of
https://github.com/1f349/go-webdav.git
synced 2024-12-22 08:14:15 +00:00
webdav: add support for ETag to client & server
This commit is contained in:
parent
3268102d5a
commit
f04c1c9421
27
client.go
27
client.go
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/emersion/go-webdav/internal"
|
||||
@ -45,6 +46,14 @@ func (c *Client) FindCurrentUserPrincipal() (string, error) {
|
||||
return prop.Href.Path, nil
|
||||
}
|
||||
|
||||
var fileInfoPropfind = internal.NewPropNamePropfind(
|
||||
internal.ResourceTypeName,
|
||||
internal.GetContentLengthName,
|
||||
internal.GetLastModifiedName,
|
||||
internal.GetContentTypeName,
|
||||
internal.GetETagName,
|
||||
)
|
||||
|
||||
func fileInfoFromResponse(resp *internal.Response) (*FileInfo, error) {
|
||||
path, err := resp.Path()
|
||||
if err != nil {
|
||||
@ -75,22 +84,24 @@ func fileInfoFromResponse(resp *internal.Response) (*FileInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var getETag internal.GetETag
|
||||
if err := resp.DecodeProp(&getETag); err != nil && !internal.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
etag, err := strconv.Unquote(getETag.ETag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("webdav: failed to unquote ETag: %v", err)
|
||||
}
|
||||
|
||||
fi.Size = getLen.Length
|
||||
fi.ModTime = time.Time(getMod.LastModified)
|
||||
fi.MIMEType = getType.Type
|
||||
fi.ETag = etag
|
||||
}
|
||||
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
// TODO: getetag
|
||||
var fileInfoPropfind = internal.NewPropNamePropfind(
|
||||
internal.ResourceTypeName,
|
||||
internal.GetContentLengthName,
|
||||
internal.GetLastModifiedName,
|
||||
internal.GetContentTypeName,
|
||||
)
|
||||
|
||||
func (c *Client) Stat(name string) (*FileInfo, error) {
|
||||
resp, err := c.ic.PropfindFlat(name, fileInfoPropfind)
|
||||
if err != nil {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
@ -49,6 +50,12 @@ func fileInfoFromOS(p string, fi os.FileInfo) *FileInfo {
|
||||
IsDir: fi.IsDir(),
|
||||
// TODO: fallback to http.DetectContentType?
|
||||
MIMEType: mime.TypeByExtension(path.Ext(p)),
|
||||
// RFC 2616 section 13.3.3 describes strong ETags. Ideally these would
|
||||
// be checksums or sequence numbers, however these are expensive to
|
||||
// compute. The modification time with nanosecond granularity is good
|
||||
// enough, as it's very unlikely for the same file to be modified twice
|
||||
// during a single nanosecond.
|
||||
ETag: fmt.Sprintf("%x%x", fi.ModTime().UnixNano(), fi.Size()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,6 +339,12 @@ type GetLastModified struct {
|
||||
LastModified Time `xml:",chardata"`
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc4918#section-15.6
|
||||
type GetETag struct {
|
||||
XMLName xml.Name `xml:"DAV: getetag"`
|
||||
ETag string `xml:",chardata"`
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc4918#section-14.5
|
||||
type Error struct {
|
||||
XMLName xml.Name `xml:"DAV: error"`
|
||||
|
12
server.go
12
server.go
@ -2,6 +2,7 @@ package webdav
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -87,13 +88,16 @@ func (b *backend) HeadGet(w http.ResponseWriter, r *http.Request) error {
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(fi.Size, 10))
|
||||
if fi.MIMEType != "" {
|
||||
w.Header().Set("Content-Type", fi.MIMEType)
|
||||
}
|
||||
if !fi.ModTime.IsZero() {
|
||||
w.Header().Set("Last-Modified", fi.ModTime.UTC().Format(http.TimeFormat))
|
||||
}
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(fi.Size, 10))
|
||||
if fi.ETag != "" {
|
||||
w.Header().Set("ETag", fmt.Sprintf("%q", fi.ETag))
|
||||
}
|
||||
|
||||
if rs, ok := f.(io.ReadSeeker); ok {
|
||||
// If it's an io.Seeker, use http.ServeContent which supports ranges
|
||||
@ -171,7 +175,11 @@ func (b *backend) propfindFile(propfind *internal.Propfind, fi *FileInfo) (*inte
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: getetag
|
||||
if fi.ETag != "" {
|
||||
props[internal.GetETagName] = func(*internal.RawXMLValue) (interface{}, error) {
|
||||
return &internal.GetETag{ETag: fmt.Sprintf("%q", fi.ETag)}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return internal.NewPropfindResponse(fi.Path, propfind, props)
|
||||
|
Loading…
Reference in New Issue
Block a user