RFC 5842

Binding Extensions to Web Distributed Authoring and Versioning (WebDAV)

πŸ“… published April 2010, from ietf

Binding Extensions to Web Distributed Authoring and Versioning (WebDAV) πŸŽ€

Okay bestie, this is RFC 5842, published in April 2010 by the IETF (Internet Engineering Task Force) πŸ¦‹ It's an Experimental doc (ISSN 2070-1721), so it's for trying things out, not a finished Internet Standard. The authors are G. Clemm (IBM), J. Crawford (IBM Research), J. Reschke, Ed. (greenbytes; the "Ed." means he's the editor πŸ’…), and J. Whitehead (U.C. Santa Cruz). The title block doesn't list anything it obsoletes or updates, so this one stands on its own ✨

Abstract

okay so this spec defines bindings and the BIND method, which is how you make multiple bindings to the same resource 🌸 Making a new binding gives a resource at least one new URI. And servers have to keep the integrity of any bindings they let people create 🫢

Status of This Memo

This doc is not an Internet Standards Track spec. It's published so people can look at it, try it out in experimental implementations, and evaluate it 🧚

It defines an Experimental Protocol for the Internet community. It's a product of the IETF and represents the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). Not every IESG-approved doc is a candidate for any level of Internet Standard, so check Section 2 of RFC 5741 if you're curious πŸ€“

You can find the current status, any errata, and how to send feedback at http://www.rfc-editor.org/info/rfc5842 πŸ’œ

Copyright (c) 2010 IETF Trust and the persons identified as the document authors. All rights reserved. 🌈

This doc is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) as they stood on the publication date. Please actually read these, because they explain your rights and restrictions for this doc πŸ“œ Code Components pulled out of this doc must include the Simplified BSD License text described in Section 4.e of the Trust Legal Provisions, and they come without warranty, as described in that license.

This doc may include material from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008. Some of the copyright holders for that older material might not have given the IETF Trust permission to allow modifications outside the IETF Standards Process. So unless you get a proper license from those holders, this doc can't be modified outside the IETF Standards Process, and derivative works can't be made outside it either. The exceptions are formatting it for publication as an RFC and translating it into languages other than English πŸ™ˆ

Table of Contents

   1. Introduction ....................................................4
      1.1. Terminology ................................................5
      1.2. Method Preconditions and Postconditions ....................6
   2. Overview of Bindings ............................................7
      2.1. Bindings to Collections ....................................7
           2.1.1. Bind Loops ..........................................8
      2.2. URI Mappings Created by a New Binding ......................8
      2.3. COPY and Bindings ..........................................9
           2.3.1. Example: COPY with "Depth: infinity" in
                  Presence of Bind Loops .............................11
           2.3.2. Example: COPY Updating Multiple Bindings ...........13
           2.3.3. Example: COPY with "Depth: infinity" with
                  Multiple Bindings to a Leaf Resource ...............14
      2.4. DELETE and Bindings .......................................15
      2.5. MOVE and Bindings .........................................15
           2.5.1. Example: Simple MOVE ...............................16
           2.5.2. Example: MOVE Request Causing a Bind Loop ..........16
      2.6. PROPFIND and Bindings .....................................18
      2.7. Determining Whether Two Bindings Are to the Same
           Resource ..................................................18
      2.8. Discovering the Bindings to a Resource ....................19
   3. Properties .....................................................19
      3.1. DAV:resource-id Property ..................................20
      3.2. DAV:parent-set Property ...................................20
           3.2.1. Example for DAV:parent-set Property ................20
   4. BIND Method ....................................................21
      4.1. Example: BIND .............................................24
   5. UNBIND Method ..................................................24
      5.1. Example: UNBIND ...........................................26
   6. REBIND Method ..................................................26
      6.1. Example: REBIND ...........................................28
      6.2. Example: REBIND in Presence of Locks and Bind Loops .......29
   7. Additional Status Codes ........................................31
      7.1. 208 Already Reported ......................................31
           7.1.1. Example: PROPFIND by Bind-Aware Client .............32
           7.1.2. Example: PROPFIND by Non-Bind-Aware Client .........34
      7.2. 508 Loop Detected .........................................34
   8. Capability Discovery ...........................................34
      8.1. OPTIONS Method ............................................34
      8.2. 'DAV' Request Header ......................................34
   9. Relationship to Locking in WebDAV ..............................35
      9.1. Example: Locking and Multiple Bindings ....................36
   10. Relationship to WebDAV Access Control Protocol ................37
   11. Relationship to Versioning Extensions to WebDAV ...............37
   12. Security Considerations .......................................40
      12.1. Privacy Concerns .........................................40
      12.2. Bind Loops ...............................................40
      12.3. Bindings and Denial of Service ...........................40
      12.4. Private Locations May Be Revealed ........................40
      12.5. DAV:parent-set and Denial of Service .....................41
   13. Internationalization Considerations ...........................41
   14. IANA Considerations ...........................................41
   15. Acknowledgements ..............................................41
   16. References ....................................................41
      16.1. Normative References .....................................41
      16.2. Informative References ...................................42
   Index .............................................................42

1. Introduction ✨

okay so this spec extends the WebDAV Distributed Authoring Protocol ([RFC4918]) so clients can make new access paths to existing resources πŸ’– That's useful for a few reasons, and I'm about to explain all of them πŸ’…

WebDAV resource URIs are hierarchical, and they line up with a hierarchy of collections in resource space. The WebDAV Distributed Authoring Protocol lets you organize resources into groupings called collections, which are way easier to browse and manage than one giant flat collection πŸ—‚οΈ The catch is that hierarchies force categorization decisions, so each resource ends up in exactly one spot. That's a real drawback when a resource fits more than one category. Picture a hierarchy of vehicle descriptions with collections for cars and boats. A description of a car/boat combo could live in either one, but ideally it'd be reachable from both πŸš—β›΅ Letting clients create new URIs that point at the existing resource means it can show up in multiple collections. Iconic πŸ‘‘

Hierarchies also make sharing resources harder, because resources that are useful across lots of collections still get jammed into just one 😀 Say the mathematics department at a university makes a collection about fractals. It has bindings to some local resources, but it also gives access to some resources at other universities πŸŒ€ A lot of the time you don't want physical copies of those shared resources on the local server. Maybe you want to save disk space, respect copyright, or have changes in the shared resources show up automatically. So being able to create new access paths to existing resources in other collections, or even on other servers, is super useful for cases like this πŸ“

The BIND method, defined here, gives clients a way to create alternative access paths to existing WebDAV resources πŸ¦‹ HTTP [RFC2616] and WebDAV [RFC4918] methods work because there are mappings between URIs and resources. A method gets addressed to a URI, the server follows the mapping from that URI to a resource, and then it applies the method to that resource. Multiple URIs may be mapped to the same resource, but until now clients had no way to create additional URIs mapped to existing resources. Not me being shocked 😭

BIND lets a client associate a new URI with an existing WebDAV resource, and then that URI can be used to send requests to the resource 🌸 WebDAV URIs are hierarchical and match a hierarchy of collections, so the BIND method also adds the resource to a collection. Every new URI tied to the resource means it appears in more collections ✨

A BIND request doesn't create a new resource. It just makes a new URI available for sending requests to an existing resource 🧚 The new URI is indistinguishable from any other URI when you send a request to a resource, and only one round trip is needed to reach the intended target. Servers are required to enforce the integrity of the relationships between the new URIs and the resources tied to them. Because of that, it may be very costly for servers to support BIND requests that cross server boundaries πŸ’€

Here's how this spec is laid out πŸŽ€ Section 1.1 defines the terminology used in the rest of the spec, and Section 2 gives an overview of bindings. Section 3 defines the new properties needed to support multiple bindings to the same resource. Section 4 specifies the BIND method, which creates multiple bindings to the same resource. Section 5 specifies the UNBIND method, which removes a binding to a resource. Section 6 specifies the REBIND method, which moves a binding to another collection 🫢

1.1. Terminology

okay bestie, let's get our vocab straight first πŸ’… this doc's terms follow and extend the ones from the WebDAV Distributed Authoring Protocol spec [RFC4918] πŸŽ€

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119]. ✨

this doc writes its XML DTD fragments ([XML]) using the notation rules from Section 17 of [RFC4918] πŸ“œ

URI Mapping

