lock.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  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. "errors"
  7. "strconv"
  8. "strings"
  9. "sync"
  10. "time"
  11. )
  12. var (
  13. // ErrConfirmationFailed is returned by a LockSystem's Confirm method.
  14. ErrConfirmationFailed = errors.New("webdav: confirmation failed")
  15. // ErrForbidden is returned by a LockSystem's Unlock method.
  16. ErrForbidden = errors.New("webdav: forbidden")
  17. // ErrLocked is returned by a LockSystem's Create, Refresh and Unlock methods.
  18. ErrLocked = errors.New("webdav: locked")
  19. // ErrNoSuchLock is returned by a LockSystem's Refresh and Unlock methods.
  20. ErrNoSuchLock = errors.New("webdav: no such lock")
  21. )
  22. // Condition can match a WebDAV resource, based on a token or ETag.
  23. // Exactly one of Token and ETag should be non-empty.
  24. type Condition struct {
  25. Not bool
  26. Token string
  27. ETag string
  28. }
  29. // Releaser releases previously confirmed lock claims.
  30. //
  31. // Calling Release does not unlock the lock, in the WebDAV UNLOCK sense, but
  32. // once LockSystem.Confirm has confirmed that a lock claim is valid, that lock
  33. // cannot be Confirmed again until it has been Released.
  34. type Releaser interface {
  35. Release()
  36. }
  37. // LockSystem manages access to a collection of named resources. The elements
  38. // in a lock name are separated by slash ('/', U+002F) characters, regardless
  39. // of host operating system convention.
  40. type LockSystem interface {
  41. // Confirm confirms that the caller can claim all of the locks specified by
  42. // the given conditions, and that holding the union of all of those locks
  43. // gives exclusive access to the named resource.
  44. //
  45. // Exactly one of r and err will be non-nil. If r is non-nil, all of the
  46. // requested locks are held until r.Release is called.
  47. //
  48. // If Confirm returns ErrConfirmationFailed then the Handler will continue
  49. // to try any other set of locks presented (a WebDAV HTTP request can
  50. // present more than one set of locks). If it returns any other non-nil
  51. // error, the Handler will write a "500 Internal Server Error" HTTP status.
  52. Confirm(now time.Time, name string, conditions ...Condition) (r Releaser, err error)
  53. // Create creates a lock with the given depth, duration, owner and root
  54. // (name). The depth will either be negative (meaning infinite) or zero.
  55. //
  56. // If Create returns ErrLocked then the Handler will write a "423 Locked"
  57. // HTTP status. If it returns any other non-nil error, the Handler will
  58. // write a "500 Internal Server Error" HTTP status.
  59. //
  60. // See http://www.webdav.org/specs/rfc4918.html#rfc.section.9.10.6 for
  61. // when to use each error.
  62. //
  63. // The token returned identifies the created lock. It should be an absolute
  64. // URI as defined by RFC 3986, Section 4.3. In particular, it should not
  65. // contain whitespace.
  66. Create(now time.Time, details LockDetails) (token string, err error)
  67. // Refresh refreshes the lock with the given token.
  68. //
  69. // If Refresh returns ErrLocked then the Handler will write a "423 Locked"
  70. // HTTP Status. If Refresh returns ErrNoSuchLock then the Handler will write
  71. // a "412 Precondition Failed" HTTP Status. If it returns any other non-nil
  72. // error, the Handler will write a "500 Internal Server Error" HTTP status.
  73. //
  74. // See http://www.webdav.org/specs/rfc4918.html#rfc.section.9.10.6 for
  75. // when to use each error.
  76. Refresh(now time.Time, token string, duration time.Duration) (LockDetails, error)
  77. // Unlock unlocks the lock with the given token.
  78. //
  79. // If Unlock returns ErrForbidden then the Handler will write a "403
  80. // Forbidden" HTTP Status. If Unlock returns ErrLocked then the Handler
  81. // will write a "423 Locked" HTTP status. If Unlock returns ErrNoSuchLock
  82. // then the Handler will write a "409 Conflict" HTTP Status. If it returns
  83. // any other non-nil error, the Handler will write a "500 Internal Server
  84. // Error" HTTP status.
  85. //
  86. // See http://www.webdav.org/specs/rfc4918.html#rfc.section.9.11.1 for
  87. // when to use each error.
  88. Unlock(now time.Time, token string) error
  89. }
  90. // LockDetails are a lock's metadata.
  91. type LockDetails struct {
  92. // Root is the root resource name being locked. For a zero-depth lock, the
  93. // root is the only resource being locked.
  94. Root string
  95. // Duration is the lock timeout. A negative duration means infinite.
  96. Duration time.Duration
  97. // OwnerXML is the verbatim <owner> XML given in a LOCK HTTP request.
  98. //
  99. // TODO: does the "verbatim" nature play well with XML namespaces?
  100. // Does the OwnerXML field need to have more structure? See
  101. // https://codereview.appspot.com/175140043/#msg2
  102. OwnerXML string
  103. // ZeroDepth is whether the lock has zero depth. If it does not have zero
  104. // depth, it has infinite depth.
  105. ZeroDepth bool
  106. }
  107. // NewMemLS returns a new in-memory LockSystem.
  108. func NewMemLS() LockSystem {
  109. return &memLS{
  110. byName: make(map[string]*memLSNode),
  111. byToken: make(map[string]*memLSNode),
  112. gen: uint64(time.Now().Unix()),
  113. }
  114. }
  115. type memLS struct {
  116. mu sync.Mutex
  117. byName map[string]*memLSNode
  118. byToken map[string]*memLSNode
  119. gen uint64
  120. }
  121. func (m *memLS) nextToken() string {
  122. m.gen++
  123. return strconv.FormatUint(m.gen, 10)
  124. }
  125. func (m *memLS) collectExpiredNodes(now time.Time) {
  126. // TODO: implement.
  127. }
  128. func (m *memLS) Confirm(now time.Time, name string, conditions ...Condition) (Releaser, error) {
  129. m.mu.Lock()
  130. defer m.mu.Unlock()
  131. m.collectExpiredNodes(now)
  132. name = slashClean(name)
  133. // TODO: touch n.held.
  134. panic("TODO")
  135. }
  136. func (m *memLS) Create(now time.Time, details LockDetails) (string, error) {
  137. m.mu.Lock()
  138. defer m.mu.Unlock()
  139. m.collectExpiredNodes(now)
  140. name := slashClean(details.Root)
  141. if !m.canCreate(name, details.ZeroDepth) {
  142. return "", ErrLocked
  143. }
  144. n := m.create(name)
  145. n.token = m.nextToken()
  146. m.byToken[n.token] = n
  147. n.details = details
  148. // TODO: set n.expiry.
  149. return n.token, nil
  150. }
  151. func (m *memLS) Refresh(now time.Time, token string, duration time.Duration) (LockDetails, error) {
  152. m.mu.Lock()
  153. defer m.mu.Unlock()
  154. m.collectExpiredNodes(now)
  155. n := m.byToken[token]
  156. if n == nil {
  157. return LockDetails{}, ErrNoSuchLock
  158. }
  159. if n.held {
  160. return LockDetails{}, ErrLocked
  161. }
  162. n.details.Duration = duration
  163. // TODO: update n.expiry.
  164. return n.details, nil
  165. }
  166. func (m *memLS) Unlock(now time.Time, token string) error {
  167. m.mu.Lock()
  168. defer m.mu.Unlock()
  169. m.collectExpiredNodes(now)
  170. n := m.byToken[token]
  171. if n == nil {
  172. return ErrNoSuchLock
  173. }
  174. if n.held {
  175. return ErrLocked
  176. }
  177. m.remove(n)
  178. return nil
  179. }
  180. func (m *memLS) canCreate(name string, zeroDepth bool) bool {
  181. return walkToRoot(name, func(name0 string, first bool) bool {
  182. n := m.byName[name0]
  183. if n == nil {
  184. return true
  185. }
  186. if first {
  187. if n.token != "" {
  188. // The target node is already locked.
  189. return false
  190. }
  191. if !zeroDepth {
  192. // The requested lock depth is infinite, and the fact that n exists
  193. // (n != nil) means that a descendent of the target node is locked.
  194. return false
  195. }
  196. } else if n.token != "" && !n.details.ZeroDepth {
  197. // An ancestor of the target node is locked with infinite depth.
  198. return false
  199. }
  200. return true
  201. })
  202. }
  203. func (m *memLS) create(name string) (ret *memLSNode) {
  204. walkToRoot(name, func(name0 string, first bool) bool {
  205. n := m.byName[name0]
  206. if n == nil {
  207. n = &memLSNode{
  208. details: LockDetails{
  209. Root: name0,
  210. },
  211. }
  212. m.byName[name0] = n
  213. }
  214. n.refCount++
  215. if first {
  216. ret = n
  217. }
  218. return true
  219. })
  220. return ret
  221. }
  222. func (m *memLS) remove(n *memLSNode) {
  223. delete(m.byToken, n.token)
  224. n.token = ""
  225. walkToRoot(n.details.Root, func(name0 string, first bool) bool {
  226. x := m.byName[name0]
  227. x.refCount--
  228. if x.refCount == 0 {
  229. delete(m.byName, name0)
  230. }
  231. return true
  232. })
  233. }
  234. func walkToRoot(name string, f func(name0 string, first bool) bool) bool {
  235. for first := true; ; first = false {
  236. if !f(name, first) {
  237. return false
  238. }
  239. if name == "/" {
  240. break
  241. }
  242. name = name[:strings.LastIndex(name, "/")]
  243. if name == "" {
  244. name = "/"
  245. }
  246. }
  247. return true
  248. }
  249. type memLSNode struct {
  250. // details are the lock metadata. Even if this node's name is not explicitly locked,
  251. // details.Root will still equal the node's name.
  252. details LockDetails
  253. // token is the unique identifier for this node's lock. An empty token means that
  254. // this node is not explicitly locked.
  255. token string
  256. // refCount is the number of self-or-descendent nodes that are explicitly locked.
  257. refCount int
  258. // expiry is when this node's lock expires.
  259. expiry time.Time
  260. // held is whether this node's lock is actively held by a Confirm call.
  261. held bool
  262. }