Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTP Cache API #1051

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
857 changes: 857 additions & 0 deletions integration-tests/js-compute/fixtures/app/src/http-cache.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions integration-tests/js-compute/fixtures/app/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import './fastly-global.js';
import './fetch-errors.js';
import './geoip.js';
import './headers.js';
import './http-cache.js';
import './include-bytes.js';
import './logger.js';
import './manual-framing-headers.js';
Expand Down
27 changes: 26 additions & 1 deletion integration-tests/js-compute/fixtures/app/tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -1332,7 +1332,32 @@
"headers": [["cuStom", "test"]]
}
},

"GET /http-cache/invalid-properties": {},
"GET /http-cache/invalid-transform": {},
"GET /http-cache/hook-errors": {},
"GET /http-cache/readonly-properties": {},
"GET /http-cache/property-errors": {},
"GET /http-cache/property-access-errors": {},
"GET /http-cache/after-send-edge-cache": {},
"GET /http-cache/after-send-browser-cache": {},
"GET /http-cache/before-send": {},
"GET /http-cache/request-mutation": {},
"GET /http-cache/request-mutation-order": {},
"GET /http-cache/response-mutations": {},
"GET /http-cache/cacheability": {},
"GET /http-cache/stale-responses": {},
"GET /http-cache/body-transform": {},
"GET /http-cache/body-transform-error": {},
"GET /http-cache/body-transform-invalid-chunk": {},
"GET /http-cache/body-transform-write-after-close": {},
"GET /http-cache/body-transform-cancel": {},
"GET /http-cache/body-transform-backpressure": {},
"GET /http-cache/request-collapsing-options": {},
"GET /http-cache/request-collapsing-uncacheable": {},
"GET /http-cache/request-collapsing-vary": {},
"GET /http-cache/concurrent-modifications": {},
"GET /http-cache/concurrent-transforms": {},
"GET /http-cache/revalidation-updates": {},
"GET /FastlyBody/interface": {
"environments": ["compute"],
"downstream_response": {
Expand Down
92 changes: 92 additions & 0 deletions runtime/fastly/builtins/cache-override.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,26 @@ void CacheOverride::set_pci(JSObject *self, bool pci) {
JS::SetReservedSlot(self, CacheOverride::Slots::PCI, JS::BooleanValue(pci));
}

JS::Value CacheOverride::beforeSend(JSObject *self) {
MOZ_ASSERT(is_instance(self));
return JS::GetReservedSlot(self, Slots::BeforeSend);
}

void CacheOverride::set_beforeSend(JSObject *self, JSObject *fn) {
MOZ_ASSERT(is_instance(self));
JS::SetReservedSlot(self, Slots::BeforeSend, JS::ObjectValue(*fn));
}

JS::Value CacheOverride::afterSend(JSObject *self) {
MOZ_ASSERT(is_instance(self));
return JS::GetReservedSlot(self, Slots::AfterSend);
}

void CacheOverride::set_afterSend(JSObject *self, JSObject *fn) {
MOZ_ASSERT(is_instance(self));
JS::SetReservedSlot(self, Slots::AfterSend, JS::ObjectValue(*fn));
}

host_api::CacheOverrideTag CacheOverride::abi_tag(JSObject *self) {
host_api::CacheOverrideTag tag;

Expand Down Expand Up @@ -285,6 +305,64 @@ bool CacheOverride::pci_set(JSContext *cx, JS::HandleObject self, JS::HandleValu
return true;
}

bool CacheOverride::before_send_get(JSContext *cx, JS::HandleObject self,
JS::MutableHandleValue rval) {
if (self == proto_obj) {
return api::throw_error(cx, api::Errors::WrongReceiver, "beforeSend get", "CacheOverride");
}
rval.set(CacheOverride::beforeSend(self));
return true;
}

bool CacheOverride::before_send_set(JSContext *cx, JS::HandleObject self, JS::HandleValue val,
JS::MutableHandleValue rval) {
if (self == proto_obj) {
return api::throw_error(cx, api::Errors::WrongReceiver, "beforeSend set", "CacheOverride");
}
if (!CacheOverride::ensure_override(cx, self, "beforeSend"))
return false;
if (val.isUndefined()) {
JS::SetReservedSlot(self, Slots::BeforeSend, val);
} else if (!val.isObject() || !JS::IsCallable(&val.toObject())) {
JS_ReportErrorUTF8(cx, "CacheOverride: beforeSend must be a function");
return false;
} else {
CacheOverride::set_beforeSend(self, &val.toObject());
}

rval.set(CacheOverride::beforeSend(self));
return true;
}

bool CacheOverride::after_send_get(JSContext *cx, JS::HandleObject self,
JS::MutableHandleValue rval) {
if (self == proto_obj) {
return api::throw_error(cx, api::Errors::WrongReceiver, "afterSend get", "CacheOverride");
}
rval.set(CacheOverride::afterSend(self));
return true;
}

bool CacheOverride::after_send_set(JSContext *cx, JS::HandleObject self, JS::HandleValue val,
JS::MutableHandleValue rval) {
if (self == proto_obj) {
return api::throw_error(cx, api::Errors::WrongReceiver, "afterSend set", "CacheOverride");
}
if (!CacheOverride::ensure_override(cx, self, "afterSend"))
return false;
if (val.isUndefined()) {
JS::SetReservedSlot(self, Slots::AfterSend, val);
} else if (!val.isObject() || !JS::IsCallable(&val.toObject())) {
JS_ReportErrorUTF8(cx, "CacheOverride: afterSend must be a function");
return false;
} else {
CacheOverride::set_afterSend(self, &val.toObject());
}

rval.set(CacheOverride::afterSend(self));
return true;
}

template <auto accessor_fn>
bool CacheOverride::accessor_get(JSContext *cx, unsigned argc, JS::Value *vp) {
METHOD_HEADER(0)
Expand All @@ -308,6 +386,10 @@ const JSPropertySpec CacheOverride::properties[] = {
JS_PSGS("surrogateKey", accessor_get<surrogate_key_get>, accessor_set<surrogate_key_set>,
JSPROP_ENUMERATE),
JS_PSGS("pci", accessor_get<pci_get>, accessor_set<pci_set>, JSPROP_ENUMERATE),
JS_PSGS("beforeSend", accessor_get<before_send_get>, accessor_set<before_send_set>,
JSPROP_ENUMERATE),
JS_PSGS("afterSend", accessor_get<after_send_get>, accessor_set<after_send_set>,
JSPROP_ENUMERATE),
JS_STRING_SYM_PS(toStringTag, "CacheOverride", JSPROP_READONLY),
JS_PS_END};

Expand Down Expand Up @@ -345,6 +427,16 @@ bool CacheOverride::constructor(JSContext *cx, unsigned argc, JS::Value *vp) {
if (!JS_GetProperty(cx, override_init, "pci", &val) || !pci_set(cx, self, val, &val)) {
return false;
}

if (!JS_GetProperty(cx, override_init, "beforeSend", &val) ||
!before_send_set(cx, self, val, &val)) {
return false;
}

if (!JS_GetProperty(cx, override_init, "afterSend", &val) ||
!after_send_set(cx, self, val, &val)) {
return false;
}
}

args.rval().setObject(*self);
Expand Down
15 changes: 14 additions & 1 deletion runtime/fastly/builtins/cache-override.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ class CacheOverride : public builtins::BuiltinImpl<CacheOverride> {
//
// `PCI` is interpreted as a boolean, and a flag gets set in the hostcall's
// `tag` parameter if `PCI` is true.
enum Slots { Mode, TTL, SWR, SurrogateKey, PCI, Count };
//
// `BeforeSend` and `AfterSend` are function callbacks that can be set
// to execute before and after sending the request.
enum Slots { Mode, TTL, SWR, SurrogateKey, PCI, BeforeSend, AfterSend, Count };

enum class CacheOverrideMode { None, Pass, Override };

Expand All @@ -44,6 +47,10 @@ class CacheOverride : public builtins::BuiltinImpl<CacheOverride> {
static JSObject *clone(JSContext *cx, JS::HandleObject self);
static JS::Value pci(JSObject *self);
static void set_pci(JSObject *self, bool pci);
static JS::Value beforeSend(JSObject *self);
static void set_beforeSend(JSObject *self, JSObject *fn);
static JS::Value afterSend(JSObject *self);
static void set_afterSend(JSObject *self, JSObject *fn);
static CacheOverrideMode mode(JSObject *self);
static void set_mode(JSObject *self, CacheOverride::CacheOverrideMode mode);
static bool mode_get(JSContext *cx, JS::HandleObject self, JS::MutableHandleValue rval);
Expand All @@ -61,6 +68,12 @@ class CacheOverride : public builtins::BuiltinImpl<CacheOverride> {
static bool pci_get(JSContext *cx, JS::HandleObject self, JS::MutableHandleValue rval);
static bool pci_set(JSContext *cx, JS::HandleObject self, JS::HandleValue val,
JS::MutableHandleValue rval);
static bool before_send_get(JSContext *cx, JS::HandleObject self, JS::MutableHandleValue rval);
static bool before_send_set(JSContext *cx, JS::HandleObject self, JS::HandleValue val,
JS::MutableHandleValue rval);
static bool after_send_get(JSContext *cx, JS::HandleObject self, JS::MutableHandleValue rval);
static bool after_send_set(JSContext *cx, JS::HandleObject self, JS::HandleValue val,
JS::MutableHandleValue rval);
template <auto accessor_fn> static bool accessor_get(JSContext *cx, unsigned argc, JS::Value *vp);
template <auto accessor_fn> static bool accessor_set(JSContext *cx, unsigned argc, JS::Value *vp);

Expand Down
Loading
Loading