a relation between an absolute URI and a resource πŸ¦‹ for an absolute URI U and the resource it identifies R, the URI mapping can be thought of as (U => R). a resource can represent stuff that isn't network retrievable as well as stuff that is, so a resource can have zero, one, or many URI mappings. mapping a resource to an "http"-scheme URI makes it possible to submit HTTP requests to the resource using that URI 🌸

Path Segment

informally, it's the characters found between slashes ("/") in a URI πŸ’œ formally, it's defined in Section 3.3 of [RFC3986].

Binding

a relation between a single path segment (in a collection) and a resource πŸŽ€ a binding is part of the state of a collection. if two different collections contain a binding between the same path segment and the same resource, those are two distinct bindings. so for a collection C, a path segment S, and a resource R, the binding can be thought of as C:(S -> R). bindings create URI mappings, which means requests can be sent to a single resource from multiple locations in a URI namespace πŸ‘‘

for example: say a collection C is accessible through the URI http://www.example.com/CollX, a path segment S is "foo.html", and there's a resource R. creating the binding C: (S -> R) makes it possible to use http://www.example.com/CollX/foo.html to access R 🧚

Collection

a resource that contains, as part of its state, a set of bindings that identify internal member resources πŸ“

Internal Member URI

the URI that identifies an internal member of a collection. it's made of the URI for the collection, then a slash character ('/'), then the path segment of the binding for that internal member 🫢

Binding Integrity

the property of a binding that says:

  • the binding continues to exist, and
  • the identity of the resource identified by that binding does not change,

unless an explicit request is executed that is defined to delete that binding. examples of requests that delete a binding are DELETE, MOVE, and (defined later on) UNBIND and REBIND 😭


1.2. Method Preconditions and Postconditions

see Section 16 of [RFC4918] for the definitions of "precondition" and "postcondition" 🌈

2. Overview of Bindings

bindings are part of the state of a collection πŸ’– they define the internal members of the collection and the names of those internal members.

bindings get added and removed by a bunch of existing HTTP methods ✨ a method that creates a new resource, like PUT, COPY, and MKCOL, adds a binding. a method that deletes a resource, like DELETE, removes a binding. a method that moves a resource (e.g., MOVE) does both: it adds a binding in the destination collection and removes one in the source collection. the BIND method introduced here is the way to add a second binding to an existing resource πŸ¦‹ there's no difference between an initial binding added by PUT, COPY, or MKCOL and additional bindings added with BIND πŸ”₯

it would be very undesirable if one binding could get destroyed as a side effect of operating on the resource through a different binding πŸ’€ especially, removing one binding to a resource (e.g., with a DELETE or a MOVE) MUST NOT disrupt another binding to that resource, for example by turning that binding into a dangling path segment. the server MUST NOT reclaim system resources after removing one binding while other bindings to the resource remain. in other words, the server MUST maintain the integrity of a binding 🫢 it is permissible, though, for future method definitions (e.g., a DESTROY method) to have semantics that explicitly remove all bindings and/or immediately reclaim system resources πŸ‘‘

Note: the collection model described herein is not compatible with systems in which resources inherit properties based solely on the access path, because the ability to create additional bindings will cause a single resource to appear as a member of several different collections at the same time. πŸ™ˆ

2.1. Bindings to Collections

creating a new binding to a collection makes each resource associated with a binding in that collection accessible via a new URI 🌸 so it creates new URI mappings to those resources, but no new bindings πŸ’…

for example, suppose a new binding CollY is created for collection C1 in the figure below. it immediately becomes possible to access resource R1 using the URI /CollY/x.gif and to access resource R2 using the URI /CollY/y.jpg, but no new bindings for these child resources were created. this is because bindings are part of the state of a collection, and they associate a URI that is relative to that collection with its target resource. no change to the bindings in Collection C1 is needed to make its children accessible using /CollY/x.gif and /CollY/y.jpg 🧚

                 +-------------------------+
                 | Root Collection         |
                 |  bindings:              |
                 |  CollX          CollY   |
                 +-------------------------+
                     |            /
                     |           /
                     |          /
                 +------------------+
                 | Collection C1    |
                 | bindings:        |
                 | x.gif     y.jpg  |
                 +------------------+
                     |          \
                     |           \
                     |            \
                 +-------------+   +-------------+
                 | Resource R1 |   | Resource R2 |
                 +-------------+   +-------------+

2.1.1. Bind Loops

bindings to collections can result in loops ("cycles"), and servers MUST detect these when processing "Depth: infinity" requests πŸŒ€ it's sometimes possible to complete an operation even with a loop around. for instance, a PROPFIND can still succeed if the server uses the new status code 208 (Already Reported) defined in Section 7.1 ✨

however, the 508 (Loop Detected) status code is defined in Section 7.2 for use in contexts where an operation gets terminated because a loop was encountered 😭

support for loops is OPTIONAL: servers MAY reject requests that would lead to the creation of a bind loop (see the DAV:cycle-allowed precondition defined in Section 4) πŸ’œ

2.2. URI Mappings Created by a New Binding

okay so suppose a binding from "Binding-Name" to resource R is about to be added to a collection, C πŸ¦‹ if C-MAP is the set of URIs that were mapped to C before the BIND request, then for each URI "C-URI" in C-MAP, the URI "C-URI/Binding-Name" gets mapped to resource R after the BIND request 🌸

for example, if a binding from "foo.html" to R is added to a collection C, and these URIs are mapped to C:

http://www.example.com/A/1/
http://example.com/A/one/

then these new mappings to R are introduced:

http://www.example.com/A/1/foo.html
http://example.com/A/one/foo.html

note that if R is a collection, additional URI mappings get created to the descendents of R πŸŽ€ also, if a binding is made in collection C to C itself (or to a parent of C), an infinite number of mappings are introduced πŸ’€

for example, if a binding from "myself" to C is then added to C, this infinite number of additional mappings to C are introduced:

http://www.example.com/A/1/myself
http://www.example.com/A/1/myself/myself
   ...

and this infinite number of additional mappings to R are introduced:

http://www.example.com/A/1/myself/foo.html
http://www.example.com/A/1/myself/myself/foo.html
   ...

2.3. COPY and Bindings

as defined in Section 9.8 of [RFC4918], COPY causes the resource identified by the Request-URI to be duplicated, and it makes the new resource accessible using the URI specified in the Destination header πŸ“ upon successful completion of a COPY, a new binding gets created between the last path segment of the Destination header and the destination resource. the new binding is added to its parent collection, which is whatever the Destination header is minus its final segment 🧚

here's the vibe with an example: a COPY is issued to URI-3 for resource R (which is also mapped to URI-1 and URI-2), with the Destination header set to URI-X. after the COPY succeeds, resource R is duplicated to create resource R', and a new binding has been created that creates at least the URI mapping between URI-X and the new resource (although other URI mappings may also have been created too) 🌈

  URI-1   URI-2    URI-3                           URI-X
     |       |        |                              |
     |       |        |   <---- URI Mappings ---->   |
     |       |        |                              |
  +---------------------+                 +------------------------+
  |     Resource R      |                 |     Resource R'        |
  +---------------------+                 +------------------------+

it might be thought that a COPY request with "Depth: 0" on a collection would duplicate its bindings, since bindings are part of the collection's state πŸ™ˆ this is not the case, though. the definition of Depth in [RFC4918] makes it clear that a "Depth: 0" request does not apply to a collection's members. so a COPY with "Depth: 0" does not duplicate the bindings contained by the collection πŸ’…

if a COPY request causes an existing resource to be updated, the bindings to that resource MUST be unaffected by the COPY request 🫢 using the example above, suppose a COPY request is issued to URI-X for resource R', with the Destination header set to URI-2. the content and dead properties of resource R would be updated to be a copy of those of resource R', but the mappings from URI-1, URI-2, and URI-3 to resource R remain unaffected πŸ¦‹ if, because of multiple bindings to a resource, more than one source resource updates a single destination resource, the order of the updates is server defined (see Section 2.3.2 for an example) πŸ‘‘

if a COPY request would cause a new resource to be created as a copy of an existing resource, and that COPY request has already created a copy of that existing resource, the COPY request instead creates another binding to the previous copy, instead of creating a new resource (see Section 2.3.3 for an example) ✨

2.3.1. Example: COPY with "Depth: infinity" in Presence of Bind Loops

