From 1f047e25c7ad217a3a9de9e303b064302302d985 Mon Sep 17 00:00:00 2001 From: zicla Date: Thu, 26 Mar 2020 01:11:16 +0800 Subject: [PATCH] Refine some webdav methods. --- code/tool/webdav/file.go | 4 +-- code/tool/webdav/file_test.go | 12 ++++----- code/tool/webdav/prop.go | 14 +++++----- code/tool/webdav/prop_test.go | 8 +++--- code/tool/webdav/webdav.go | 50 +++++++++++++++++------------------ code/tool/webdav/xml.go | 30 ++++++++++----------- code/tool/webdav/xml_test.go | 2 +- go.sum | 1 + 8 files changed, 61 insertions(+), 60 deletions(-) diff --git a/code/tool/webdav/file.go b/code/tool/webdav/file.go index 9f53778..cc718cc 100644 --- a/code/tool/webdav/file.go +++ b/code/tool/webdav/file.go @@ -697,7 +697,7 @@ func copyFiles(ctx context.Context, fs FileSystem, src, dst string, overwrite bo if err := fs.Mkdir(ctx, dst, srcPerm); err != nil { return http.StatusForbidden, err } - if depth == infiniteDepth { + if depth == InfiniteDepth { children, err := srcFile.Readdir(-1) if err != nil { return http.StatusForbidden, err @@ -745,7 +745,7 @@ func copyFiles(ctx context.Context, fs FileSystem, src, dst string, overwrite bo // walkFS traverses filesystem fs starting at name up to depth levels. // -// 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 // 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 { diff --git a/code/tool/webdav/file_test.go b/code/tool/webdav/file_test.go index 04b8a45..cba2025 100644 --- a/code/tool/webdav/file_test.go +++ b/code/tool/webdav/file_test.go @@ -467,7 +467,7 @@ func testFS(t *testing.T, fs FileSystem) { case "copy__": depth := 0 if parts[1] == "d=∞" { - depth = infiniteDepth + depth = InfiniteDepth } _, opErr = copyFiles(ctx, fs, parts[2], parts[3], parts[0] == "o=T", depth, 0) case "mk-dir": @@ -913,7 +913,7 @@ func TestCopyMoveProps(t *testing.T) { if err := patch("/src", Proppatch{Props: []Property{p0, p1}}); err != nil { t.Fatalf("patch /src +p0 +p1: %v", err) } - if _, err := copyFiles(ctx, fs, "/src", "/tmp", true, infiniteDepth, 0); err != nil { + if _, err := copyFiles(ctx, fs, "/src", "/tmp", true, InfiniteDepth, 0); err != nil { t.Fatalf("copyFiles /src /tmp: %v", err) } if _, err := moveFiles(ctx, fs, "/tmp", "/dst", true); err != nil { @@ -969,7 +969,7 @@ func TestWalkFS(t *testing.T) { "just root", []string{}, "/", - infiniteDepth, + InfiniteDepth, nil, []string{ "/", @@ -985,7 +985,7 @@ func TestWalkFS(t *testing.T) { "touch /f", }, "/", - infiniteDepth, + InfiniteDepth, nil, []string{ "/", @@ -1007,7 +1007,7 @@ func TestWalkFS(t *testing.T) { "touch /f", }, "/a", - infiniteDepth, + InfiniteDepth, nil, []string{ "/a", @@ -1096,7 +1096,7 @@ func TestWalkFS(t *testing.T) { "touch /a/b/z", }, "/", - infiniteDepth, + InfiniteDepth, func(path string, info os.FileInfo, err error) error { if path == "/a/b/g" { return filepath.SkipDir diff --git a/code/tool/webdav/prop.go b/code/tool/webdav/prop.go index 2c48bc4..8a88360 100644 --- a/code/tool/webdav/prop.go +++ b/code/tool/webdav/prop.go @@ -160,13 +160,13 @@ var liveProps = map[xml.Name]struct { }, } -// TODO(nigeltao) merge props and allprop? +// TODO(nigeltao) merge Props and Allprop? // Props returns the status of the properties named pnames for resource name. // // Each Propstat has a unique status and each property name will only be part // 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) if err != nil { 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. -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) if err != nil { return nil, err @@ -249,13 +249,13 @@ func propnames(ctx context.Context, fs FileSystem, ls LockSystem, name string) ( // Allprop returns the properties defined for resource name and the properties // named in include. // -// Note that RFC 4918 defines 'allprop' to return the DAV: properties defined +// Note that RFC 4918 defines 'Allprop' to return the DAV: properties defined // within the RFC plus dead properties. Other live properties should only be // returned if they are named in 'include'. // // 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) { - pnames, err := propnames(ctx, fs, ls, name) +func Allprop(ctx context.Context, fs FileSystem, ls LockSystem, name string, include []xml.Name) ([]Propstat, error) { + pnames, err := PropNames(ctx, fs, ls, name) if err != nil { return nil, err } @@ -269,7 +269,7 @@ func allprop(ctx context.Context, fs FileSystem, ls LockSystem, name string, inc 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 diff --git a/code/tool/webdav/prop_test.go b/code/tool/webdav/prop_test.go index 855446b..c9d71d0 100644 --- a/code/tool/webdav/prop_test.go +++ b/code/tool/webdav/prop_test.go @@ -522,7 +522,7 @@ func TestMemPS(t *testing.T) { var propstats []Propstat switch op.op { case "propname": - pnames, err := propnames(ctx, fs, ls, op.name) + pnames, err := PropNames(ctx, fs, ls, op.name) if err != nil { t.Errorf("%s: got error %v, want nil", desc, err) continue @@ -534,9 +534,9 @@ func TestMemPS(t *testing.T) { } continue case "allprop": - propstats, err = allprop(ctx, fs, ls, op.name, op.pnames) + propstats, err = Allprop(ctx, fs, ls, op.name, op.pnames) case "propfind": - propstats, err = props(ctx, fs, ls, op.name, op.pnames) + propstats, err = Props(ctx, fs, ls, op.name, op.pnames) case "proppatch": propstats, err = patch(ctx, fs, ls, op.name, op.patches) default: @@ -546,7 +546,7 @@ func TestMemPS(t *testing.T) { t.Errorf("%s: got error %v, want nil", desc, err) continue } - // Compare return values from allprop, propfind or proppatch. + // Compare return values from Allprop, propfind or proppatch. for _, pst := range propstats { sort.Sort(byPropname(pst.Props)) } diff --git a/code/tool/webdav/webdav.go b/code/tool/webdav/webdav.go index 8f16ee4..ea11636 100644 --- a/code/tool/webdav/webdav.go +++ b/code/tool/webdav/webdav.go @@ -360,10 +360,10 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request) (status // Section 9.8.3 says that "The COPY method on a collection without a Depth // header must act as if a Depth header with value "infinity" was included". - depth := infiniteDepth + depth := InfiniteDepth if hdr := r.Header.Get("Depth"); hdr != "" { - depth = parseDepth(hdr) - if depth != 0 && depth != infiniteDepth { + depth = ParseDepth(hdr) + if depth != 0 && depth != InfiniteDepth { // Section 9.8.3 says that "A client may submit a Depth header on a // COPY on a collection with a value of "0" or "infinity"." return http.StatusBadRequest, errInvalidDepth @@ -382,7 +382,7 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request) (status // a "Depth: infinity" header was used on it. A client must not submit a // Depth header on a MOVE on a collection with any value but "infinity"." if hdr := r.Header.Get("Depth"); hdr != "" { - if parseDepth(hdr) != infiniteDepth { + if ParseDepth(hdr) != InfiniteDepth { return http.StatusBadRequest, errInvalidDepth } } @@ -424,10 +424,10 @@ func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request) (retStatus } else { // Section 9.10.3 says that "If no Depth header is submitted on a LOCK request, // then the request MUST act as if a "Depth:infinity" had been submitted." - depth := infiniteDepth + depth := InfiniteDepth if hdr := r.Header.Get("Depth"); hdr != "" { - depth = parseDepth(hdr) - if depth != 0 && depth != infiniteDepth { + depth = ParseDepth(hdr) + if depth != 0 && depth != InfiniteDepth { // Section 9.10.3 says that "Values other than 0 or infinity must not be // used with the Depth header on a LOCK method". return http.StatusBadRequest, errInvalidDepth @@ -519,10 +519,10 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status } return http.StatusMethodNotAllowed, err } - depth := infiniteDepth + depth := InfiniteDepth if hdr := r.Header.Get("Depth"); hdr != "" { - depth = parseDepth(hdr) - if depth == invalidDepth { + depth = ParseDepth(hdr) + if depth == InvalidDepth { return http.StatusBadRequest, errInvalidDepth } } @@ -531,7 +531,7 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status return status, err } - mw := multistatusWriter{w: w} + mw := MultiStatusWriter{Writer: w} walkFn := func(reqPath string, info os.FileInfo, err error) error { if err != nil { @@ -539,7 +539,7 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status } var pstats []Propstat 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 { return err } @@ -549,9 +549,9 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status } pstats = append(pstats, pstat) } 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 { - 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 { return err @@ -560,7 +560,7 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status if href != "/" && info.IsDir() { href += "/" } - return mw.write(makePropstatResponse(href, pstats)) + return mw.write(MakePropstatResponse(href, pstats)) } walkErr := walkFS(ctx, h.FileSystem, depth, reqPath, fi, walkFn) @@ -601,8 +601,8 @@ func (h *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (statu 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 @@ -613,7 +613,7 @@ func (h *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (statu return 0, nil } -func makePropstatResponse(href string, pstats []Propstat) *response { +func MakePropstatResponse(href string, pstats []Propstat) *response { resp := response{ Href: []string{(&url.URL{Path: href}).EscapedPath()}, Propstat: make([]propstat, 0, len(pstats)), @@ -634,12 +634,12 @@ func makePropstatResponse(href string, pstats []Propstat) *response { } const ( - infiniteDepth = -1 - invalidDepth = -2 + InfiniteDepth = -1 + InvalidDepth = -2 ) -// parseDepth maps the strings "0", "1" and "infinity" to 0, 1 and -// infiniteDepth. Parsing any other string returns invalidDepth. +// ParseDepth maps the strings "0", "1" and "infinity" to 0, 1 and +// InfiniteDepth. Parsing any other string returns InvalidDepth. // // Different WebDAV methods have further constraints on valid depths: // - PROPFIND has no further restrictions, as per section 9.1. @@ -647,16 +647,16 @@ const ( // - MOVE accepts only "infinity", as per section 9.9.2. // - LOCK accepts only "0" or "infinity", as per section 9.10.3. // These constraints are enforced by the handleXxx methods. -func parseDepth(s string) int { +func ParseDepth(s string) int { switch s { case "0": return 0 case "1": return 1 case "infinity": - return infiniteDepth + return InfiniteDepth } - return invalidDepth + return InvalidDepth } // http://www.webdav.org/specs/rfc4918.html#status.code.extensions.to.http11 diff --git a/code/tool/webdav/xml.go b/code/tool/webdav/xml.go index 67fe83c..1a856bc 100644 --- a/code/tool/webdav/xml.go +++ b/code/tool/webdav/xml.go @@ -180,7 +180,7 @@ func ReadPropfind(r io.Reader) (pf Propfind, status int, err error) { if err = ixml.NewDecoder(&c).Decode(&pf); err != nil { if err == io.EOF { 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 return Propfind{Allprop: new(struct{})}, 0, nil } @@ -233,14 +233,14 @@ type ixmlProperty struct { } // 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 { 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. +// See MultiStatusWriter for the "D:" namespace prefix. type propstat struct { Prop []Property `xml:"D:prop>_ignored_"` Status string `xml:"D:status"` @@ -258,7 +258,7 @@ type ixmlPropstat struct { } // 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 { // Convert from a propstat to an 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 -// See multistatusWriter for the "D:" namespace prefix. +// See MultiStatusWriter for the "D:" namespace prefix. type response struct { XMLName ixml.Name `xml:"D:response"` Href []string `xml:"D:href"` @@ -306,15 +306,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 - w http.ResponseWriter - enc *ixml.Encoder + Writer http.ResponseWriter + enc *ixml.Encoder } // Write validates and emits a DAV response as part of a multistatus response @@ -325,7 +325,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 (w *multistatusWriter) write(r *response) error { +func (w *MultiStatusWriter) write(r *response) error { switch len(r.Href) { case 0: return errInvalidResponse @@ -348,17 +348,17 @@ func (w *multistatusWriter) write(r *response) error { // 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 (w *multistatusWriter) writeHeader() error { +func (w *MultiStatusWriter) writeHeader() error { if w.enc != nil { return nil } - w.w.Header().Add("Content-Type", "text/xml; charset=utf-8") - w.w.WriteHeader(StatusMulti) - _, err := fmt.Fprintf(w.w, ``) + w.Writer.Header().Add("Content-Type", "text/xml; charset=utf-8") + w.Writer.WriteHeader(StatusMulti) + _, err := fmt.Fprintf(w.Writer, ``) if err != nil { return err } - w.enc = ixml.NewEncoder(w.w) + w.enc = ixml.NewEncoder(w.Writer) return w.enc.EncodeToken(ixml.StartElement{ Name: ixml.Name{ Space: "DAV:", @@ -375,7 +375,7 @@ func (w *multistatusWriter) writeHeader() error { // 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 // been written. -func (w *multistatusWriter) close() error { +func (w *MultiStatusWriter) close() error { if w.enc == nil { return nil } diff --git a/code/tool/webdav/xml_test.go b/code/tool/webdav/xml_test.go index 50b8452..804cc2a 100644 --- a/code/tool/webdav/xml_test.go +++ b/code/tool/webdav/xml_test.go @@ -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) diff --git a/go.sum b/go.sum index 8f93496..7a4dd7c 100644 --- a/go.sum +++ b/go.sum @@ -175,6 +175,7 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65 h1:+rhAzEzT3f4JtomfC371qB+0Ola2caSKcY69NUBZrRQ= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=