RE: Implementing HTTP PUT

> -----Original Message-----
> From: Aaron Swartz [mailto:aswartz@swartzfam.com]
> Sent: Tuesday, March 27, 2001 6:33 AM
> To: Fish; www-talk@w3.org
> Subject: Re: Implementing HTTP PUT
>
>
> Fish <fish@infidels.org> wrote:
>
> > What do you mean by: "What type of RESPONSE should be returned with
> > an error [RESPONSE] and a 200 OK [RESPONSE]..."? (emphasis mine)
> >
> > Could you perhaps mean which of the variety of *response HEADERS*
> > should/must one include in their response to a PUT request??
>
> You're right, I don't quite know the proper terminology, so I'm being a bit
> unclear. What I'm really curious about is the mix of headers and body that
> is included. Mostly the body, to be truthful. Does it matter?

I don't think so. AFAIK the body of your response can be pretty much whatever
you want.

> In my first
> experimentation with it, when I returned a 200 response with a text/plain
> body of "OK", Amaya claimed there was an error of "OK". So I believe I'm
> missing something.

Probably.

Was there an "Expect: 100-continue" header in the request? If so, then you
should have responded to the PUT request with two separate responses: the first
being a "100 Continue" response and the second a "200 OK" final status response.
See RFC 2616 section 8.2.3 "Use of the 100 (Continue) Status". (But you knew
that, right? How familiar are you with HTTP/1.1 anyway? Please understand I'm
not bashing you; I'm just curious, that's all. It helps to know the background
and experience of the person asking the question.) (Also please understand that
I'm certainly no expert in this either. I'm just familiar with RFC 2616, having
written my own proxy server, that's all. However, I must if course quickly add
that PUT and DELETE requests and their associated responses is not something I'm
very familiar with, so if anyone else out there has first hand experience,
*please* jump in and correct me! Thanks. :)  (That includes you too, Nick. >;-)

Anyway, from the sounds of it (and from my quick reading of RFC 2616), how one
responds to a PUT request depends largely on what the request looks like and
what the two of you (the client, and you, the server) are trying to accomplish.

The way RFC 2616 is worded, it sounds very much like the PUT and DELETE requests
(and their associated responses) were designed for something like CVS clients
and servers (or something very similar).

What follows below are some brief excerpts from what I believe are the pertinent
sections of RFC 2616 and a few intervening comments by myself:

-----------------------------------
9.6 PUT

   [...]

   If a
   new resource is created, the origin server MUST inform the user agent
   via the 201 (Created) response. If an existing resource is modified,
   either the 200 (OK) or 204 (No Content) response codes SHOULD be sent
   to indicate successful completion of the request. If the resource
   could not be created or modified with the Request-URI, an appropriate
   error response SHOULD be given that reflects the nature of the
   problem. The recipient of the entity MUST NOT ignore any Content-*
   (e.g. Content-Range) headers that it does not understand or implement
   and MUST return a 501 (Not Implemented) response in such cases.
-----------------------------------

Ok. Here's where we begin. A PUT request. The entity we're "PUT"ting may or may
not already exist. If it doesn't, we should respond with "201 Created" whereas
if it already does (and the request thus simply modified the already existing
entity) then we should respond with "200 OK". If some "Content-Range" headers
were included in the request (to request modification of only a portion of the
entity in question and not the complete entity) and we don't support such
requests, we should respond "501 Not Implemented". So far so good.

Now the "201 Created" response also appears to carry with it some other
requirements:

-----------------------------------
10.2.2 201 Created

   The request has been fulfilled and resulted in a new resource being
   created. The newly created resource can be referenced by the URI(s)
   returned in the entity of the response, with the most specific URI
   for the resource given by a Location header field. The response
   SHOULD include an entity containing a list of resource
   characteristics and location(s) from which the user or user agent can
   choose the one most appropriate. The entity format is specified by
   the media type given in the Content-Type header field. [...]
-----------------------------------

Ah-ha. It looks like your response should include a list of URIs that tells the
client how it should refer to the just-created entity. That is to say, now that
the entity has been created, if it (the client) should then need to issue a
'GET' request at some later point in time to retrieve what it just PUT, what URI
should it use in that 'GET' request? (Apparently the URI used in the PUT request
can differ from the URI used to 'GET' it. Within the context of the CVS analogy,
this makes sense.) So... your response should include a "Location:" header
specifying the *preferred* URI, with all the other possibly URIs (if any)
specified in the actual body of your response (with an appropriate
"Content-Type:" header in your response specifying which format the entity body
of your response is in; e.g. "text/plain" or "text/html", etc.)

-----------------------------------
("10.2.2 201 Created" continued):

   [...] The origin
   server MUST create the resource before returning the 201 status code.
   If the action cannot be carried out immediately, the server SHOULD
   respond with 202 (Accepted) response instead.

   A 201 response MAY contain an ETag response header field indicating
   the current value of the entity tag for the requested variant just
   created, see section 14.19.
-----------------------------------

Ok. Since the very nature of PUT requests is to modify entities on the server
and there could theoretically be several different versions of that one entity
(using the previously mentioned CVS analogy), the protocol allows for the
requestor (the client making the PUT request) to specify *which* of the various
entity versions it wishes to modify by including an "If-Match:",
"If-None-Match:" or "If-Unmodified-Since:" header in its request (At least
that's my reading of it. Do you concur?), and your response should (may) include
an "ETag:" header, indicating which entity was actually created/updated:

-----------------------------------
14.19 ETag

   The ETag response-header field provides the current value of the
   entity tag for the requested variant.
-----------------------------------

Or, you can simply include a "Last-Modified:" header in your response from the
sounds of it:

-----------------------------------
14.29 Last-Modified

   The Last-Modified entity-header field indicates the date and time at
   which the origin server believes the variant was last modified.
-----------------------------------

As I said earlier, it all depends on who you're talking to (i.e. the client) and
what they (the client) expect their response to look like. <shrug>

-----------------------------------
14.26 If-None-Match

   [...]

   The meaning of "If-None-Match: *" is that the method MUST NOT be
   performed if the representation selected by the origin server (or by
   a cache, possibly using the Vary mechanism, see section 14.44)
   exists, and SHOULD be performed if the representation does not exist.
   This feature is intended to be useful in preventing races between PUT
   operations.
-----------------------------------

Ok. That sounds simple enough. If the client making the request believe it's
*creating* a new entity, it should include an "If-None-Match: *" header, meaning
its requests should be interpreted as meaning "only PUT this entity if it does
NOT already exist". If it does (or of any of the other request conditions cannot
be honored (i.e. "If-Match:" and "If-Unmodified-Since"), then the response
should of course be "412 Precondition Failed":

-----------------------------------
10.4.13 412 Precondition Failed

   The precondition given in one or more of the request-header fields
   evaluated to false when it was tested on the server. This response
   code allows the client to place preconditions on the current resource
   metainformation (header field data) and thus prevent the requested
   method from being applied to a resource other than the one intended.
-----------------------------------

The only thing left is "409 Conflict":

-----------------------------------
10.4.10 409 Conflict

   The request could not be completed due to a conflict with the current
   state of the resource. This code is only allowed in situations where
   it is expected that the user might be able to resolve the conflict
   and resubmit the request. The response body SHOULD include enough
   information for the user to recognize the source of the conflict.
   Ideally, the response entity would include enough information for the
   user or user agent to fix the problem; however, that might not be
   possible and is not required.

   Conflicts are most likely to occur in response to a PUT request. For
   example, if versioning were being used and the entity being PUT
   included changes to a resource which conflict with those made by an
   earlier (third-party) request, the server might use the 409 response
   to indicate that it can't complete the request. In this case, the
   response entity would likely contain a list of the differences
   between the two versions in a format defined by the response
   Content-Type.
-----------------------------------

This is the wording I was talking about that makes it sound like PUT/DELETE were
designed for CVS or similar client/server exchanges. If a client requests a PUT
for entity "foo.c" using a URI that specified version 1.0 for example (e.g. "PUT
/usr/cvs/project/version_1.0/foo.c HTTP/1.1") but version 1.0 is no longer the
current version (because presumably someone just uploaded a new version in the
mean time), then a "409 Conflict" response might, in this case, be appropriate
and not a "200 OK" as would normally be expected.

All in all, it sounds like there are a few headers and response body
requirements (or at least *expectations*) that accompany proper handling of PUT
(and DELETE) requests, and, as I said, it all depends on what you and the client
are doing (i.e. trying to accomplish). <shrug>

At least, that's *my* take on it.

But as I said, I'm certainly no expert in this. I'm just going by what I'm
reading in the RFC.

> What I'd love is just simple traces of a 200 response, an error response,
> and even a 201 response would be nice.

I agree. That would certainly help. Unfortunately I don't have any. :(

Maybe your client was expecting an "ETag:" header in the response? I don't know.
Perhaps you could include the exact text of the request sent to you and the
response you sent back. Maybe that would help shed some might on what's going
on.

> Thanks for your help,
>
> --
> [ Aaron Swartz | me@aaronsw.com | http://www.aaronsw.com ]

You're very welcome, Aaron. I only hope that I indeed *did* help some. I don't
know. Did I? Or did I just confuse you further? %P

  :)

--
"Fish" (David B. Trout)
   fish@infidels.org

Received on Tuesday, 27 March 2001 21:54:06 UTC