okay so let's see how COPY with "Depth: infinity" behaves when bindings are involved 🌸 here's the collection we're working with:

                 +------------------+
                 | Root Collection  |
                 |  bindings:       |
                 |  CollX           |
                 +------------------+
                     |
                     |
                 +-------------------------------+
                 | Collection C1                 |<-------+
                 | bindings:                     |        |
                 | x.gif      CollY              |        |
                 +-------------------------------+        |
                     |            \        (creates loop) |
                     |             \                      |
                 +-------------+   +------------------+   |
                 | Resource R1 |   | Collection C2    |   |
                 +-------------+   | bindings:        |   |
                                   | y.gif     CollZ  |   |
                                   +------------------+   |
                                       |         |        |
                                       |         +--------+
                                       |
                                   +-------------+
                                   | Resource R2 |
                                   +-------------+

so if a COPY with "Depth: infinity" gets sent to /CollX with a destination of /CollA, the copy operation ends up replicating the whole tree over to /CollA ✨

                +------------------+
                | Root Collection  |
                |  bindings:       |
                |  CollX     CollA |
                +------------------+
                   |           |
                   |           +---------------------------+
                   |                                       |
                +-------------------+                      |
                | Collection C1     |<------------------+  |
                | bindings:         |                   |  |
                | x.gif      CollY  |                   |  |
                +-------------------+                   |  |
                   |            \        (creates loop) |  |
                   |             \                      |  |
                +-------------+   +-----------------+   |  |
                | Resource R1 |   | Collection C2   |   |  |
                +-------------+   | bindings:       |   |  |
                                  | y.gif     CollZ |   |  |
                                  +-----------------+   |  |
                                      |         |       |  |
                                      |         +-------+  |
                                      |                    |
                                  +-------------+          |
                                  | Resource R2 |          |
                                  +-------------+          |
                                                           |
                           +-------------------------------+
                           |
                +-------------------+
                | Collection C3     |<------------------+
                | bindings:         |                   |
                | x.gif      CollY  |                   |
                +-------------------+                   |
                   |            \        (creates loop) |
                   |             \                      |
                +-------------+   +-----------------+   |
                | Resource R3 |   | Collection C4   |   |
                +-------------+   | bindings:       |   |
                                  | y.gif     CollZ |   |
                                  +-----------------+   |
                                      |         |       |
                                      |         +-------+
                                      |
                                  +-------------+
                                  | Resource R4 |
                                  +-------------+

and yes, ngl, the exact same thing happens for way more complicated loops πŸ’€πŸ”₯

2.3.2. Example: COPY Updating Multiple Bindings

let's say we start with this collection hierarchy πŸ¦‹

                                +------------------+
                                | Root Collection  |
                                |  bindings:       |
                                |  CollX     CollY |
                                +------------------+
                                   /              \
                                  /                \
                                 /                  \
              +--------------------------+   +-----------------+
              |      Collection C1       |   | Collection C2   |
              |      bindings:           |   | bindings:       |
              |     x.gif     y.gif      |   | x.gif     y.gif |
              +--------------------------+   +-----------------+
                      |         |                |         |
                      |         |                |         |
            +-------------+  +-------------+   +-------------+
            | Resource R1 |  | Resource R2 |   | Resource R3 |
            +-------------+  +-------------+   +-------------+

so a COPY of /CollX with "Depth: infinity" sent to /CollY doesn't change the hierarchy at all πŸ™ˆ what does happen is that Resource R3 gets updated with the content of either Resource R1 or Resource R2. main character energy, but make it chaotic πŸ’…

2.3.3. Example: COPY with "Depth: infinity" with Multiple Bindings to a Leaf Resource

starting hierarchy, bestie πŸŽ€

                            +------------------+
                            | Root Collection  |
                            |  bindings:       |
                            |  CollX           |
                            +------------------+
                               |
                               |
                               |
                            +----------------+
                            | Collection C1  |
                            | bindings:      |
                            | x.gif    y.gif |
                            +----------------+
                               |         |
                               |         |
                             +-------------+
                             | Resource R1 |
                             +-------------+

now a COPY of /CollX with "Depth: infinity" to /CollY gives us this hierarchy 🧚

                  +------------------+
                  | Root Collection  |
                  |  bindings:       |
                  |  CollX     CollY |
                  +------------------+
                     |              \
                     |               \
                     |                \
                  +----------------+  +-----------------+
                  | Collection C1  |  | Collection C2   |
                  | bindings:      |  | bindings:       |
                  | x.gif    y.gif |  | x.gif     y.gif |
                  +----------------+  +-----------------+
                     |         |          |         |
                     |         |          |         |
                   +-------------+      +-------------+
                   | Resource R1 |      | Resource R2 |
                   +-------------+      +-------------+

2.4. DELETE and Bindings

okay this part is about what DELETE is allowed to wipe out when a resource has more than one binding πŸ‘‘ when a resource has multiple bindings, a DELETE aimed at that resource MUST NOT remove any binding to it other than the one named in the Request-URI. example time: say the collection at "/a" has a binding called "x" to a resource R, and the collection at "/b" has a binding called "y" to that same R. a DELETE on "/a/x" takes out the "x" binding from "/a", but it MUST NOT touch the "y" binding in "/b" (so after the DELETE, "/y/b" still points at resource R) 😭

when DELETE hits a collection, it MUST NOT change the membership of any other collection that isn't itself a member of the one being deleted. for instance, if both "/a/.../x" and "/b/.../y" point at the same collection C, then deleting "/a" must not remove an internal member from C, or from any other collection that's a member of C, because that would change the membership of "/b" 🚫

if a collection supports the UNBIND method (see Section 5), a DELETE on an internal member of that collection MAY be carried out as an UNBIND request. in that case, a DELETE on a Request-URI just removes the binding named by the last segment of the Request-URI from the collection you get by chopping that last segment off. even though [RFC4918] lets a DELETE be non-atomic, when it's done as an UNBIND, it's atomic ✨ so a DELETE on a whole hierarchy is basically just removing one binding to the collection the Request-URI names πŸ¦‹

2.5. MOVE and Bindings

when MOVE is applied to a resource, the other bindings to that resource MUST be unaffected 🌈 and if the thing being moved is a collection, the bindings to any of its members MUST be unaffected too. also, if MOVE uses Overwrite:T to delete an existing resource, the DELETE rules above apply πŸ’–

if the destination collection of a MOVE supports the REBIND method (see Section 6), a MOVE of a resource into that collection MAY be carried out as a REBIND request. even though [RFC4918] lets a MOVE be non-atomic, when it's done as a REBIND, it's atomic 🫢 so applying a MOVE to a Request-URI and a Destination URI basically means removing one binding to a resource (at the Request-URI) and creating a new binding to that same resource (at the Destination URI). even when the Request-URI names a collection, MOVE just swaps out one binding to that collection for another πŸŽ€

2.5.1. Example: Simple MOVE

okay here's the example πŸ’… say a MOVE goes to URI-3 for resource R (which is also mapped to URI-1 and URI-2), with the Destination header set to URI-X. once the MOVE finishes successfully, a new binding exists that maps URI-X to resource R. the binding for the last segment of URI-3 is gone, and that also removes the URI-3 mapping to R. if R were a collection, the old URI-3-based mappings to its members would be removed, and new URI-X-based mappings to those members would be created πŸ“

   >> Before Request:

                URI-1   URI-2    URI-3
                  |       |        |
                  |       |        |      <---- URI Mappings
                  |       |        |
               +---------------------+
               |     Resource R      |
               +---------------------+

   >> After Request:

                URI-1   URI-2    URI-X
                  |       |        |
                  |       |        |      <---- URI Mappings
                  |       |        |
               +---------------------+
               |     Resource R      |
               +---------------------+

2.5.2. Example: MOVE Request Causing a Bind Loop πŸŒ€

okay bestie, MOVE requests are sneaky πŸ™ˆ Even when everything looks fine, a MOVE can accidentally create a bind loop if collection bindings are involved. 😭

