2

Breaking down what makes a conditional GET:

In RFC 2616 it states that the GET method change to a "conditional GET" if the request message includes an If-* (If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match, or If-Range) header field.

It then states:

A conditional GET method requests that the entity be transferred ONLY under the circumstances described by the conditional header field(s).

From my understanding this is saying it will only return the data being requested if the condition is met with the "If-*" in any new subsequent requests. For example, if a GET request returns a response with a Etag header then the next request must include the If-None-Match with the ETag value to transfer the client back the requested resource.

However, If a client has to send an initial request before getting the returned "ETag" header (to return with If-None-Match) then they already have the requested resource. Thus, any future requests that return the If-None-Match header with the ETag value only dictate the return of the requested value, returning 200 OK (if the client does not return the If-None-Matchand ETag value from initial request) or 304 Not Modified (if they do), where this helps the client and server by caching the resource.

My Question:

Why does it state the entity (the resource from a request) will "be transferred ONLY" if the If-* condition is met (like in my example where the client returns the ETag value with anIf-None-Match in order to cache the requested resource) if the resource or "entity" is being returned with or without a "If-*" being returned? It doesn't return a resource "only under the circumstances described by the conditional header" because it returns the resource despiteless returning 200 OK or 304 Not Modified depending on if a "If-*" header is returned. What am I misunderstanding about this?

Full conditional GET reference from RFC 2616:

The semantics of the GET method change to a "conditional GET" if the request message includes an If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match, or If-Range header field. A conditional GET method requests that the entity be transferred only under the circumstances described by the conditional header field(s). The conditional GET method is intended to reduce unnecessary network usage by allowing cached entities to be refreshed without requiring multiple requests or transferring data already held by the client.

garrettmac
  • 8,417
  • 3
  • 41
  • 60

1 Answers1

7

First of all, please note that RFC 2616 is obsolete, and you should refer instead to RFC 7232.

It's hard to see what exactly is confusing you. So let me just illustrate with examples instead.

Scenario 1

Client A: I need http://example.com/foo/bar.

GET /foo/bar HTTP/1.1
Host: example.com

Server: Here you go.

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 12
ETag: "2ac07d4"

Hello world!

(some time passes)

Client A: I need http://example.com/foo/bar again. But I already have the "2ac07d4" version in my cache. Maybe that will do?

GET /foo/bar HTTP/1.1
Host: example.com
If-None-Match: "2ac07d4"

Server: Yeah, "2ac07d4" is fine. Just take it from your cache, I'm not sending it to you.

HTTP/1.1 304 Not Modified

Scenario 2

Client A: I need http://example.com/foo/bar.

GET /foo/bar HTTP/1.1
Host: example.com

Server: Here you go.

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 12
ETag: "2ac07d4"

Hello world!

(some time passes)

Client B: I want to upload a new version of http://example.com/foo/bar.

PUT /foo/bar HTTP/1.1
Content-Type: text/plain
Content-Length: 17

Hello dear world!

Server: This looks good, I'm saving it. I will call this version "f6049b9".

HTTP/1.1 204 No Content
ETag: "f6049b9"

(more time passes)

Client A: I need http://example.com/foo/bar again. But I already have the "2ac07d4" version in my cache. Maybe that will do?

GET /foo/bar HTTP/1.1
Host: example.com
If-None-Match: "2ac07d4"

Server: I'm sorry, but "2ac07d4" is out of date. We have a new version now, it's called "f6049b9". Here, let me send it to you.

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 17
ETag: "f6049b9"

Hello dear world!

Analysis

A conditional GET method requests that the entity be transferred ONLY under the circumstances described by the conditional header field(s).

Consider Client A's second request (in both scenarios).

The conditional header field is: If-None-Match: "2ac07d4".

The circumstances described by it are: "a selected representation of the resource does not match entity-tag "2ac07d4"".

Scenario 1: the circumstances do not hold, because the selected representation of the resource (the one containing Hello world!) does indeed match entity-tag "2ac07d4". Therefore, in accordance with the protocol, the server does not transfer the entity in its response.

Scenario 2: the circumstances do hold: the selected representation of the resource (the one containing Hello dear world!) doesn't match entity-tag "2ac07d4" (it matches "f6049b9" instead). Therefore, in accordance with the protocol, the server does transfer the entity in its response.

How does the server come up with these "2ac07d4" and "f6049b9", anyway? Of course, this depends on the application, but one straightforward way to do it is to compute a hash (such as SHA-1) of the entity body--a value that changes dramatically when even small changes are introduced.

Community
  • 1
  • 1
Vasiliy Faronov
  • 11,840
  • 2
  • 38
  • 49
  • You can include the current cached ETag in an `If-Match` for the `PUT` if you want to make sure the new version being put on the server is a modification of the version that is in your cache. If someone else uploaded a different version before you, the server can thus reject your `PUT` so you don't accidentally wipe out their modifications. – Remy Lebeau Nov 12 '15 at 23:48
  • Either way, on a successful `PUT`, the server should be including the updated ETag in the response. – Remy Lebeau Nov 12 '15 at 23:49
  • @RemyLebeau The protocol does not require that a successful response to `PUT` include an `ETag`. In fact, there are strict limitations on when it *can* include an `ETag`: see [RFC 7231 page 28](http://tools.ietf.org/html/rfc7231#page-28). – Vasiliy Faronov Nov 12 '15 at 23:53
  • That RFC clearly states: "*An origin server MUST NOT send a validator header field (Section 7.2), **such as an ETag** or Last-Modified field, in a successful response to PUT **unless the request's representation data was saved without any transformation applied to the body (i.e., the resource's new representation data is identical to the representation data received in the PUT request) and the validator field value reflects the new representation**...*" – Remy Lebeau Nov 12 '15 at 23:57
  • "*This requirement allows a user agent to know when the representation body it has in memory remains current as a result of the PUT, thus not in need of being retrieved again from the origin server, and that **the new validator(s) received in the response can be used for future conditional requests** in order to prevent accidental overwrites (Section 5.2).*" – Remy Lebeau Nov 13 '15 at 00:04
  • @RemyLebeau It *can*, yes; but I can see no indication in the standard that it *should*. That said, I agree that the example is better with the `ETag`. I was unsure if such a usage was correct, but [RFC 7231 section 6.3.5](https://tools.ietf.org/html/rfc7231#section-6.3.5) clearly allows it. So thanks for the edit. – Vasiliy Faronov Nov 13 '15 at 00:04
  • In your example, the data is changing and being saved as-is by the server, so it can (and should) send back a new ETag for the new data. Without this, the client would never know that the data it PUT is still current or not, without downloading it again, defeating the purpose of conditional GETs. – Remy Lebeau Nov 13 '15 at 00:04
  • Right, this is covered in section 6.3.5: "*For example, if a 204 status code is received in response to a PUT request and the response contains an ETag header field, then the PUT was successful and the ETag field-value contains the entity-tag for the new representation of that target resource.*" – Remy Lebeau Nov 13 '15 at 00:06
  • @vasiliyfaronov @RemyLebeau This example was very helpful and informative and I can to find that my issue that resulted in my question was one that was `cache-control` related and I thought that the issue was `condtitional get` related when it was not. This clears it up. thank you – garrettmac Nov 14 '15 at 22:27