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 {
|
if err != nil {
|
||||||
return http.StatusBadRequest, err
|
return http.StatusBadRequest, err
|
||||||
}
|
}
|
||||||
li, status, err := readLockInfo(r.Body)
|
li, status, err := ReadLockInfo(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return status, err
|
return status, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
token, ld, now, created := "", LockDetails{}, time.Now(), false
|
token, ld, now, created := "", LockDetails{}, time.Now(), false
|
||||||
if li == (lockInfo{}) {
|
if li == (LockInfo{}) {
|
||||||
// An empty lockInfo means to refresh the lock.
|
// An empty lockInfo means to refresh the lock.
|
||||||
ih, ok := parseIfHeader(r.Header.Get("If"))
|
ih, ok := parseIfHeader(r.Header.Get("If"))
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -479,7 +479,7 @@ func (this *Handler) handleLock(w http.ResponseWriter, r *http.Request) (retStat
|
|||||||
// and Handler.ServeHTTP would otherwise write "Created".
|
// and Handler.ServeHTTP would otherwise write "Created".
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
}
|
}
|
||||||
writeLockInfo(w, token, ld)
|
WriteLockInfo(w, token, ld)
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,12 +527,12 @@ func (this *Handler) handlePropfind(writer http.ResponseWriter, request *http.Re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//读取出request希望获取的文件属性。
|
//读取出request希望获取的文件属性。
|
||||||
pf, status, err := readPropfind(request.Body)
|
pf, status, err := ReadPropfind(request.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return status, err
|
return status, err
|
||||||
}
|
}
|
||||||
|
|
||||||
multiStatusWriter := multistatusWriter{w: writer}
|
multiStatusWriter := MultiStatusWriter{Writer: writer}
|
||||||
|
|
||||||
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 {
|
||||||
@ -566,7 +566,7 @@ func (this *Handler) handlePropfind(writer http.ResponseWriter, request *http.Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
propstatResponse := makePropstatResponse(href, propstats)
|
propstatResponse := makePropstatResponse(href, propstats)
|
||||||
return multiStatusWriter.write(propstatResponse)
|
return multiStatusWriter.Write(propstatResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
walkErr := walkFS(ctx, this.FileSystem, depth, reqPath, fileInfo, walkFn)
|
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
|
return http.StatusMethodNotAllowed, err
|
||||||
}
|
}
|
||||||
patches, status, err := readProppatch(r.Body)
|
patches, status, err := ReadProppatch(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return status, err
|
return status, err
|
||||||
}
|
}
|
||||||
@ -607,8 +607,8 @@ func (this *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (st
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
mw := multistatusWriter{w: w}
|
mw := MultiStatusWriter{Writer: 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
|
||||||
@ -619,17 +619,17 @@ func (this *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (st
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePropstatResponse(href string, pstats []Propstat) *response {
|
func makePropstatResponse(href string, pstats []Propstat) *Response {
|
||||||
resp := response{
|
resp := Response{
|
||||||
Href: []string{(&url.URL{Path: href}).EscapedPath()},
|
Href: []string{(&url.URL{Path: href}).EscapedPath()},
|
||||||
Propstat: make([]propstat, 0, len(pstats)),
|
Propstat: make([]SubPropstat, 0, len(pstats)),
|
||||||
}
|
}
|
||||||
for _, p := range pstats {
|
for _, p := range pstats {
|
||||||
var xmlErr *xmlError
|
var xmlErr *XmlError
|
||||||
if p.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)),
|
Status: fmt.Sprintf("HTTP/1.1 %d %s", p.Status, StatusText(p.Status)),
|
||||||
Prop: p.Props,
|
Prop: p.Props,
|
||||||
ResponseDescription: p.ResponseDescription,
|
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
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_lockinfo
|
||||||
type lockInfo struct {
|
type LockInfo struct {
|
||||||
XMLName ixml.Name `xml:"lockinfo"`
|
XMLName ixml.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"`
|
||||||
Owner owner `xml:"owner"`
|
Owner Owner `xml:"owner"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_owner
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_owner
|
||||||
type owner struct {
|
type Owner struct {
|
||||||
InnerXML string `xml:",innerxml"`
|
InnerXML string `xml:",innerxml"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func readLockInfo(r io.Reader) (li lockInfo, status int, err error) {
|
func ReadLockInfo(r io.Reader) (li LockInfo, status int, err error) {
|
||||||
c := &countingReader{r: r}
|
c := &CountingReader{r: r}
|
||||||
if err = ixml.NewDecoder(c).Decode(&li); err != nil {
|
if err = ixml.NewDecoder(c).Decode(&li); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
if c.n == 0 {
|
if c.n == 0 {
|
||||||
// An empty body means to refresh the lock.
|
// An empty body means to refresh the lock.
|
||||||
// http://www.webdav.org/specs/rfc4918.html#refreshing-locks
|
// http://www.webdav.org/specs/rfc4918.html#refreshing-locks
|
||||||
return lockInfo{}, 0, nil
|
return LockInfo{}, 0, nil
|
||||||
}
|
}
|
||||||
err = errInvalidLockInfo
|
err = errInvalidLockInfo
|
||||||
}
|
}
|
||||||
return lockInfo{}, http.StatusBadRequest, err
|
return LockInfo{}, http.StatusBadRequest, err
|
||||||
}
|
}
|
||||||
// We only support exclusive (non-shared) write locks. In practice, these are
|
// We only support exclusive (non-shared) write locks. In practice, these are
|
||||||
// the only types of locks that seem to matter.
|
// the only types of locks that seem to matter.
|
||||||
if li.Exclusive == nil || li.Shared != nil || li.Write == nil {
|
if li.Exclusive == nil || li.Shared != nil || li.Write == nil {
|
||||||
return lockInfo{}, http.StatusNotImplemented, errUnsupportedLockInfo
|
return LockInfo{}, http.StatusNotImplemented, errUnsupportedLockInfo
|
||||||
}
|
}
|
||||||
return li, 0, nil
|
return li, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//这是一个带字节计数器的Reader,可以知道总共读取了多少个字节。
|
//这是一个带字节计数器的Reader,可以知道总共读取了多少个字节。
|
||||||
type countingReader struct {
|
type CountingReader struct {
|
||||||
n int
|
n int
|
||||||
r io.Reader
|
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)
|
n, err := c.r.Read(p)
|
||||||
c.n += n
|
c.n += n
|
||||||
return n, err
|
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"
|
depth := "infinity"
|
||||||
if ld.ZeroDepth {
|
if ld.ZeroDepth {
|
||||||
depth = "0"
|
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)
|
// 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.
|
// UnmarshalXML appends the property names enclosed within start to pn.
|
||||||
//
|
//
|
||||||
// It returns an error if start does not contain any properties or if
|
// It returns an error if start does not contain any properties or if
|
||||||
// properties contain values. Character data between properties is ignored.
|
// 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 {
|
for {
|
||||||
t, err := next(d)
|
t, err := next(d)
|
||||||
if err != nil {
|
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
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propfind
|
||||||
type propfind struct {
|
type Propfind struct {
|
||||||
XMLName ixml.Name `xml:"DAV: propfind"`
|
XMLName ixml.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"`
|
||||||
Include propfindProps `xml:"DAV: include"`
|
Include PropfindProps `xml:"DAV: include"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//从request中读出需要的属性。比如:getcontentlength 大小 creationdate 创建时间
|
//从request中读出需要的属性。比如:getcontentlength 大小 creationdate 创建时间
|
||||||
func readPropfind(r io.Reader) (pf propfind, status int, err error) {
|
func ReadPropfind(r io.Reader) (pf Propfind, status int, err error) {
|
||||||
c := countingReader{r: r}
|
c := CountingReader{r: r}
|
||||||
if err = ixml.NewDecoder(&c).Decode(&pf); err != nil {
|
if err = ixml.NewDecoder(&c).Decode(&pf); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
if c.n == 0 {
|
if c.n == 0 {
|
||||||
// An empty body means to propfind allprop.
|
// An empty body means to propfind allprop.
|
||||||
// http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND
|
// 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
|
err = errInvalidPropfind
|
||||||
}
|
}
|
||||||
return propfind{}, http.StatusBadRequest, err
|
return Propfind{}, http.StatusBadRequest, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if pf.Allprop == nil && pf.Include != nil {
|
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) {
|
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 {
|
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 {
|
if pf.Propname == nil && pf.Allprop == nil && pf.Prop == nil {
|
||||||
return propfind{}, http.StatusBadRequest, errInvalidPropfind
|
return Propfind{}, http.StatusBadRequest, errInvalidPropfind
|
||||||
}
|
}
|
||||||
return pf, 0, nil
|
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
|
// ixmlProperty is the same as the Property type except it holds an ixml.Name
|
||||||
// instead of an xml.Name.
|
// instead of an xml.Name.
|
||||||
type ixmlProperty struct {
|
type IxmlProperty struct {
|
||||||
XMLName ixml.Name
|
XMLName ixml.Name
|
||||||
Lang string `xml:"xml:lang,attr,omitempty"`
|
Lang string `xml:"xml:lang,attr,omitempty"`
|
||||||
InnerXML []byte `xml:",innerxml"`
|
InnerXML []byte `xml:",innerxml"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
|
||||||
// See multistatusWriter for the "D:" namespace prefix.
|
// See MultiStatusWriter for the "D:" namespace prefix.
|
||||||
type xmlError struct {
|
type XmlError struct {
|
||||||
XMLName ixml.Name `xml:"D:error"`
|
XMLName ixml.Name `xml:"D: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
|
||||||
// See multistatusWriter for the "D:" namespace prefix.
|
// See MultiStatusWriter for the "D:" namespace prefix.
|
||||||
type propstat struct {
|
type SubPropstat struct {
|
||||||
Prop []Property `xml:"D:prop>_ignored_"`
|
Prop []Property `xml:"D:prop>_ignored_"`
|
||||||
Status string `xml:"D:status"`
|
Status string `xml:"D:status"`
|
||||||
Error *xmlError `xml:"D:error"`
|
Error *XmlError `xml:"D:error"`
|
||||||
ResponseDescription string `xml:"D:responsedescription,omitempty"`
|
ResponseDescription string `xml:"D:responsedescription,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ixmlPropstat is the same as the propstat type except it holds an ixml.Name
|
// ixmlPropstat is the same as the propstat type except it holds an ixml.Name
|
||||||
// instead of an xml.Name.
|
// instead of an xml.Name.
|
||||||
type ixmlPropstat struct {
|
type IxmlPropstat struct {
|
||||||
Prop []ixmlProperty `xml:"D:prop>_ignored_"`
|
Prop []IxmlProperty `xml:"D:prop>_ignored_"`
|
||||||
Status string `xml:"D:status"`
|
Status string `xml:"D:status"`
|
||||||
Error *xmlError `xml:"D:error"`
|
Error *XmlError `xml:"D:error"`
|
||||||
ResponseDescription string `xml:"D:responsedescription,omitempty"`
|
ResponseDescription string `xml:"D:responsedescription,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalXML prepends the "D:" namespace prefix on properties in the DAV: namespace
|
// MarshalXML prepends the "D:" namespace prefix on properties in the DAV: namespace
|
||||||
// before encoding. See multistatusWriter.
|
// before encoding. See MultiStatusWriter.
|
||||||
func (ps propstat) MarshalXML(e *ixml.Encoder, start ixml.StartElement) error {
|
func (ps SubPropstat) MarshalXML(e *ixml.Encoder, start ixml.StartElement) error {
|
||||||
// Convert from a propstat to an ixmlPropstat.
|
// Convert from a propstat to an ixmlPropstat.
|
||||||
ixmlPs := ixmlPropstat{
|
ixmlPs := IxmlPropstat{
|
||||||
Prop: make([]ixmlProperty, len(ps.Prop)),
|
Prop: make([]IxmlProperty, len(ps.Prop)),
|
||||||
Status: ps.Status,
|
Status: ps.Status,
|
||||||
Error: ps.Error,
|
Error: ps.Error,
|
||||||
ResponseDescription: ps.ResponseDescription,
|
ResponseDescription: ps.ResponseDescription,
|
||||||
}
|
}
|
||||||
for k, prop := range ps.Prop {
|
for k, prop := range ps.Prop {
|
||||||
ixmlPs.Prop[k] = ixmlProperty{
|
ixmlPs.Prop[k] = IxmlProperty{
|
||||||
XMLName: ixml.Name(prop.XMLName),
|
XMLName: ixml.Name(prop.XMLName),
|
||||||
Lang: prop.Lang,
|
Lang: prop.Lang,
|
||||||
InnerXML: prop.InnerXML,
|
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.
|
// Distinct type to avoid infinite recursion of MarshalXML.
|
||||||
type newpropstat ixmlPropstat
|
type newpropstat IxmlPropstat
|
||||||
return e.EncodeElement(newpropstat(ixmlPs), start)
|
return e.EncodeElement(newpropstat(ixmlPs), start)
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_response
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_response
|
||||||
// See multistatusWriter for the "D:" namespace prefix.
|
// See MultiStatusWriter for the "D:" namespace prefix.
|
||||||
type response struct {
|
type Response struct {
|
||||||
XMLName ixml.Name `xml:"D:response"`
|
XMLName ixml.Name `xml:"D:response"`
|
||||||
Href []string `xml:"D:href"`
|
Href []string `xml:"D:href"`
|
||||||
Propstat []propstat `xml:"D:propstat"`
|
Propstat []SubPropstat `xml:"D:propstat"`
|
||||||
Status string `xml:"D:status,omitempty"`
|
Status string `xml:"D:status,omitempty"`
|
||||||
Error *xmlError `xml:"D:error"`
|
Error *XmlError `xml:"D:error"`
|
||||||
ResponseDescription string `xml:"D:responsedescription,omitempty"`
|
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
|
// 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
|
// 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
|
// 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
|
// 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
|
||||||
// written.
|
// written.
|
||||||
responseDescription string
|
ResponseDescription string
|
||||||
|
|
||||||
w http.ResponseWriter
|
Writer http.ResponseWriter
|
||||||
enc *ixml.Encoder
|
Encoder *ixml.Encoder
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write validates and emits a DAV response as part of a multistatus response
|
// 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
|
// 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 (this *multistatusWriter) write(r *response) error {
|
func (this *MultiStatusWriter) Write(r *Response) error {
|
||||||
switch len(r.Href) {
|
switch len(r.Href) {
|
||||||
case 0:
|
case 0:
|
||||||
return errInvalidResponse
|
return errInvalidResponse
|
||||||
@ -344,24 +344,24 @@ func (this *multistatusWriter) write(r *response) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return this.enc.Encode(r)
|
return this.Encoder.Encode(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (this *multistatusWriter) writeHeader() error {
|
func (this *MultiStatusWriter) writeHeader() error {
|
||||||
if this.enc != nil {
|
if this.Encoder != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
this.w.Header().Add("Content-Type", "text/xml; charset=utf-8")
|
this.Writer.Header().Add("Content-Type", "text/xml; charset=utf-8")
|
||||||
this.w.WriteHeader(StatusMulti)
|
this.Writer.WriteHeader(StatusMulti)
|
||||||
_, err := fmt.Fprintf(this.w, `<?xml version="1.0" encoding="UTF-8"?>`)
|
_, err := fmt.Fprintf(this.Writer, `<?xml version="1.0" encoding="UTF-8"?>`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
this.enc = ixml.NewEncoder(this.w)
|
this.Encoder = ixml.NewEncoder(this.Writer)
|
||||||
return this.enc.EncodeToken(ixml.StartElement{
|
return this.Encoder.EncodeToken(ixml.StartElement{
|
||||||
Name: ixml.Name{
|
Name: ixml.Name{
|
||||||
Space: "DAV:",
|
Space: "DAV:",
|
||||||
Local: "multistatus",
|
Local: "multistatus",
|
||||||
@ -375,18 +375,18 @@ func (this *multistatusWriter) writeHeader() error {
|
|||||||
|
|
||||||
// Close completes the marshalling of the multistatus response. It returns
|
// Close completes the marshalling of the multistatus response. It returns
|
||||||
// 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 Encoder of w are nil, then no multistatus response has
|
||||||
// been written.
|
// been written.
|
||||||
func (this *multistatusWriter) close() error {
|
func (this *MultiStatusWriter) close() error {
|
||||||
if this.enc == nil {
|
if this.Encoder == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var end []ixml.Token
|
var end []ixml.Token
|
||||||
if this.responseDescription != "" {
|
if this.ResponseDescription != "" {
|
||||||
name := ixml.Name{Space: "DAV:", Local: "responsedescription"}
|
name := ixml.Name{Space: "DAV:", Local: "responsedescription"}
|
||||||
end = append(end,
|
end = append(end,
|
||||||
ixml.StartElement{Name: name},
|
ixml.StartElement{Name: name},
|
||||||
ixml.CharData(this.responseDescription),
|
ixml.CharData(this.ResponseDescription),
|
||||||
ixml.EndElement{Name: name},
|
ixml.EndElement{Name: name},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -394,12 +394,12 @@ func (this *multistatusWriter) close() error {
|
|||||||
Name: ixml.Name{Space: "DAV:", Local: "multistatus"},
|
Name: ixml.Name{Space: "DAV:", Local: "multistatus"},
|
||||||
})
|
})
|
||||||
for _, t := range end {
|
for _, t := range end {
|
||||||
err := this.enc.EncodeToken(t)
|
err := this.Encoder.EncodeToken(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.enc.Flush()
|
return this.Encoder.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
var xmlLangName = ixml.Name{Space: "http://www.w3.org/XML/1998/namespace", Local: "lang"}
|
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
|
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.
|
// The XML value of a property can be arbitrary, mixed-content XML.
|
||||||
// To make sure that the unmarshalled value contains all required
|
// To make sure that the unmarshalled value contains all required
|
||||||
// namespaces, we encode all the property value XML tokens into a
|
// 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)
|
// 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
|
// UnmarshalXML appends the property names and values enclosed within start
|
||||||
// to ps.
|
// to ps.
|
||||||
@ -453,7 +453,7 @@ type proppatchProps []Property
|
|||||||
//
|
//
|
||||||
// UnmarshalXML returns an error if start does not contain any properties or if
|
// UnmarshalXML returns an error if start does not contain any properties or if
|
||||||
// property values contain syntactically incorrect XML.
|
// 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, "")
|
lang := xmlLang(start, "")
|
||||||
for {
|
for {
|
||||||
t, err := next(d)
|
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),
|
XMLName: xml.Name(t.(ixml.StartElement).Name),
|
||||||
Lang: xmlLang(t.(ixml.StartElement), lang),
|
Lang: xmlLang(t.(ixml.StartElement), lang),
|
||||||
}
|
}
|
||||||
err = d.DecodeElement(((*xmlValue)(&p.InnerXML)), &elem)
|
err = d.DecodeElement(((*XmlValue)(&p.InnerXML)), &elem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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_set
|
||||||
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_remove
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_remove
|
||||||
type setRemove struct {
|
type SetRemove struct {
|
||||||
XMLName ixml.Name
|
XMLName ixml.Name
|
||||||
Lang string `xml:"xml:lang,attr,omitempty"`
|
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
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_propertyupdate
|
||||||
type propertyupdate struct {
|
type PropertyUpdate struct {
|
||||||
XMLName ixml.Name `xml:"DAV: propertyupdate"`
|
XMLName ixml.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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func readProppatch(r io.Reader) (patches []Proppatch, status int, err error) {
|
func ReadProppatch(r io.Reader) (patches []Proppatch, status int, err error) {
|
||||||
var pu propertyupdate
|
var pu PropertyUpdate
|
||||||
if err = ixml.NewDecoder(r).Decode(&pu); err != nil {
|
if err = ixml.NewDecoder(r).Decode(&pu); err != nil {
|
||||||
return nil, http.StatusBadRequest, err
|
return nil, http.StatusBadRequest, err
|
||||||
}
|
}
|
||||||
|
@ -25,12 +25,12 @@ func TestReadLockInfo(t *testing.T) {
|
|||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
input string
|
input string
|
||||||
wantLI lockInfo
|
wantLI LockInfo
|
||||||
wantStatus int
|
wantStatus int
|
||||||
}{{
|
}{{
|
||||||
"bad: junk",
|
"bad: junk",
|
||||||
"xxx",
|
"xxx",
|
||||||
lockInfo{},
|
LockInfo{},
|
||||||
http.StatusBadRequest,
|
http.StatusBadRequest,
|
||||||
}, {
|
}, {
|
||||||
"bad: invalid owner XML",
|
"bad: invalid owner XML",
|
||||||
@ -42,7 +42,7 @@ func TestReadLockInfo(t *testing.T) {
|
|||||||
" <D:href> no end tag \n" +
|
" <D:href> no end tag \n" +
|
||||||
" </D:owner>\n" +
|
" </D:owner>\n" +
|
||||||
"</D:lockinfo>",
|
"</D:lockinfo>",
|
||||||
lockInfo{},
|
LockInfo{},
|
||||||
http.StatusBadRequest,
|
http.StatusBadRequest,
|
||||||
}, {
|
}, {
|
||||||
"bad: invalid UTF-8",
|
"bad: invalid UTF-8",
|
||||||
@ -54,7 +54,7 @@ func TestReadLockInfo(t *testing.T) {
|
|||||||
" <D:href> \xff </D:href>\n" +
|
" <D:href> \xff </D:href>\n" +
|
||||||
" </D:owner>\n" +
|
" </D:owner>\n" +
|
||||||
"</D:lockinfo>",
|
"</D:lockinfo>",
|
||||||
lockInfo{},
|
LockInfo{},
|
||||||
http.StatusBadRequest,
|
http.StatusBadRequest,
|
||||||
}, {
|
}, {
|
||||||
"bad: unfinished XML #1",
|
"bad: unfinished XML #1",
|
||||||
@ -62,7 +62,7 @@ func TestReadLockInfo(t *testing.T) {
|
|||||||
"<D:lockinfo xmlns:D='DAV:'>\n" +
|
"<D:lockinfo xmlns:D='DAV:'>\n" +
|
||||||
" <D:lockscope><D:exclusive/></D:lockscope>\n" +
|
" <D:lockscope><D:exclusive/></D:lockscope>\n" +
|
||||||
" <D:locktype><D:write/></D:locktype>\n",
|
" <D:locktype><D:write/></D:locktype>\n",
|
||||||
lockInfo{},
|
LockInfo{},
|
||||||
http.StatusBadRequest,
|
http.StatusBadRequest,
|
||||||
}, {
|
}, {
|
||||||
"bad: unfinished XML #2",
|
"bad: unfinished XML #2",
|
||||||
@ -71,12 +71,12 @@ func TestReadLockInfo(t *testing.T) {
|
|||||||
" <D:lockscope><D:exclusive/></D:lockscope>\n" +
|
" <D:lockscope><D:exclusive/></D:lockscope>\n" +
|
||||||
" <D:locktype><D:write/></D:locktype>\n" +
|
" <D:locktype><D:write/></D:locktype>\n" +
|
||||||
" <D:owner>\n",
|
" <D:owner>\n",
|
||||||
lockInfo{},
|
LockInfo{},
|
||||||
http.StatusBadRequest,
|
http.StatusBadRequest,
|
||||||
}, {
|
}, {
|
||||||
"good: empty",
|
"good: empty",
|
||||||
"",
|
"",
|
||||||
lockInfo{},
|
LockInfo{},
|
||||||
0,
|
0,
|
||||||
}, {
|
}, {
|
||||||
"good: plain-text owner",
|
"good: plain-text owner",
|
||||||
@ -86,11 +86,11 @@ func TestReadLockInfo(t *testing.T) {
|
|||||||
" <D:locktype><D:write/></D:locktype>\n" +
|
" <D:locktype><D:write/></D:locktype>\n" +
|
||||||
" <D:owner>gopher</D:owner>\n" +
|
" <D:owner>gopher</D:owner>\n" +
|
||||||
"</D:lockinfo>",
|
"</D:lockinfo>",
|
||||||
lockInfo{
|
LockInfo{
|
||||||
XMLName: ixml.Name{Space: "DAV:", Local: "lockinfo"},
|
XMLName: ixml.Name{Space: "DAV:", Local: "lockinfo"},
|
||||||
Exclusive: new(struct{}),
|
Exclusive: new(struct{}),
|
||||||
Write: new(struct{}),
|
Write: new(struct{}),
|
||||||
Owner: owner{
|
Owner: Owner{
|
||||||
InnerXML: "gopher",
|
InnerXML: "gopher",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -105,11 +105,11 @@ func TestReadLockInfo(t *testing.T) {
|
|||||||
" <D:href>http://example.org/~ejw/contact.html</D:href>\n" +
|
" <D:href>http://example.org/~ejw/contact.html</D:href>\n" +
|
||||||
" </D:owner>\n" +
|
" </D:owner>\n" +
|
||||||
"</D:lockinfo>",
|
"</D:lockinfo>",
|
||||||
lockInfo{
|
LockInfo{
|
||||||
XMLName: ixml.Name{Space: "DAV:", Local: "lockinfo"},
|
XMLName: ixml.Name{Space: "DAV:", Local: "lockinfo"},
|
||||||
Exclusive: new(struct{}),
|
Exclusive: new(struct{}),
|
||||||
Write: new(struct{}),
|
Write: new(struct{}),
|
||||||
Owner: owner{
|
Owner: Owner{
|
||||||
InnerXML: "\n <D:href>http://example.org/~ejw/contact.html</D:href>\n ",
|
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 {
|
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 tc.wantStatus != 0 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("%s: got nil error, want non-nil", tc.desc)
|
t.Errorf("%s: got nil error, want non-nil", tc.desc)
|
||||||
@ -139,7 +139,7 @@ func TestReadPropfind(t *testing.T) {
|
|||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
input string
|
input string
|
||||||
wantPF propfind
|
wantPF Propfind
|
||||||
wantStatus int
|
wantStatus int
|
||||||
}{{
|
}{{
|
||||||
desc: "propfind: propname",
|
desc: "propfind: propname",
|
||||||
@ -147,14 +147,14 @@ func TestReadPropfind(t *testing.T) {
|
|||||||
"<A:propfind xmlns:A='DAV:'>\n" +
|
"<A:propfind xmlns:A='DAV:'>\n" +
|
||||||
" <A:propname/>\n" +
|
" <A:propname/>\n" +
|
||||||
"</A:propfind>",
|
"</A:propfind>",
|
||||||
wantPF: propfind{
|
wantPF: Propfind{
|
||||||
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
|
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
|
||||||
Propname: new(struct{}),
|
Propname: new(struct{}),
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
desc: "propfind: empty body means allprop",
|
desc: "propfind: empty body means allprop",
|
||||||
input: "",
|
input: "",
|
||||||
wantPF: propfind{
|
wantPF: Propfind{
|
||||||
Allprop: new(struct{}),
|
Allprop: new(struct{}),
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
@ -163,7 +163,7 @@ func TestReadPropfind(t *testing.T) {
|
|||||||
"<A:propfind xmlns:A='DAV:'>\n" +
|
"<A:propfind xmlns:A='DAV:'>\n" +
|
||||||
" <A:allprop/>\n" +
|
" <A:allprop/>\n" +
|
||||||
"</A:propfind>",
|
"</A:propfind>",
|
||||||
wantPF: propfind{
|
wantPF: Propfind{
|
||||||
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
|
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
|
||||||
Allprop: new(struct{}),
|
Allprop: new(struct{}),
|
||||||
},
|
},
|
||||||
@ -174,10 +174,10 @@ func TestReadPropfind(t *testing.T) {
|
|||||||
" <A:allprop/>\n" +
|
" <A:allprop/>\n" +
|
||||||
" <A:include><A:displayname/></A:include>\n" +
|
" <A:include><A:displayname/></A:include>\n" +
|
||||||
"</A:propfind>",
|
"</A:propfind>",
|
||||||
wantPF: propfind{
|
wantPF: Propfind{
|
||||||
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
|
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
|
||||||
Allprop: new(struct{}),
|
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",
|
desc: "propfind: include followed by allprop",
|
||||||
@ -186,10 +186,10 @@ func TestReadPropfind(t *testing.T) {
|
|||||||
" <A:include><A:displayname/></A:include>\n" +
|
" <A:include><A:displayname/></A:include>\n" +
|
||||||
" <A:allprop/>\n" +
|
" <A:allprop/>\n" +
|
||||||
"</A:propfind>",
|
"</A:propfind>",
|
||||||
wantPF: propfind{
|
wantPF: Propfind{
|
||||||
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
|
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
|
||||||
Allprop: new(struct{}),
|
Allprop: new(struct{}),
|
||||||
Include: propfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
|
Include: PropfindProps{xml.Name{Space: "DAV:", Local: "displayname"}},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
desc: "propfind: propfind",
|
desc: "propfind: propfind",
|
||||||
@ -197,9 +197,9 @@ func TestReadPropfind(t *testing.T) {
|
|||||||
"<A:propfind xmlns:A='DAV:'>\n" +
|
"<A:propfind xmlns:A='DAV:'>\n" +
|
||||||
" <A:prop><A:displayname/></A:prop>\n" +
|
" <A:prop><A:displayname/></A:prop>\n" +
|
||||||
"</A:propfind>",
|
"</A:propfind>",
|
||||||
wantPF: propfind{
|
wantPF: Propfind{
|
||||||
XMLName: ixml.Name{Space: "DAV:", Local: "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",
|
desc: "propfind: prop with ignored comments",
|
||||||
@ -210,9 +210,9 @@ func TestReadPropfind(t *testing.T) {
|
|||||||
" <A:displayname><!-- ignore --></A:displayname>\n" +
|
" <A:displayname><!-- ignore --></A:displayname>\n" +
|
||||||
" </A:prop>\n" +
|
" </A:prop>\n" +
|
||||||
"</A:propfind>",
|
"</A:propfind>",
|
||||||
wantPF: propfind{
|
wantPF: Propfind{
|
||||||
XMLName: ixml.Name{Space: "DAV:", Local: "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",
|
desc: "propfind: propfind with ignored whitespace",
|
||||||
@ -220,9 +220,9 @@ func TestReadPropfind(t *testing.T) {
|
|||||||
"<A:propfind xmlns:A='DAV:'>\n" +
|
"<A:propfind xmlns:A='DAV:'>\n" +
|
||||||
" <A:prop> <A:displayname/></A:prop>\n" +
|
" <A:prop> <A:displayname/></A:prop>\n" +
|
||||||
"</A:propfind>",
|
"</A:propfind>",
|
||||||
wantPF: propfind{
|
wantPF: Propfind{
|
||||||
XMLName: ixml.Name{Space: "DAV:", Local: "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",
|
desc: "propfind: propfind with ignored mixed-content",
|
||||||
@ -230,9 +230,9 @@ func TestReadPropfind(t *testing.T) {
|
|||||||
"<A:propfind xmlns:A='DAV:'>\n" +
|
"<A:propfind xmlns:A='DAV:'>\n" +
|
||||||
" <A:prop>foo<A:displayname/>bar</A:prop>\n" +
|
" <A:prop>foo<A:displayname/>bar</A:prop>\n" +
|
||||||
"</A:propfind>",
|
"</A:propfind>",
|
||||||
wantPF: propfind{
|
wantPF: Propfind{
|
||||||
XMLName: ixml.Name{Space: "DAV:", Local: "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)",
|
desc: "propfind: propname with ignored element (section A.4)",
|
||||||
@ -241,7 +241,7 @@ func TestReadPropfind(t *testing.T) {
|
|||||||
" <A:propname/>\n" +
|
" <A:propname/>\n" +
|
||||||
" <E:leave-out xmlns:E='E:'>*boss*</E:leave-out>\n" +
|
" <E:leave-out xmlns:E='E:'>*boss*</E:leave-out>\n" +
|
||||||
"</A:propfind>",
|
"</A:propfind>",
|
||||||
wantPF: propfind{
|
wantPF: Propfind{
|
||||||
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
|
XMLName: ixml.Name{Space: "DAV:", Local: "propfind"},
|
||||||
Propname: new(struct{}),
|
Propname: new(struct{}),
|
||||||
},
|
},
|
||||||
@ -330,7 +330,7 @@ func TestReadPropfind(t *testing.T) {
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
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 tc.wantStatus != 0 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("%s: got nil error, want non-nil", tc.desc)
|
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
|
// http://www.webdav.org/specs/rfc4918.html
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
responses []response
|
responses []Response
|
||||||
respdesc string
|
respdesc string
|
||||||
writeHeader bool
|
writeHeader bool
|
||||||
wantXML string
|
wantXML string
|
||||||
@ -361,9 +361,9 @@ func TestMultistatusWriter(t *testing.T) {
|
|||||||
wantErr error
|
wantErr error
|
||||||
}{{
|
}{{
|
||||||
desc: "section 9.2.2 (failed dependency)",
|
desc: "section 9.2.2 (failed dependency)",
|
||||||
responses: []response{{
|
responses: []Response{{
|
||||||
Href: []string{"http://example.com/foo"},
|
Href: []string{"http://example.com/foo"},
|
||||||
Propstat: []propstat{{
|
Propstat: []SubPropstat{{
|
||||||
Prop: []Property{{
|
Prop: []Property{{
|
||||||
XMLName: xml.Name{
|
XMLName: xml.Name{
|
||||||
Space: "http://ns.example.com/",
|
Space: "http://ns.example.com/",
|
||||||
@ -405,10 +405,10 @@ func TestMultistatusWriter(t *testing.T) {
|
|||||||
wantCode: StatusMulti,
|
wantCode: StatusMulti,
|
||||||
}, {
|
}, {
|
||||||
desc: "section 9.6.2 (lock-token-submitted)",
|
desc: "section 9.6.2 (lock-token-submitted)",
|
||||||
responses: []response{{
|
responses: []Response{{
|
||||||
Href: []string{"http://example.com/foo"},
|
Href: []string{"http://example.com/foo"},
|
||||||
Status: "HTTP/1.1 423 Locked",
|
Status: "HTTP/1.1 423 Locked",
|
||||||
Error: &xmlError{
|
Error: &XmlError{
|
||||||
InnerXML: []byte(`<lock-token-submitted xmlns="DAV:"/>`),
|
InnerXML: []byte(`<lock-token-submitted xmlns="DAV:"/>`),
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
@ -424,9 +424,9 @@ func TestMultistatusWriter(t *testing.T) {
|
|||||||
wantCode: StatusMulti,
|
wantCode: StatusMulti,
|
||||||
}, {
|
}, {
|
||||||
desc: "section 9.1.3",
|
desc: "section 9.1.3",
|
||||||
responses: []response{{
|
responses: []Response{{
|
||||||
Href: []string{"http://example.com/foo"},
|
Href: []string{"http://example.com/foo"},
|
||||||
Propstat: []propstat{{
|
Propstat: []SubPropstat{{
|
||||||
Prop: []Property{{
|
Prop: []Property{{
|
||||||
XMLName: xml.Name{Space: "http://ns.example.com/boxschema/", Local: "bigbox"},
|
XMLName: xml.Name{Space: "http://ns.example.com/boxschema/", Local: "bigbox"},
|
||||||
InnerXML: []byte(`` +
|
InnerXML: []byte(`` +
|
||||||
@ -492,8 +492,8 @@ func TestMultistatusWriter(t *testing.T) {
|
|||||||
wantCode: StatusMulti,
|
wantCode: StatusMulti,
|
||||||
}, {
|
}, {
|
||||||
desc: "bad: no href",
|
desc: "bad: no href",
|
||||||
responses: []response{{
|
responses: []Response{{
|
||||||
Propstat: []propstat{{
|
Propstat: []SubPropstat{{
|
||||||
Prop: []Property{{
|
Prop: []Property{{
|
||||||
XMLName: xml.Name{
|
XMLName: xml.Name{
|
||||||
Space: "http://example.com/",
|
Space: "http://example.com/",
|
||||||
@ -508,7 +508,7 @@ func TestMultistatusWriter(t *testing.T) {
|
|||||||
wantCode: http.StatusOK,
|
wantCode: http.StatusOK,
|
||||||
}, {
|
}, {
|
||||||
desc: "bad: multiple hrefs and no status",
|
desc: "bad: multiple hrefs and no status",
|
||||||
responses: []response{{
|
responses: []Response{{
|
||||||
Href: []string{"http://example.com/foo", "http://example.com/bar"},
|
Href: []string{"http://example.com/foo", "http://example.com/bar"},
|
||||||
}},
|
}},
|
||||||
wantErr: errInvalidResponse,
|
wantErr: errInvalidResponse,
|
||||||
@ -516,7 +516,7 @@ func TestMultistatusWriter(t *testing.T) {
|
|||||||
wantCode: http.StatusOK,
|
wantCode: http.StatusOK,
|
||||||
}, {
|
}, {
|
||||||
desc: "bad: one href and no propstat",
|
desc: "bad: one href and no propstat",
|
||||||
responses: []response{{
|
responses: []Response{{
|
||||||
Href: []string{"http://example.com/foo"},
|
Href: []string{"http://example.com/foo"},
|
||||||
}},
|
}},
|
||||||
wantErr: errInvalidResponse,
|
wantErr: errInvalidResponse,
|
||||||
@ -524,9 +524,9 @@ func TestMultistatusWriter(t *testing.T) {
|
|||||||
wantCode: http.StatusOK,
|
wantCode: http.StatusOK,
|
||||||
}, {
|
}, {
|
||||||
desc: "bad: status with one href and propstat",
|
desc: "bad: status with one href and propstat",
|
||||||
responses: []response{{
|
responses: []Response{{
|
||||||
Href: []string{"http://example.com/foo"},
|
Href: []string{"http://example.com/foo"},
|
||||||
Propstat: []propstat{{
|
Propstat: []SubPropstat{{
|
||||||
Prop: []Property{{
|
Prop: []Property{{
|
||||||
XMLName: xml.Name{
|
XMLName: xml.Name{
|
||||||
Space: "http://example.com/",
|
Space: "http://example.com/",
|
||||||
@ -542,12 +542,12 @@ func TestMultistatusWriter(t *testing.T) {
|
|||||||
wantCode: http.StatusOK,
|
wantCode: http.StatusOK,
|
||||||
}, {
|
}, {
|
||||||
desc: "bad: multiple hrefs and propstat",
|
desc: "bad: multiple hrefs and propstat",
|
||||||
responses: []response{{
|
responses: []Response{{
|
||||||
Href: []string{
|
Href: []string{
|
||||||
"http://example.com/foo",
|
"http://example.com/foo",
|
||||||
"http://example.com/bar",
|
"http://example.com/bar",
|
||||||
},
|
},
|
||||||
Propstat: []propstat{{
|
Propstat: []SubPropstat{{
|
||||||
Prop: []Property{{
|
Prop: []Property{{
|
||||||
XMLName: xml.Name{
|
XMLName: xml.Name{
|
||||||
Space: "http://example.com/",
|
Space: "http://example.com/",
|
||||||
@ -566,7 +566,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{Writer: 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)
|
||||||
@ -574,7 +574,7 @@ loop:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, r := range tc.responses {
|
for _, r := range tc.responses {
|
||||||
if err := w.write(&r); err != nil {
|
if err := w.Write(&r); err != nil {
|
||||||
if err != tc.wantErr {
|
if err != tc.wantErr {
|
||||||
t.Errorf("%s: got write error %v, want %v",
|
t.Errorf("%s: got write error %v, want %v",
|
||||||
tc.desc, err, tc.wantErr)
|
tc.desc, err, tc.wantErr)
|
||||||
@ -705,7 +705,7 @@ func TestReadProppatch(t *testing.T) {
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
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 tc.wantStatus != 0 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("%s: got nil error, want non-nil", tc.desc)
|
t.Errorf("%s: got nil error, want non-nil", tc.desc)
|
||||||
@ -800,7 +800,7 @@ func TestUnmarshalXMLValue(t *testing.T) {
|
|||||||
var n xmlNormalizer
|
var n xmlNormalizer
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
d := ixml.NewDecoder(strings.NewReader(tc.input))
|
d := ixml.NewDecoder(strings.NewReader(tc.input))
|
||||||
var v xmlValue
|
var v XmlValue
|
||||||
if err := d.Decode(&v); err != nil {
|
if err := d.Decode(&v); err != nil {
|
||||||
t.Errorf("%s: got error %v, want nil", tc.desc, err)
|
t.Errorf("%s: got error %v, want nil", tc.desc, err)
|
||||||
continue
|
continue
|
||||||
|
@ -98,7 +98,7 @@ func (this *DavController) Index(writer http.ResponseWriter, request *http.Reque
|
|||||||
this.logger.Info("请求访问来了:%s %s", request.RequestURI, subPath)
|
this.logger.Info("请求访问来了:%s %s", request.RequestURI, subPath)
|
||||||
|
|
||||||
method := request.Method
|
method := request.Method
|
||||||
if method == "PROPFIND1" {
|
if method == "PROPFIND" {
|
||||||
|
|
||||||
this.davService.HandlePropfind(writer, request, subPath)
|
this.davService.HandlePropfind(writer, request, subPath)
|
||||||
|
|
||||||
|
@ -1,33 +1,3 @@
|
|||||||
package rest
|
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
|
package rest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/xml"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"tank/rest/dav"
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* WebDav协议文档
|
* WebDav协议文档
|
||||||
* https://tools.ietf.org/html/rfc4918
|
* https://tools.ietf.org/html/rfc4918
|
||||||
*
|
* 主要参考 golang.org/x/net/webdav
|
||||||
*/
|
*/
|
||||||
//@Service
|
//@Service
|
||||||
type DavService struct {
|
type DavService struct {
|
||||||
@ -30,22 +30,24 @@ func (this *DavService) Init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//从request中读取深度
|
func (this *DavService) makePropstatResponse(href string, pstats []dav.Propstat) *dav.Response {
|
||||||
func (this *DavService) readDepth(request *http.Request) int {
|
resp := dav.Response{
|
||||||
|
Href: []string{(&url.URL{Path: href}).EscapedPath()},
|
||||||
depth := INFINITE_DEPTH
|
Propstat: make([]dav.SubPropstat, 0, len(pstats)),
|
||||||
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格式错误!")
|
|
||||||
}
|
}
|
||||||
|
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)
|
user := this.checkUser(writer, request)
|
||||||
|
|
||||||
//读取希望访问的深度。
|
|
||||||
depth := this.readDepth(request)
|
|
||||||
|
|
||||||
//找寻请求的目录
|
//找寻请求的目录
|
||||||
matter := this.matterDao.checkByUserUuidAndPath(user.Uuid, subPath)
|
matter := this.matterDao.checkByUserUuidAndPath(user.Uuid, subPath)
|
||||||
|
|
||||||
//TODO: 读取请求参数。按照用户的参数请求返回内容。
|
//读取请求参数。按照用户的参数请求返回内容。
|
||||||
propfind := &Propfind{}
|
propfind, _, err := dav.ReadPropfind(request.Body)
|
||||||
body, err := ioutil.ReadAll(request.Body)
|
|
||||||
this.PanicError(err)
|
this.PanicError(err)
|
||||||
|
|
||||||
//从xml中解析内容到struct
|
//准备一个输出结果的Writer
|
||||||
err = xml.Unmarshal(body, &propfind)
|
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)
|
this.PanicError(err)
|
||||||
|
|
||||||
//从struct还原到xml
|
fmt.Printf("%v %v \n", matter.Name, propfind.Prop)
|
||||||
output, err := xml.MarshalIndent(propfind, " ", " ")
|
|
||||||
this.PanicError(err)
|
|
||||||
fmt.Println(string(output))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fmt.Printf("%v %v \n", depth, matter.Name)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user