mirror of
https://github.com/1f349/go-webdav.git
synced 2024-12-22 16:24:14 +00:00
Expose MultistatusWriter
This commit is contained in:
parent
9131ab3eec
commit
777948e9c1
32
webdav.go
32
webdav.go
@ -8,7 +8,6 @@ package webdav
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -560,7 +559,7 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status
|
|||||||
return status, err
|
return status, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mw := multistatusWriter{w: w}
|
mw := MultistatusWriter{w: w}
|
||||||
|
|
||||||
walkFn := func(reqPath string, info os.FileInfo, err error) error {
|
walkFn := func(reqPath string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -585,11 +584,11 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)
|
walkErr := walkFS(ctx, h.FileSystem, depth, reqPath, fi, walkFn)
|
||||||
closeErr := mw.close()
|
closeErr := mw.Close()
|
||||||
if walkErr != nil {
|
if walkErr != nil {
|
||||||
return http.StatusInternalServerError, walkErr
|
return http.StatusInternalServerError, walkErr
|
||||||
}
|
}
|
||||||
@ -628,9 +627,9 @@ func (h *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (statu
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
mw := multistatusWriter{w: w}
|
mw := MultistatusWriter{w: w}
|
||||||
writeErr := mw.write(makePropstatResponse(r.URL.Path, pstats))
|
writeErr := mw.Write(makePropstatResponse(r.URL.Path, pstats))
|
||||||
closeErr := mw.close()
|
closeErr := mw.Close()
|
||||||
if writeErr != nil {
|
if writeErr != nil {
|
||||||
return http.StatusInternalServerError, writeErr
|
return http.StatusInternalServerError, writeErr
|
||||||
}
|
}
|
||||||
@ -640,24 +639,11 @@ func (h *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (statu
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePropstatResponse(href string, pstats []Propstat) *response {
|
func makePropstatResponse(href string, pstats []Propstat) *Response {
|
||||||
resp := response{
|
return &Response{
|
||||||
Href: []string{(&url.URL{Path: href}).EscapedPath()},
|
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 (
|
const (
|
||||||
|
55
xml.go
55
xml.go
@ -18,7 +18,7 @@ import (
|
|||||||
|
|
||||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_lockinfo
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_lockinfo
|
||||||
type lockInfo struct {
|
type lockInfo struct {
|
||||||
XMLName xml.Name `xml:"lockinfo"`
|
XMLName xml.Name `xml:"lockinfo"`
|
||||||
Exclusive *struct{} `xml:"lockscope>exclusive"`
|
Exclusive *struct{} `xml:"lockscope>exclusive"`
|
||||||
Shared *struct{} `xml:"lockscope>shared"`
|
Shared *struct{} `xml:"lockscope>shared"`
|
||||||
Write *struct{} `xml:"locktype>write"`
|
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
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
|
||||||
type propfind struct {
|
type propfind struct {
|
||||||
XMLName xml.Name `xml:"DAV: propfind"`
|
XMLName xml.Name `xml:"DAV: propfind"`
|
||||||
Allprop *struct{} `xml:"DAV: allprop"`
|
Allprop *struct{} `xml:"DAV: allprop"`
|
||||||
Propname *struct{} `xml:"DAV: propname"`
|
Propname *struct{} `xml:"DAV: propname"`
|
||||||
Prop propfindProps `xml:"DAV: prop"`
|
Prop propfindProps `xml:"DAV: prop"`
|
||||||
@ -208,7 +208,7 @@ type Property struct {
|
|||||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
|
||||||
type xmlError struct {
|
type xmlError struct {
|
||||||
XMLName xml.Name `xml:"DAV: error"`
|
XMLName xml.Name `xml:"DAV: error"`
|
||||||
InnerXML []byte `xml:",innerxml"`
|
InnerXML []byte `xml:",innerxml"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat
|
||||||
@ -219,9 +219,18 @@ type propstat struct {
|
|||||||
ResponseDescription string `xml:"DAV: responsedescription,omitempty"`
|
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
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_response
|
||||||
type response struct {
|
type response struct {
|
||||||
XMLName xml.Name `xml:"DAV: response"`
|
XMLName xml.Name `xml:"DAV: response"`
|
||||||
Href []string `xml:"DAV: href"`
|
Href []string `xml:"DAV: href"`
|
||||||
Propstat []propstat `xml:"DAV: propstat"`
|
Propstat []propstat `xml:"DAV: propstat"`
|
||||||
Status string `xml:"DAV: status,omitempty"`
|
Status string `xml:"DAV: status,omitempty"`
|
||||||
@ -232,13 +241,7 @@ type response struct {
|
|||||||
// MultistatusWriter marshals one or more Responses into a XML
|
// MultistatusWriter marshals one or more Responses into a XML
|
||||||
// multistatus response.
|
// multistatus response.
|
||||||
// See http://www.webdav.org/specs/rfc4918.html#ELEMENT_multistatus
|
// See http://www.webdav.org/specs/rfc4918.html#ELEMENT_multistatus
|
||||||
// TODO(rsto, mpl): As a workaround, the "D:" namespace prefix, defined as
|
type MultistatusWriter struct {
|
||||||
// "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 {
|
|
||||||
// ResponseDescription contains the optional responsedescription
|
// ResponseDescription contains the optional responsedescription
|
||||||
// of the multistatus XML element. Only the latest content before
|
// of the multistatus XML element. Only the latest content before
|
||||||
// close will be emitted. Empty response descriptions are not
|
// 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
|
// 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
|
// of r with a multistatus tag. Callers must call close after the last response
|
||||||
// has been written.
|
// 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) {
|
switch len(r.Href) {
|
||||||
case 0:
|
case 0:
|
||||||
return errInvalidResponse
|
return errInvalidResponse
|
||||||
@ -280,7 +305,7 @@ func (w *multistatusWriter) write(r *response) error {
|
|||||||
// writeHeader writes a XML multistatus start element on w's underlying
|
// writeHeader writes a XML multistatus start element on w's underlying
|
||||||
// http.ResponseWriter and returns the result of the write operation.
|
// http.ResponseWriter and returns the result of the write operation.
|
||||||
// After the first write attempt, writeHeader becomes a no-op.
|
// After the first write attempt, writeHeader becomes a no-op.
|
||||||
func (w *multistatusWriter) writeHeader() error {
|
func (w *MultistatusWriter) writeHeader() error {
|
||||||
if w.enc != nil {
|
if w.enc != nil {
|
||||||
return 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
|
// 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
|
// return value and field enc of w are nil, then no multistatus response has
|
||||||
// been written.
|
// been written.
|
||||||
func (w *multistatusWriter) close() error {
|
func (w *MultistatusWriter) Close() error {
|
||||||
if w.enc == nil {
|
if w.enc == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -420,7 +445,7 @@ type setRemove struct {
|
|||||||
|
|
||||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propertyupdate
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propertyupdate
|
||||||
type propertyupdate struct {
|
type propertyupdate struct {
|
||||||
XMLName xml.Name `xml:"DAV: propertyupdate"`
|
XMLName xml.Name `xml:"DAV: propertyupdate"`
|
||||||
Lang string `xml:"xml:lang,attr,omitempty"`
|
Lang string `xml:"xml:lang,attr,omitempty"`
|
||||||
SetRemove []setRemove `xml:",any"`
|
SetRemove []setRemove `xml:",any"`
|
||||||
}
|
}
|
||||||
|
@ -564,7 +564,7 @@ func TestMultistatusWriter(t *testing.T) {
|
|||||||
loop:
|
loop:
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
w := multistatusWriter{w: rec, responseDescription: tc.respdesc}
|
w := MultistatusWriter{w: rec, responseDescription: tc.respdesc}
|
||||||
if tc.writeHeader {
|
if tc.writeHeader {
|
||||||
if err := w.writeHeader(); err != nil {
|
if err := w.writeHeader(); err != nil {
|
||||||
t.Errorf("%s: got writeHeader error %v, want nil", tc.desc, err)
|
t.Errorf("%s: got writeHeader error %v, want nil", tc.desc, err)
|
||||||
@ -580,7 +580,7 @@ loop:
|
|||||||
continue 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",
|
t.Errorf("%s: got close error %v, want %v",
|
||||||
tc.desc, err, tc.wantErr)
|
tc.desc, err, tc.wantErr)
|
||||||
continue
|
continue
|
||||||
|
Loading…
Reference in New Issue
Block a user