You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
CAS: let browsers cache downloads (Authorization header, ETag, 304) #3549
A browser cannot keep a copy of a CAS download. Each open of the same artifact downloads it again in full, and the CAS copies the object from the storage backend each time. For large artifacts, such as AI coding session transcripts, this cost is high.
There are two causes:
The download URL holds a new token in the query (?t=<token>) on each call. The browser cache uses the URL as its key, so it never finds a match.
The download response has no ETag and no Cache-Control header. The browser cannot check its copy with the CAS.
Proposal
Change the CAS download endpoint (/download/<digest>):
Accept the token in an Authorization: Bearer header. Today the endpoint reads the token only from the t query parameter. It must accept the token from the header or from the query. Clients that use the query token keep working.
Send cache headers on the download response:
ETag: "<digest>", for example "sha256:abc...". Put the value in quotes. Without the quotes, browsers can ignore the ETag. The digest identifies the bytes, so the ETag never goes stale.
Cache-Control: private, no-cache. The browser keeps the copy, but it checks with the CAS before each use. Shared proxies do not store it.
Answer a conditional request with 304. When the request has If-None-Match with the digest, the CAS does these steps:
It checks the token.
It checks that the object exists in the storage backend, with a metadata call. It does not copy the object.
It answers 304 Not Modified with no content. The 304 also has the ETag and Cache-Control headers.
The CORS preflight already allows Authorization and sends Access-Control-Max-Age: 86400, so no CORS change is necessary.
sequenceDiagram
participant B as Browser
participant CAS
participant S as Storage backend
alt First open
B->>CAS: GET /download/digest (Authorization)
CAS->>S: Copy object, check digest
CAS-->>B: 200, ETag, Cache-Control private no-cache
else Next open
B->>CAS: GET /download/digest (Authorization, If-None-Match)
CAS->>CAS: Check token
CAS->>S: Check that the object exists
CAS-->>B: 304 Not Modified
end
Loading
Acceptance criteria
A request with a valid token in Authorization: Bearer and no t query parameter downloads the artifact.
A request with the t query parameter works as before.
The 200 response has ETag: "<digest>" and Cache-Control: private, no-cache.
A request with a valid token and a matching If-None-Match gets a 304 with no content. The CAS does not copy the object from the storage backend.
A request with a matching If-None-Match and a bad or missing token gets a 401.
A request with a matching If-None-Match for an object that is not in the storage backend does not get a 304.
Problem
A browser cannot keep a copy of a CAS download. Each open of the same artifact downloads it again in full, and the CAS copies the object from the storage backend each time. For large artifacts, such as AI coding session transcripts, this cost is high.
There are two causes:
?t=<token>) on each call. The browser cache uses the URL as its key, so it never finds a match.ETagand noCache-Controlheader. The browser cannot check its copy with the CAS.Proposal
Change the CAS download endpoint (
/download/<digest>):Authorization: Bearerheader. Today the endpoint reads the token only from thetquery parameter. It must accept the token from the header or from the query. Clients that use the query token keep working.ETag: "<digest>", for example"sha256:abc...". Put the value in quotes. Without the quotes, browsers can ignore the ETag. The digest identifies the bytes, so the ETag never goes stale.Cache-Control: private, no-cache. The browser keeps the copy, but it checks with the CAS before each use. Shared proxies do not store it.If-None-Matchwith the digest, the CAS does these steps:304 Not Modifiedwith no content. The 304 also has theETagandCache-Controlheaders.The CORS preflight already allows
Authorizationand sendsAccess-Control-Max-Age: 86400, so no CORS change is necessary.sequenceDiagram participant B as Browser participant CAS participant S as Storage backend alt First open B->>CAS: GET /download/digest (Authorization) CAS->>S: Copy object, check digest CAS-->>B: 200, ETag, Cache-Control private no-cache else Next open B->>CAS: GET /download/digest (Authorization, If-None-Match) CAS->>CAS: Check token CAS->>S: Check that the object exists CAS-->>B: 304 Not Modified endAcceptance criteria
Authorization: Bearerand notquery parameter downloads the artifact.tquery parameter works as before.ETag: "<digest>"andCache-Control: private, no-cache.If-None-Matchgets a 304 with no content. The CAS does not copy the object from the storage backend.If-None-Matchand a bad or missing token gets a 401.If-None-Matchfor an object that is not in the storage backend does not get a 304.