Keep resolve the xml.

This commit is contained in:
zicla 2019-04-18 03:40:57 +08:00
parent ba7b632046
commit 4611735283
6 changed files with 176 additions and 207 deletions

View File

@ -394,14 +394,14 @@ func (this *Handler) handleLock(w http.ResponseWriter, r *http.Request) (retStat
if err != nil {
return http.StatusBadRequest, err
}
li, status, err := readLockInfo(r.Body)
li, status, err := ReadLockInfo(r.Body)
if err != nil {
return status, err
}
ctx := r.Context()
token, ld, now, created := "", LockDetails{}, time.Now(), false
if li == (lockInfo{}) {
if li == (LockInfo{}) {
// An empty lockInfo means to refresh the lock.
ih, ok := parseIfHeader(r.Header.Get("If"))
if !ok {
@ -479,7 +479,7 @@ func (this *Handler) handleLock(w http.ResponseWriter, r *http.Request) (retStat
// and Handler.ServeHTTP would otherwise write "Created".
w.WriteHeader(http.StatusCreated)
}
writeLockInfo(w, token, ld)
WriteLockInfo(w, token, ld)
return 0, nil
}
@ -527,12 +527,12 @@ func (this *Handler) handlePropfind(writer http.ResponseWriter, request *http.Re
}
}
//读取出request希望获取的文件属性。
pf, status, err := readPropfind(request.Body)
pf, status, err := ReadPropfind(request.Body)
if err != nil {
return status, err
}
multiStatusWriter := multistatusWriter{w: writer}
multiStatusWriter := MultiStatusWriter{Writer: writer}
walkFn := func(reqPath string, info os.FileInfo, err error) error {
if err != nil {
@ -566,7 +566,7 @@ func (this *Handler) handlePropfind(writer http.ResponseWriter, request *http.Re
}
propstatResponse := makePropstatResponse(href, propstats)
return multiStatusWriter.write(propstatResponse)
return multiStatusWriter.Write(propstatResponse)
}
walkErr := walkFS(ctx, this.FileSystem, depth, reqPath, fileInfo, walkFn)
@ -599,7 +599,7 @@ func (this *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (st
}
return http.StatusMethodNotAllowed, err
}
patches, status, err := readProppatch(r.Body)
patches, status, err := ReadProppatch(r.Body)
if err != nil {
return status, err
}
@ -607,8 +607,8 @@ func (this *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (st
if err != nil {
return http.StatusInternalServerError, err
}
mw := multistatusWriter{w: w}
writeErr := mw.write(makePropstatResponse(r.URL.Path, pstats))
mw := MultiStatusWriter{Writer: w}
writeErr := mw.Write(makePropstatResponse(r.URL.Path, pstats))
closeErr := mw.close()
if writeErr != nil {
return http.StatusInternalServerError, writeErr
@ -619,17 +619,17 @@ func (this *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (st
return 0, nil
}
func makePropstatResponse(href string, pstats []Propstat) *response {
resp := response{
func makePropstatResponse(href string, pstats []Propstat) *Response {
resp := Response{
Href: []string{(&url.URL{Path: href}).EscapedPath()},
Propstat: make([]propstat, 0, len(pstats)),
Propstat: make([]SubPropstat, 0, len(pstats)),
}
for _, p := range pstats {
var xmlErr *xmlError
var xmlErr *XmlError
if p.XMLError != "" {
xmlErr = &xmlError{InnerXML: []byte(p.XMLError)}
xmlErr = &XmlError{InnerXML: []byte(p.XMLError)}
}
resp.Propstat = append(resp.Propstat, propstat{
resp.Propstat = append(resp.Propstat, SubPropstat{
Status: fmt.Sprintf("HTTP/1.1 %d %s", p.Status, StatusText(p.Status)),
Prop: p.Props,
ResponseDescription: p.ResponseDescription,

View File

@ -36,53 +36,53 @@ import (
)
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_lockinfo
type lockInfo struct {
type LockInfo struct {
XMLName ixml.Name `xml:"lockinfo"`
Exclusive *struct{} `xml:"lockscope>exclusive"`
Shared *struct{} `xml:"lockscope>shared"`
Write *struct{} `xml:"locktype>write"`
Owner owner `xml:"owner"`
Owner Owner `xml:"owner"`
}
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_owner
type owner struct {
type Owner struct {
InnerXML string `xml:",innerxml"`
}
func readLockInfo(r io.Reader) (li lockInfo, status int, err error) {
c := &countingReader{r: r}
func ReadLockInfo(r io.Reader) (li LockInfo, status int, err error) {
c := &CountingReader{r: r}
if err = ixml.NewDecoder(c).Decode(&li); err != nil {
if err == io.EOF {
if c.n == 0 {
// An empty body means to refresh the lock.
// http://www.webdav.org/specs/rfc4918.html#refreshing-locks
return lockInfo{}, 0, nil
return LockInfo{}, 0, nil
}
err = errInvalidLockInfo
}
return lockInfo{}, http.StatusBadRequest, err
return LockInfo{}, http.StatusBadRequest, err
}
// We only support exclusive (non-shared) write locks. In practice, these are
// the only types of locks that seem to matter.
if li.Exclusive == nil || li.Shared != nil || li.Write == nil {
return lockInfo{}, http.StatusNotImplemented, errUnsupportedLockInfo
return LockInfo{}, http.StatusNotImplemented, errUnsupportedLockInfo
}
return li, 0, nil
}
//这是一个带字节计数器的Reader可以知道总共读取了多少个字节。
type countingReader struct {
type CountingReader struct {
n int
r io.Reader
}
func (c *countingReader) Read(p []byte) (int, error) {
func (c *CountingReader) Read(p []byte) (int, error) {
n, err := c.r.Read(p)
c.n += n
return n, err
}
func writeLockInfo(w io.Writer, token string, ld LockDetails) (int, error) {
func WriteLockInfo(w io.Writer, token string, ld LockDetails) (int, error) {
depth := "infinity"
if ld.ZeroDepth {
depth = "0"
@ -135,13 +135,13 @@ func next(d *ixml.Decoder) (ixml.Token, error) {
}
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for propfind)
type propfindProps []xml.Name
type PropfindProps []xml.Name
// UnmarshalXML appends the property names enclosed within start to pn.
//
// It returns an error if start does not contain any properties or if
// properties contain values. Character data between properties is ignored.
func (pn *propfindProps) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) error {
func (pn *PropfindProps) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) error {
for {
t, err := next(d)
if err != nil {
@ -168,40 +168,40 @@ func (pn *propfindProps) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement)
}
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
type propfind struct {
type Propfind struct {
XMLName ixml.Name `xml:"DAV: propfind"`
Allprop *struct{} `xml:"DAV: allprop"`
Propname *struct{} `xml:"DAV: propname"`
Prop propfindProps `xml:"DAV: prop"`
Include propfindProps `xml:"DAV: include"`
Prop PropfindProps `xml:"DAV: prop"`
Include PropfindProps `xml:"DAV: include"`
}
//从request中读出需要的属性。比如getcontentlength 大小 creationdate 创建时间
func readPropfind(r io.Reader) (pf propfind, status int, err error) {
c := countingReader{r: r}
func ReadPropfind(r io.Reader) (pf Propfind, status int, err error) {
c := CountingReader{r: r}
if err = ixml.NewDecoder(&c).Decode(&pf); err != nil {
if err == io.EOF {
if c.n == 0 {
// An empty body means to propfind allprop.
// http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND
return propfind{Allprop: new(struct{})}, 0, nil
return Propfind{Allprop: new(struct{})}, 0, nil
}
err = errInvalidPropfind
}
return propfind{}, http.StatusBadRequest, err
return Propfind{}, http.StatusBadRequest, err
}
if pf.Allprop == nil && pf.Include != nil {
return propfind{}, http.StatusBadRequest, errInvalidPropfind
return Propfind{}, http.StatusBadRequest, errInvalidPropfind
}
if pf.Allprop != nil && (pf.Prop != nil || pf.Propname != nil) {
return propfind{}, http.StatusBadRequest, errInvalidPropfind
return Propfind{}, http.StatusBadRequest, errInvalidPropfind
}
if pf.Prop != nil && pf.Propname != nil {
return propfind{}, http.StatusBadRequest, errInvalidPropfind
return Propfind{}, http.StatusBadRequest, errInvalidPropfind
}
if pf.Propname == nil && pf.Allprop == nil && pf.Prop == nil {
return propfind{}, http.StatusBadRequest, errInvalidPropfind
return Propfind{}, http.StatusBadRequest, errInvalidPropfind
}
return pf, 0, nil
}
@ -228,49 +228,49 @@ type Property struct {
// ixmlProperty is the same as the Property type except it holds an ixml.Name
// instead of an xml.Name.
type ixmlProperty struct {
type IxmlProperty struct {
XMLName ixml.Name
Lang string `xml:"xml:lang,attr,omitempty"`
InnerXML []byte `xml:",innerxml"`
}
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
// See multistatusWriter for the "D:" namespace prefix.
type xmlError struct {
// See MultiStatusWriter for the "D:" namespace prefix.
type XmlError struct {
XMLName ixml.Name `xml:"D:error"`
InnerXML []byte `xml:",innerxml"`
}
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propstat
// See multistatusWriter for the "D:" namespace prefix.
type propstat struct {
// See MultiStatusWriter for the "D:" namespace prefix.
type SubPropstat struct {
Prop []Property `xml:"D:prop>_ignored_"`
Status string `xml:"D:status"`
Error *xmlError `xml:"D:error"`
Error *XmlError `xml:"D:error"`
ResponseDescription string `xml:"D:responsedescription,omitempty"`
}
// ixmlPropstat is the same as the propstat type except it holds an ixml.Name
// instead of an xml.Name.
type ixmlPropstat struct {
Prop []ixmlProperty `xml:"D:prop>_ignored_"`
type IxmlPropstat struct {
Prop []IxmlProperty `xml:"D:prop>_ignored_"`
Status string `xml:"D:status"`
Error *xmlError `xml:"D:error"`
Error *XmlError `xml:"D:error"`
ResponseDescription string `xml:"D:responsedescription,omitempty"`
}
// MarshalXML prepends the "D:" namespace prefix on properties in the DAV: namespace
// before encoding. See multistatusWriter.
func (ps propstat) MarshalXML(e *ixml.Encoder, start ixml.StartElement) error {
// before encoding. See MultiStatusWriter.
func (ps SubPropstat) MarshalXML(e *ixml.Encoder, start ixml.StartElement) error {
// Convert from a propstat to an ixmlPropstat.
ixmlPs := ixmlPropstat{
Prop: make([]ixmlProperty, len(ps.Prop)),
ixmlPs := IxmlPropstat{
Prop: make([]IxmlProperty, len(ps.Prop)),
Status: ps.Status,
Error: ps.Error,
ResponseDescription: ps.ResponseDescription,
}
for k, prop := range ps.Prop {
ixmlPs.Prop[k] = ixmlProperty{
ixmlPs.Prop[k] = IxmlProperty{
XMLName: ixml.Name(prop.XMLName),
Lang: prop.Lang,
InnerXML: prop.InnerXML,
@ -284,18 +284,18 @@ func (ps propstat) MarshalXML(e *ixml.Encoder, start ixml.StartElement) error {
}
}
// Distinct type to avoid infinite recursion of MarshalXML.
type newpropstat ixmlPropstat
type newpropstat IxmlPropstat
return e.EncodeElement(newpropstat(ixmlPs), start)
}
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_response
// See multistatusWriter for the "D:" namespace prefix.
type response struct {
// See MultiStatusWriter for the "D:" namespace prefix.
type Response struct {
XMLName ixml.Name `xml:"D:response"`
Href []string `xml:"D:href"`
Propstat []propstat `xml:"D:propstat"`
Propstat []SubPropstat `xml:"D:propstat"`
Status string `xml:"D:status,omitempty"`
Error *xmlError `xml:"D:error"`
Error *XmlError `xml:"D:error"`
ResponseDescription string `xml:"D:responsedescription,omitempty"`
}
@ -308,15 +308,15 @@ type response struct {
// 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
// written.
responseDescription string
ResponseDescription string
w http.ResponseWriter
enc *ixml.Encoder
Writer http.ResponseWriter
Encoder *ixml.Encoder
}
// Write validates and emits a DAV response as part of a multistatus response
@ -327,7 +327,7 @@ 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 (this *multistatusWriter) write(r *response) error {
func (this *MultiStatusWriter) Write(r *Response) error {
switch len(r.Href) {
case 0:
return errInvalidResponse
@ -344,24 +344,24 @@ func (this *multistatusWriter) write(r *response) error {
if err != nil {
return err
}
return this.enc.Encode(r)
return this.Encoder.Encode(r)
}
// 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 (this *multistatusWriter) writeHeader() error {
if this.enc != nil {
func (this *MultiStatusWriter) writeHeader() error {
if this.Encoder != nil {
return nil
}
this.w.Header().Add("Content-Type", "text/xml; charset=utf-8")
this.w.WriteHeader(StatusMulti)
_, err := fmt.Fprintf(this.w, `<?xml version="1.0" encoding="UTF-8"?>`)
this.Writer.Header().Add("Content-Type", "text/xml; charset=utf-8")
this.Writer.WriteHeader(StatusMulti)
_, err := fmt.Fprintf(this.Writer, `<?xml version="1.0" encoding="UTF-8"?>`)
if err != nil {
return err
}
this.enc = ixml.NewEncoder(this.w)
return this.enc.EncodeToken(ixml.StartElement{
this.Encoder = ixml.NewEncoder(this.Writer)
return this.Encoder.EncodeToken(ixml.StartElement{
Name: ixml.Name{
Space: "DAV:",
Local: "multistatus",
@ -375,18 +375,18 @@ func (this *multistatusWriter) writeHeader() error {
// Close completes the marshalling of the multistatus response. It returns
// 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 Encoder of w are nil, then no multistatus response has
// been written.
func (this *multistatusWriter) close() error {
if this.enc == nil {
func (this *MultiStatusWriter) close() error {
if this.Encoder == nil {
return nil
}
var end []ixml.Token
if this.responseDescription != "" {
if this.ResponseDescription != "" {
name := ixml.Name{Space: "DAV:", Local: "responsedescription"}
end = append(end,
ixml.StartElement{Name: name},
ixml.CharData(this.responseDescription),
ixml.CharData(this.ResponseDescription),
ixml.EndElement{Name: name},
)
}
@ -394,12 +394,12 @@ func (this *multistatusWriter) close() error {
Name: ixml.Name{Space: "DAV:", Local: "multistatus"},
})
for _, t := range end {
err := this.enc.EncodeToken(t)
err := this.Encoder.EncodeToken(t)
if err != nil {
return err
}
}
return this.enc.Flush()
return this.Encoder.Flush()
}
var xmlLangName = ixml.Name{Space: "http://www.w3.org/XML/1998/namespace", Local: "lang"}
@ -413,9 +413,9 @@ func xmlLang(s ixml.StartElement, d string) string {
return d
}
type xmlValue []byte
type XmlValue []byte
func (v *xmlValue) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) error {
func (v *XmlValue) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) error {
// The XML value of a property can be arbitrary, mixed-content XML.
// To make sure that the unmarshalled value contains all required
// namespaces, we encode all the property value XML tokens into a
@ -443,7 +443,7 @@ func (v *xmlValue) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) error
}
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for proppatch)
type proppatchProps []Property
type ProppatchProps []Property
// UnmarshalXML appends the property names and values enclosed within start
// to ps.
@ -453,7 +453,7 @@ type proppatchProps []Property
//
// UnmarshalXML returns an error if start does not contain any properties or if
// property values contain syntactically incorrect XML.
func (ps *proppatchProps) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) error {
func (ps *ProppatchProps) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement) error {
lang := xmlLang(start, "")
for {
t, err := next(d)
@ -471,7 +471,7 @@ func (ps *proppatchProps) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement)
XMLName: xml.Name(t.(ixml.StartElement).Name),
Lang: xmlLang(t.(ixml.StartElement), lang),
}
err = d.DecodeElement(((*xmlValue)(&p.InnerXML)), &elem)
err = d.DecodeElement(((*XmlValue)(&p.InnerXML)), &elem)
if err != nil {
return err
}
@ -482,21 +482,21 @@ func (ps *proppatchProps) UnmarshalXML(d *ixml.Decoder, start ixml.StartElement)
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_set
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_remove
type setRemove struct {
type SetRemove struct {
XMLName ixml.Name
Lang string `xml:"xml:lang,attr,omitempty"`
Prop proppatchProps `xml:"DAV: prop"`
Prop ProppatchProps `xml:"DAV: prop"`
}
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propertyupdate
type propertyupdate struct {
type PropertyUpdate struct {
XMLName ixml.Name `xml:"DAV: propertyupdate"`
Lang string `xml:"xml:lang,attr,omitempty"`
SetRemove []setRemove `xml:",any"`
SetRemove []SetRemove `xml:",any"`
}
func readProppatch(r io.Reader) (patches []Proppatch, status int, err error) {
var pu propertyupdate
func ReadProppatch(r io.Reader) (patches []Proppatch, status int, err error) {
var pu PropertyUpdate
if err = ixml.NewDecoder(r).Decode(&pu); err != nil {
return nil, http.StatusBadRequest, err
}

View File

@ -25,12 +25,12 @@ func TestReadLockInfo(t *testing.T) {
testCases := []struct {
desc string
input string
wantLI lockInfo
wantLI LockInfo
wantStatus int
}{{
"bad: junk",
"xxx",
lockInfo{},
LockInfo{},
http.StatusBadRequest,
}, {
"bad: invalid owner XML",
@ -42,7 +42,7 @@ func TestReadLockInfo(t *testing.T) {
" <D:href> no end tag \n" +
" </D:owner>\n" +
"</D:lockinfo>",
lockInfo{},
LockInfo{},
http.StatusBadRequest,
}, {
"bad: invalid UTF-8",
@ -54,7 +54,7 @@ func TestReadLockInfo(t *testing.T) {
" <D:href> \xff </D:href>\n" +
" </D:owner>\n" +
"</D:lockinfo>",
lockInfo{},
LockInfo{},
http.StatusBadRequest,
}, {
"bad: unfinished XML #1",
@ -62,7 +62,7 @@ func TestReadLockInfo(t *testing.T) {
"<D:lockinfo xmlns:D='DAV:'>\n" +
" <D:lockscope><D:exclusive/></D:lockscope>\n" +
" <D:locktype><D:write/></D:locktype>\n",
lockInfo{},
LockInfo{},
http.StatusBadRequest,
}, {
"bad: unfinished XML #2",
@ -71,12 +71,12 @@ func TestReadLockInfo(t *testing.T) {
" <D:lockscope><D:exclusive/></D:lockscope>\n" +
" <D:locktype><D:write/></D:locktype>\n" +
" <D:owner>\n",
lockInfo{},
LockInfo{},
http.StatusBadRequest,
}, {
"good: empty",
"",
lockInfo{},
LockInfo{},
0,
}, {
"good: plain-text owner",
@ -86,11 +86,11 @@ func TestReadLockInfo(t *testing.T) {
" <D:locktype><D:write/></D:locktype>\n" +
" <D:owner>gopher</D:owner>\n" +
"</D:lockinfo>",
lockInfo{
LockInfo{
XMLName: ixml.Name{Space: "DAV:", Local: "lockinfo"},
Exclusive: new(struct{}),
Write: new(struct{}),
Owner: owner{
Owner: Owner{
InnerXML: "gopher",
},
},
@ -105,11 +105,11 @@ func TestReadLockInfo(t *testing.T) {
" <D:href>http://example.org/~ejw/contact.html</D:href>\n" +
" </D:owner>\n" +
"</D:lockinfo>",
lockInfo{
LockInfo{
XMLName: ixml.Name{Space: "DAV:", Local: "lockinfo"},
Exclusive: new(struct{}),
Write: new(struct{}),
Owner: owner{
Owner: Owner{
InnerXML: "\n <D:href>http://example.org/~ejw/contact.html</D:href>\n ",
},
},
@ -117,7 +117,7 @@ func TestReadLockInfo(t *testing.T) {
}}
for _, tc := range testCases {
li, status, err := readLockInfo(strings.NewReader(tc.input))
li, status, err := ReadLockInfo(strings.NewReader(tc.input))
if tc.wantStatus != 0 {
if err == nil {
t.Errorf("%s: got nil error, want non-nil", tc.desc)
@ -139,7 +139,7 @@ func TestReadPropfind(t *testing.T) {
testCases := []struct {
desc string
input string
wantPF propfind
wantPF Propfind
wantStatus int
}{{
desc: "propfind: propname",
@ -147,14 +147,14 @@ func TestReadPropfind(t *testing.T) {
"<A:propfind xmlns:A='DAV:'>\n" +
" <A:propname/>\n" +
"</A:propfind>",
wantPF: propfind{
wantPF: Propfind{
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
Propname: new(struct{}),
},
}, {
desc: "propfind: empty body means allprop",
input: "",
wantPF: propfind{
wantPF: Propfind{
Allprop: new(struct{}),
},
}, {
@ -163,7 +163,7 @@ func TestReadPropfind(t *testing.T) {
"<A:propfind xmlns:A='DAV:'>\n" +
" <A:allprop/>\n" +
"</A:propfind>",
wantPF: propfind{
wantPF: Propfind{
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
Allprop: new(struct{}),
},
@ -174,10 +174,10 @@ func TestReadPropfind(t *testing.T) {
" <A:allprop/>\n" +
" <A:include><A:displayname/></A:include>\n" +
"</A:propfind>",
wantPF: propfind{
wantPF: Propfind{
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
Allprop: new(struct{}),
Include: propfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
Include: PropfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
},
}, {
desc: "propfind: include followed by allprop",
@ -186,10 +186,10 @@ func TestReadPropfind(t *testing.T) {
" <A:include><A:displayname/></A:include>\n" +
" <A:allprop/>\n" +
"</A:propfind>",
wantPF: propfind{
wantPF: Propfind{
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
Allprop: new(struct{}),
Include: propfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
Include: PropfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
},
}, {
desc: "propfind: propfind",
@ -197,9 +197,9 @@ func TestReadPropfind(t *testing.T) {
"<A:propfind xmlns:A='DAV:'>\n" +
" <A:prop><A:displayname/></A:prop>\n" +
"</A:propfind>",
wantPF: propfind{
wantPF: Propfind{
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
Prop: propfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
Prop: PropfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
},
}, {
desc: "propfind: prop with ignored comments",
@ -210,9 +210,9 @@ func TestReadPropfind(t *testing.T) {
" <A:displayname><!-- ignore --></A:displayname>\n" +
" </A:prop>\n" +
"</A:propfind>",
wantPF: propfind{
wantPF: Propfind{
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
Prop: propfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
Prop: PropfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
},
}, {
desc: "propfind: propfind with ignored whitespace",
@ -220,9 +220,9 @@ func TestReadPropfind(t *testing.T) {
"<A:propfind xmlns:A='DAV:'>\n" +
" <A:prop> <A:displayname/></A:prop>\n" +
"</A:propfind>",
wantPF: propfind{
wantPF: Propfind{
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
Prop: propfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
Prop: PropfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
},
}, {
desc: "propfind: propfind with ignored mixed-content",
@ -230,9 +230,9 @@ func TestReadPropfind(t *testing.T) {
"<A:propfind xmlns:A='DAV:'>\n" +
" <A:prop>foo<A:displayname/>bar</A:prop>\n" +
"</A:propfind>",
wantPF: propfind{
wantPF: Propfind{
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
Prop: propfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
Prop: PropfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
},
}, {
desc: "propfind: propname with ignored element (section A.4)",
@ -241,7 +241,7 @@ func TestReadPropfind(t *testing.T) {
" <A:propname/>\n" +
" <E:leave-out xmlns:E='E:'>*boss*</E:leave-out>\n" +
"</A:propfind>",
wantPF: propfind{
wantPF: Propfind{
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
Propname: new(struct{}),
},
@ -330,7 +330,7 @@ func TestReadPropfind(t *testing.T) {
}}
for _, tc := range testCases {
pf, status, err := readPropfind(strings.NewReader(tc.input))
pf, status, err := ReadPropfind(strings.NewReader(tc.input))
if tc.wantStatus != 0 {
if err == nil {
t.Errorf("%s: got nil error, want non-nil", tc.desc)
@ -353,7 +353,7 @@ func TestMultistatusWriter(t *testing.T) {
// http://www.webdav.org/specs/rfc4918.html
testCases := []struct {
desc string
responses []response
responses []Response
respdesc string
writeHeader bool
wantXML string
@ -361,9 +361,9 @@ func TestMultistatusWriter(t *testing.T) {
wantErr error
}{{
desc: "section 9.2.2 (failed dependency)",
responses: []response{{
responses: []Response{{
Href: []string{"http://example.com/foo"},
Propstat: []propstat{{
Propstat: []SubPropstat{{
Prop: []Property{{
XMLName: xml.Name{
Space: "http://ns.example.com/",
@ -405,10 +405,10 @@ func TestMultistatusWriter(t *testing.T) {
wantCode: StatusMulti,
}, {
desc: "section 9.6.2 (lock-token-submitted)",
responses: []response{{
responses: []Response{{
Href: []string{"http://example.com/foo"},
Status: "HTTP/1.1 423 Locked",
Error: &xmlError{
Error: &XmlError{
InnerXML: []byte(`<lock-token-submitted xmlns="DAV:"/>`),
},
}},
@ -424,9 +424,9 @@ func TestMultistatusWriter(t *testing.T) {
wantCode: StatusMulti,
}, {
desc: "section 9.1.3",
responses: []response{{
responses: []Response{{
Href: []string{"http://example.com/foo"},
Propstat: []propstat{{
Propstat: []SubPropstat{{
Prop: []Property{{
XMLName: xml.Name{Space: "http://ns.example.com/boxschema/", Local: "bigbox"},
InnerXML: []byte(`` +
@ -492,8 +492,8 @@ func TestMultistatusWriter(t *testing.T) {
wantCode: StatusMulti,
}, {
desc: "bad: no href",
responses: []response{{
Propstat: []propstat{{
responses: []Response{{
Propstat: []SubPropstat{{
Prop: []Property{{
XMLName: xml.Name{
Space: "http://example.com/",
@ -508,7 +508,7 @@ func TestMultistatusWriter(t *testing.T) {
wantCode: http.StatusOK,
}, {
desc: "bad: multiple hrefs and no status",
responses: []response{{
responses: []Response{{
Href: []string{"http://example.com/foo", "http://example.com/bar"},
}},
wantErr: errInvalidResponse,
@ -516,7 +516,7 @@ func TestMultistatusWriter(t *testing.T) {
wantCode: http.StatusOK,
}, {
desc: "bad: one href and no propstat",
responses: []response{{
responses: []Response{{
Href: []string{"http://example.com/foo"},
}},
wantErr: errInvalidResponse,
@ -524,9 +524,9 @@ func TestMultistatusWriter(t *testing.T) {
wantCode: http.StatusOK,
}, {
desc: "bad: status with one href and propstat",
responses: []response{{
responses: []Response{{
Href: []string{"http://example.com/foo"},
Propstat: []propstat{{
Propstat: []SubPropstat{{
Prop: []Property{{
XMLName: xml.Name{
Space: "http://example.com/",
@ -542,12 +542,12 @@ func TestMultistatusWriter(t *testing.T) {
wantCode: http.StatusOK,
}, {
desc: "bad: multiple hrefs and propstat",
responses: []response{{
responses: []Response{{
Href: []string{
"http://example.com/foo",
"http://example.com/bar",
},
Propstat: []propstat{{
Propstat: []SubPropstat{{
Prop: []Property{{
XMLName: xml.Name{
Space: "http://example.com/",
@ -566,7 +566,7 @@ func TestMultistatusWriter(t *testing.T) {
loop:
for _, tc := range testCases {
rec := httptest.NewRecorder()
w := multistatusWriter{w: rec, responseDescription: tc.respdesc}
w := MultiStatusWriter{Writer: 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)
@ -574,7 +574,7 @@ loop:
}
}
for _, r := range tc.responses {
if err := w.write(&r); err != nil {
if err := w.Write(&r); err != nil {
if err != tc.wantErr {
t.Errorf("%s: got write error %v, want %v",
tc.desc, err, tc.wantErr)
@ -705,7 +705,7 @@ func TestReadProppatch(t *testing.T) {
}}
for _, tc := range testCases {
pp, status, err := readProppatch(strings.NewReader(tc.input))
pp, status, err := ReadProppatch(strings.NewReader(tc.input))
if tc.wantStatus != 0 {
if err == nil {
t.Errorf("%s: got nil error, want non-nil", tc.desc)
@ -800,7 +800,7 @@ func TestUnmarshalXMLValue(t *testing.T) {
var n xmlNormalizer
for _, tc := range testCases {
d := ixml.NewDecoder(strings.NewReader(tc.input))
var v xmlValue
var v XmlValue
if err := d.Decode(&v); err != nil {
t.Errorf("%s: got error %v, want nil", tc.desc, err)
continue

View File

@ -98,7 +98,7 @@ func (this *DavController) Index(writer http.ResponseWriter, request *http.Reque
this.logger.Info("请求访问来了:%s %s", request.RequestURI, subPath)
method := request.Method
if method == "PROPFIND1" {
if method == "PROPFIND" {
this.davService.HandlePropfind(writer, request, subPath)

View File

@ -1,33 +1,3 @@
package rest
import (
"encoding/xml"
)
/**
*
* WebDav协议文档
* https://tools.ietf.org/html/rfc4918
* http://www.webdav.org/specs/rfc4918.html
*
*/
const (
//有多少层展示多少层
INFINITE_DEPTH = -1
)
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
//PROPFIND方法请求时POST BODY入参
type Propfind struct {
XMLName xml.Name `xml:"D:propfind"`
XmlNS string `xml:"xmlns:D,attr"`
Allprop *struct{} `xml:"D:allprop"`
Propname *struct{} `xml:"D:propname"`
Prop PropfindProps `xml:"D:prop"`
Include PropfindProps `xml:"D:include"`
}
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for propfind)
type PropfindProps []xml.Name

View File

@ -1,17 +1,17 @@
package rest
import (
"encoding/xml"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"tank/rest/dav"
)
/**
*
* WebDav协议文档
* https://tools.ietf.org/html/rfc4918
*
* 主要参考 golang.org/x/net/webdav
*/
//@Service
type DavService struct {
@ -30,22 +30,24 @@ func (this *DavService) Init() {
}
}
//从request中读取深度
func (this *DavService) readDepth(request *http.Request) int {
depth := INFINITE_DEPTH
if hdr := request.Header.Get("Depth"); hdr != "" {
if hdr == "0" {
depth = 0
} else if hdr == "1" {
depth = 1
} else if hdr == "infinity" {
depth = INFINITE_DEPTH
} else {
panic("Depth格式错误")
func (this *DavService) makePropstatResponse(href string, pstats []dav.Propstat) *dav.Response {
resp := dav.Response{
Href: []string{(&url.URL{Path: href}).EscapedPath()},
Propstat: make([]dav.SubPropstat, 0, len(pstats)),
}
for _, p := range pstats {
var xmlErr *dav.XmlError
if p.XMLError != "" {
xmlErr = &dav.XmlError{InnerXML: []byte(p.XMLError)}
}
return depth
resp.Propstat = append(resp.Propstat, dav.SubPropstat{
Status: fmt.Sprintf("HTTP/1.1 %d %s", p.Status, dav.StatusText(p.Status)),
Prop: p.Props,
ResponseDescription: p.ResponseDescription,
Error: xmlErr,
})
}
return &resp
}
//处理 方法
@ -54,28 +56,25 @@ func (this *DavService) HandlePropfind(writer http.ResponseWriter, request *http
//获取请求者
user := this.checkUser(writer, request)
//读取希望访问的深度。
depth := this.readDepth(request)
//找寻请求的目录
matter := this.matterDao.checkByUserUuidAndPath(user.Uuid, subPath)
//TODO: 读取请求参数。按照用户的参数请求返回内容。
propfind := &Propfind{}
body, err := ioutil.ReadAll(request.Body)
//读取请求参数。按照用户的参数请求返回内容。
propfind, _, err := dav.ReadPropfind(request.Body)
this.PanicError(err)
//从xml中解析内容到struct
err = xml.Unmarshal(body, &propfind)
//准备一个输出结果的Writer
multiStatusWriter := dav.MultiStatusWriter{Writer: writer}
var propstats []dav.Propstat
propstats = append(propstats, dav.Propstat{
ResponseDescription: "有点问题",
})
response := this.makePropstatResponse("/eyeblue/ready/go", propstats)
err = multiStatusWriter.Write(response)
this.PanicError(err)
//从struct还原到xml
output, err := xml.MarshalIndent(propfind, " ", " ")
this.PanicError(err)
fmt.Println(string(output))
fmt.Printf("%v %v \n", depth, matter.Name)
fmt.Printf("%v %v \n", matter.Name, propfind.Prop)
}