Picture two top-level collections, C1 and C2, living at /CollW/ and /CollX/. On top of that, C1 has an extra binding called CollY that points over to C2. It's giving tangled 🧢

                                     +------------------+
                                     | Root Collection  |
                                     |  bindings:       |
                                     |  CollW    CollX  |
                                     +------------------+
                                         |          |
                                         |          |
                            +------------------+    |
                            | Collection C1    |    |
                            |  bindings:       |    |
                            |           CollY  |    |
                            +------------------+    |
                                         |          |
                                         |          |
                                     +------------------+
                                     | Collection C2    |
                                     |                  |
                                     |                  |
                                     +------------------+

In this setup, the MOVE request below would cause a bind loop. πŸ’€

>> Request:

MOVE /CollW HTTP/1.1
Host: example.com
Destination: /CollX/CollZ

If the request succeeded, the resulting state would be:

                                     +------------------+
                                     | Root Collection  |
                                     |  bindings:       |
                                     |           CollX  |
                                     +------------------+
                                                    |
                                                    |
                            +------------------+    |
                            | Collection C1    |    |
                     +----> |  bindings:       |    |
                     |      |           CollY  |    |
                     |      +------------------+    |
                     |                   |          |
                     |                   |          |
                     |               +------------------+
                     |               | Collection C2    |
                     |               |  bindings:       |
                     |               | CollZ            |
                     |               +------------------+
                     |                   |
                     |                   |
                     +-------------------+

See that loop? C1 β†’ CollY β†’ C2 β†’ back around to C1. It's a whole circle of drama πŸŒ€πŸ’…

2.6. PROPFIND and Bindings πŸŽ€

Okay so the big rule here is that dead properties stay consistent, no matter what. 🫢 The value of a dead property MUST be independent of the number of bindings to its host resource or of the path submitted to PROPFIND. Basically, it doesn't care how many doors lead to the room. πŸšͺ

Live properties are a different story, because each one follows its own definition. For example, see [RFC3744], Section 5, Paragraph 2 for a case where the value is independent of its path and bindings. Then check out [RFC4918], Section 8.8 for the live properties DAV:getetag and DAV:getlastmodified, which may behave differently. Plot twist, they're not all the same πŸ¦‹

2.7. Determining Whether Two Bindings Are to the Same Resource πŸ”

Being able to tell whether two bindings point to the same resource is super useful ✨ Two resources can have identical contents and properties and still be totally different resources. Like, if you update one, the other one doesn't change at all. Not the same girl, different vibes πŸ’œ

The REQUIRED DAV:resource-id property (defined in Section 3.1) is the resource identifier, and it MUST be unique across all resources for all time. Yes, all time 😭 If the DAV:resource-id values returned by PROPFIND through two bindings match character by character, the client can be sure both bindings lead to the same resource. πŸ‘‘

The DAV:resource-id property gets created, and its value gets assigned, when the resource is born 🍼 That value MUST NOT be changed. And even after a resource is totally unreachable through any URI, that value MUST NOT be reassigned to another resource's DAV:resource-id property. Never recycling IDs, periodt πŸ’…

