Keep resolve the xml.
This commit is contained in:
parent
ba7b632046
commit
4611735283
@ -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,
|
||||
|
148
rest/dav/xml.go
148
rest/dav/xml.go
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user