Selaa lähdekoodia

webdav: fixes for RFC/litmus compliance.

- Handles absence of If header.
- Reject Mkcol requests with a body.
- Delete on non-existant item should return 404.
- Support OPTIONS request.
- Ensure logger is invoked for all requests.

See original CL: golang.org/cl/178930043

Change-Id: Ic96aed10c54bb5ed0641092178ad6f15b1440cb4
Nick Cooper 11 vuotta sitten
vanhempi
commit
7b488c195c
1 muutettua tiedostoa jossa 40 lisäystä ja 4 poistoa
  1. 40 4
      webdav/webdav.go

+ 40 - 4
webdav/webdav.go

@@ -26,7 +26,7 @@ type Handler struct {
 	// PropSystem is an optional property management system. If non-nil, TODO.
 	PropSystem PropSystem
 	// Logger is an optional error logger. If non-nil, it will be called
-	// whenever handling a http.Request results in an error.
+	// for all HTTP requests.
 	Logger func(*http.Request, error)
 }
 
@@ -37,8 +37,10 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	} else if h.LockSystem == nil {
 		status, err = http.StatusInternalServerError, errNoLockSystem
 	} else {
-		// TODO: COPY, MOVE, PROPFIND, PROPPATCH methods. Also, OPTIONS??
+		// TODO: COPY, MOVE, PROPFIND, PROPPATCH methods.
 		switch r.Method {
+		case "OPTIONS":
+			status, err = h.handleOptions(w, r)
 		case "GET", "HEAD", "POST":
 			status, err = h.handleGetHeadPost(w, r)
 		case "DELETE":
@@ -60,13 +62,23 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 			w.Write([]byte(StatusText(status)))
 		}
 	}
-	if h.Logger != nil && err != nil {
+	if h.Logger != nil {
 		h.Logger(r, err)
 	}
 }
 
+type nopCloser struct{}
+
+func (nopCloser) Close() error {
+	return nil
+}
+
 func (h *Handler) confirmLocks(r *http.Request) (closer io.Closer, status int, err error) {
-	ih, ok := parseIfHeader(r.Header.Get("If"))
+	hdr := r.Header.Get("If")
+	if hdr == "" {
+		return nopCloser{}, 0, nil
+	}
+	ih, ok := parseIfHeader(hdr)
 	if !ok {
 		return nil, http.StatusBadRequest, errInvalidIfHeader
 	}
@@ -88,6 +100,24 @@ func (h *Handler) confirmLocks(r *http.Request) (closer io.Closer, status int, e
 	return nil, http.StatusPreconditionFailed, errLocked
 }
 
+func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request) (status int, err error) {
+	allow := "OPTIONS, LOCK, PUT, MKCOL"
+	if fi, err := h.FileSystem.Stat(r.URL.Path); err == nil {
+		if fi.IsDir() {
+			allow = "OPTIONS, LOCK, GET, HEAD, POST, DELETE, TRACE, PROPPATCH, COPY, MOVE, UNLOCK, PUT, PROPFIND"
+		} else {
+			allow = "OPTIONS, LOCK, GET, HEAD, POST, DELETE, TRACE, PROPPATCH, COPY, MOVE, UNLOCK"
+		}
+	}
+
+	// http://www.webdav.org/specs/rfc4918.html#dav.compliance.classes
+	w.Header().Set("DAV", "1, 2")
+	// http://msdn.microsoft.com/en-au/library/cc250217.aspx
+	w.Header().Set("MS-Author-Via", "DAV")
+	w.Header().Set("Allow", allow)
+	return 0, nil
+}
+
 func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request) (status int, err error) {
 	// TODO: check locks for read-only access??
 	f, err := h.FileSystem.OpenFile(r.URL.Path, os.O_RDONLY, 0)
@@ -111,6 +141,9 @@ func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request) (status i
 	defer closer.Close()
 
 	if err := h.FileSystem.RemoveAll(r.URL.Path); err != nil {
+		if os.IsNotExist(err) {
+			return http.StatusNotFound, err
+		}
 		// TODO: MultiStatus.
 		return http.StatusMethodNotAllowed, err
 	}
@@ -142,6 +175,9 @@ func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request) (status in
 	}
 	defer closer.Close()
 
+	if r.ContentLength > 0 {
+		return http.StatusUnsupportedMediaType, nil
+	}
 	if err := h.FileSystem.Mkdir(r.URL.Path, 0777); err != nil {
 		if os.IsNotExist(err) {
 			return http.StatusConflict, err