Commit 610bfeeb authored by Robert Stepanek's avatar Robert Stepanek Committed by Nigel Tao

webdav: Simplify handling of Etag and Content-Type headers for GET, HEAD,

POST and PUT requests.

Change-Id: Id7b8a0aff7af1edefc45e2441565a2261f539895
Reviewed-on: https://go-review.googlesource.com/10395Reviewed-by: 's avatarNigel Tao <nigeltao@golang.org>
parent 4dbd2a10
...@@ -359,13 +359,8 @@ func findContentType(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) ...@@ -359,13 +359,8 @@ func findContentType(fs FileSystem, ls LockSystem, name string, fi os.FileInfo)
} }
func findETag(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) { func findETag(fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
return detectETag(fi), nil
}
// detectETag determines the ETag for the file described by fi.
func detectETag(fi os.FileInfo) string {
// The Apache http 2.4 web server by default concatenates the // The Apache http 2.4 web server by default concatenates the
// modification time and size of a file. We replicate the heuristic // modification time and size of a file. We replicate the heuristic
// with nanosecond granularity. // with nanosecond granularity.
return fmt.Sprintf(`"%x%x"`, fi.ModTime().UnixNano(), fi.Size()) return fmt.Sprintf(`"%x%x"`, fi.ModTime().UnixNano(), fi.Size()), nil
} }
...@@ -17,7 +17,7 @@ import ( ...@@ -17,7 +17,7 @@ import (
func TestMemPS(t *testing.T) { func TestMemPS(t *testing.T) {
// calcProps calculates the getlastmodified and getetag DAV: property // calcProps calculates the getlastmodified and getetag DAV: property
// values in pstats for resource name in file-system fs. // values in pstats for resource name in file-system fs.
calcProps := func(name string, fs FileSystem, pstats []Propstat) error { calcProps := func(name string, fs FileSystem, ls LockSystem, pstats []Propstat) error {
fi, err := fs.Stat(name) fi, err := fs.Stat(name)
if err != nil { if err != nil {
return err return err
...@@ -32,7 +32,11 @@ func TestMemPS(t *testing.T) { ...@@ -32,7 +32,11 @@ func TestMemPS(t *testing.T) {
if fi.IsDir() { if fi.IsDir() {
continue continue
} }
p.InnerXML = []byte(detectETag(fi)) etag, err := findETag(fs, ls, name, fi)
if err != nil {
return err
}
p.InnerXML = []byte(etag)
pst.Props[i] = p pst.Props[i] = p
} }
} }
...@@ -494,7 +498,7 @@ func TestMemPS(t *testing.T) { ...@@ -494,7 +498,7 @@ func TestMemPS(t *testing.T) {
ls := NewMemLS() ls := NewMemLS()
for _, op := range tc.propOp { for _, op := range tc.propOp {
desc := fmt.Sprintf("%s: %s %s", tc.desc, op.op, op.name) desc := fmt.Sprintf("%s: %s %s", tc.desc, op.op, op.name)
if err = calcProps(op.name, fs, op.wantPropstats); err != nil { if err = calcProps(op.name, fs, ls, op.wantPropstats); err != nil {
t.Fatalf("%s: calcProps: %v", desc, err) t.Fatalf("%s: calcProps: %v", desc, err)
} }
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
package webdav // import "golang.org/x/net/webdav" package webdav // import "golang.org/x/net/webdav"
import ( import (
"encoding/xml"
"errors" "errors"
"fmt" "fmt"
"io" "io"
...@@ -203,14 +202,14 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request) (sta ...@@ -203,14 +202,14 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request) (sta
if err != nil { if err != nil {
return http.StatusNotFound, err return http.StatusNotFound, err
} }
pstats, err := props(h.FileSystem, h.LockSystem, r.URL.Path, []xml.Name{ if !fi.IsDir() {
{Space: "DAV:", Local: "getetag"}, etag, err := findETag(h.FileSystem, h.LockSystem, r.URL.Path, fi)
{Space: "DAV:", Local: "getcontenttype"},
})
if err != nil { if err != nil {
return http.StatusInternalServerError, err return http.StatusInternalServerError, err
} }
writeDAVHeaders(w, pstats) w.Header().Set("ETag", etag)
}
// Let ServeContent determine the Content-Type header.
http.ServeContent(w, r, r.URL.Path, fi.ModTime(), f) http.ServeContent(w, r, r.URL.Path, fi.ModTime(), f)
return 0, nil return 0, nil
} }
...@@ -245,26 +244,31 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request) (status int, ...@@ -245,26 +244,31 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request) (status int,
return status, err return status, err
} }
defer release() defer release()
// TODO(rost): Support the If-Match, If-None-Match headers? See bradfitz'
// comments in http.checkEtag.
f, err := h.FileSystem.OpenFile(r.URL.Path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) f, err := h.FileSystem.OpenFile(r.URL.Path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil { if err != nil {
return http.StatusNotFound, err return http.StatusNotFound, err
} }
_, copyErr := io.Copy(f, r.Body) _, copyErr := io.Copy(f, r.Body)
fi, statErr := f.Stat()
closeErr := f.Close() closeErr := f.Close()
// TODO(rost): Returning 405 Method Not Allowed might not be appropriate.
if copyErr != nil { if copyErr != nil {
return http.StatusMethodNotAllowed, copyErr return http.StatusMethodNotAllowed, copyErr
} }
if statErr != nil {
return http.StatusMethodNotAllowed, statErr
}
if closeErr != nil { if closeErr != nil {
return http.StatusMethodNotAllowed, closeErr return http.StatusMethodNotAllowed, closeErr
} }
pstats, err := props(h.FileSystem, h.LockSystem, r.URL.Path, []xml.Name{ etag, err := findETag(h.FileSystem, h.LockSystem, r.URL.Path, fi)
{Space: "DAV:", Local: "getetag"},
})
if err != nil { if err != nil {
return http.StatusInternalServerError, err return http.StatusInternalServerError, err
} }
writeDAVHeaders(w, pstats) w.Header().Set("ETag", etag)
return http.StatusCreated, nil return http.StatusCreated, nil
} }
...@@ -557,26 +561,6 @@ func (h *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (statu ...@@ -557,26 +561,6 @@ func (h *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (statu
return 0, nil return 0, nil
} }
// davHeaderNames maps the names of DAV properties to their corresponding
// HTTP response headers.
var davHeaderNames = map[xml.Name]string{
xml.Name{Space: "DAV:", Local: "getetag"}: "ETag",
xml.Name{Space: "DAV:", Local: "getcontenttype"}: "Content-Type",
}
func writeDAVHeaders(w http.ResponseWriter, pstats []Propstat) {
for _, pst := range pstats {
if pst.Status == http.StatusOK {
for _, p := range pst.Props {
if n, ok := davHeaderNames[p.XMLName]; ok {
w.Header().Set(n, string(p.InnerXML))
}
}
break
}
}
}
func makePropstatResponse(href string, pstats []Propstat) *response { func makePropstatResponse(href string, pstats []Propstat) *response {
resp := response{ resp := response{
Href: []string{href}, Href: []string{href},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment