8.4 Using Locked Resources

Whenever a locked resource is updated, the lock token must be provided in the request. This is done by including the lock token in the If header along with the request.

The If header is a conditional header, like the HTTP headers If-Match, If-None-Match, and If-Modified-Since. Its role as a conditional header is combined with an additional role: It's used by the client to provide the lock token when updating locked resource. Since a WebDAV server must receive the lock token to allow any change to a locked resource, the client must both provide the lock token and make sure that the condition in the If header is true.

The dual purpose of the If header was intended to ensure that clients don't overwrite a resource in a mistaken belief that it is locked and protected from lost updates. If a client issues a LOCK request and then a PUT request with the lock token, it's possible that the lock was lost in between those two requests through a server failure, a client failure, or a connection failure. If the lock was lost, then it's possible that the resource changed, and the PUT request is overwriting an important change. It would be good client design to provide the lock token as well as provide a conditional on the request, saying to the server, "Do this PUT operation, using this lock token, if the lock is still there." The design of the If header combines the provision of the lock token and the conditional together to enforce this good client design. As we'll see later, clients don't always want this to be enforced (Section 8.4.13).

When a change request fails because the correct lock tokens are missing, the response code is 423 Locked. When the request fails because the If header conditional checks fail, then the status code is 412 Precondition Failed (as with the HTTP/1.1 conditional headers). If both problems occur on a single request, the server must choose one of the two status codes.

Although the If header is only necessary with write operations on locked resources (PUT, MOVE, COPY, etc.), it can also be used with read operations or on unlocked resources. In those situations, the If header behaves solely as a conditional on the request operation.

8.4.1 Overwriting a Single Locked Resource

I've shown a simple and common example of the If header in refreshing a lock (Section 8.2.4). The next most common use of the If header is in a request that updates a single locked resource. This kind of request uses the If header in the same way, providing a single lock token that corresponds to the resource identified in the Request URI (see Listing 8-5).

Listing 8-5 Request using PUT with lock token.
 PUT /hr/ergonomics/posture.doc HTTP/1.1 Host: www.example.com If: (<opaquelocktoken:e71d4fae-5dec-22d6-fea5-00a0c91e6>) Content-Type: application/ms-word Content-Length: xxx graphics/enter.gif [Word file goes here...] 

If the lock token in this If header matches the target resource (i.e., if there is a valid lock on posture.doc with this lock token), then this request will succeed.

This is a simple example of the untagged list syntax, one of two syntaxes supported by the If header.

8.4.2 If Header Features

The If header supports a number of features by supporting two different syntaxes and two kinds of tokens:

  • Lock tokens can appear in the If header, enclosed in angle brackets (already shown previously).

  • ETags can appear in the If header, enclosed in square brackets. Section 8.4.3 shows an example and explains what this accomplishes.

  • Multiple tokens are ANDed together as shown in Section 8.4.3.

  • Any token can be checked for not matching by including a NOT keyword, as shown in Section 8.4.7.

  • An untagged list is one in which each token must apply to every resource affected by the current operation. These are described in Section 8.4.3 as applied to a single resource and in Section 8.4.5 as applied to multiple resources.

  • Tagged lists offer a slightly more complicated but more powerful syntax, where each token is tagged with a full URL to specify what resource to compare the token to. The full syntax for tagged lists is in Section 8.4.6.

  • Parentheses can group together multiple tokens in either tagged lists or untagged lists. Multiple groups are ORed together, as shown in Sections 8.4.3 and 8.4.5.

Tagged lists can do almost everything untagged lists can do except apply a single token to multiple resources. The best syntax for the job depends on the job sometimes one syntax is easier to get right than the other. It seems to require a fair amount of experience and experimentation for client implementors to choose the right If header syntax and apply it to an operation on locked resources. The examples here should give some idea.

8.4.3 Untagged Token Lists

The simpler of the two syntaxes defined for the If header is the untagged syntax. An untagged token list contains tokens without any URL "tags." Each token must be compared to all the resources affected by the operation.

