add the first version of dav

This commit is contained in:
zicla 2019-04-12 19:52:35 +08:00
parent d3a2ba9837
commit e0b78932a7
5 changed files with 103 additions and 105 deletions

View File

@ -748,7 +748,7 @@ func copyFiles(ctx context.Context, fs FileSystem, src, dst string, overwrite bo
// Allowed values for depth are 0, 1 or infiniteDepth. For each visited node, // Allowed values for depth are 0, 1 or infiniteDepth. For each visited node,
// walkFS calls walkFn. If a visited file system node is a directory and // walkFS calls walkFn. If a visited file system node is a directory and
// walkFn returns filepath.SkipDir, walkFS will skip traversal of this node. // walkFn returns filepath.SkipDir, walkFS will skip traversal of this node.
func walkFS(ctx context.Context, fs FileSystem, depth int, name string, info os.FileInfo, walkFn filepath.WalkFunc) error { func WalkFS(ctx context.Context, fs FileSystem, depth int, name string, info os.FileInfo, walkFn filepath.WalkFunc) error {
// This implementation is based on Walk's code in the standard path/filepath package. // This implementation is based on Walk's code in the standard path/filepath package.
err := walkFn(name, info, nil) err := walkFn(name, info, nil)
if err != nil { if err != nil {
@ -783,7 +783,7 @@ func walkFS(ctx context.Context, fs FileSystem, depth int, name string, info os.
return err return err
} }
} else { } else {
err = walkFS(ctx, fs, depth, filename, fileInfo, walkFn) err = WalkFS(ctx, fs, depth, filename, fileInfo, walkFn)
if err != nil { if err != nil {
if !fileInfo.IsDir() || err != filepath.SkipDir { if !fileInfo.IsDir() || err != filepath.SkipDir {
return err return err

View File

@ -166,7 +166,7 @@ var liveProps = map[xml.Name]struct {
// //
// Each Propstat has a unique status and each property name will only be part // Each Propstat has a unique status and each property name will only be part
// of one Propstat element. // of one Propstat element.
func props(ctx context.Context, fs FileSystem, ls LockSystem, name string, pnames []xml.Name) ([]Propstat, error) { func Props(ctx context.Context, fs FileSystem, ls LockSystem, name string, pnames []xml.Name) ([]Propstat, error) {
f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0) f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0)
if err != nil { if err != nil {
return nil, err return nil, err
@ -214,7 +214,7 @@ func props(ctx context.Context, fs FileSystem, ls LockSystem, name string, pname
} }
// Propnames returns the property names defined for resource name. // Propnames returns the property names defined for resource name.
func propnames(ctx context.Context, fs FileSystem, ls LockSystem, name string) ([]xml.Name, error) { func Propnames(ctx context.Context, fs FileSystem, ls LockSystem, name string) ([]xml.Name, error) {
f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0) f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0)
if err != nil { if err != nil {
return nil, err return nil, err
@ -254,8 +254,8 @@ func propnames(ctx context.Context, fs FileSystem, ls LockSystem, name string) (
// returned if they are named in 'include'. // returned if they are named in 'include'.
// //
// See http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND // See http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND
func allprop(ctx context.Context, fs FileSystem, ls LockSystem, name string, include []xml.Name) ([]Propstat, error) { func Allprop(ctx context.Context, fs FileSystem, ls LockSystem, name string, include []xml.Name) ([]Propstat, error) {
pnames, err := propnames(ctx, fs, ls, name) pnames, err := Propnames(ctx, fs, ls, name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -269,7 +269,7 @@ func allprop(ctx context.Context, fs FileSystem, ls LockSystem, name string, inc
pnames = append(pnames, pn) pnames = append(pnames, pn)
} }
} }
return props(ctx, fs, ls, name, pnames) return Props(ctx, fs, ls, name, pnames)
} }
// Patch patches the properties of resource name. The return values are // Patch patches the properties of resource name. The return values are

View File

@ -527,12 +527,12 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status
return http.StatusBadRequest, errInvalidDepth return http.StatusBadRequest, errInvalidDepth
} }
} }
pf, status, err := readPropfind(r.Body) pf, status, err := ReadPropfind(r.Body)
if err != nil { if err != nil {
return status, err return status, err
} }
mw := multistatusWriter{w: w} mw := MultiStatusWriter{Writer: w}
walkFn := func(reqPath string, info os.FileInfo, err error) error { walkFn := func(reqPath string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
@ -540,7 +540,7 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status
} }
var pstats []Propstat var pstats []Propstat
if pf.Propname != nil { if pf.Propname != nil {
pnames, err := propnames(ctx, h.FileSystem, h.LockSystem, reqPath) pnames, err := Propnames(ctx, h.FileSystem, h.LockSystem, reqPath)
if err != nil { if err != nil {
return err return err
} }
@ -550,9 +550,9 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status
} }
pstats = append(pstats, pstat) pstats = append(pstats, pstat)
} else if pf.Allprop != nil { } else if pf.Allprop != nil {
pstats, err = allprop(ctx, h.FileSystem, h.LockSystem, reqPath, pf.Prop) pstats, err = Allprop(ctx, h.FileSystem, h.LockSystem, reqPath, pf.Prop)
} else { } else {
pstats, err = props(ctx, h.FileSystem, h.LockSystem, reqPath, pf.Prop) pstats, err = Props(ctx, h.FileSystem, h.LockSystem, reqPath, pf.Prop)
} }
if err != nil { if err != nil {
return err return err
@ -561,11 +561,11 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status
if info.IsDir() { if info.IsDir() {
href += "/" href += "/"
} }
return mw.write(makePropstatResponse(href, pstats)) return mw.Write(MakePropstatResponse(href, pstats))
} }
walkErr := walkFS(ctx, h.FileSystem, depth, reqPath, fi, walkFn) walkErr := WalkFS(ctx, h.FileSystem, depth, reqPath, fi, walkFn)
closeErr := mw.close() closeErr := mw.Close()
if walkErr != nil { if walkErr != nil {
return http.StatusInternalServerError, walkErr return http.StatusInternalServerError, walkErr
} }
@ -602,9 +602,9 @@ func (h *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (statu
if err != nil { if err != nil {
return http.StatusInternalServerError, err return http.StatusInternalServerError, err
} }
mw := multistatusWriter{w: w} mw := MultiStatusWriter{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
} }
@ -614,7 +614,7 @@ func (h *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (statu
return 0, nil return 0, nil
} }
func makePropstatResponse(href string, pstats []Propstat) *response { func MakePropstatResponse(href string, pstats []Propstat) *response {
resp := response{ resp := response{
Href: []string{(&url.URL{Path: href}).EscapedPath()}, Href: []string{(&url.URL{Path: href}).EscapedPath()},
Propstat: make([]propstat, 0, len(pstats)), Propstat: make([]propstat, 0, len(pstats)),

View File

@ -50,7 +50,7 @@ type owner struct {
} }
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 {
@ -70,12 +70,12 @@ func readLockInfo(r io.Reader) (li lockInfo, status int, err error) {
return li, 0, nil return li, 0, nil
} }
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
@ -175,8 +175,8 @@ type Propfind struct {
Include PropfindProps `xml:"DAV: include"` Include PropfindProps `xml:"DAV: include"`
} }
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 {
@ -233,14 +233,14 @@ type ixmlProperty struct {
} }
// 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 propstat struct {
Prop []Property `xml:"D:prop>_ignored_"` Prop []Property `xml:"D:prop>_ignored_"`
Status string `xml:"D:status"` Status string `xml:"D:status"`
@ -258,7 +258,7 @@ type ixmlPropstat struct {
} }
// 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 propstat) 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{
@ -287,7 +287,7 @@ func (ps propstat) MarshalXML(e *ixml.Encoder, start ixml.StartElement) error {
} }
// 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"`
@ -306,14 +306,14 @@ 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 enc *ixml.Encoder
} }
@ -325,7 +325,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 (w *multistatusWriter) write(r *response) error { func (w *MultiStatusWriter) Write(r *response) error {
switch len(r.Href) { switch len(r.Href) {
case 0: case 0:
return errInvalidResponse return errInvalidResponse
@ -338,7 +338,7 @@ func (w *multistatusWriter) write(r *response) error {
return errInvalidResponse return errInvalidResponse
} }
} }
err := w.writeHeader() err := w.WriteHeader()
if err != nil { if err != nil {
return err return err
} }
@ -348,17 +348,17 @@ func (w *multistatusWriter) write(r *response) error {
// writeHeader writes a XML multistatus start element on w's underlying // writeHeader writes a XML multistatus start element on w's underlying
// http.ResponseWriter and returns the result of the write operation. // http.ResponseWriter and returns the result of the write operation.
// After the first write attempt, writeHeader becomes a no-op. // After the first write attempt, writeHeader becomes a no-op.
func (w *multistatusWriter) writeHeader() error { func (w *MultiStatusWriter) WriteHeader() error {
if w.enc != nil { if w.enc != nil {
return nil return nil
} }
w.w.Header().Add("Content-Type", "text/xml; charset=utf-8") w.Writer.Header().Add("Content-Type", "text/xml; charset=utf-8")
w.w.WriteHeader(StatusMulti) w.Writer.WriteHeader(StatusMulti)
_, err := fmt.Fprintf(w.w, `<?xml version="1.0" encoding="UTF-8"?>`) _, err := fmt.Fprintf(w.Writer, `<?xml version="1.0" encoding="UTF-8"?>`)
if err != nil { if err != nil {
return err return err
} }
w.enc = ixml.NewEncoder(w.w) w.enc = ixml.NewEncoder(w.Writer)
return w.enc.EncodeToken(ixml.StartElement{ return w.enc.EncodeToken(ixml.StartElement{
Name: ixml.Name{ Name: ixml.Name{
Space: "DAV:", Space: "DAV:",
@ -375,7 +375,7 @@ func (w *multistatusWriter) writeHeader() error {
// an error if the multistatus response could not be completed. If both the // an error if the multistatus response could not be completed. If both the
// return value and field enc of w are nil, then no multistatus response has // return value and field enc of w are nil, then no multistatus response has
// been written. // been written.
func (w *multistatusWriter) close() error { func (w *MultiStatusWriter) Close() error {
if w.enc == nil { if w.enc == nil {
return nil return nil
} }

View File

@ -2,6 +2,8 @@ package rest
import ( import (
"net/http" "net/http"
"os"
"tank/rest/dav"
) )
//@Service //@Service
@ -47,74 +49,70 @@ func parseDepth(s string) int {
return invalidDepth return invalidDepth
} }
//处理 方法 //处理 方法
func (this *DavService) HandlePropfind(w http.ResponseWriter, r *http.Request) { func (this *DavService) HandlePropfind(w http.ResponseWriter, r *http.Request) {
//basePath := "/Users/fusu/d/group/golang/src/tank/tmp/dav" basePath := "/Users/fusu/d/group/golang/src/tank/tmp/dav"
//
//reqPath := r.URL.Path fileSystem := dav.Dir("/Users/fusu/d/group/golang/src/tank/tmp/dav")
// lockSystem := dav.NewMemLS()
//ctx := r.Context()
// reqPath := r.URL.Path
//fi, err := os.Stat(basePath + reqPath)
//if err != nil { ctx := r.Context()
// this.PanicError(err)
//} fi, err := os.Stat(basePath + reqPath)
// if err != nil {
//depth := infiniteDepth this.PanicError(err)
//if hdr := r.Header.Get("Depth"); hdr != "" { }
// depth = parseDepth(hdr)
// if depth == invalidDepth { depth := infiniteDepth
// this.PanicBadRequest("Depth指定错误") if hdr := r.Header.Get("Depth"); hdr != "" {
// } depth = parseDepth(hdr)
//} if depth == invalidDepth {
// this.PanicBadRequest("Depth指定错误")
//pf, status, err := readPropfind(r.Body) }
//if err != nil { }
// return status, err
//} pf, _, err := dav.ReadPropfind(r.Body)
//
//mw := multistatusWriter{w: w} this.PanicError(err)
//
//walkFn := func(reqPath string, info os.FileInfo, err error) error { mw := dav.MultiStatusWriter{Writer: w}
// if err != nil {
// return err walkFn := func(reqPath string, info os.FileInfo, err error) error {
// } if err != nil {
// var pstats []Propstat return err
// if pf.Propname != nil { }
// pnames, err := propnames(ctx, h.FileSystem, h.LockSystem, reqPath) var pstats []dav.Propstat
// if err != nil { if pf.Propname != nil {
// return err pnames, err := dav.Propnames(ctx, fileSystem, lockSystem, reqPath)
// } if err != nil {
// pstat := Propstat{Status: http.StatusOK} return err
// for _, xmlname := range pnames { }
// pstat.Props = append(pstat.Props, Property{XMLName: xmlname}) pstat := dav.Propstat{Status: http.StatusOK}
// } for _, xmlname := range pnames {
// pstats = append(pstats, pstat) pstat.Props = append(pstat.Props, dav.Property{XMLName: xmlname})
// } else if pf.Allprop != nil { }
// pstats, err = allprop(ctx, h.FileSystem, h.LockSystem, reqPath, pf.Prop) pstats = append(pstats, pstat)
// } else { } else if pf.Allprop != nil {
// pstats, err = props(ctx, h.FileSystem, h.LockSystem, reqPath, pf.Prop) pstats, err = dav.Allprop(ctx, fileSystem, lockSystem, reqPath, pf.Prop)
// } } else {
// if err != nil { pstats, err = dav.Props(ctx, fileSystem, lockSystem, reqPath, pf.Prop)
// return err }
// } if err != nil {
// href := path.Join(h.Prefix, reqPath) return err
// if info.IsDir() { }
// href += "/" href := reqPath
// } if info.IsDir() {
// return mw.write(makePropstatResponse(href, pstats)) href += "/"
//} }
// return mw.Write(dav.MakePropstatResponse(href, pstats))
//walkErr := walkFS(ctx, h.FileSystem, depth, reqPath, fi, walkFn) }
//closeErr := mw.close()
//if walkErr != nil { walkErr := dav.WalkFS(ctx, fileSystem, depth, reqPath, fi, walkFn)
// return http.StatusInternalServerError, walkErr closeErr := mw.Close()
//} this.PanicError(walkErr)
//if closeErr != nil { this.PanicError(closeErr)
// return http.StatusInternalServerError, closeErr
//}
//return 0, nil
} }