diff --git a/webdav.go b/webdav.go index 0307863..c2dbf6d 100644 --- a/webdav.go +++ b/webdav.go @@ -8,7 +8,6 @@ package webdav import ( "errors" - "fmt" "io" "net/http" "net/url" @@ -560,7 +559,7 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status return status, err } - mw := multistatusWriter{w: w} + mw := MultistatusWriter{w: w} walkFn := func(reqPath string, info os.FileInfo, err error) error { if err != nil { @@ -585,11 +584,11 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status if err != nil { return err } - return mw.write(makePropstatResponse(path.Join(h.Prefix, reqPath), pstats)) + return mw.Write(makePropstatResponse(path.Join(h.Prefix, reqPath), pstats)) } walkErr := walkFS(ctx, h.FileSystem, depth, reqPath, fi, walkFn) - closeErr := mw.close() + closeErr := mw.Close() if walkErr != nil { return http.StatusInternalServerError, walkErr } @@ -628,9 +627,9 @@ func (h *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (statu if err != nil { return http.StatusInternalServerError, err } - mw := multistatusWriter{w: w} - writeErr := mw.write(makePropstatResponse(r.URL.Path, pstats)) - closeErr := mw.close() + mw := MultistatusWriter{w: w} + writeErr := mw.Write(makePropstatResponse(r.URL.Path, pstats)) + closeErr := mw.Close() if writeErr != nil { return http.StatusInternalServerError, writeErr } @@ -640,24 +639,11 @@ func (h *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (statu return 0, nil } -func makePropstatResponse(href string, pstats []Propstat) *response { - resp := response{ +func makePropstatResponse(href string, pstats []Propstat) *Response { + return &Response{ Href: []string{(&url.URL{Path: href}).EscapedPath()}, - Propstat: make([]propstat, 0, len(pstats)), + Propstat: pstats, } - for _, p := range pstats { - var xmlErr *xmlError - if p.XMLError != "" { - xmlErr = &xmlError{InnerXML: []byte(p.XMLError)} - } - resp.Propstat = append(resp.Propstat, propstat{ - Status: fmt.Sprintf("HTTP/1.1 %d %s", p.Status, StatusText(p.Status)), - Prop: p.Props, - ResponseDescription: p.ResponseDescription, - Error: xmlErr, - }) - } - return &resp } const ( diff --git a/xml.go b/xml.go index 0c8ac75..4fb0b2b 100644 --- a/xml.go +++ b/xml.go @@ -18,7 +18,7 @@ import ( // http://www.webdav.org/specs/rfc4918.html#ELEMENT_lockinfo type lockInfo struct { - XMLName xml.Name `xml:"lockinfo"` + XMLName xml.Name `xml:"lockinfo"` Exclusive *struct{} `xml:"lockscope>exclusive"` Shared *struct{} `xml:"lockscope>shared"` Write *struct{} `xml:"locktype>write"` @@ -149,7 +149,7 @@ func (pn *propfindProps) UnmarshalXML(d *xml.Decoder, start xml.StartElement) er // http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind type propfind struct { - XMLName xml.Name `xml:"DAV: propfind"` + XMLName xml.Name `xml:"DAV: propfind"` Allprop *struct{} `xml:"DAV: allprop"` Propname *struct{} `xml:"DAV: propname"` Prop propfindProps `xml:"DAV: prop"` @@ -208,7 +208,7 @@ type Property struct { // http://www.webdav.org/specs/rfc4918.html#ELEMENT_error type xmlError struct { XMLName xml.Name `xml:"DAV: error"` - InnerXML []byte `xml:",innerxml"` + InnerXML []byte `xml:",innerxml"` } // http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat @@ -219,9 +219,18 @@ type propstat struct { ResponseDescription string `xml:"DAV: responsedescription,omitempty"` } +// http://www.webdav.org/specs/rfc4918.html#ELEMENT_response +type Response struct { + Href []string + Propstat []Propstat + Status int + XMLError string + ResponseDescription string +} + // http://www.webdav.org/specs/rfc4918.html#ELEMENT_response type response struct { - XMLName xml.Name `xml:"DAV: response"` + XMLName xml.Name `xml:"DAV: response"` Href []string `xml:"DAV: href"` Propstat []propstat `xml:"DAV: propstat"` Status string `xml:"DAV: status,omitempty"` @@ -232,13 +241,7 @@ type response struct { // MultistatusWriter marshals one or more Responses into a XML // multistatus response. // See http://www.webdav.org/specs/rfc4918.html#ELEMENT_multistatus -// TODO(rsto, mpl): As a workaround, the "D:" namespace prefix, defined as -// "DAV:" on this element, is prepended on the nested response, as well as on all -// its nested elements. All property names in the DAV: namespace are prefixed as -// well. This is because some versions of Mini-Redirector (on windows 7) ignore -// elements with a default namespace (no prefixed namespace). A less intrusive fix -// should be possible after golang.org/cl/11074. See https://golang.org/issue/11177 -type multistatusWriter struct { +type MultistatusWriter struct { // ResponseDescription contains the optional responsedescription // of the multistatus XML element. Only the latest content before // close will be emitted. Empty response descriptions are not @@ -257,7 +260,29 @@ type multistatusWriter struct { // first, valid response to be written, Write prepends the XML representation // of r with a multistatus tag. Callers must call close after the last response // has been written. -func (w *multistatusWriter) write(r *response) error { +func (w *MultistatusWriter) Write(r *Response) error { + rr := &response{ + Href: r.Href, + Propstat: make([]propstat, 0, len(r.Propstat)), + } + + for _, p := range r.Propstat { + var xmlErr *xmlError + if p.XMLError != "" { + xmlErr = &xmlError{InnerXML: []byte(p.XMLError)} + } + rr.Propstat = append(rr.Propstat, propstat{ + Status: fmt.Sprintf("HTTP/1.1 %d %s", p.Status, StatusText(p.Status)), + Prop: p.Props, + ResponseDescription: p.ResponseDescription, + Error: xmlErr, + }) + } + + return w.write(rr) +} + +func (w *MultistatusWriter) write(r *response) error { switch len(r.Href) { case 0: return errInvalidResponse @@ -280,7 +305,7 @@ func (w *multistatusWriter) write(r *response) error { // writeHeader writes a XML multistatus start element on w's underlying // http.ResponseWriter and returns the result of the write operation. // After the first write attempt, writeHeader becomes a no-op. -func (w *multistatusWriter) writeHeader() error { +func (w *MultistatusWriter) writeHeader() error { if w.enc != nil { return nil } @@ -307,7 +332,7 @@ func (w *multistatusWriter) writeHeader() error { // an error if the multistatus response could not be completed. If both the // return value and field enc of w are nil, then no multistatus response has // been written. -func (w *multistatusWriter) close() error { +func (w *MultistatusWriter) Close() error { if w.enc == nil { return nil } @@ -420,7 +445,7 @@ type setRemove struct { // http://www.webdav.org/specs/rfc4918.html#ELEMENT_propertyupdate type propertyupdate struct { - XMLName xml.Name `xml:"DAV: propertyupdate"` + XMLName xml.Name `xml:"DAV: propertyupdate"` Lang string `xml:"xml:lang,attr,omitempty"` SetRemove []setRemove `xml:",any"` } diff --git a/xml_test.go b/xml_test.go index 12b599a..7fe5683 100644 --- a/xml_test.go +++ b/xml_test.go @@ -564,7 +564,7 @@ func TestMultistatusWriter(t *testing.T) { loop: for _, tc := range testCases { rec := httptest.NewRecorder() - w := multistatusWriter{w: rec, responseDescription: tc.respdesc} + w := MultistatusWriter{w: rec, responseDescription: tc.respdesc} if tc.writeHeader { if err := w.writeHeader(); err != nil { t.Errorf("%s: got writeHeader error %v, want nil", tc.desc, err) @@ -580,7 +580,7 @@ loop: continue loop } } - if err := w.close(); err != tc.wantErr { + if err := w.Close(); err != tc.wantErr { t.Errorf("%s: got close error %v, want %v", tc.desc, err, tc.wantErr) continue