One header can have multiple token lists. Each list is grouped with outer parentheses. Within one list, all tokens must match for the request to succeed: In Boolean terms, the token matches are combined with AND logic. Between lists, the server must apply Boolean OR logic. Therefore, for a request with the If header containing untagged token lists to succeed, all tokens in at least one list must match the request-URI resource.

Preventing Caching

RFC2518 recommends that whenever a client uses the If header, the server should take steps to prevent non-WebDAV proxies from returning cached results. HTTP/1.0 and HTTP/1.1 defined different headers for this purpose, so the client may include both headers in any GET request with the If header.

 
 HTTP/1.1: Cache-control: no-cache HTTP/1.0: Pragma: no-cache 


The client will delete the locked resource shown in Figure 8-3. To delete this file only if it's unchanged, the client needs to provide both an ETag (to ensure that it's unchanged) and a lock token (for the delete to be permitted). The request in Listing 8-6 will succeed only if the ETag is correct AND if the resource is still locked with the lock token given.

Figure 8-3. ETag and lock token for resource.

graphics/08fig03.jpg

8.4.4 Untagged Token List and OR

Imagine a backup client has archived copies of an old resource. Over time, the client has made several backed-up copies. Now it wants to delete the old resource from the main repository, but only if the archive contains the correct backed-up copies. There are two copies in backup for which source ETags were recorded when the resource was retrieved. Figure 8-4 shows the backup client, its table of archived resources and ETags, and the state of the server.

Listing 8-6 Use of If header with lock token and ETag.
 DELETE /hr/ergonomics/chairs.doc HTTP/1.1 Host: www.example.com Cache-control: no-cache Pragma: no-cache If: (<opaquelocktoken:e71d4fae-5dec-22d6-fea5-00a0c91e6>    [etag1284467]) graphics/enter.gif 
Figure 8-4. Comparing two ETags to one resource to see if it is already backed up.

graphics/08fig04.jpg

To construct a single efficient DELETE request that confirms that a backup exists, the backup client must provide both ETags and tell the server to compare them with a logical OR operation. This is accomplished with the request shown in Listing 8-7. The If header contains two lists, each enclosed in parentheses, and the conditions from the two lists are compared using OR logic. Each list contains one ETag. Thus, either ETag can match the resource being deleted, and the request will succeed.

Listing 8-7 If header example matching either of two ETags.
 DELETE /hr/ergonomics/chairs.doc HTTP/1.1 Host: www.example.com Cache-control: no-cache Pragma: no-cache If: ([etag1284458]) ([etag1284467]) graphics/enter.gif 

8.4.5 Untagged Lists on Requests Covering Multiple Resources

Requests can apply to multiple resources when the depth of the operation is 1 or infinity, and when the request has a destination as well as a source. When multiple resources are addressed with untagged tokens in the If header, each token must be compared to every addressed resource.

For this example, the client will attempt to move an entire collection to an archive location, which is merely another collection within the top-level collection (see Figure 8-5). The whole top-level collection is locked in order to achieve this goal. (There may be other top-level collections like finance/ that are not locked.)

Figure 8-5. Moving a resource to another location within a collection lock.

graphics/08fig05.jpg

To be allowed to perform this action, since hr/ is locked, the client must provide the lock token somehow (see Listing 8-8).

Listing 8-8 If header with one lock token applying to multiple resources.
 MOVE /hr/recruiting/resumes/ HTTP/1.1 Host: www.example.com Destination: http://www.example.com/hr/archives/resumes/ Depth: infinity Overwrite: T Cache-control: no-cache Pragma: no-cache If: (<locktoken:e71d4fae-5dec-22d6-fea5-00a0c91e6>) graphics/enter.gif 

This request will only succeed if all the resources involved are locked with the same lock, because only one lock token is provided and it isn't tagged with a specific resource's URL.

What if the two collections (source and destination) aren't covered by the same lock (see Figure 8-6)?

