lock.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. // Copyright 2014 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package webdav
  5. import (
  6. "container/heap"
  7. "errors"
  8. "strconv"
  9. "strings"
  10. "sync"
  11. "time"
  12. )
  13. var (
  14. // ErrConfirmationFailed is returned by a LockSystem's Confirm method.
  15. ErrConfirmationFailed = errors.New("webdav: confirmation failed")
  16. // ErrForbidden is returned by a LockSystem's Unlock method.
  17. ErrForbidden = errors.New("webdav: forbidden")
  18. // ErrLocked is returned by a LockSystem's Create, Refresh and Unlock methods.
  19. ErrLocked = errors.New("webdav: locked")
  20. // ErrNoSuchLock is returned by a LockSystem's Refresh and Unlock methods.
  21. ErrNoSuchLock = errors.New("webdav: no such lock")
  22. )
  23. // Condition can match a WebDAV resource, based on a token or ETag.
  24. // Exactly one of Token and ETag should be non-empty.
  25. type Condition struct {
  26. Not bool
  27. Token string
  28. ETag string
  29. }
  30. // Releaser releases previously confirmed lock claims.
  31. //
  32. // Calling Release does not unlock the lock, in the WebDAV UNLOCK sense, but
  33. // once LockSystem.Confirm has confirmed that a lock claim is valid, that lock
  34. // cannot be Confirmed again until it has been Released.
  35. type Releaser interface {
  36. Release()
  37. }
  38. // LockSystem manages access to a collection of named resources. The elements
  39. // in a lock name are separated by slash ('/', U+002F) characters, regardless
  40. // of host operating system convention.
  41. type LockSystem interface {
  42. // Confirm confirms that the caller can claim all of the locks specified by
  43. // the given conditions, and that holding the union of all of those locks
  44. // gives exclusive access to the named resource.
  45. //
  46. // Exactly one of r and err will be non-nil. If r is non-nil, all of the
  47. // requested locks are held until r.Release is called.
  48. //
  49. // If Confirm returns ErrConfirmationFailed then the Handler will continue
  50. // to try any other set of locks presented (a WebDAV HTTP request can
  51. // present more than one set of locks). If it returns any other non-nil
  52. // error, the Handler will write a "500 Internal Server Error" HTTP status.
  53. Confirm(now time.Time, name string, conditions ...Condition) (r Releaser, err error)
  54. // Create creates a lock with the given depth, duration, owner and root
  55. // (name). The depth will either be negative (meaning infinite) or zero.
  56. //
  57. // If Create returns ErrLocked then the Handler will write a "423 Locked"
  58. // HTTP status. If it returns any other non-nil error, the Handler will
  59. // write a "500 Internal Server Error" HTTP status.
  60. //
  61. // See http://www.webdav.org/specs/rfc4918.html#rfc.section.9.10.6 for
  62. // when to use each error.
  63. //
  64. // The token returned identifies the created lock. It should be an absolute
  65. // URI as defined by RFC 3986, Section 4.3. In particular, it should not
  66. // contain whitespace.
  67. Create(now time.Time, details LockDetails) (token string, err error)
  68. // Refresh refreshes the lock with the given token.
  69. //
  70. // If Refresh returns ErrLocked then the Handler will write a "423 Locked"
  71. // HTTP Status. If Refresh returns ErrNoSuchLock then the Handler will write
  72. // a "412 Precondition Failed" HTTP Status. If it returns any other non-nil
  73. // error, the Handler will write a "500 Internal Server Error" HTTP status.
  74. //
  75. // See http://www.webdav.org/specs/rfc4918.html#rfc.section.9.10.6 for
  76. // when to use each error.
  77. Refresh(now time.Time, token string, duration time.Duration) (LockDetails, error)
  78. // Unlock unlocks the lock with the given token.
  79. //
  80. // If Unlock returns ErrForbidden then the Handler will write a "403
  81. // Forbidden" HTTP Status. If Unlock returns ErrLocked then the Handler
  82. // will write a "423 Locked" HTTP status. If Unlock returns ErrNoSuchLock
  83. // then the Handler will write a "409 Conflict" HTTP Status. If it returns
  84. // any other non-nil error, the Handler will write a "500 Internal Server
  85. // Error" HTTP status.
  86. //
  87. // See http://www.webdav.org/specs/rfc4918.html#rfc.section.9.11.1 for
  88. // when to use each error.
  89. Unlock(now time.Time, token string) error
  90. }
  91. // LockDetails are a lock's metadata.
  92. type LockDetails struct {
  93. // Root is the root resource name being locked. For a zero-depth lock, the
  94. // root is the only resource being locked.
  95. Root string
  96. // Duration is the lock timeout. A negative duration means infinite.
  97. Duration time.Duration
  98. // OwnerXML is the verbatim <owner> XML given in a LOCK HTTP request.
  99. //
  100. // TODO: does the "verbatim" nature play well with XML namespaces?
  101. // Does the OwnerXML field need to have more structure? See
  102. // https://codereview.appspot.com/175140043/#msg2
  103. OwnerXML string
  104. // ZeroDepth is whether the lock has zero depth. If it does not have zero
  105. // depth, it has infinite depth.
  106. ZeroDepth bool
  107. }
  108. // NewMemLS returns a new in-memory LockSystem.
  109. func NewMemLS() LockSystem {
  110. return &memLS{
  111. byName: make(map[string]*memLSNode),
  112. byToken: make(map[string]*memLSNode),
  113. gen: uint64(time.Now().Unix()),
  114. }
  115. }
  116. type memLS struct {
  117. mu sync.Mutex
  118. byName map[string]*memLSNode
  119. byToken map[string]*memLSNode
  120. gen uint64
  121. // byExpiry only contains those nodes whose LockDetails have a finite
  122. // Duration and are yet to expire.
  123. byExpiry byExpiry
  124. }
  125. func (m *memLS) nextToken() string {
  126. m.gen++
  127. return strconv.FormatUint(m.gen, 10)
  128. }
  129. func (m *memLS) collectExpiredNodes(now time.Time) {
  130. for len(m.byExpiry) > 0 {
  131. if now.Before(m.byExpiry[0].expiry) {
  132. break
  133. }
  134. m.remove(m.byExpiry[0])
  135. }
  136. }
  137. func (m *memLS) Confirm(now time.Time, name string, conditions ...Condition) (Releaser, error) {
  138. m.mu.Lock()
  139. defer m.mu.Unlock()
  140. m.collectExpiredNodes(now)
  141. name = slashClean(name)
  142. // TODO: touch n.held.
  143. panic("TODO")
  144. }
  145. func (m *memLS) Create(now time.Time, details LockDetails) (string, error) {
  146. m.mu.Lock()
  147. defer m.mu.Unlock()
  148. m.collectExpiredNodes(now)
  149. name := slashClean(details.Root)
  150. if !m.canCreate(name, details.ZeroDepth) {
  151. return "", ErrLocked
  152. }
  153. n := m.create(name)
  154. n.token = m.nextToken()
  155. m.byToken[n.token] = n
  156. n.details = details
  157. if n.details.Duration >= 0 {
  158. n.expiry = now.Add(n.details.Duration)
  159. heap.Push(&m.byExpiry, n)
  160. }
  161. return n.token, nil
  162. }
  163. func (m *memLS) Refresh(now time.Time, token string, duration time.Duration) (LockDetails, error) {
  164. m.mu.Lock()
  165. defer m.mu.Unlock()
  166. m.collectExpiredNodes(now)
  167. n := m.byToken[token]
  168. if n == nil {
  169. return LockDetails{}, ErrNoSuchLock
  170. }
  171. if n.held {
  172. return LockDetails{}, ErrLocked
  173. }
  174. if n.byExpiryIndex >= 0 {
  175. heap.Remove(&m.byExpiry, n.byExpiryIndex)
  176. n.byExpiryIndex = -1
  177. }
  178. n.details.Duration = duration
  179. if n.details.Duration >= 0 {
  180. n.expiry = now.Add(n.details.Duration)
  181. heap.Push(&m.byExpiry, n)
  182. }
  183. return n.details, nil
  184. }
  185. func (m *memLS) Unlock(now time.Time, token string) error {
  186. m.mu.Lock()
  187. defer m.mu.Unlock()
  188. m.collectExpiredNodes(now)
  189. n := m.byToken[token]
  190. if n == nil {
  191. return ErrNoSuchLock
  192. }
  193. if n.held {
  194. return ErrLocked
  195. }
  196. m.remove(n)
  197. return nil
  198. }
  199. func (m *memLS) canCreate(name string, zeroDepth bool) bool {
  200. return walkToRoot(name, func(name0 string, first bool) bool {
  201. n := m.byName[name0]
  202. if n == nil {
  203. return true
  204. }
  205. if first {
  206. if n.token != "" {
  207. // The target node is already locked.
  208. return false
  209. }
  210. if !zeroDepth {
  211. // The requested lock depth is infinite, and the fact that n exists
  212. // (n != nil) means that a descendent of the target node is locked.
  213. return false
  214. }
  215. } else if n.token != "" && !n.details.ZeroDepth {
  216. // An ancestor of the target node is locked with infinite depth.
  217. return false
  218. }
  219. return true
  220. })
  221. }
  222. func (m *memLS) create(name string) (ret *memLSNode) {
  223. walkToRoot(name, func(name0 string, first bool) bool {
  224. n := m.byName[name0]
  225. if n == nil {
  226. n = &memLSNode{
  227. details: LockDetails{
  228. Root: name0,
  229. },
  230. byExpiryIndex: -1,
  231. }
  232. m.byName[name0] = n
  233. }
  234. n.refCount++
  235. if first {
  236. ret = n
  237. }
  238. return true
  239. })
  240. return ret
  241. }
  242. func (m *memLS) remove(n *memLSNode) {
  243. delete(m.byToken, n.token)
  244. n.token = ""
  245. walkToRoot(n.details.Root, func(name0 string, first bool) bool {
  246. x := m.byName[name0]
  247. x.refCount--
  248. if x.refCount == 0 {
  249. delete(m.byName, name0)
  250. }
  251. return true
  252. })
  253. if n.byExpiryIndex >= 0 {
  254. heap.Remove(&m.byExpiry, n.byExpiryIndex)
  255. n.byExpiryIndex = -1
  256. }
  257. }
  258. func walkToRoot(name string, f func(name0 string, first bool) bool) bool {
  259. for first := true; ; first = false {
  260. if !f(name, first) {
  261. return false
  262. }
  263. if name == "/" {
  264. break
  265. }
  266. name = name[:strings.LastIndex(name, "/")]
  267. if name == "" {
  268. name = "/"
  269. }
  270. }
  271. return true
  272. }
  273. type memLSNode struct {
  274. // details are the lock metadata. Even if this node's name is not explicitly locked,
  275. // details.Root will still equal the node's name.
  276. details LockDetails
  277. // token is the unique identifier for this node's lock. An empty token means that
  278. // this node is not explicitly locked.
  279. token string
  280. // refCount is the number of self-or-descendent nodes that are explicitly locked.
  281. refCount int
  282. // expiry is when this node's lock expires.
  283. expiry time.Time
  284. // byExpiryIndex is the index of this node in memLS.byExpiry. It is -1
  285. // if this node does not expire, or has expired.
  286. byExpiryIndex int
  287. // held is whether this node's lock is actively held by a Confirm call.
  288. held bool
  289. }
  290. type byExpiry []*memLSNode
  291. func (b *byExpiry) Len() int {
  292. return len(*b)
  293. }
  294. func (b *byExpiry) Less(i, j int) bool {
  295. return (*b)[i].expiry.Before((*b)[j].expiry)
  296. }
  297. func (b *byExpiry) Swap(i, j int) {
  298. (*b)[i], (*b)[j] = (*b)[j], (*b)[i]
  299. (*b)[i].byExpiryIndex = i
  300. (*b)[j].byExpiryIndex = j
  301. }
  302. func (b *byExpiry) Push(x interface{}) {
  303. n := x.(*memLSNode)
  304. n.byExpiryIndex = len(*b)
  305. *b = append(*b, n)
  306. }
  307. func (b *byExpiry) Pop() interface{} {
  308. i := len(*b) - 1
  309. n := (*b)[i]
  310. (*b)[i] = nil
  311. n.byExpiryIndex = -1
  312. *b = (*b)[:i]
  313. return n
  314. }
  315. const infiniteTimeout = -1
  316. // parseTimeout parses the Timeout HTTP header, as per section 10.7. If s is
  317. // empty, an infiniteTimeout is returned.
  318. func parseTimeout(s string) (time.Duration, error) {
  319. if s == "" {
  320. return infiniteTimeout, nil
  321. }
  322. if i := strings.IndexByte(s, ','); i >= 0 {
  323. s = s[:i]
  324. }
  325. s = strings.TrimSpace(s)
  326. if s == "Infinite" {
  327. return infiniteTimeout, nil
  328. }
  329. const pre = "Second-"
  330. if !strings.HasPrefix(s, pre) {
  331. return 0, errInvalidTimeout
  332. }
  333. s = s[len(pre):]
  334. if s == "" || s[0] < '0' || '9' < s[0] {
  335. return 0, errInvalidTimeout
  336. }
  337. n, err := strconv.ParseInt(s, 10, 64)
  338. if err != nil || 1<<32-1 < n {
  339. return 0, errInvalidTimeout
  340. }
  341. return time.Duration(n) * time.Second, nil
  342. }