diff --git a/code/rest/dav_service.go b/code/rest/dav_service.go index 894900f..44ab028 100644 --- a/code/rest/dav_service.go +++ b/code/rest/dav_service.go @@ -14,6 +14,7 @@ import ( "path" "regexp" "strings" + "time" ) /** @@ -28,6 +29,7 @@ type DavService struct { BaseBean matterDao *MatterDao matterService *MatterService + lockSystem webdav.LockSystem } func (this *DavService) Init() { @@ -43,6 +45,8 @@ func (this *DavService) Init() { this.matterService = b } + // init the webdav lock system. + this.lockSystem = webdav.NewMemLS() } //get the depth in header. Not support infinity yet. @@ -536,9 +540,100 @@ func (this *DavService) HandleCopy(writer http.ResponseWriter, request *http.Req } //lock. -func (this *DavService) HandleLock(writer http.ResponseWriter, request *http.Request, user *User, subPath string) { +func (this *DavService) HandleLock(w http.ResponseWriter, r *http.Request, user *User, subPath string) { + + xLimits := r.Header.Get("X-Litmus") + if xLimits == "locks: 6 (lock_excl)" { + fmt.Println("stop here!") + } + + duration, err := webdav.ParseTimeout(r.Header.Get("Timeout")) + if err != nil { + panic(result.BadRequest(err.Error())) + } + li, status, err := webdav.ReadLockInfo(r.Body) + if err != nil { + panic(result.BadRequest(fmt.Sprintf("error:%s, status=%d", err.Error(), status))) + } + + token, ld, now, created := "", webdav.LockDetails{}, time.Now(), false + if li == (webdav.LockInfo{}) { + // An empty LockInfo means to refresh the lock. + ih, ok := webdav.ParseIfHeader(r.Header.Get("If")) + if !ok { + panic(result.BadRequest(webdav.ErrInvalidIfHeader.Error())) + } + if len(ih.Lists) == 1 && len(ih.Lists[0].Conditions) == 1 { + token = ih.Lists[0].Conditions[0].Token + } + if token == "" { + panic(result.BadRequest(webdav.ErrInvalidLockToken.Error())) + } + ld, err = this.lockSystem.Refresh(now, token, duration) + if err != nil { + if err == webdav.ErrNoSuchLock { + panic(result.StatusCodeWebResult(http.StatusPreconditionFailed, err.Error())) + } + panic(result.StatusCodeWebResult(http.StatusInternalServerError, err.Error())) + } + + } 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 := webdav.InfiniteDepth + if hdr := r.Header.Get("Depth"); hdr != "" { + depth = webdav.ParseDepth(hdr) + if depth != 0 && depth != webdav.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". + panic(result.StatusCodeWebResult(http.StatusBadRequest, webdav.ErrInvalidDepth.Error())) + } + } + + ld = webdav.LockDetails{ + Root: subPath, + Duration: duration, + OwnerXML: li.Owner.InnerXML, + ZeroDepth: depth == 0, + } + token, err = this.lockSystem.Create(now, ld) + if err != nil { + if err == webdav.ErrLocked { + panic(result.StatusCodeWebResult(http.StatusLocked, err.Error())) + } + panic(result.StatusCodeWebResult(http.StatusInternalServerError, err.Error())) + } + defer func() { + //when error occur, rollback. + //this.lockSystem.Unlock(now, token) + }() + + // Create the resource if it didn't previously exist. + // ctx := r.Context() + //if _, err := this.FileSystem.Stat(ctx, subPath); err != nil { + // f, err := h.FileSystem.OpenFile(ctx, reqPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) + // if err != nil { + // // TODO: detect missing intermediate dirs and return http.StatusConflict? + // return http.StatusInternalServerError, err + // } + // f.Close() + // created = true + //} + + // http://www.webdav.org/specs/rfc4918.html#HEADER_Lock-Token says that the + // Lock-Token value is a Coded-URL. We add angle brackets. + w.Header().Set("Lock-Token", "<"+token+">") + } + + w.Header().Set("Content-Type", "application/xml; charset=utf-8") + if created { + // This is "w.WriteHeader(http.StatusCreated)" and not "return + // http.StatusCreated, nil" because we write our own (XML) response to w + // and Handler.ServeHTTP would otherwise write "Created". + w.WriteHeader(http.StatusCreated) + } + _, _ = webdav.WriteLockInfo(w, token, ld) - panic(result.BadRequest("not support LOCK yet.")) } //unlock diff --git a/code/tool/result/web_result.go b/code/tool/result/web_result.go index 96e7e46..a39ef13 100644 --- a/code/tool/result/web_result.go +++ b/code/tool/result/web_result.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/eyebluecn/tank/code/tool/i18n" "net/http" + "strconv" ) type WebResult struct { @@ -73,7 +74,13 @@ func FetchHttpStatus(code string) int { } else if code == SERVER.Code { return SERVER.HttpStatus } else { - return UNKNOWN.HttpStatus + //if this is an int. regard it as statusCode + statusCode, err := strconv.Atoi(code) + if err != nil { + return UNKNOWN.HttpStatus + } else { + return statusCode + } } } @@ -104,6 +111,18 @@ func CustomWebResult(codeWrapper *CodeWrapper, description string) *WebResult { return wr } +//use standard http status code. +func StatusCodeWebResult(statusCode int, description string) *WebResult { + if description == "" { + description = http.StatusText(statusCode) + } + wr := &WebResult{ + Code: fmt.Sprintf("%d", statusCode), + Msg: description, + } + return wr +} + func BadRequestI18n(request *http.Request, item *i18n.Item, v ...interface{}) *WebResult { return CustomWebResult(BAD_REQUEST, fmt.Sprintf(item.Message(request), v...)) } diff --git a/code/tool/webdav/file.go b/code/tool/webdav/file.go index cc718cc..1b9dbc3 100644 --- a/code/tool/webdav/file.go +++ b/code/tool/webdav/file.go @@ -370,10 +370,10 @@ func (fs *memFS) Rename(ctx context.Context, oldName, newName string) error { if oNode.children != nil { if nNode, ok := nDir.children[nFrag]; ok { if nNode.children == nil { - return errNotADirectory + return ErrNotADirectory } if len(nNode.children) != 0 { - return errDirectoryNotEmpty + return ErrDirectoryNotEmpty } } } @@ -653,7 +653,7 @@ func copyProps(dst, src File) error { // See section 9.8.5 for when various HTTP status codes apply. func copyFiles(ctx context.Context, fs FileSystem, src, dst string, overwrite bool, depth int, recursion int) (status int, err error) { if recursion == 1000 { - return http.StatusInternalServerError, errRecursionTooDeep + return http.StatusInternalServerError, ErrRecursionTooDeep } recursion++ diff --git a/code/tool/webdav/if.go b/code/tool/webdav/if.go index 416e81c..37e3180 100644 --- a/code/tool/webdav/if.go +++ b/code/tool/webdav/if.go @@ -11,39 +11,39 @@ import ( "strings" ) -// ifHeader is a disjunction (OR) of ifLists. -type ifHeader struct { - lists []ifList +// IfHeader is a disjunction (OR) of ifLists. +type IfHeader struct { + Lists []IfList } -// ifList is a conjunction (AND) of Conditions, and an optional resource tag. -type ifList struct { - resourceTag string - conditions []Condition +// IfList is a conjunction (AND) of Conditions, and an optional resource tag. +type IfList struct { + ResourceTag string + Conditions []Condition } -// parseIfHeader parses the "If: foo bar" HTTP header. The httpHeader string +// ParseIfHeader parses the "If: foo bar" HTTP header. The httpHeader string // should omit the "If:" prefix and have any "\r\n"s collapsed to a " ", as is // returned by req.Header.Get("If") for a http.Request req. -func parseIfHeader(httpHeader string) (h ifHeader, ok bool) { +func ParseIfHeader(httpHeader string) (h IfHeader, ok bool) { s := strings.TrimSpace(httpHeader) switch tokenType, _, _ := lex(s); tokenType { case '(': - return parseNoTagLists(s) - case angleTokenType: - return parseTaggedLists(s) + return ParseNoTagLists(s) + case AngleTokenType: + return ParseTaggedLists(s) default: - return ifHeader{}, false + return IfHeader{}, false } } -func parseNoTagLists(s string) (h ifHeader, ok bool) { +func ParseNoTagLists(s string) (h IfHeader, ok bool) { for { - l, remaining, ok := parseList(s) + l, remaining, ok := ParseList(s) if !ok { - return ifHeader{}, false + return IfHeader{}, false } - h.lists = append(h.lists, l) + h.Lists = append(h.Lists, l) if remaining == "" { return h, true } @@ -51,67 +51,67 @@ func parseNoTagLists(s string) (h ifHeader, ok bool) { } } -func parseTaggedLists(s string) (h ifHeader, ok bool) { +func ParseTaggedLists(s string) (h IfHeader, ok bool) { resourceTag, n := "", 0 for first := true; ; first = false { tokenType, tokenStr, remaining := lex(s) switch tokenType { - case angleTokenType: + case AngleTokenType: if !first && n == 0 { - return ifHeader{}, false + return IfHeader{}, false } resourceTag, n = tokenStr, 0 s = remaining case '(': n++ - l, remaining, ok := parseList(s) + l, remaining, ok := ParseList(s) if !ok { - return ifHeader{}, false + return IfHeader{}, false } - l.resourceTag = resourceTag - h.lists = append(h.lists, l) + l.ResourceTag = resourceTag + h.Lists = append(h.Lists, l) if remaining == "" { return h, true } s = remaining default: - return ifHeader{}, false + return IfHeader{}, false } } } -func parseList(s string) (l ifList, remaining string, ok bool) { +func ParseList(s string) (l IfList, remaining string, ok bool) { tokenType, _, s := lex(s) if tokenType != '(' { - return ifList{}, "", false + return IfList{}, "", false } for { tokenType, _, remaining = lex(s) if tokenType == ')' { - if len(l.conditions) == 0 { - return ifList{}, "", false + if len(l.Conditions) == 0 { + return IfList{}, "", false } return l, remaining, true } - c, remaining, ok := parseCondition(s) + c, remaining, ok := ParseCondition(s) if !ok { - return ifList{}, "", false + return IfList{}, "", false } - l.conditions = append(l.conditions, c) + l.Conditions = append(l.Conditions, c) s = remaining } } -func parseCondition(s string) (c Condition, remaining string, ok bool) { +func ParseCondition(s string) (c Condition, remaining string, ok bool) { tokenType, tokenStr, s := lex(s) - if tokenType == notTokenType { + if tokenType == NotTokenType { c.Not = true tokenType, tokenStr, s = lex(s) } switch tokenType { - case strTokenType, angleTokenType: + case StrTokenType, AngleTokenType: c.Token = tokenStr - case squareTokenType: + case SquareTokenType: c.ETag = tokenStr default: return Condition{}, "", false @@ -122,12 +122,12 @@ func parseCondition(s string) (c Condition, remaining string, ok bool) { // Single-rune tokens like '(' or ')' have a token type equal to their rune. // All other tokens have a negative token type. const ( - errTokenType = rune(-1) - eofTokenType = rune(-2) - strTokenType = rune(-3) - notTokenType = rune(-4) - angleTokenType = rune(-5) - squareTokenType = rune(-6) + ErrTokenType = rune(-1) + EofTokenType = rune(-2) + StrTokenType = rune(-3) + NotTokenType = rune(-4) + AngleTokenType = rune(-5) + SquareTokenType = rune(-6) ) func lex(s string) (tokenType rune, tokenStr string, remaining string) { @@ -138,7 +138,7 @@ func lex(s string) (tokenType rune, tokenStr string, remaining string) { s = s[1:] } if len(s) == 0 { - return eofTokenType, "", "" + return EofTokenType, "", "" } i := 0 loop: @@ -152,22 +152,22 @@ loop: if i != 0 { tokenStr, remaining = s[:i], s[i:] if tokenStr == "Not" { - return notTokenType, "", remaining + return NotTokenType, "", remaining } - return strTokenType, tokenStr, remaining + return StrTokenType, tokenStr, remaining } j := 0 switch s[0] { case '<': - j, tokenType = strings.IndexByte(s, '>'), angleTokenType + j, tokenType = strings.IndexByte(s, '>'), AngleTokenType case '[': - j, tokenType = strings.IndexByte(s, ']'), squareTokenType + j, tokenType = strings.IndexByte(s, ']'), SquareTokenType default: return rune(s[0]), "", s[1:] } if j < 0 { - return errTokenType, "", "" + return ErrTokenType, "", "" } return tokenType, s[1:j], s[j+1:] } diff --git a/code/tool/webdav/if_test.go b/code/tool/webdav/if_test.go index aad61a4..901c8f9 100644 --- a/code/tool/webdav/if_test.go +++ b/code/tool/webdav/if_test.go @@ -16,61 +16,61 @@ func TestParseIfHeader(t *testing.T) { testCases := []struct { desc string input string - want ifHeader + want IfHeader }{{ "bad: empty", ``, - ifHeader{}, + IfHeader{}, }, { "bad: no parens", `foobar`, - ifHeader{}, + IfHeader{}, }, { "bad: empty list #1", `()`, - ifHeader{}, + IfHeader{}, }, { "bad: empty list #2", `(a) (b c) () (d)`, - ifHeader{}, + IfHeader{}, }, { "bad: no list after resource #1", ``, - ifHeader{}, + IfHeader{}, }, { "bad: no list after resource #2", ` (a)`, - ifHeader{}, + IfHeader{}, }, { "bad: no list after resource #3", ` (a) (b) `, - ifHeader{}, + IfHeader{}, }, { "bad: no-tag-list followed by tagged-list", `(a) (b) (c)`, - ifHeader{}, + IfHeader{}, }, { "bad: unfinished list", `(a`, - ifHeader{}, + IfHeader{}, }, { "bad: unfinished ETag", `([b`, - ifHeader{}, + IfHeader{}, }, { "bad: unfinished Notted list", `(Not a`, - ifHeader{}, + IfHeader{}, }, { "bad: double Not", `(Not Not a)`, - ifHeader{}, + IfHeader{}, }, { "good: one list with a Token", `(a)`, - ifHeader{ - lists: []ifList{{ - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + Conditions: []Condition{{ Token: `a`, }}, }}, @@ -78,9 +78,9 @@ func TestParseIfHeader(t *testing.T) { }, { "good: one list with an ETag", `([a])`, - ifHeader{ - lists: []ifList{{ - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + Conditions: []Condition{{ ETag: `a`, }}, }}, @@ -88,9 +88,9 @@ func TestParseIfHeader(t *testing.T) { }, { "good: one list with three Nots", `(Not a Not b Not [d])`, - ifHeader{ - lists: []ifList{{ - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + Conditions: []Condition{{ Not: true, Token: `a`, }, { @@ -105,13 +105,13 @@ func TestParseIfHeader(t *testing.T) { }, { "good: two lists", `(a) (b)`, - ifHeader{ - lists: []ifList{{ - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + Conditions: []Condition{{ Token: `a`, }}, }, { - conditions: []Condition{{ + Conditions: []Condition{{ Token: `b`, }}, }}, @@ -119,14 +119,14 @@ func TestParseIfHeader(t *testing.T) { }, { "good: two Notted lists", `(Not a) (Not b)`, - ifHeader{ - lists: []ifList{{ - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + Conditions: []Condition{{ Not: true, Token: `a`, }}, }, { - conditions: []Condition{{ + Conditions: []Condition{{ Not: true, Token: `b`, }}, @@ -136,10 +136,10 @@ func TestParseIfHeader(t *testing.T) { "section 7.5.1", ` ()`, - ifHeader{ - lists: []ifList{{ - resourceTag: `http://www.example.com/users/f/fielding/index.html`, - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + ResourceTag: `http://www.example.com/users/f/fielding/index.html`, + Conditions: []Condition{{ Token: `urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6`, }}, }}, @@ -147,9 +147,9 @@ func TestParseIfHeader(t *testing.T) { }, { "section 7.5.2 #1", `()`, - ifHeader{ - lists: []ifList{{ - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + Conditions: []Condition{{ Token: `urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf`, }}, }}, @@ -158,10 +158,10 @@ func TestParseIfHeader(t *testing.T) { "section 7.5.2 #2", ` ()`, - ifHeader{ - lists: []ifList{{ - resourceTag: `http://example.com/locked/`, - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + ResourceTag: `http://example.com/locked/`, + Conditions: []Condition{{ Token: `urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf`, }}, }}, @@ -170,10 +170,10 @@ func TestParseIfHeader(t *testing.T) { "section 7.5.2 #3", ` ()`, - ifHeader{ - lists: []ifList{{ - resourceTag: `http://example.com/locked/member`, - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + ResourceTag: `http://example.com/locked/member`, + Conditions: []Condition{{ Token: `urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf`, }}, }}, @@ -182,13 +182,13 @@ func TestParseIfHeader(t *testing.T) { "section 9.9.6", `() ()`, - ifHeader{ - lists: []ifList{{ - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + Conditions: []Condition{{ Token: `urn:uuid:fe184f2e-6eec-41d0-c765-01adc56e6bb4`, }}, }, { - conditions: []Condition{{ + Conditions: []Condition{{ Token: `urn:uuid:e454f3f3-acdc-452a-56c7-00a5c91e4b77`, }}, }}, @@ -196,9 +196,9 @@ func TestParseIfHeader(t *testing.T) { }, { "section 9.10.8", `()`, - ifHeader{ - lists: []ifList{{ - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + Conditions: []Condition{{ Token: `urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4`, }}, }}, @@ -208,15 +208,15 @@ func TestParseIfHeader(t *testing.T) { `( ["I am an ETag"]) (["I am another ETag"])`, - ifHeader{ - lists: []ifList{{ - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + Conditions: []Condition{{ Token: `urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2`, }, { ETag: `"I am an ETag"`, }}, }, { - conditions: []Condition{{ + Conditions: []Condition{{ ETag: `"I am another ETag"`, }}, }}, @@ -225,9 +225,9 @@ func TestParseIfHeader(t *testing.T) { "section 10.4.7", `(Not )`, - ifHeader{ - lists: []ifList{{ - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + Conditions: []Condition{{ Not: true, Token: `urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2`, }, { @@ -239,13 +239,13 @@ func TestParseIfHeader(t *testing.T) { "section 10.4.8", `() (Not )`, - ifHeader{ - lists: []ifList{{ - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + Conditions: []Condition{{ Token: `urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2`, }}, }, { - conditions: []Condition{{ + Conditions: []Condition{{ Not: true, Token: `DAV:no-lock`, }}, @@ -256,17 +256,17 @@ func TestParseIfHeader(t *testing.T) { ` ( [W/"A weak ETag"]) (["strong ETag"])`, - ifHeader{ - lists: []ifList{{ - resourceTag: `/resource1`, - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + ResourceTag: `/resource1`, + Conditions: []Condition{{ Token: `urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2`, }, { ETag: `W/"A weak ETag"`, }}, }, { - resourceTag: `/resource1`, - conditions: []Condition{{ + ResourceTag: `/resource1`, + Conditions: []Condition{{ ETag: `"strong ETag"`, }}, }}, @@ -275,10 +275,10 @@ func TestParseIfHeader(t *testing.T) { "section 10.4.10", ` ()`, - ifHeader{ - lists: []ifList{{ - resourceTag: `http://www.example.com/specs/`, - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + ResourceTag: `http://www.example.com/specs/`, + Conditions: []Condition{{ Token: `urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2`, }}, }}, @@ -286,10 +286,10 @@ func TestParseIfHeader(t *testing.T) { }, { "section 10.4.11 #1", ` (["4217"])`, - ifHeader{ - lists: []ifList{{ - resourceTag: `/specs/rfc2518.doc`, - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + ResourceTag: `/specs/rfc2518.doc`, + Conditions: []Condition{{ ETag: `"4217"`, }}, }}, @@ -297,10 +297,10 @@ func TestParseIfHeader(t *testing.T) { }, { "section 10.4.11 #2", ` (Not ["4217"])`, - ifHeader{ - lists: []ifList{{ - resourceTag: `/specs/rfc2518.doc`, - conditions: []Condition{{ + IfHeader{ + Lists: []IfList{{ + ResourceTag: `/specs/rfc2518.doc`, + Conditions: []Condition{{ Not: true, ETag: `"4217"`, }}, @@ -309,8 +309,8 @@ func TestParseIfHeader(t *testing.T) { }} for _, tc := range testCases { - got, ok := parseIfHeader(strings.Replace(tc.input, "\n", "", -1)) - if gotEmpty := reflect.DeepEqual(got, ifHeader{}); gotEmpty == ok { + got, ok := ParseIfHeader(strings.Replace(tc.input, "\n", "", -1)) + if gotEmpty := reflect.DeepEqual(got, IfHeader{}); gotEmpty == ok { t.Errorf("%s: should be different: empty header == %t, ok == %t", tc.desc, gotEmpty, ok) continue } diff --git a/code/tool/webdav/lock.go b/code/tool/webdav/lock.go index 344ac5c..2d96b3e 100644 --- a/code/tool/webdav/lock.go +++ b/code/tool/webdav/lock.go @@ -37,7 +37,7 @@ type Condition struct { // of host operating system convention. type LockSystem interface { // Confirm confirms that the caller can claim all of the locks specified by - // the given conditions, and that holding the union of all of those locks + // the given Conditions, and that holding the union of all of those locks // gives exclusive access to all of the named resources. Up to two resources // can be named. Empty names are ignored. // @@ -113,14 +113,14 @@ type LockDetails struct { // NewMemLS returns a new in-memory LockSystem. func NewMemLS() LockSystem { - return &memLS{ + return &MemLS{ byName: make(map[string]*memLSNode), byToken: make(map[string]*memLSNode), gen: uint64(time.Now().Unix()), } } -type memLS struct { +type MemLS struct { mu sync.Mutex byName map[string]*memLSNode byToken map[string]*memLSNode @@ -130,12 +130,12 @@ type memLS struct { byExpiry byExpiry } -func (m *memLS) nextToken() string { +func (m *MemLS) nextToken() string { m.gen++ return strconv.FormatUint(m.gen, 10) } -func (m *memLS) collectExpiredNodes(now time.Time) { +func (m *MemLS) collectExpiredNodes(now time.Time) { for len(m.byExpiry) > 0 { if now.Before(m.byExpiry[0].expiry) { break @@ -144,7 +144,7 @@ func (m *memLS) collectExpiredNodes(now time.Time) { } } -func (m *memLS) Confirm(now time.Time, name0, name1 string, conditions ...Condition) (func(), error) { +func (m *MemLS) Confirm(now time.Time, name0, name1 string, conditions ...Condition) (func(), error) { m.mu.Lock() defer m.mu.Unlock() m.collectExpiredNodes(now) @@ -185,11 +185,11 @@ func (m *memLS) Confirm(now time.Time, name0, name1 string, conditions ...Condit } // lookup returns the node n that locks the named resource, provided that n -// matches at least one of the given conditions and that lock isn't held by +// matches at least one of the given Conditions and that lock isn't held by // another party. Otherwise, it returns nil. // // n may be a parent of the named resource, if n is an infinite depth lock. -func (m *memLS) lookup(name string, conditions ...Condition) (n *memLSNode) { +func (m *MemLS) lookup(name string, conditions ...Condition) (n *memLSNode) { // TODO: support Condition.Not and Condition.ETag. for _, c := range conditions { n = m.byToken[c.Token] @@ -209,9 +209,9 @@ func (m *memLS) lookup(name string, conditions ...Condition) (n *memLSNode) { return nil } -func (m *memLS) hold(n *memLSNode) { +func (m *MemLS) hold(n *memLSNode) { if n.held { - panic("webdav: memLS inconsistent held state") + panic("webdav: MemLS inconsistent held state") } n.held = true if n.details.Duration >= 0 && n.byExpiryIndex >= 0 { @@ -219,9 +219,9 @@ func (m *memLS) hold(n *memLSNode) { } } -func (m *memLS) unhold(n *memLSNode) { +func (m *MemLS) unhold(n *memLSNode) { if !n.held { - panic("webdav: memLS inconsistent held state") + panic("webdav: MemLS inconsistent held state") } n.held = false if n.details.Duration >= 0 { @@ -229,7 +229,7 @@ func (m *memLS) unhold(n *memLSNode) { } } -func (m *memLS) Create(now time.Time, details LockDetails) (string, error) { +func (m *MemLS) Create(now time.Time, details LockDetails) (string, error) { m.mu.Lock() defer m.mu.Unlock() m.collectExpiredNodes(now) @@ -249,7 +249,7 @@ func (m *memLS) Create(now time.Time, details LockDetails) (string, error) { return n.token, nil } -func (m *memLS) Refresh(now time.Time, token string, duration time.Duration) (LockDetails, error) { +func (m *MemLS) Refresh(now time.Time, token string, duration time.Duration) (LockDetails, error) { m.mu.Lock() defer m.mu.Unlock() m.collectExpiredNodes(now) @@ -272,7 +272,7 @@ func (m *memLS) Refresh(now time.Time, token string, duration time.Duration) (Lo return n.details, nil } -func (m *memLS) Unlock(now time.Time, token string) error { +func (m *MemLS) Unlock(now time.Time, token string) error { m.mu.Lock() defer m.mu.Unlock() m.collectExpiredNodes(now) @@ -288,7 +288,7 @@ func (m *memLS) Unlock(now time.Time, token string) error { return nil } -func (m *memLS) canCreate(name string, zeroDepth bool) bool { +func (m *MemLS) canCreate(name string, zeroDepth bool) bool { return walkToRoot(name, func(name0 string, first bool) bool { n := m.byName[name0] if n == nil { @@ -312,7 +312,7 @@ func (m *memLS) canCreate(name string, zeroDepth bool) bool { }) } -func (m *memLS) create(name string) (ret *memLSNode) { +func (m *MemLS) create(name string) (ret *memLSNode) { walkToRoot(name, func(name0 string, first bool) bool { n := m.byName[name0] if n == nil { @@ -333,7 +333,7 @@ func (m *memLS) create(name string) (ret *memLSNode) { return ret } -func (m *memLS) remove(n *memLSNode) { +func (m *MemLS) remove(n *memLSNode) { delete(m.byToken, n.token) n.token = "" walkToRoot(n.details.Root, func(name0 string, first bool) bool { @@ -376,7 +376,7 @@ type memLSNode struct { refCount int // expiry is when this node's lock expires. expiry time.Time - // byExpiryIndex is the index of this node in memLS.byExpiry. It is -1 + // byExpiryIndex is the index of this node in MemLS.byExpiry. It is -1 // if this node does not expire, or has expired. byExpiryIndex int // held is whether this node's lock is actively held by a Confirm call. @@ -416,9 +416,9 @@ func (b *byExpiry) Pop() interface{} { const infiniteTimeout = -1 -// parseTimeout parses the Timeout HTTP header, as per section 10.7. If s is +// ParseTimeout parses the Timeout HTTP header, as per section 10.7. If s is // empty, an infiniteTimeout is returned. -func parseTimeout(s string) (time.Duration, error) { +func ParseTimeout(s string) (time.Duration, error) { if s == "" { return infiniteTimeout, nil } @@ -431,15 +431,15 @@ func parseTimeout(s string) (time.Duration, error) { } const pre = "Second-" if !strings.HasPrefix(s, pre) { - return 0, errInvalidTimeout + return 0, ErrInvalidTimeout } s = s[len(pre):] if s == "" || s[0] < '0' || '9' < s[0] { - return 0, errInvalidTimeout + return 0, ErrInvalidTimeout } n, err := strconv.ParseInt(s, 10, 64) if err != nil || 1<<32-1 < n { - return 0, errInvalidTimeout + return 0, ErrInvalidTimeout } return time.Duration(n) * time.Second, nil } diff --git a/code/tool/webdav/lock_test.go b/code/tool/webdav/lock_test.go index 561ada4..93ce664 100644 --- a/code/tool/webdav/lock_test.go +++ b/code/tool/webdav/lock_test.go @@ -97,7 +97,7 @@ func lockTestZeroDepth(name string) bool { func TestMemLSCanCreate(t *testing.T) { now := time.Unix(0, 0) - m := NewMemLS().(*memLS) + m := NewMemLS().(*MemLS) for _, name := range lockTestNames { _, err := m.Create(now, LockDetails{ @@ -157,7 +157,7 @@ func TestMemLSCanCreate(t *testing.T) { func TestMemLSLookup(t *testing.T) { now := time.Unix(0, 0) - m := NewMemLS().(*memLS) + m := NewMemLS().(*MemLS) badToken := m.nextToken() t.Logf("badToken=%q", badToken) @@ -206,7 +206,7 @@ func TestMemLSLookup(t *testing.T) { func TestMemLSConfirm(t *testing.T) { now := time.Unix(0, 0) - m := NewMemLS().(*memLS) + m := NewMemLS().(*MemLS) alice, err := m.Create(now, LockDetails{ Root: "/alice", Duration: infiniteTimeout, @@ -302,7 +302,7 @@ func TestMemLSConfirm(t *testing.T) { func TestMemLSNonCanonicalRoot(t *testing.T) { now := time.Unix(0, 0) - m := NewMemLS().(*memLS) + m := NewMemLS().(*MemLS) token, err := m.Create(now, LockDetails{ Root: "/foo/./bar//", Duration: 1 * time.Second, @@ -322,7 +322,7 @@ func TestMemLSNonCanonicalRoot(t *testing.T) { } func TestMemLSExpiry(t *testing.T) { - m := NewMemLS().(*memLS) + m := NewMemLS().(*MemLS) testCases := []string{ "setNow 0", "create /a.5", @@ -451,7 +451,7 @@ func TestMemLSExpiry(t *testing.T) { func TestMemLS(t *testing.T) { now := time.Unix(0, 0) - m := NewMemLS().(*memLS) + m := NewMemLS().(*MemLS) rng := rand.New(rand.NewSource(0)) tokens := map[string]string{} nConfirm, nCreate, nRefresh, nUnlock := 0, 0, 0, 0 @@ -535,7 +535,7 @@ func TestMemLS(t *testing.T) { } } -func (m *memLS) consistent() error { +func (m *MemLS) consistent() error { m.mu.Lock() defer m.mu.Unlock() @@ -653,11 +653,11 @@ func TestParseTimeout(t *testing.T) { }, { "Infinitesimal", 0, - errInvalidTimeout, + ErrInvalidTimeout, }, { "infinite", 0, - errInvalidTimeout, + ErrInvalidTimeout, }, { "Second-0", 0 * time.Second, @@ -677,31 +677,31 @@ func TestParseTimeout(t *testing.T) { }, { "junk", 0, - errInvalidTimeout, + ErrInvalidTimeout, }, { "Second-", 0, - errInvalidTimeout, + ErrInvalidTimeout, }, { "Second--1", 0, - errInvalidTimeout, + ErrInvalidTimeout, }, { "Second--123", 0, - errInvalidTimeout, + ErrInvalidTimeout, }, { "Second-+123", 0, - errInvalidTimeout, + ErrInvalidTimeout, }, { "Second-0x123", 0, - errInvalidTimeout, + ErrInvalidTimeout, }, { "second-123", 0, - errInvalidTimeout, + ErrInvalidTimeout, }, { "Second-4294967295", 4294967295 * time.Second, @@ -711,7 +711,7 @@ func TestParseTimeout(t *testing.T) { // must not be greater than 2^32-1." "Second-4294967296", 0, - errInvalidTimeout, + ErrInvalidTimeout, }, { // This test case comes from section 9.10.9 of the spec. It says, // @@ -727,7 +727,7 @@ func TestParseTimeout(t *testing.T) { }} for _, tc := range testCases { - got, gotErr := parseTimeout(tc.s) + got, gotErr := ParseTimeout(tc.s) if got != tc.want || gotErr != tc.wantErr { t.Errorf("parsing %q:\ngot %v, %v\nwant %v, %v", tc.s, got, gotErr, tc.want, tc.wantErr) } diff --git a/code/tool/webdav/webdav.go b/code/tool/webdav/webdav.go index 5162df0..22de075 100644 --- a/code/tool/webdav/webdav.go +++ b/code/tool/webdav/webdav.go @@ -36,15 +36,15 @@ func (h *Handler) stripPrefix(p string) (string, int, error) { if r := strings.TrimPrefix(p, h.Prefix); len(r) < len(p) { return r, http.StatusOK, nil } - return p, http.StatusNotFound, errPrefixMismatch + return p, http.StatusNotFound, ErrPrefixMismatch } func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - status, err := http.StatusBadRequest, errUnsupportedMethod + status, err := http.StatusBadRequest, ErrUnsupportedMethod if h.FileSystem == nil { - status, err = http.StatusInternalServerError, errNoFileSystem + status, err = http.StatusInternalServerError, ErrNoFileSystem } else if h.LockSystem == nil { - status, err = http.StatusInternalServerError, errNoLockSystem + status, err = http.StatusInternalServerError, ErrNoLockSystem } else { switch r.Method { case "OPTIONS": @@ -131,13 +131,13 @@ func (h *Handler) confirmLocks(r *http.Request, src, dst string) (release func() }, 0, nil } - ih, ok := parseIfHeader(hdr) + ih, ok := ParseIfHeader(hdr) if !ok { - return nil, http.StatusBadRequest, errInvalidIfHeader + return nil, http.StatusBadRequest, ErrInvalidIfHeader } - // ih is a disjunction (OR) of ifLists, so any ifList will do. - for _, l := range ih.lists { - lsrc := l.resourceTag + // ih is a disjunction (OR) of ifLists, so any IfList will do. + for _, l := range ih.Lists { + lsrc := l.ResourceTag if lsrc == "" { lsrc = src } else { @@ -153,7 +153,7 @@ func (h *Handler) confirmLocks(r *http.Request, src, dst string) (release func() return nil, status, err } } - release, err = h.LockSystem.Confirm(time.Now(), lsrc, dst, l.conditions...) + release, err = h.LockSystem.Confirm(time.Now(), lsrc, dst, l.Conditions...) if err == ErrConfirmationFailed { continue } @@ -317,14 +317,14 @@ func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request) (status in func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request) (status int, err error) { hdr := r.Header.Get("Destination") if hdr == "" { - return http.StatusBadRequest, errInvalidDestination + return http.StatusBadRequest, ErrInvalidDestination } u, err := url.Parse(hdr) if err != nil { - return http.StatusBadRequest, errInvalidDestination + return http.StatusBadRequest, ErrInvalidDestination } if u.Host != "" && u.Host != r.Host { - return http.StatusBadGateway, errInvalidDestination + return http.StatusBadGateway, ErrInvalidDestination } src, status, err := h.stripPrefix(r.URL.Path) @@ -338,10 +338,10 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request) (status } if dst == "" { - return http.StatusBadGateway, errInvalidDestination + return http.StatusBadGateway, ErrInvalidDestination } if dst == src { - return http.StatusForbidden, errDestinationEqualsSource + return http.StatusForbidden, ErrDestinationEqualsSource } ctx := r.Context() @@ -366,7 +366,7 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request) (status 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 + return http.StatusBadRequest, ErrInvalidDepth } } return copyFiles(ctx, h.FileSystem, src, dst, r.Header.Get("Overwrite") != "F", depth, 0) @@ -383,35 +383,35 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request) (status // Depth header on a MOVE on a collection with any value but "infinity"." if hdr := r.Header.Get("Depth"); hdr != "" { if ParseDepth(hdr) != InfiniteDepth { - return http.StatusBadRequest, errInvalidDepth + return http.StatusBadRequest, ErrInvalidDepth } } return moveFiles(ctx, h.FileSystem, src, dst, r.Header.Get("Overwrite") == "T") } func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request) (retStatus int, retErr error) { - duration, err := parseTimeout(r.Header.Get("Timeout")) + duration, err := ParseTimeout(r.Header.Get("Timeout")) 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{}) { - // An empty lockInfo means to refresh the lock. - ih, ok := parseIfHeader(r.Header.Get("If")) + if li == (LockInfo{}) { + // An empty LockInfo means to refresh the lock. + ih, ok := ParseIfHeader(r.Header.Get("If")) if !ok { - return http.StatusBadRequest, errInvalidIfHeader + return http.StatusBadRequest, ErrInvalidIfHeader } - if len(ih.lists) == 1 && len(ih.lists[0].conditions) == 1 { - token = ih.lists[0].conditions[0].Token + if len(ih.Lists) == 1 && len(ih.Lists[0].Conditions) == 1 { + token = ih.Lists[0].Conditions[0].Token } if token == "" { - return http.StatusBadRequest, errInvalidLockToken + return http.StatusBadRequest, ErrInvalidLockToken } ld, err = h.LockSystem.Refresh(now, token, duration) if err != nil { @@ -430,7 +430,7 @@ func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request) (retStatus 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 + return http.StatusBadRequest, ErrInvalidDepth } } reqPath, status, err := h.stripPrefix(r.URL.Path) @@ -479,7 +479,7 @@ func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request) (retStatus // and Handler.ServeHTTP would otherwise write "Created". w.WriteHeader(http.StatusCreated) } - writeLockInfo(w, token, ld) + _, _ = WriteLockInfo(w, token, ld) return 0, nil } @@ -488,7 +488,7 @@ func (h *Handler) handleUnlock(w http.ResponseWriter, r *http.Request) (status i // Lock-Token value is a Coded-URL. We strip its angle brackets. t := r.Header.Get("Lock-Token") if len(t) < 2 || t[0] != '<' || t[len(t)-1] != '>' { - return http.StatusBadRequest, errInvalidLockToken + return http.StatusBadRequest, ErrInvalidLockToken } t = t[1 : len(t)-1] @@ -523,7 +523,7 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status if hdr := r.Header.Get("Depth"); hdr != "" { depth = ParseDepth(hdr) if depth == InvalidDepth { - return http.StatusBadRequest, errInvalidDepth + return http.StatusBadRequest, ErrInvalidDepth } } pf, status, err := ReadPropfind(r.Body) @@ -685,22 +685,22 @@ func StatusText(code int) string { } var ( - errDestinationEqualsSource = errors.New("webdav: destination equals source") - errDirectoryNotEmpty = errors.New("webdav: directory not empty") - errInvalidDepth = errors.New("webdav: invalid depth") - errInvalidDestination = errors.New("webdav: invalid destination") - errInvalidIfHeader = errors.New("webdav: invalid If header") - errInvalidLockInfo = errors.New("webdav: invalid lock info") - errInvalidLockToken = errors.New("webdav: invalid lock token") - errInvalidPropfind = errors.New("webdav: invalid propfind") - errInvalidProppatch = errors.New("webdav: invalid proppatch") - errInvalidResponse = errors.New("webdav: invalid response") - errInvalidTimeout = errors.New("webdav: invalid timeout") - errNoFileSystem = errors.New("webdav: no file system") - errNoLockSystem = errors.New("webdav: no lock system") - errNotADirectory = errors.New("webdav: not a directory") - errPrefixMismatch = errors.New("webdav: prefix mismatch") - errRecursionTooDeep = errors.New("webdav: recursion too deep") - errUnsupportedLockInfo = errors.New("webdav: unsupported lock info") - errUnsupportedMethod = errors.New("webdav: unsupported method") + ErrDestinationEqualsSource = errors.New("webdav: destination equals source") + ErrDirectoryNotEmpty = errors.New("webdav: directory not empty") + ErrInvalidDepth = errors.New("webdav: invalid depth") + ErrInvalidDestination = errors.New("webdav: invalid destination") + ErrInvalidIfHeader = errors.New("webdav: invalid If header") + ErrInvalidLockInfo = errors.New("webdav: invalid lock info") + ErrInvalidLockToken = errors.New("webdav: invalid lock token") + ErrInvalidPropfind = errors.New("webdav: invalid propfind") + ErrInvalidProppatch = errors.New("webdav: invalid proppatch") + ErrInvalidResponse = errors.New("webdav: invalid response") + ErrInvalidTimeout = errors.New("webdav: invalid timeout") + ErrNoFileSystem = errors.New("webdav: no file system") + ErrNoLockSystem = errors.New("webdav: no lock system") + ErrNotADirectory = errors.New("webdav: not a directory") + ErrPrefixMismatch = errors.New("webdav: prefix mismatch") + ErrRecursionTooDeep = errors.New("webdav: recursion too deep") + ErrUnsupportedLockInfo = errors.New("webdav: unsupported lock info") + ErrUnsupportedMethod = errors.New("webdav: unsupported method") ) diff --git a/code/tool/webdav/xml.go b/code/tool/webdav/xml.go index bb6ae48..7b30971 100644 --- a/code/tool/webdav/xml.go +++ b/code/tool/webdav/xml.go @@ -36,7 +36,7 @@ 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"` @@ -49,23 +49,23 @@ type owner struct { 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} 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 + 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 } @@ -81,7 +81,7 @@ func (c *countingReader) Read(p []byte) (int, error) { 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" @@ -184,22 +184,22 @@ func ReadPropfind(r io.Reader) (pf Propfind, status int, err error) { // http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND return Propfind{Allprop: new(struct{})}, 0, nil } - err = errInvalidPropfind + err = ErrInvalidPropfind } 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 } @@ -328,14 +328,14 @@ type MultiStatusWriter struct { func (w *MultiStatusWriter) write(r *response) error { switch len(r.Href) { case 0: - return errInvalidResponse + return ErrInvalidResponse case 1: if len(r.Propstat) > 0 != (r.Status == "") { - return errInvalidResponse + return ErrInvalidResponse } default: if len(r.Propstat) > 0 || r.Status == "" { - return errInvalidResponse + return ErrInvalidResponse } } err := w.writeHeader() @@ -506,12 +506,12 @@ func ReadProppatch(r io.Reader) (patches []Proppatch, status int, err error) { case ixml.Name{Space: "DAV:", Local: "remove"}: for _, p := range op.Prop { if len(p.InnerXML) > 0 { - return nil, http.StatusBadRequest, errInvalidProppatch + return nil, http.StatusBadRequest, ErrInvalidProppatch } } remove = true default: - return nil, http.StatusBadRequest, errInvalidProppatch + return nil, http.StatusBadRequest, ErrInvalidProppatch } patches = append(patches, Proppatch{Remove: remove, Props: op.Prop}) } diff --git a/code/tool/webdav/xml_test.go b/code/tool/webdav/xml_test.go index 783c069..bafc754 100644 --- a/code/tool/webdav/xml_test.go +++ b/code/tool/webdav/xml_test.go @@ -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) { " no end tag \n" + " \n" + "", - lockInfo{}, + LockInfo{}, http.StatusBadRequest, }, { "bad: invalid UTF-8", @@ -54,7 +54,7 @@ func TestReadLockInfo(t *testing.T) { " \xff \n" + " \n" + "", - lockInfo{}, + LockInfo{}, http.StatusBadRequest, }, { "bad: unfinished XML #1", @@ -62,7 +62,7 @@ func TestReadLockInfo(t *testing.T) { "\n" + " \n" + " \n", - lockInfo{}, + LockInfo{}, http.StatusBadRequest, }, { "bad: unfinished XML #2", @@ -71,12 +71,12 @@ func TestReadLockInfo(t *testing.T) { " \n" + " \n" + " \n", - lockInfo{}, + LockInfo{}, http.StatusBadRequest, }, { "good: empty", "", - lockInfo{}, + LockInfo{}, 0, }, { "good: plain-text owner", @@ -86,7 +86,7 @@ func TestReadLockInfo(t *testing.T) { " \n" + " gopher\n" + "", - lockInfo{ + LockInfo{ XMLName: ixml.Name{Space: "DAV:", Local: "lockinfo"}, Exclusive: new(struct{}), Write: new(struct{}), @@ -105,7 +105,7 @@ func TestReadLockInfo(t *testing.T) { " http://example.org/~ejw/contact.html\n" + " \n" + "", - lockInfo{ + LockInfo{ XMLName: ixml.Name{Space: "DAV:", Local: "lockinfo"}, Exclusive: new(struct{}), Write: new(struct{}), @@ -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) @@ -128,7 +128,7 @@ func TestReadLockInfo(t *testing.T) { continue } if !reflect.DeepEqual(li, tc.wantLI) || status != tc.wantStatus { - t.Errorf("%s:\ngot lockInfo=%v, status=%v\nwant lockInfo=%v, status=%v", + t.Errorf("%s:\ngot LockInfo=%v, status=%v\nwant LockInfo=%v, status=%v", tc.desc, li, status, tc.wantLI, tc.wantStatus) continue } @@ -503,7 +503,7 @@ func TestMultistatusWriter(t *testing.T) { Status: "HTTP/1.1 200 OK", }}, }}, - wantErr: errInvalidResponse, + wantErr: ErrInvalidResponse, // default of http.responseWriter wantCode: http.StatusOK, }, { @@ -511,7 +511,7 @@ func TestMultistatusWriter(t *testing.T) { responses: []response{{ Href: []string{"http://example.com/foo", "http://example.com/bar"}, }}, - wantErr: errInvalidResponse, + wantErr: ErrInvalidResponse, // default of http.responseWriter wantCode: http.StatusOK, }, { @@ -519,7 +519,7 @@ func TestMultistatusWriter(t *testing.T) { responses: []response{{ Href: []string{"http://example.com/foo"}, }}, - wantErr: errInvalidResponse, + wantErr: ErrInvalidResponse, // default of http.responseWriter wantCode: http.StatusOK, }, { @@ -537,7 +537,7 @@ func TestMultistatusWriter(t *testing.T) { }}, Status: "HTTP/1.1 200 OK", }}, - wantErr: errInvalidResponse, + wantErr: ErrInvalidResponse, // default of http.responseWriter wantCode: http.StatusOK, }, { @@ -557,7 +557,7 @@ func TestMultistatusWriter(t *testing.T) { Status: "HTTP/1.1 200 OK", }}, }}, - wantErr: errInvalidResponse, + wantErr: ErrInvalidResponse, // default of http.responseWriter wantCode: http.StatusOK, }}