Figure 8-6. Moving a locked resource into a locked collection.

graphics/08fig06.jpg

To make this MOVE request succeed, the client must include both lock tokens in the If header. One way of providing both lock tokens and still having the condition check pass is to allow each resource to match one lock token, using the OR syntax (see Listing 8-9).

Since the source matches one lock token and the destination matches the other (it doesn't matter which), this request will succeed. The server must make sure that both lock tokens are provided, and they are. The condition evaluates to true because one of the tests succeeds for each resource.

Listing 8-9 If header matching two lock tokens against two resources.
 MOVE /hr/recruiting/resumes/gburlow.txt HTTP/1.1 Host: http://www.example.com Destination:    http://www.example.com/hr/archives/resumes/gburlow.txt Depth: 0 Overwrite: T Cache-control: no-cache Pragma: no-cache If: (<opaquelocktoken:e71d4fae-5dec-22d6-fea5-00a0c91e6>)    (<opaquelocktoken:e71d4fae-5dec-22d6-cc76-121d8d23f>) graphics/enter.gif 

graphics/roadt_icon.jpg

Each of the two lock tokens must appear in separate parentheses (instructing the server to compare with an OR, not an AND) because otherwise the server will attempt to match both lock tokens against both resources.

8.4.6 Tagged Token Lists

The previous MOVE example (Listing 8-9) could have succeeded in a number of cases, not just with a lock on resumes/ and a lock on archives/. The request would also succeed if the resumes/ collection were locked with one token, and the other token did not refer to any lock. Sometimes, the client may want to construct loose conditions as in Listing 8-9. However, it's also possible to construct much more specific conditions, which explicitly state what resource is locked and with exactly which lock token.

Tagged token lists are the mechanism used to apply multiple lock tokens to specific resources. "Tagged" means that each token list is associated with a complete URL for the resource to which the token applies. The tokens associated with a URL are only compared to that resource, not to all resources.

A client addressing the scenario in Figure 8-6 can also issue the request in Listing 8-10, to be very explicit about the state of resources. For the request to succeed, the two URLs in the If header must each be locked with a specific lock token.

Listing 8-10 Tagged If header matching tokens against specific URLs.
 MOVE /hr/recruiting/resumes/gburlow.txt HTTP/1.1 Host: http://www.example.com Destination:    http://www.example.com/hr/archives/resumes/gburlow.txt Depth: 0 Overwrite: T Cache-control: no-cache Pragma: no-cache If: <http://www.example.com/hr/recruiting/resumes/gburlow.txt>    (<opaquelocktoken:e71d4fae-5dec-22d6-fea5-00a0c91e6>)    <http://www.example.com/hr/archives/resumes/>    (<opaquelocktoken:e71d4fae-5dec-22d6-cc76-121d8d23f>) graphics/enter.gif 

graphics/roadt_icon.jpg

The URLs in tagged token lists must be absolute URLs and they must be encapsulated in angle brackets. Otherwise, the syntax is the same as that of an untagged list, and the logic for combining tokens inside and outside of parentheses remains the same:

When multiple tagged lists are present, a successful match must be reported from every tagged list. In other words, tagged list conditional results are combined with the Boolean AND operation. For example, if the If header contains two resources and four tokens as follows:

<resource1> (token1) (token2) <resource2> (token3) <resource2> (token4)

The server must then translate to Boolean logic:

( (resource1 matches token1) OR (resource1 matches token2) )AND (resource2 matches token3) AND (resource2 matches token4)

Note that tagged lists are not compatible with untagged lists. Thus, there is no way to set up a condition asking if "all resources addressed match token A, AND one resource matches token B." In addition, since each set of tokens is associated with only one URL, there is no way to compare a group of resources with a token, except by specifying each resource individually.

Tagged lists are also useful for the case when only a few locked resources are affected in an operation addressing many resources. Let's take a scenario where the client wants to DELETE a collection in which there were many resources but only one is locked, as shown in Figure 8-7.

Figure 8-7. DELETE collection with one locked child.

graphics/08fig07.jpg

To make this operation succeed, the client could issue the request shown in Listing 8-11.

Listing 8-11 Tagged If header matching one token against only one resource.
 DELETE /hr/archives/resumes/ HTTP/1.1 Host: http://www.example.com Cache-control: no-cache Pragma: no-cache If: <http://www.example.com/hr/archives/resumes/fred.txt>    (<opaquelocktoken:e71d4fae-5dec-22d6-fea5-00a0c91e6>) graphics/enter.gif 

There may be other ways to delete a collection containing a locked resource using the Not syntax (Section 8.4.8), but this is a common approach to providing the lock token required in that scenario.

8.4.7 If and Not

Finally, the If header also supports negation when checking for matches. A token match may be negated by preceding the token with the word Not. Each negation applies to only one token and may not apply to a clause.

 
 If: <http://www.example.com/hr/index.html>    (Not <locktoken1> [Etag]) 

Here the Not refers only to the first token following it, <locktoken1>. Thus, the request will succeed if locktoken1 does not match a lock on index.html and if the ETag does match the content ETag of index.html. That's a contrived example but a legal one.

The only case I've seen for the Not syntax being particularly useful is to allow the client to construct a condition that always evaluates to true and use that condition in an OR clause so the entire header evaluates to true.

This header cannot possibly fail the precondition check, because the client used an obviously false lock token with the Not syntax, in the context of several lists compared with OR. The request could still fail with 423 Locked if the client did not provide enough lock tokens, but it can't possibly fail from having too many lock tokens or having the right lock tokens associated with the wrong URLs.

The <no-lock> token is not part of the WebDAV specification and is unlikely to be what the server expects to see in a lock token. A server might respond to a request with the If header in Listing 8-12 with the status 400 Bad Request or 500 Server Error, but this would not help the client. We recommend that WebDAV servers make sure that they handle this or similar tokens as semantically correct tokens.

Listing 8-12 If header with clause guaranteed to succeed.
 If: (<opaquelocktoken:example.com:2907>)    (<opaquelocktoken:example.com:2908>)    (Not <no-lock>) 

8.4.8 No-tag-list Productions

Table 8-2 provides a number of examples of valid "no-tag-list" If header syntaxes with a brief explanation of what they mean.

Table 8-2. If Header no-tag-list Examples

Example

Explanation

If: (["7618-7118"])

The resources affected by the request must have this ETag.

If: (<opaquelocktoken:example.com:2907>)

The resources affected by the request must all be locked with this lock token.

If: (<opaquelocktoken:example.com:2907> ["7618-7118"])

The resources affected by the request must all be locked with this lock token AND have this ETag.

If: (<opaquelocktoken:example.com:2907>) (["7618-7118"])

The resources affected by the request must be locked with this token OR have this ETag.

If: (<opaquelocktoken:example.com:2907>) (<opaquelocktoken:example.com:2908>) (Not <no-lock>)

The resources affected by the request must be locked with one of the real lock tokens OR not locked with the made-up token.

8.4.9 Tagged List Productions

Table 8-3 provides a number of examples of valid tagged list If header syntaxes with a brief explanation what they mean.

8.4.10 Matching Tokens to Indirectly Locked Resources

graphics/bomb_icon.jpg

The WebDAV specification states that "If the state of the resource to which the header is applied does not match any of the specified state lists, then the request MUST fail." It also says that "Every state token or ETag is either current, and hence describes the state of a resource, or is not current, and does not describe the state of a resource." Clearly, when a resource is directly locked, the lock's token must "match" that resource for as long as the lock is valid.

Table 8-3. If Header Tagged List Examples

Example

Explanation

If: <http://www.example.com/file1> (<opaquelocktoken:example.com:2907>)

File1 must be locked with the given lock token.

If: <http://www.example.com/file1> (<opaquelocktoken:example.com:2907>) <http://www.example.com/file2> (<opaquelocktoken:example.com:2908>)

File1 must be locked with the lock token "...2907" AND File2 must be locked with the lock token "...2908."

If: <http://www.example.com/file1> (<opaquelocktoken:example.com:2907>) <http://www.example.com/file2> (<opaquelocktoken:example.com:2908>) (["1234-5678"])

The clause for File1 says that File1 must be locked with the lock token "...2907."

The clause for File2 has two lists. Multiple lists are combined with an OR, so File2 must be locked with the lock token "...2908" OR have the ETag "1234-5678."

Finally, the two clauses are combined with AND.

If: <http://www.example.com/file1> (<opaquelocktoken:example.com:2907>) <http://www.example.com/file2> (<opaquelocktoken:example.com:2908> ["1234-5678"])

The clause for File1 says that File1 must be locked with the lock token "...2907," as above.

The clause for File2 contains only one list with two tokens. Two tokens in the same list are combined with AND. File2 must match its lock token, AND File2 must match the ETag.

Thus, all three conditions are ANDed together.

When a resource is included within a collection lock (depth infinity), the lock token must also match child resources. In 2002, some WebDAV implementors argued that a lock token would only match the exact URL of the resource where the lock was created (the collection URL). Meanwhile, some client software had already been deployed assuming that the URL of any resource indirectly included in the lock ought to match the lock token a more liberal policy that is easier for the client to get right. That implies that servers should apply the more liberal policy in order to interoperate widely with client software. The current consensus on the WebDAV mailing list is that the liberal policy is beneficial and has no drawbacks.

8.4.11 Matching Tokens to Unmapped URLs

If a collection is locked with depth infinity, the client must provide the lock token to create a new resource anywhere within the collection or any subcollection. In the first example in Section 8.4.5, the collection /hr/ contained both the source and the destination resources, and /hr/ was locked with depth infinity. The destination resource does not exist until it is created, yet it is being created within a locked collection. The example used an untagged lock token in the If header:

 
 If: (<locktoken:e71d4fae-5dec-22d6-fea5-00a0c91e6>) 

WebDAV requires that any token in the If header match every resource affected, and clearly both the destination collection and the new resource are affected by the request. One could split hairs about whether the not-yet-created resource's URL can possibly match any token when the resource doesn't exist yet, but it's simpler if servers allow this to work. The intent is clear and the correct lock token is present to allow modifications. In this case as in the previous, a liberal policy is preferred because otherwise clients have too much trouble making their requests succeed.

If the collection is locked with a depth 0 lock, a new resource added to the collection does not become indirectly locked. However, the collection's lock token is still required in order to add the new resource to the collection's member list. In this case, the client would have to issue a more carefully constructed If header to make the request succeed. The next section gives some suggestions.

8.4.12 Adding a Resource to a Locked Collection

There is another way for clients to create resources inside locked collections, in which the If header can be parsed unambiguously. The client can tag the lock token with the URL of the root of the lock. If the collection /hr/recruiting/resumes/ is locked with the lock token opaquelocktoken:f81d4fae-7dec-11d0-a765-00a0c91e6bf6, a resource may be added to the locked collection as shown in Listing 8-13.

Listing 8-13 Adding a resource to a locked collection.
 PUT /hr/recruiting/resumes/ldusseault.txt HTTP/1.1 Host: www.example.com Content-Type: text/plain Cache-control: no-cache Pragma: no-cache If: <http://www.example.com/hr/recruiting/resumes/>    (<opaquelocktoken:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>) If-None-Match: * Content-Length: xxxx graphics/enter.gif Resume of Lisa Dusseault [...] 

The client uses the If-None-Match:* header when it is using PUT and intending to create a new resource. The server will fail the request if the resource already exists.

After this request is handled, if the collection is still locked with depth infinity, then the new resource must also now be affected by the existing infinite-depth lock, and the new resource can only be altered by providing the same lock token. If the collection is locked with depth 0, the new resource can now be altered without providing the collection's lock token.

8.4.13 Comments on the If Header

The If header has a number of drawbacks and issues. Implementation and interoperability testing experience have addressed most of these issues.

Confuses Two Purposes
graphics/bomb_icon.jpg

The If header confuses two purposes. First, there's the "conditional" purpose: Expressions provided by the client must evaluate to true before the server is allowed to process a request. Second, it's the only way the client can provide the correct lock token in a request to write a locked resource. Since these two purposes were combined into one header, it's impossible to separate the concerns. For example, the client can't easily send a lock token for a lock that may be expired and have the request succeed whether it is expired or not.

This approach forces certain choices on the client. It presumes that the client prefers to have the request fail if the lock has disappeared. However, in some situations, client implementors would like to make other choices.

The client can attempt to circumvent the enforcement of the If header by causing the conditional statement to evaluate to true under all circumstances. The client could put all its lock tokens in parentheses and append (Not <no-lock>) at the end of an untagged list production, as shown in Table 8-2. Since that clause evaluates to true, and since the server combines groups of untagged lists with OR, the entire If header must evaluate to true. This approach requires careful use of ETags to avoid overwrite, because now it's possible for the client to shoot itself in the foot and overwrite a resource where the lock expired and the resource already changed. This approach hasn't been tried much in practice, so it may expose server bugs.

Complete Boolean Logic Not Possible

Despite the complexity of the If header, it doesn't allow complete Boolean logic in matching state tokens. The tagged list syntax allows an AND clause to appear within an OR clause, but not the inverse. A NOT production can apply to a single token but can't be applied to a clause. The same URL may not appear more than once in the same IF header, so there's no way that two conditions on the same resource can be compared with OR. It's impossible to express the statement resource A matches lock1 or lock2 or resource B matches lock3 or lock4 because the If header syntax does not allow nested tagged lists.

A clever client implementor can translate many kinds of complex conditions into the format required by applying transformations. For example, the statement NOT (resource A matches lock1 or resource B matches lock2) can't be directly expressed because the negation here applies to the whole statement, whereas the If header syntax applies the NOT keyword only to one list. However, the statement can be transformed into (NOT resource A matches lock1) AND (NOT resource B matches lock2), which can be expressed in the If header syntax.

Ignored Clauses

RFC2518 specifies that the server determines which resources the current operation applies to. When it parses the If header, the specification requires that the server only evaluate the clauses tagged by those resources. The rest of the clauses are ignored. That makes it too hard for clients to know what clauses will be evaluated and which will be ignored. The WebDAV Working Group has discussed this issue and decided to recommend instead that servers should go ahead and process all clauses anyway. The next revision of the WebDAV specification will redefine this requirement.

Advanced Syntax Is Not Used Much

Not many clients create multiclause conditionals for the If header. The full syntax is complex, and there's some evidence that servers do not support the If header in the same way. A vicious circle could ensue because features that clients don't use aren't reliably implemented by servers. When a server feature hasn't been tested, it almost certainly doesn't work. Interoperability testing events and shared test suites are helping to fix this situation.

Header May Be Too Long to Be Transmitted Correctly

Tagged token lists can get pretty long, and Web infrastructure may not support that well. Proxy servers in particular have proved incapable of handling very long header values. Thus, the client may have all the lock tokens for a big MOVE operation and be able to correctly marshal them with the right URL tags into a big If header, but the request may be rejected by a proxy before reaching the server. Alternatively, the proxy could unknowingly truncate or split the header value, most probably transforming it into a malformed header that the server rejects.

It's really hard to say what header length is safe or unsafe. Some products designed to secure [Alliance01] or boost the performance of [Servertec03] Web servers and proxies can be configured by the administrator to reject requests with headers of certain lengths or with a specified total header length. Since the administrator can choose the length, an overzealous administrator could cause real interoperability problems.



WebDAV. Next Generation Collaborative Web Authoring
WebDAV. Next Generation Collaborative Web Authoring
ISBN: 130652083
EAN: N/A
Year: 2003
Pages: 146

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net