-
Notifications
You must be signed in to change notification settings - Fork 209
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
An initial implementation of the cache API (#428)
* Initial cache implementation * Address PR comments * Address comments
- Loading branch information
Showing
16 changed files
with
626 additions
and
32 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# The Cache API | ||
The Cache API within `workerd` is _extremely_ lenient with errors, and will work with a service that doesn't fully support the expected API. This is what it _should_ behave like: | ||
|
||
## .add() | ||
Unimplemented in the runtime | ||
|
||
## .addAll() | ||
Unimplemented in the runtime | ||
|
||
## .match() | ||
`workerd` guarantees: | ||
- The method will always be `GET` | ||
- The request headers will include `Cache-Control: only-if-cached` (which Miniflare ignores) | ||
- The request headers will include `Cf-Cache-Namespaces` if this is a namespaced cache (i.e. `caches.open(...)`) | ||
`workerd` expects: | ||
- The `Cf-Cache-Status` header to be present with the value: | ||
- `MISS` if it's a cache miss, in which case the rest of the response is ignored by `workerd` | ||
- `HIT` if it's a cache hit, in which case `workerd` sends the response on to the user, including the full headers and full body | ||
|
||
## .put() | ||
`workerd` guarantees: | ||
- The method will always be `PUT`, and the cache key method will always be `GET` | ||
- The headers will be the headers of the cache key, and the URL will be the URL of the cache key | ||
- The headers will include `Cf-Cache-Namespaces` if this is a namespaced cache (i.e. `caches.open(...)`) | ||
- The body contains the serialised response for storage | ||
- The serialised response will never: | ||
- Have a `206` status code | ||
- Have a `Vary: *` header | ||
- Have a `304` status code | ||
`workerd` expects: | ||
- A `204` (success) or `413` (failure) response code. It doesn't do anything with either | ||
|
||
## .delete() | ||
`workerd` guarantees: | ||
- The method will always be `PURGE`, and the cache key method will always be `GET` | ||
- The headers will include `Cf-Cache-Namespaces` if this is a namespaced cache (i.e. `caches.open(...)`) | ||
- The header `X-Real-IP` will be set to `127.0.0.1` | ||
- The remaining headers will be the cache key headers | ||
`workerd` expects: | ||
- Status `200` on success | ||
- Status `404` on failure | ||
- Status `429` on rate limit (which will throw in the user worker) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { HeadersInit, Response } from "undici"; | ||
import { CfHeader } from "../shared/constants"; | ||
|
||
enum Status { | ||
PayloadTooLarge = 413, | ||
NotFound = 404, | ||
CacheMiss = 504, | ||
} | ||
|
||
export async function fallible<T>(promise: Promise<T>): Promise<T | Response> { | ||
try { | ||
return await promise; | ||
} catch (e) { | ||
if (e instanceof CacheError) { | ||
return e.toResponse(); | ||
} | ||
throw e; | ||
} | ||
} | ||
|
||
export class CacheError extends Error { | ||
constructor( | ||
private status: number, | ||
message: string, | ||
readonly headers: HeadersInit = [] | ||
) { | ||
super(message); | ||
this.name = "CacheError"; | ||
} | ||
|
||
toResponse() { | ||
return new Response(null, { | ||
status: this.status, | ||
headers: this.headers, | ||
}); | ||
} | ||
|
||
context(info: string) { | ||
this.message += ` (${info})`; | ||
return this; | ||
} | ||
} | ||
|
||
export class StorageFailure extends CacheError { | ||
constructor() { | ||
super(Status.PayloadTooLarge, "Cache storage failed"); | ||
} | ||
} | ||
|
||
export class PurgeFailure extends CacheError { | ||
constructor() { | ||
super(Status.NotFound, "Couldn't find asset to purge"); | ||
} | ||
} | ||
|
||
export class CacheMiss extends CacheError { | ||
constructor() { | ||
super( | ||
// workerd ignores this, but it's the correct status code | ||
Status.CacheMiss, | ||
"Asset not found in cache", | ||
[[CfHeader.CacheStatus, "MISS"]] | ||
); | ||
} | ||
} |
Oops, something went wrong.