4.2 Ranges, ETags, conditional GETs, and HEAD
Stores version every resource, which makes caching and partial reads work
cleanly. This section covers the HTTP-level features file (and, for ETags,
data) give you for free.
ETags: every resource is versioned
Every store read returns an ETag — a version stamp. For file it reflects
the stored version; for data it's a content hash of the record. You don't
compute it; you use it.
ETags drive two things: conditional reads (below) and optimistic concurrency on
writes (next) — the same If-Match mechanism the services config API uses
(Part 10), now on every store.
Conditional GET → 304
Send the ETag you already have back as If-None-Match:
GET /files/docs/big.json
If-None-Match: "v7"
→ 304 Not Modified (no body, ETag echoed)
If the resource hasn't changed, you get 304 with no body — the bytes don't
cross the wire. file and data both honor this. Paired with the revalidate
caching mode (9.4), this gives you "always fresh, near-zero bandwidth": the
client revalidates every time, but only pays for the body when it actually
changed.
Conditional writes → 412 (the conditional-write facet)
Every store also returns an ETag on PUT and honours conditional write
headers, so a read-modify-write is race-safe:
PUT /files/docs/big.json
If-Match: "v7" → 200/201 + new ETag (writes only if still "v7")
→ 412 Precondition Failed (someone else changed it)
If-None-Match: * is create-only — it writes only if the resource doesn't yet
exist (412 if it does). The check is server-side: race-free where the backing
adapter has atomic compare-and-swap, best-effort otherwise — your client does the
same thing either way (send the header, treat 412 as "re-read and retry"). The
facet is declared on the discovery surface as conditional-write.
Range requests → 206 (file only)
file declares the range facet and supports a single Range: header on GET:
GET /files/video/clip.mp4
Range: bytes=0-1048575
→ 206 Partial Content
Accept-Ranges: bytes
Content-Range: bytes 0-1048575/...
This is what lets media players seek and large downloads resume. data does not
support ranges (it's record-oriented) — check the range facet (3.4) rather than
assuming.
Last-Modified and HEAD
file also returns Last-Modified, and HEAD <child> returns all the metadata
headers without a body. Use HEAD to check existence, size, or version cheaply
before deciding to fetch.
Putting it together
A well-behaved client:
GETa resource, keep itsETag.- On the next fetch, send
If-None-Match— take the304fast path when unchanged. - For large media, use
Range(where therangefacet is present) to fetch or resume in chunks.
These are standard HTTP mechanisms; RS2's contribution is that every store
implements them uniformly, so the same client logic works across mounts. Next:
turning a file mount into a static-site / SPA host.
← Previous: 4.1 The file service · Manual home · Next: 4.3 Static-site & SPA hosting →