Any method that creates a brand-new resource MUST assign a new, unique value to its DAV:resource-id property. For example, a PUT applied to a null resource, a COPY (when it's not overwriting an existing target), and a CHECKIN (see [RFC3253], Section 4.4) must assign a new, unique value to the DAV:resource-id property of the new resource they create. ✨

On the other hand, any method that affects an existing resource must not change the value of its DAV:resource-id property. Specifically, a PUT or a COPY that updates an existing resource must not change the value of its DAV:resource-id property. And a REBIND doesn't create a new resource, it just moves an existing one to a new location, so it must not change the value of the DAV:resource-id property either. Same person, new address 🏑

2.8. Discovering the Bindings to a Resource πŸ—ΊοΈ

The DAV:parent-set property is OPTIONAL on a resource, and it gives you a list of the bindings that pair a collection and a URI segment with that resource. If the DAV:parent-set property exists on a given resource, it MUST contain a complete list of all bindings to that resource that the client is authorized to see. No hiding the good stuff from the people who are allowed to see it πŸ‘€

Before adding support for DAV:parent-set, server implementers and administrators should weigh its benefits against the cost of maintaining the property and the security risks listed in Sections 12.4 and 12.5. Real talk, security matters πŸ”

3. Properties πŸ’Ž

The bind feature brings along the properties defined below. ✨

A DAV:allprop PROPFIND request SHOULD NOT return any of the properties defined by this document. This is for the sake of the server 🫢 It lets a binding server stay efficient when a naive client (one that doesn't understand how expensive it is to ask a server to compute every possible live property) sends a DAV:allprop PROPFIND request.

3.1. DAV:resource-id Property πŸͺͺ

The DAV:resource-id property is a REQUIRED property. It's how clients figure out whether two bindings lead to the same resource. Its value is a URI, and it can use any registered URI scheme that guarantees uniqueness across all resources for all time. Examples include the urn:uuid: URN namespace defined in [RFC4122] and the opaquelocktoken: URI scheme defined in [RFC4918]. 🌈

<!ELEMENT resource-id (href)>

3.2. DAV:parent-set Property 🧚

The DAV:parent-set property is an OPTIONAL property that lets clients find out which collections contain a binding to this resource (in other words, which collections have that resource as an internal member). It holds an href/segment pair for each collection with a binding to the resource. The href identifies the collection, and the segment is the binding name of that resource inside that collection. πŸ“

A given collection MUST appear only once in the DAV:parent-set for any given binding, even if there are multiple URI mappings to that collection. One entry per collection, no duplicates, bestie πŸ’…

<!ELEMENT parent-set (parent)*>
<!ELEMENT parent (href, segment)>
<!ELEMENT segment (#PCDATA)>
<!-- PCDATA value: segment, as defined in Section 3.3 of
     [RFC3986] -->

3.2.1. Example for DAV:parent-set Property πŸŽ€

Let's say collection C1 is mapped to both /CollX and /CollY, and C1 has a binding called "x.gif" pointing at a resource R1. In that case, either [/CollX, x.gif] or [/CollY, x.gif] can show up in the DAV:parent-set of R1, but not both. πŸ™…β€β™€οΈ Now if C1 also has a binding called "y.gif" to R1, then C1 shows up twice in the DAV:parent-set of R1. You'd get both [/CollX, x.gif] and [/CollX, y.gif], or alternatively both [/CollY, x.gif] and [/CollY, y.gif]. Two entries for the same collection, because two different segments are involved πŸ¦‹

                        +-------------------------+
                        | Root Collection         |
                        |  bindings:              |
                        |  CollX          CollY   |
                        +-------------------------+
                            |            /
                            |           /
                            |          /
                        +-----------------+
                        | Collection C1   |
                        | bindings:       |
                        | x.gif    y.gif  |
                        +-----------------+
                             |      |
                             |      |
                             |      |
                         +-------------+
                         | Resource R1 |
                         +-------------+

In this case, one possible value for the DAV:parent-set property on "/CollX/x.gif" would be:

<parent-set xmlns="DAV:">
  <parent>
    <href>/CollX</href>
    <segment>x.gif</segment>
  </parent>
  <parent>
    <href>/CollX</href>
    <segment>y.gif</segment>
  </parent>
</parent-set>

4. BIND Method ✨

okay so BIND is the method that edits a collection (the Request-URI) by adding a brand-new binding. The binding links the segment from the BIND body to the resource named in the BIND body. Think of it as giving a resource a new nickname inside a folder πŸ’…

If a server can't guarantee the integrity of the binding, the BIND request MUST fail. And this part is lowkey scary: keeping cross-server bindings intact is really hard 😭 Unless the server holding the resource knows about every binding to that resource on every server, it could accidentally destroy the resource or make it unreachable without telling the other server that has a binding to it. So if server A lets you bind to a resource on server B, server A has to tell server B about the binding and have an agreement with B that B won't destroy the resource while A's binding still exists. Otherwise B might get a DELETE that it thinks removes the last binding, destroy the resource, and A's binding is left pointing at nothing. The precondition DAV:cross-server-binding covers the case where servers reject cross-server BIND requests because they can't guarantee that integrity. Protective, iconic behaviour πŸ›‘οΈ

By default, if the collection already has a binding for the specified segment, the new binding replaces the old one. You can override that replacement using the Overwrite header defined in Section 10.6 of [RFC4918] πŸ¦‹

If a BIND request fails, the server state from before the request MUST be restored. BIND is unsafe and idempotent (see [RFC2616], Section 9.1). 🌸

Marshalling:

The request MAY include an Overwrite header.

The request body MUST be a DAV:bind XML element.

<!ELEMENT bind (segment, href)>

If the request succeeds, the server MUST return 201 (Created) when it creates a new binding, and 200 (OK) or 204 (No Content) when it replaces an existing one.

If a response body comes back for a successful request, it MUST be a DAV:bind-response XML element. This document doesn't define any elements for the BIND response body, but DAV:bind-response exists so future extensions that do define those elements can stay interoperable 🧚

<!ELEMENT bind-response ANY>

Preconditions:

(DAV:bind-into-collection): The Request-URI MUST identify a collection.

(DAV:bind-source-exists): The DAV:href element MUST identify a resource.

(DAV:binding-allowed): The resource identified by the DAV:href supports multiple bindings to it.

(DAV:cross-server-binding): If the resource identified by the DAV:href element in the request body lives on a different server from the collection identified by the Request-URI, the server MUST support cross-server bindings. Servers that don't support them can use this condition code to tell the client exactly why the request failed πŸ™ˆ

(DAV:name-allowed): The name specified by the DAV:segment is available for use as a new binding name.

(DAV:can-overwrite): If the collection already has a binding with the specified path segment, and an Overwrite header is included, the value of the Overwrite header MUST be "T".

(DAV:cycle-allowed): If the DAV:href element identifies a collection, and the Request-URI identifies a collection that is a member of that collection, the server MUST support cycles in the URI namespace. Servers that don't support cycles can use this condition code to tell the client exactly why the request failed πŸ”

(DAV:locked-update-allowed): If the collection identified by the Request-URI is write-locked, the appropriate token MUST be specified in an If request header.

(DAV:locked-overwrite-allowed): If the collection already has a binding with the specified path segment, and that binding is protected by a write lock, the appropriate token MUST be specified in an If request header.

Postconditions:

(DAV:new-binding): The collection MUST have a binding that maps the segment specified in the DAV:segment element in the request body to the resource identified by the DAV:href element in the request body. πŸŽ€

4.1. Example: BIND

Request:
BIND /CollY HTTP/1.1
Host: www.example.com
Content-Type: application/xml; charset="utf-8"
Content-Length: 172

<?xml version="1.0" encoding="utf-8" ?>
<D:bind xmlns:D="DAV:">
   <D:segment>bar.html</D:segment>
   <D:href>http://www.example.com/CollX/foo.html</D:href>
</D:bind>
Response:
HTTP/1.1 201 Created
Location: http://www.example.com/CollY/bar.html

Okay, the server added a new binding to the collection http://www.example.com/CollY, linking bar.html to the resource at http://www.example.com/CollX/foo.html. Now clients can use http://www.example.com/CollY/bar.html to send requests to that resource. That's main character energy πŸ‘‘


5. UNBIND Method

UNBIND is the opposite move πŸ’” It edits the collection (the Request-URI) by removing the binding named by the segment in the UNBIND body.

Once a resource can't be reached through any URI mapping, the server MAY reclaim the system resources tied to it. But if UNBIND removes a binding and there are still URI mappings to that resource, the server MUST NOT reclaim those system resources. Don't delete stuff people can still reach, bestie 🫢

If an UNBIND request fails, the server state from before the request MUST be restored. UNBIND is unsafe and idempotent (see [RFC2616], Section 9.1). πŸ“

Marshalling:

The request body MUST be a DAV:unbind XML element.

<!ELEMENT unbind (segment)>

If the request succeeds, the server MUST return 200 (OK) or 204 (No Content) when the binding was successfully deleted.

If a response body comes back for a successful request, it MUST be a DAV:unbind-response XML element. Again, this document doesn't define any elements for the UNBIND response body, but DAV:unbind-response is there so future extensions that do define them stay interoperable 🌈

<!ELEMENT unbind-response ANY>

Preconditions:

(DAV:unbind-from-collection): The Request-URI MUST identify a collection.

(DAV:unbind-source-exists): The DAV:segment element MUST identify a binding in the collection identified by the Request-URI.

(DAV:locked-update-allowed): If the collection identified by the Request-URI is write-locked, the appropriate token MUST be specified in the request.

(DAV:protected-url-deletion-allowed): If the binding identified by the segment is protected by a write lock, the appropriate token MUST be specified in the request. πŸ”’

Postconditions:

(DAV:binding-deleted): The collection MUST NOT have a binding for the segment specified in the DAV:segment element in the request body.

(DAV:lock-deleted): If the internal member URI of the binding specified by the Request-URI and the DAV:segment element in the request body was protected by a write lock at the time of the request, that write lock must have been deleted by the request. 🧹

5.1. Example: UNBIND

Request:
UNBIND /CollX HTTP/1.1
Host: www.example.com
Content-Type: application/xml; charset="utf-8"
Content-Length: 117

<?xml version="1.0" encoding="utf-8" ?>
<D:unbind xmlns:D="DAV:">
   <D:segment>foo.html</D:segment>
</D:unbind>
Response:
HTTP/1.1 200 OK

Okay, so the server removed the binding named "foo.html" from the collection http://www.example.com/CollX. If someone then requests http://www.example.com/CollX/foo.html, they get a 404 (Not Found) response. Gone, but not dramatic about it πŸ’€βœ¨

6. REBIND Method πŸ¦‹

Okay so REBIND is giving "remove it from over there and add it over here, all in one go" ✨ It removes a binding to a resource from a collection, and adds a binding to that resource into the collection identified by the Request-URI. The request body specifies the binding to be added (segment) and the old binding to be removed (href). It's effectively an atomic form of a MOVE request, so it MUST be treated the same way as MOVE when it comes to access permissions πŸ”₯

If a REBIND request fails, the server state from before the request MUST be restored. This method is unsafe and idempotent (see [RFC2616], Section 9.1). 🫢

Marshalling:

  • The request MAY include an Overwrite header.
  • The request body MUST be a DAV:rebind XML element.
<!ELEMENT rebind (segment, href)>

If the request succeeds, the server MUST return 201 (Created) when a new binding was created, and 200 (OK) or 204 (No Content) when an existing binding was replaced. ✨

If a response body for a successful request is included, it MUST be a DAV:rebind-response XML element. This document doesn't define any elements for the REBIND response body, but the DAV:rebind-response element exists so future extensions that do define them still play nicely together πŸ’œ

<!ELEMENT rebind-response ANY>

Preconditions:

  • (DAV:rebind-into-collection): The Request-URI MUST identify a collection.
  • (DAV:rebind-source-exists): The DAV:href element MUST identify a resource.
  • (DAV:cross-server-binding): If the resource identified by the DAV:href element in the request body is on another server from the collection identified by the Request-URI, the server MUST support cross-server bindings. (Servers that don't support them can use this condition code to tell the client exactly why the request failed πŸ™ˆ)
  • (DAV:name-allowed): The name specified by the DAV:segment is available for use as a new binding name.
  • (DAV:can-overwrite): If the collection already contains a binding with the specified path segment, and an Overwrite header is included, the value of the Overwrite header MUST be "T".
  • (DAV:cycle-allowed): If the DAV:href element identifies a collection, and the Request-URI identifies a collection that is a member of that collection, the server MUST support cycles in the URI namespace. (Servers that don't support cycles can use this condition code to tell the client exactly why the request failed πŸ’€)
  • (DAV:locked-update-allowed): If the collection identified by the Request-URI is write-locked, then the appropriate token MUST be specified in the request.
  • (DAV:protected-url-modification-allowed): If the collection identified by the Request-URI already contains a binding with the specified path segment, and that binding is protected by a write lock, then the appropriate token MUST be specified in the request.
  • (DAV:locked-source-collection-update-allowed): If the collection identified by the parent collection prefix of the DAV:href URI is write-locked, then the appropriate token MUST be specified in the request.
  • (DAV:protected-source-url-deletion-allowed): If the DAV:href URI is protected by a write lock, then the appropriate token MUST be specified in the request.

Postconditions:

  • (DAV:new-binding): The collection MUST have a binding that maps the segment specified in the DAV:segment element in the request body to the resource identified by the DAV:href element in the request body.
  • (DAV:binding-deleted): The URL specified in the DAV:href element in the request body MUST NOT be mapped to a resource.
  • (DAV:lock-deleted): If the URL specified in the DAV:href element in the request body was protected by a write lock at the time of the request, that write lock must have been deleted by the request.

6.1. Example: REBIND

Request:

REBIND /CollX HTTP/1.1
Host: www.example.com
Content-Type: application/xml; charset="utf-8"
Content-Length: 176

<?xml version="1.0" encoding="utf-8" ?>
<D:rebind xmlns:D="DAV:">
   <D:segment>foo.html</D:segment>
   <D:href>http://www.example.com/CollY/bar.html</D:href>
</D:rebind>

Response:

HTTP/1.1 200 OK

Ta-da! πŸŽ€ The server adds a new binding to the collection http://www.example.com/CollX, associating foo.html with the resource identified by http://www.example.com/CollY/bar.html, and removes the binding named bar.html from the collection identified by http://www.example.com/CollY. Clients can now use http://www.example.com/CollX/foo.html to send requests to that resource, but requests on http://www.example.com/CollY/bar.html will fail with a 404 (Not Found) response πŸ¦‹


6.2. Example: REBIND in Presence of Locks and Bind Loops

Let's see how locks and bind loops affect a REBIND, starting with this collection πŸ‘€

              +------------------+
              | Root Collection  |
              |  bindings:       |
              |  CollW           |
              +------------------+
                   |
                   |
                   |
              +-------------------------------+
              | Collection C1                 |<--------+
              | LOCKED infinity               |         |
              | (lock token L1)               |         |
              | bindings:                     |         |
              | CollX               CollY     |         |
              +-------------------------------+         |
                   |                  |                 |
                   |                  |  (creates loop) |
                   |                  |                 |
              +-----------------+  +------------------+ |
              | Collection C2   |  | Collection C3    | |
              | (inherit lock)  |  | (inherit lock)   | |
              | (lock token L1) |  | (lock token L1)  | |
              | bindings:       |  | bindings:        | |
              |  {none}         |  | y.gif     CollZ  | |
              +-----------------+  +------------------+ |
                                     |            |     |
                                     |            +-----+
                                     |
                                 +---------------------------+
                                 | Resource R2               |
                                 | (lock inherited from C1)  |
                                 | (lock token L1)           |
                                 +---------------------------+

(where L1 is "urn:uuid:f92d4fae-7012-11ab-a765-00c0ca1f6bf9").

The binding between CollZ and C1 creates a loop in the containment hierarchy. Servers aren't required to support loops like this, though the server in this example does πŸ”

The REBIND request below removes the segment "CollZ" from C3 and adds a new binding from "CollA" to the collection C2 🌸

REBIND /CollW/CollX HTTP/1.1
Host: www.example.com
If: (<urn:uuid:f92d4fae-7012-11ab-a765-00c0ca1f6bf9>)
Content-Type: application/xml; charset="utf-8"
Content-Length: 152

<?xml version="1.0" encoding="utf-8" ?>
<D:rebind xmlns:D="DAV:">
  <D:segment>CollA</D:segment>
  <D:href>/CollW/CollY/CollZ</D:href>
</D:rebind>

And here's the outcome of the REBIND operation ✨

              +------------------+
              | Root Collection  |
              |  bindings:       |
              |  CollW           |
              +------------------+
                   |
                   |
                   |
              +-------------------------------+
              | Collection C1                 |
              | LOCKED infinity               |
              | (lock token L1)               |
              | bindings:                     |
              | CollX                  CollY  |
              +-------------------------------+
                   |              ^      |
                   |              |      |
              +-----------------+ | +------------------+
              | Collection C2   | | | Collection C3    |
              |(inherited lock) | | | (inherited lock) |
              |(lock token L1)  | | | (lock token L1)  |
              | bindings:       | | | bindings:        |
              | CollA           | | | y.gif            |
              +-----------------+ | +------------------+
                  |               |    |
                  +---------------+    |
                   (creates loop)      |
                                 +---------------------------+
                                 | Resource R2               |
                                 | (inherited lock from C1)  |
                                 | (lock token L1)           |
                                 +---------------------------+

7. Additional Status Codes ✨

7.1. 208 Already Reported

The 208 (Already Reported) status code can be used inside a DAV:propstat response element to avoid listing the internal members of multiple bindings to the same collection over and over πŸ™Œ For each binding to a collection inside the request's scope, only one will be reported with a 200 status. Subsequent DAV:response elements for all other bindings use the 208 status, and no DAV:response elements for their descendants are included.

Note that the 208 status will only occur for Depth: infinity requests, and it matters most when multiple collection bindings cause a bind loop, as discussed in Section 2.2 πŸ”

A client can request the DAV:resource-id property in a PROPFIND request to make sure it can accurately rebuild the binding structure of a collection with multiple bindings to a single resource 🧚

For backward compatibility with clients that aren't aware of the 208 status code appearing in multistatus response bodies, it SHOULD NOT be used unless the client has signaled support for this specification using the DAV request header (see Section 8.2). Instead, a 508 status should be returned when a binding loop is discovered. That way the server can return the 508 as the top-level status if it finds the loop before it starts the response, or in the middle of a multistatus if it finds the loop while streaming out a multistatus response πŸ’–

7.1.1. Example: PROPFIND by Bind-Aware Client ✨

okay so picture this πŸŽ€ we send a PROPFIND on /Coll (bound to collection C). the members of /Coll are /Coll/Foo (bound to resource R) and /Coll/Bar (bound to collection C). this is giving loop energy πŸŒ€

>> Request:

PROPFIND /Coll/ HTTP/1.1
Host: www.example.com
Depth: infinity
DAV: bind
Content-Type: application/xml; charset="utf-8"
Content-Length: 152

<?xml version="1.0" encoding="utf-8" ?>
<D:propfind xmlns:D="DAV:">
  <D:prop>
   <D:displayname/>
   <D:resource-id/>
  </D:prop>
</D:propfind>

>> Response:

HTTP/1.1 207 Multi-Status
Content-Type: application/xml; charset="utf-8"
Content-Length: 1241

<?xml version="1.0" encoding="utf-8" ?>
<D:multistatus xmlns:D="DAV:">
  <D:response>
    <D:href>http://www.example.com/Coll/</D:href>
    <D:propstat>
      <D:prop>
        <D:displayname>Loop Demo</D:displayname>
        <D:resource-id>
          <D:href
>urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf8</D:href>
        </D:resource-id>
      </D:prop>
      <D:status>HTTP/1.1 200 OK</D:status>
    </D:propstat>
  </D:response>
  <D:response>
    <D:href>http://www.example.com/Coll/Foo</D:href>
    <D:propstat>
      <D:prop>
        <D:displayname>Bird Inventory</D:displayname>
        <D:resource-id>
          <D:href
>urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf9</D:href>
        </D:resource-id>
      </D:prop>
      <D:status>HTTP/1.1 200 OK</D:status>
    </D:propstat>
  </D:response>
  <D:response>
    <D:href>http://www.example.com/Coll/Bar</D:href>
    <D:propstat>
      <D:prop>
        <D:displayname>Loop Demo</D:displayname>
        <D:resource-id>
          <D:href
>urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf8</D:href>
        </D:resource-id>
      </D:prop>
      <D:status>HTTP/1.1 208 Already Reported</D:status>
    </D:propstat>
  </D:response>
</D:multistatus>

7.1.2. Example: PROPFIND by Non-Bind-Aware Client πŸ™ˆ

in this one the client doesn't know about the 208 status code yet, so it's not bind-aware πŸ’€ a "Depth: infinity" PROPFIND would cause a loop condition, so the whole request gets rejected with a 508 status.

>> Request:

PROPFIND /Coll/ HTTP/1.1
Host: www.example.com
Depth: infinity
Content-Type: application/xml; charset="utf-8"
Content-Length: 125

<?xml version="1.0" encoding="utf-8" ?>
<D:propfind xmlns:D="DAV:">
   <D:prop> <D:displayname/> </D:prop>
</D:propfind>

>> Response:

HTTP/1.1 508 Loop Detected

7.2. 508 Loop Detected πŸ”

the 508 (Loop Detected) status code means the server stopped an operation because it ran into an infinite loop while handling a request with "Depth: infinity". it's a full stop, so the entire operation counts as failed 😭

8. Capability Discovery πŸ‘‘

8.1. OPTIONS Method πŸŽ€

if the server supports bindings, it MUST return the compliance class name bind as a field in the DAV response header (see [RFC4918], Section 10.1) from an OPTIONS request on any resource that server implements. a value of bind in the DAV header MUST indicate that the server supports all MUST-level requirements and REQUIRED features specified in this document. it's the server's little promise card πŸ’Œ

8.2. 'DAV' Request Header πŸ’–

clients SHOULD signal support for all MUST-level requirements and REQUIRED features by sending a "DAV" request header with the compliance class name "bind". and in particular, the client MUST understand the 208 status code defined in Section 7.1. no skipping that one bestie 🫢

9. Relationship to Locking in WebDAV πŸ”’

locking is an optional feature of WebDAV ([RFC4918]). the base WebDAV spec and this extension were designed side by side, so that every WebDAV feature can still work on a server that does this protocol too ✨

unfortunately WebDAV uses the term "lock-root" inconsistently πŸ™ƒ it's introduced in point 2 of Section 6.1 of [RFC4918] like this:

2. A resource becomes directly locked when a LOCK request to a URL of that resource creates a new lock. The "lock-root" of the new lock is that URL. If at the time of the request, the URL is not mapped to a resource, a new empty resource is created and directly locked.

and then [RFC4918], Section 9.10.1 says:

A LOCK request to an existing resource will create a lock on the resource identified by the Request-URI, provided the resource is not already locked with a conflicting lock. The resource identified in the Request-URI becomes the root of the lock.

so the two definitions disagree, and that's the tea 🍡 servers that do both WebDAV locking and multiple bindings MUST use the first interpretation: the lock-root is the URI the lock was created through, not a resource. that URI, plus any aliases of it ([RFC4918], Section 5), are called "protected" by the lock πŸ›‘οΈ

as defined in the intro to Section 7 of [RFC4918], write operations that change the state of a locked resource need the lock token sent along with the request. consistent with WebDAV, the state of the resource is its content ("any variant"), its dead properties, its lockable live properties (item 1), plus, for a collection, all its bindings (item 2). and this doesn't depend on which Request-URI the write goes to, since the locked state belongs to the resource, not its URI πŸ’…

BUT the lock-root is the URI the lock was requested through, so the protection from item 3 of the list doesn't cover the extra URIs that point at the same resource thanks to multiple bindings. that's the twist πŸŒ€

9.1. Example: Locking and Multiple Bindings πŸ¦‹

picture a root collection /, containing two collections C1 and C2, named /CollX and /CollY, and a child resource R, bound to C1 as /CollX/test and bound to C2 as /CollY/test:

                     +-------------------------+
                     | Root Collection         |
                     |  bindings:              |
                     |  CollX          CollY   |
                     +-------------------------+
                         |                |
                         |                |
                         |                |
                +---------------+  +---------------+
                | Collection C1 |  | Collection C2 |
                | bindings:     |  | bindings:     |
                |     test      |  |     test      |
                +---------------+  +---------------+
                         |               |
                         |               |
                         |               |
                        +------------------+
                        |    Resource R    |
                        +------------------+

with the host name www.example.com, putting a depth-zero write lock on /CollX/test locks resource R, and the lock-root of that lock is http://www.example.com/CollX/test πŸ”

so these operations will need the matching lock token in the "If" request header ([RFC4918], Section 10.4):

  • a PUT or PROPPATCH request that changes the content or lockable properties of resource R (since R is locked), no matter which URI is used as the request target, and
  • a MOVE, REBIND, UNBIND, or DELETE request that stops /CollX/test from mapping to resource R (whether it's addressed to /CollX or /CollX/test).

and these ones DON'T need the lock token πŸ™Œ

  • a DELETE request addressed to /CollY or /CollY/test, since it doesn't touch resource R or the lock-root,
  • for the same reason, an UNBIND request that removes the binding test from collection C2, or the binding CollY from the root collection, and
  • similarly, a MOVE or REBIND request that stops /CollY/test from mapping to resource R.

one more thing πŸ‘€ even though the lock-root is http://www.example.com/CollX/test, an UNLOCK request can be sent through any URI that maps to resource R, because UNLOCK works on the resource identified by the Request-URI, not that URI (see [RFC4918], Section 9.11) 🫧

10. Relationship to WebDAV Access Control Protocol πŸ›‚

the WebDAV Access Control Protocol was designed to be compatible with systems where multiple URIs map to the same resource (see [RFC3744], Section 5), and it says:

Access control properties (especially DAV:acl and DAV:inherited-acl-set) are defined on the resource identified by the Request-URI of a PROPFIND request. A direct consequence is that if the resource is accessible via multiple URI, the value of access control properties is the same across these URI.

and finally πŸ’… BIND and REBIND behave the same as MOVE when it comes to the DAV:acl property (see [RFC3744], Section 7.3).

11. Relationship to Versioning Extensions to WebDAV ✨

Servers that implement Workspaces ([RFC3253], Section 6) and Version-Controlled Collections ([RFC3253], Section 14) already need BIND-like behavior to handle UPDATE and UNCHECKOUT semantics. It's honestly the same energy πŸ’…

Picture a workspace "/ws1/" with two version-controlled, checked-out collections inside it, C1 and C2, named "/ws1/CollX" and "/ws1/CollY". There's also a version-controlled resource R, bound to C1 as "/ws1/CollX/test". Here's the vibe:

                     +-------------------------+
                     | Workspace               |
                     |  bindings:              |
                     |  CollX          CollY   |
                     +-------------------------+
                         |                |
                         |                |
                         |                |
                +---------------+  +---------------+
                | Collection C1 |  | Collection C2 |
                | bindings:     |  |               |
                |     test      |  |               |
                +---------------+  +---------------+
                         |
                         |
                         |
                        +------------------+
                        |    Resource R    |
                        +------------------+

So here's the tea β˜• If you MOVE "/ws1/CollX/test" into "/ws1/CollY", check in C2, but undo the checkout on C1, you undo part of the MOVE request. That restores the binding from C1 to R, but keeps the new binding from C2 to R πŸ¦‹

>> Request:

MOVE /ws1/CollX/test HTTP/1.1
Host: www.example.com
Destination: /ws1/CollY/test

>> Response:

HTTP/1.1 204 No Content

>> Request:

CHECKIN /ws1/CollY/ HTTP/1.1
Host: www.example.com

>> Response:

HTTP/1.1 201 Created
Cache-Control: no-cache
Location: http://repo.example.com/his/17/ver/42
>> Request:

UNCHECKOUT /ws1/CollX/ HTTP/1.1
Host: www.example.com

>> Response:

HTTP/1.1 200 OK
Cache-Control: no-cache

And the end result? Both C1 and C2 have a binding to R πŸŽ€

                     +-------------------------+
                     | Workspace               |
                     |  bindings:              |
                     |  CollX          CollY   |
                     +-------------------------+
                         |                |
                         |                |
                         |                |
                +---------------+  +---------------+
                | Collection C1 |  | Collection C2 |
                | bindings:     |  | bindings:     |
                |     test      |  |     test      |
                +---------------+  +---------------+
                         |                |
                         |                |
                         |                |
                        +------------------+
                        |    Resource R    |
                        +------------------+

The MOVE semantics in Section 3.15 of [RFC3253] already require that "/ws1/CollX/test" and "/ws1/CollY/test" have the same version history, as exposed in the DAV:version-history property. Then the UNCHECKOUT semantics (which in this case are similar to UPDATE, see Section 14.11 of [RFC3253]) add this rule:

If a new version-controlled member is in a workspace that already has a version-controlled resource for that version history, then the new version-controlled member MUST be just a binding (i.e., another name for) that existing version-controlled resource.

So "/ws1/CollX/test" and "/ws1/CollY/test" end up as bindings to the same resource R, with identical DAV:resource-id properties. Iconic behavior, honestly πŸ‘‘

12. Security Considerations πŸ”

This section is here to make WebDAV implementers aware of the security implications of this protocol. Please take it seriously, bestie 😭

Everything in the security considerations of HTTP/1.1 ([RFC2616], Section 15) and the WebDAV Distributed Authoring Protocol specification ([RFC4918], Section 20) also applies to this protocol. On top of that, bindings bring some brand-new security concerns and make a few existing threats more likely. Here's the breakdown πŸ‘‡

12.1. Privacy Concerns

Where cross-server bindings are supported, creating bindings on a trusted server may let a hostile agent trick users into sending private information to a target on a different server. Yikes πŸ™ˆ

12.2. Bind Loops

Bind loops were already possible in HTTP 1.1, but the BIND method gives clients a new way to create loops, whether by accident or on purpose πŸŒ€ If the binding and its target live on the same server, the server may be able to detect BIND requests that would create loops. Servers are required to detect loops caused by bindings to collections while processing any request with "Depth: infinity".

12.3. Bindings and Denial of Service

Denial-of-service attacks were already possible by posting URIs meant for limited use at heavily used Web sites. BIND creates a new way to run similar attacks πŸ’€ If cross-server bindings are supported, clients can create bindings at heavily used sites that point to locations never designed for heavy traffic.

12.4. Private Locations May Be Revealed

If the DAV:parent-set property is maintained on a resource, the owners of the bindings risk revealing private locations 🧚 The directory structures where bindings live are visible to anyone with access to the DAV:parent-set property on the resource. Moving a binding may also reveal its new location to anyone with access to DAV:parent-set on its resource.

12.5. DAV:parent-set and Denial of Service

If the server maintains the DAV:parent-set property for bindings created in other administrative domains, it's open to hostile attempts to make it spend resources adding bindings to that list πŸ₯€

13. Internationalization Considerations 🌍

All internationalization considerations in Section 19 of [RFC4918] also apply to this document.

14. IANA Considerations πŸ“‹

Section 7 defines the HTTP status codes 208 (Already Reported) and 508 (Loop Detected), which have been added to the HTTP Status Code Registry. Slay ✨

15. Acknowledgements 🫢

This document is the collaborative product of the authors and Tyson Chihaya, Jim Davis, Chuck Fay and Judith Slein. It also benefited from thoughtful discussion by Jim Amsden, Peter Carlson, Steve Carter, Ken Coar, Ellis Cohen, Dan Connolly, Bruce Cragun, Cyrus Daboo, Spencer Dawkins, Mark Day, Werner Donne, Rajiv Dulepet, David Durand, Lisa Dusseault, Stefan Eissing, Roy Fielding, Yaron Goland, Joe Hildebrand, Fred Hitt, Alex Hopmann, James Hunt, Marcus Jager, Chris Kaler, Manoj Kasichainula, Rohit Khare, Brian Korver, Daniel LaLiberte, Steve Martin, Larry Masinter, Jeff McAffer, Alexey Melnikov, Surendra Koduru Reddy, Max Rible, Sam Ruby, Bradley Sergeant, Nick Shelness, John Stracke, John Tigue, John Turner, Kevin Wiggen, and other members of the concluded WebDAV working group. Everyone in that list is iconic 🌈

16. References πŸ“š

16.1. Normative References

[RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
           Requirement Levels", BCP 14, RFC 2119, March 1997.

[RFC2616]  Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
           Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext
           Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.

[RFC3986]  Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
           Resource Identifier (URI): Generic Syntax", STD 66,
           RFC 3986, January 2005.

[RFC4918]  Dusseault, L., Ed., "HTTP Extensions for Web Distributed
           Authoring and Versioning (WebDAV)", RFC 4918, June 2007.

[XML]      Bray, T., Paoli, J., Sperberg-McQueen, C., Maler, E., and
           F. Yergeau, "Extensible Markup Language (XML) 1.0 (Fifth
           Edition)", W3C REC-xml-20081126, November 2008,
           <http://www.w3.org/TR/2008/REC-xml-20081126/>.

16.2. Informative References

[RFC3253]  Clemm, G., Amsden, J., Ellison, T., Kaler, C., and J.
           Whitehead, "Versioning Extensions to WebDAV (Web
           Distributed Authoring and Versioning)", RFC 3253,
           March 2002.

[RFC3744]  Clemm, G., Reschke, J., Sedlar, E., and J. Whitehead, "Web
           Distributed Authoring and Versioning (WebDAV) Access
           Control Protocol", RFC 3744, May 2004.

[RFC4122]  Leach, P., Mealling, M., and R. Salz, "A Universally
           Unique IDentifier (UUID) URN Namespace", RFC 4122,
           July 2005.

Index ✨

okay so this is the index, aka the cheat sheet that tells you exactly where everything lives in the spec πŸ—‚οΈ every status code, method, condition name, and property is listed with the page numbers where it shows up. no cap, it's the fastest way to find the exact thing you're hunting for πŸ’…

   2
      208 Already Reported (status code)  31, 41

   5
      508 Loop Detected (status code)  34, 41

   B
      BIND method  21
         Marshalling  22
         Postconditions  23
         Preconditions  22
      Binding  6
      Binding Integrity  6-7, 21

   C
      Collection  6
      Condition Names
         DAV:bind-into-collection (pre)  22
         DAV:bind-source-exists (pre)  22
         DAV:binding-allowed (pre)  23
         DAV:binding-deleted (post)  25, 28
         DAV:can-overwrite (pre)  23, 27
         DAV:cross-server-binding (pre)  23, 27
         DAV:cycle-allowed (pre)  23, 27
         DAV:lock-deleted (post)  25, 28
         DAV:locked-overwrite-allowed (pre)  23
         DAV:locked-source-collection-update-allowed (pre)  28
         DAV:locked-update-allowed (pre)  23, 25, 27
         DAV:name-allowed (pre)  23, 27
         DAV:new-binding (post)  23, 28
         DAV:protected-source-url-deletion-allowed (pre)  28
         DAV:protected-url-deletion-allowed (pre)  25
         DAV:protected-url-modification-allowed (pre)  27
         DAV:rebind-into-collection (pre)  27
         DAV:rebind-source-exists (pre)  27
         DAV:unbind-from-collection (pre)  25
         DAV:unbind-source-exists (pre)  25

   D
      DAV header
         compliance class 'bind'  34
      DAV:bind-into-collection precondition  22
      DAV:bind-source-exists precondition  22
      DAV:binding-allowed precondition  23
      DAV:binding-deleted postcondition  25, 28
      DAV:can-overwrite precondition  23, 27
      DAV:cross-server-binding precondition  23, 27
      DAV:cycle-allowed precondition  23, 27
      DAV:lock-deleted postcondition  25, 28
      DAV:locked-overwrite-allowed precondition  23
      DAV:locked-source-collection-update-allowed precondition  28
      DAV:locked-update-allowed precondition  23, 25, 27
      DAV:name-allowed precondition  23, 27
      DAV:new-binding postcondition  23, 28
      DAV:parent-set property  20
      DAV:protected-source-url-deletion-allowed precondition  28
      DAV:protected-url-deletion-allowed precondition  25
      DAV:protected-url-modification-allowed precondition  27
      DAV:rebind-into-collection precondition  27
      DAV:rebind-source-exists precondition  27
      DAV:resource-id property  19
      DAV:unbind-from-collection precondition  25
      DAV:unbind-source-exists precondition  25

   I
      Internal Member URI  6

   L
      Locking  35

   M
      Methods
         BIND  21
         REBIND  26
         UNBIND  24

   P
      Path Segment  5
      Properties
         DAV:parent-set  20
         DAV:resource-id  19

   R
      REBIND method  26
         Marshalling  26
         Postconditions  28
         Preconditions  27

   S
      Status Codes
         208 Already Reported  31, 41
         508 Loop Detected  34, 41

   U
      UNBIND method  24
         Marshalling  24
         Postconditions  25
         Preconditions  25
      URI Mapping  5

Authors' Addresses πŸ’Œ

and here are the people who wrote this spec, with their mailing addresses and emails so you can reach out 🫢 these are the authors' own contact details, copied exactly as published.

   Geoffrey Clemm
   IBM
   550 King Street
   Littleton, MA  01460

   EMail: geoffrey.clemm@us.ibm.com


   Jason Crawford
   IBM Research
   P.O. Box 704
   Yorktown Heights, NY  10598

   EMail: ccjason@us.ibm.com


   Julian F. Reschke (editor)
   greenbytes GmbH
   Hafenweg 16
   Muenster, NW  48155
   Germany

   EMail: julian.reschke@greenbytes.de


   Jim Whitehead
   UC Santa Cruz, Dept. of Computer Science
   1156 High Street
   Santa Cruz, CA  95064

   EMail: ejw@cse.ucsc.edu
πŸ“¦ where this page comes from