Skip to content

Commit

Permalink
Add http cache mode autogate, initialize cache control headers, repea…
Browse files Browse the repository at this point in the history
…t of #2074
  • Loading branch information
AdityaAtulTewari committed Aug 12, 2024
1 parent 8852ad7 commit 77fdc91
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 12 deletions.
13 changes: 11 additions & 2 deletions src/workerd/api/http-test-ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ async function assertFetchCacheRejectsError(cacheHeader: RequestCache,
await assert.rejects((async () => {
const header = { cache : cacheHeader};
const req: RequestInit = header;
await fetch('http://example.org', req);
await fetch('https://example.org', req);
})(), {
name: errorName,
message: errorMessage,
Expand All @@ -46,7 +46,16 @@ export const cacheMode = {
await assertFetchCacheRejectsError(cacheMode);
}
} else {
for(var cacheMode of allowedCacheModes) {
{
const req = new Request('https://example.org', {cache : "no-store"});
assert.strictEqual(req.cache, "no-store");
}
{
await fetch('https://example.org', {cache : "no-store"});
}
let failureCacheModes: Array<RequestCache> =
["default", "no-cache", "force-cache", "only-if-cached", "reload"];
for(var cacheMode of failureCacheModes) {
await assertRequestCacheThrowsError(cacheMode,
'TypeError',
'Unsupported cache mode: ' + cacheMode);
Expand Down
3 changes: 3 additions & 0 deletions src/workerd/api/http-test-ts.ts-wd-test
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ const unitTests :Workerd.Config = (
)
),
],
autogates = [
"workerd-autogate-http-request-cache",
]
);
16 changes: 9 additions & 7 deletions src/workerd/api/http-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ async function assertFetchCacheRejectsError(cacheHeader,
errorName = 'Error',
errorMessage = "The 'cache' field on 'RequestInitializerDict' is not implemented.") {
await assert.rejects((async () => {
await fetch('http://example.org', { cache: cacheHeader });
await fetch('https://example.org', { cache: cacheHeader });
})(), {
name: errorName,
message: errorMessage,
Expand All @@ -269,9 +269,14 @@ export const cacheMode = {
await assertFetchCacheRejectsError('no-transform');
await assertFetchCacheRejectsError('unsupported');
} else {
await assertRequestCacheThrowsError('no-store',
'TypeError',
"Unsupported cache mode: no-store");
{
const req = new Request('https://example.org', {cache : "no-store"});
assert.strictEqual(req.cache, "no-store");
}
{
const req = await fetch('https://example.org', { cache: "no-store"});
console.log(req);
}
await assertRequestCacheThrowsError('no-cache',
'TypeError',
"Unsupported cache mode: no-cache");
Expand All @@ -281,9 +286,6 @@ export const cacheMode = {
await assertRequestCacheThrowsError('unsupported',
'TypeError',
"Unsupported cache mode: unsupported");
await assertFetchCacheRejectsError('no-store',
'TypeError',
"Unsupported cache mode: no-store");
await assertFetchCacheRejectsError('no-cache',
'TypeError',
"Unsupported cache mode: no-cache");
Expand Down
3 changes: 3 additions & 0 deletions src/workerd/api/http-test.wd-test
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@ const unitTests :Workerd.Config = (
compatibilityFlags = ["nodejs_compat", "service_binding_extra_handlers", "cache_option_enabled"],
))
],
autogates = [
"workerd-autogate-http-request-cache",
]
);
43 changes: 41 additions & 2 deletions src/workerd/api/http.c++
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <kj/parse/char.h>
#include <workerd/io/features.h>
#include <workerd/util/http-util.h>
#include <workerd/util/autogate.h>
#include <workerd/util/mimetype.h>
#include <workerd/util/stream-utils.h>
#include <workerd/util/thread-scopes.h>
Expand Down Expand Up @@ -1195,6 +1196,22 @@ void Request::shallowCopyHeadersTo(kj::HttpHeaders& out) {
}

kj::Maybe<kj::String> Request::serializeCfBlobJson(jsg::Lock& js) {
if(cacheMode != CacheMode::NONE &&
util::Autogate::isEnabled(util::AutogateKey::HTTP_REQUEST_CACHE)) {
// Works because of the fact there are only two non-none cache modes
auto ttl = (CacheMode::NOSTORE == cacheMode) ? -1 : 0;
CfProperty clone;
KJ_IF_SOME(_, cf.get(js)) {
clone = cf.deepClone(js);
} else {
clone = CfProperty(js, js.obj());
}
auto obj = KJ_ASSERT_NONNULL(clone.get(js));

obj.set(js, "cacheTtl", js.num(ttl));
obj.set(js, "cf-cache-level", js.str("byc"_kjc));
return clone.serialize(js);
}
return cf.serialize(js);
}

Expand All @@ -1204,7 +1221,10 @@ void RequestInitializerDict::validate(jsg::Lock& js) {
JSG_REQUIRE(FeatureFlags::get(js).getCacheOptionEnabled(), Error, kj::str(
"The 'cache' field on 'RequestInitializerDict' is not implemented."));

JSG_FAIL_REQUIRE(TypeError, kj::str("Unsupported cache mode: ", c));
JSG_REQUIRE(util::Autogate::isEnabled(util::AutogateKey::HTTP_REQUEST_CACHE), TypeError, kj::str("Unsupported cache mode: ", c));
// Validate that the cache type is valid
auto cacheMode = getCacheModeFromName(c);
JSG_REQUIRE(cacheMode != Request::CacheMode::NOCACHE, TypeError, kj::str("Unsupported cache mode: ", c));
}
}

Expand Down Expand Up @@ -1816,7 +1836,26 @@ jsg::Promise<jsg::Ref<Response>> fetchImplNoOutputLock(
// If the jsRequest has a CacheMode, we need to handle that here.
// Currently, the only cache mode we support is undefined, but we will soon support
// no-cache and no-store. These additional modes will be hidden behind an autogate.
KJ_ASSERT(jsRequest->getCacheMode() == Request::CacheMode::NONE);
auto headerIds = ioContext.getHeaderIds();
const auto cacheMode = jsRequest->getCacheMode();
if(util::Autogate::isEnabled(util::AutogateKey::HTTP_REQUEST_CACHE)) {
switch(cacheMode) {
case Request::CacheMode::NOSTORE:
case Request::CacheMode::NOCACHE:
if(headers.get(headerIds.cacheControl) == kj::none) {
headers.set(headerIds.cacheControl, "no-cache");
}
if(headers.get(headerIds.pragma) == kj::none) {
headers.set(headerIds.pragma, "no-cache");
}
case Request::CacheMode::NONE:
break;
default:
KJ_UNREACHABLE;
}
} else {
KJ_ASSERT(cacheMode == Request::CacheMode::NONE);
}

kj::String url = uriEncodeControlChars(
urlList.back().toString(kj::Url::HTTP_PROXY_REQUEST).asBytes());
Expand Down
1 change: 1 addition & 0 deletions src/workerd/io/io-thread-context.c++
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ ThreadContext::HeaderIdBundle::HeaderIdBundle(kj::HttpHeaderTable::Builder& buil
contentEncoding(builder.add("Content-Encoding")),
cfCacheStatus(builder.add("CF-Cache-Status")),
cacheControl(builder.add("Cache-Control")),
pragma(builder.add("Pragma")),
cfCacheNamespace(builder.add("CF-Cache-Namespace")),
cfKvMetadata(builder.add("CF-KV-Metadata")),
cfR2ErrorHeader(builder.add("CF-R2-Error")),
Expand Down
1 change: 1 addition & 0 deletions src/workerd/io/io-thread-context.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class ThreadContext {
const kj::HttpHeaderId contentEncoding;
const kj::HttpHeaderId cfCacheStatus; // used by cache API implementation
const kj::HttpHeaderId cacheControl;
const kj::HttpHeaderId pragma;
const kj::HttpHeaderId cfCacheNamespace; // used by Cache binding implementation
const kj::HttpHeaderId cfKvMetadata; // used by KV binding implementation
const kj::HttpHeaderId cfR2ErrorHeader; // used by R2 binding implementation
Expand Down
4 changes: 3 additions & 1 deletion src/workerd/server/server-test.c++
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,9 @@ public:
}
}),
fakeDate(kj::UNIX_EPOCH),
mockNetwork(*this, {}, {}) {}
mockNetwork(*this, {}, {}) {
//workerd::util::Autogate::initAutogate({});
}

~TestServer() noexcept(false) {
for (auto& subq: subrequests) {
Expand Down
2 changes: 2 additions & 0 deletions src/workerd/util/autogate.c++
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ kj::StringPtr KJ_STRINGIFY(AutogateKey key) {
return "test-workerd"_kj;
case AutogateKey::PYODIDE_LOAD_EXTERNAL:
return "pyodide-load-external"_kj;
case workerd::util::AutogateKey::HTTP_REQUEST_CACHE:
return "http-request-cache"_kj;
case AutogateKey::NumOfKeys:
KJ_FAIL_ASSERT("NumOfKeys should not be used in getName");
}
Expand Down
1 change: 1 addition & 0 deletions src/workerd/util/autogate.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace workerd::util {
enum class AutogateKey {
TEST_WORKERD,
PYODIDE_LOAD_EXTERNAL,
HTTP_REQUEST_CACHE,
NumOfKeys // Reserved for iteration.
};

Expand Down

0 comments on commit 77fdc91

Please sign in to comment.