Skip to content

Commit f604859

Browse files
committed
Complete implementation
1 parent 26562cb commit f604859

File tree

13 files changed

+666
-215
lines changed

13 files changed

+666
-215
lines changed

integration-tests/js-compute/fixtures/app/src/crypto.js

Lines changed: 176 additions & 127 deletions
Large diffs are not rendered by default.

integration-tests/js-compute/fixtures/app/src/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { routes } from './routes.js';
55
import { env } from 'fastly:env';
66
import { enableDebugLogging } from 'fastly:experimental';
7+
import { setReusableSandboxOptions } from 'fastly:experimental';
78

89
import './async-select.js';
910
import './btoa.js';
@@ -51,10 +52,18 @@ import './secret-store.js';
5152
import './security.js';
5253
import './server.js';
5354
import './shielding.js';
55+
import './stale-if-error.js';
5456
import './tee.js';
5557
import './timers.js';
5658
import './urlsearchparams.js';
5759

60+
setReusableSandboxOptions({
61+
maxRequests: 10000, // Default is 1, 0 means unlimited
62+
//betweenRequestTimeoutMs: 10000000, // 100ms, default is 0 (no timeout)
63+
//maxMemoryMiB: 128, // 128MiB, default is 0 (no limit)
64+
//sandboxTimeoutMs: 1, // 1000ms, default is 0 (no timeout)
65+
});
66+
5867
addEventListener('fetch', (event) => {
5968
event.respondWith(app(event));
6069
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { assert, assertThrows } from './assertions.js';
2+
import { routes } from './routes.js';
3+
4+
routes.set('/stale-if-error/use-stale-if-error', async (event) => {
5+
fetch(event.request, { backend: 'httpme' });
6+
});

runtime/fastly/builtins/fastly.cpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ JS::PersistentRooted<JSString *> Fastly::defaultBackend;
5454
bool allowDynamicBackendsCalled = false;
5555
bool Fastly::allowDynamicBackends = true;
5656
bool ENABLE_EXPERIMENTAL_HTTP_CACHE = false;
57+
ReusableSandboxOptions Fastly::reusableSandboxOptions{};
5758

5859
bool Fastly::dump(JSContext *cx, unsigned argc, JS::Value *vp) {
5960
JS::CallArgs args = CallArgsFromVp(argc, vp);
@@ -682,6 +683,86 @@ bool Fastly::allowDynamicBackends_set(JSContext *cx, unsigned argc, JS::Value *v
682683
return true;
683684
}
684685

686+
bool Fastly::setReusableSandboxOptions(JSContext *cx, unsigned argc, JS::Value *vp) {
687+
JS::CallArgs args = CallArgsFromVp(argc, vp);
688+
if (Fastly::reusableSandboxOptions.frozen()) {
689+
JS_ReportErrorUTF8(cx, "Reusable sandbox options can only be set at initialization time");
690+
return false;
691+
}
692+
if (!args.requireAtLeast(cx, "fastly.setReusableSandboxOptions", 1)) {
693+
return false;
694+
}
695+
JS::HandleValue options_value = args.get(0);
696+
if (!options_value.isObject()) {
697+
JS_ReportErrorUTF8(cx, "Options parameter must be an object");
698+
return false;
699+
}
700+
RootedObject options_obj(cx, &options_value.toObject());
701+
702+
auto get_non_negative_int = [cx, &options_obj](const char* prop_name, bool* defined, int32_t* out_value) -> bool {
703+
RootedValue val(cx);
704+
if (!JS_GetProperty(cx, options_obj, prop_name, &val)) {
705+
return false;
706+
}
707+
if (val.isUndefined()) {
708+
*defined = false;
709+
return true;
710+
}
711+
*defined = true;
712+
if (!val.isInt32()) {
713+
JS_ReportErrorUTF8(cx, "%s option must be an integer", prop_name);
714+
return false;
715+
}
716+
int32_t int_val = val.toInt32();
717+
if (int_val < 0) {
718+
JS_ReportErrorUTF8(cx, "%s option must be a non-negative integer", prop_name);
719+
return false;
720+
}
721+
*out_value = int_val;
722+
return true;
723+
};
724+
725+
bool defined = false;
726+
int32_t max_requests;
727+
if (get_non_negative_int("maxRequests", &defined, &max_requests)) {
728+
if (defined) {
729+
Fastly::reusableSandboxOptions.set_max_requests(max_requests);
730+
}
731+
} else {
732+
return false;
733+
}
734+
735+
int32_t between_request_timeout_ms;
736+
if (get_non_negative_int("betweenRequestTimeoutMs", &defined, &between_request_timeout_ms)) {
737+
if (defined) {
738+
Fastly::reusableSandboxOptions.set_between_request_timeout(std::chrono::milliseconds(between_request_timeout_ms));
739+
}
740+
} else {
741+
return false;
742+
}
743+
744+
int32_t max_memory_mib;
745+
if (get_non_negative_int("maxMemoryMiB", &defined, &max_memory_mib)) {
746+
if (defined) {
747+
Fastly::reusableSandboxOptions.set_max_memory_mib(max_memory_mib);
748+
}
749+
} else {
750+
return false;
751+
}
752+
753+
int32_t sandbox_timeout_ms;
754+
if (get_non_negative_int("sandboxTimeoutMs", &defined, &sandbox_timeout_ms)) {
755+
if (defined) {
756+
Fastly::reusableSandboxOptions.set_sandbox_timeout(std::chrono::milliseconds(sandbox_timeout_ms));
757+
}
758+
} else {
759+
return false;
760+
}
761+
762+
args.rval().setUndefined();
763+
return true;
764+
}
765+
685766
const JSPropertySpec Fastly::properties[] = {
686767
JS_PSG("env", env_get, JSPROP_ENUMERATE),
687768
JS_PSGS("baseURL", baseURL_get, baseURL_set, JSPROP_ENUMERATE),
@@ -731,6 +812,7 @@ bool install(api::Engine *engine) {
731812
JS_FN("includeBytes", Fastly::includeBytes, 1, JSPROP_ENUMERATE),
732813
JS_FN("createFanoutHandoff", Fastly::createFanoutHandoff, 2, JSPROP_ENUMERATE),
733814
JS_FN("createWebsocketHandoff", Fastly::createWebsocketHandoff, 2, JSPROP_ENUMERATE),
815+
JS_FN("setReusableSandboxOptions", Fastly::setReusableSandboxOptions, 1, JSPROP_ENUMERATE),
734816
ENABLE_EXPERIMENTAL_HIGH_RESOLUTION_TIME_METHODS ? nowfn : end,
735817
end};
736818

@@ -830,6 +912,17 @@ bool install(api::Engine *engine) {
830912
allow_dynamic_backends_val)) {
831913
return false;
832914
}
915+
auto set_reusable_sandbox_options =
916+
JS_NewFunction(engine->cx(), &Fastly::setReusableSandboxOptions, 1, 0,
917+
"setReusableSandboxOptions");
918+
RootedObject set_reusable_sandbox_options_obj(engine->cx(),
919+
JS_GetFunctionObject(set_reusable_sandbox_options));
920+
RootedValue set_reusable_sandbox_options_val(
921+
engine->cx(), ObjectValue(*set_reusable_sandbox_options_obj));
922+
if (!JS_SetProperty(engine->cx(), experimental, "setReusableSandboxOptions",
923+
set_reusable_sandbox_options_val)) {
924+
return false;
925+
}
833926
RootedString version_str(
834927
engine->cx(), JS_NewStringCopyN(engine->cx(), RUNTIME_VERSION, strlen(RUNTIME_VERSION)));
835928
RootedValue version_str_val(engine->cx(), StringValue(version_str));

runtime/fastly/builtins/fastly.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,55 @@ class Env : public builtins::BuiltinNoConstructor<Env> {
2929
static JSObject *create(JSContext *cx);
3030
};
3131

32+
class ReusableSandboxOptions {
33+
public:
34+
ReusableSandboxOptions() = default;
35+
ReusableSandboxOptions(const ReusableSandboxOptions &) = delete;
36+
ReusableSandboxOptions &operator=(const ReusableSandboxOptions &) = delete;
37+
38+
std::optional<uint32_t> max_requests() const { return max_requests_; }
39+
bool set_max_requests(uint32_t max_requests) {
40+
if (frozen_) {
41+
return false;
42+
}
43+
max_requests_ = max_requests;
44+
return true;
45+
}
46+
std::optional<std::chrono::milliseconds> between_request_timeout() const { return between_request_timeout_; }
47+
bool set_between_request_timeout(std::chrono::milliseconds timeout) {
48+
if (frozen_) {
49+
return false;
50+
}
51+
between_request_timeout_ = timeout;
52+
return true;
53+
}
54+
std::optional<uint32_t> max_memory_mib() const { return max_memory_mib_; }
55+
bool set_max_memory_mib(uint32_t max_memory_mib) {
56+
if (frozen_) {
57+
return false;
58+
}
59+
max_memory_mib_ = max_memory_mib;
60+
return true;
61+
}
62+
std::optional<std::chrono::milliseconds> sandbox_timeout() const { return sandbox_timeout_; }
63+
bool set_sandbox_timeout(std::chrono::milliseconds timeout) {
64+
if (frozen_) {
65+
return false;
66+
}
67+
sandbox_timeout_ = timeout;
68+
return true;
69+
}
70+
bool frozen() const { return frozen_; }
71+
void freeze() { frozen_ = true; }
72+
73+
private:
74+
bool frozen_ = false;
75+
std::optional<uint32_t> max_requests_;
76+
std::optional<std::chrono::milliseconds> between_request_timeout_;
77+
std::optional<uint32_t> max_memory_mib_;
78+
std::optional<std::chrono::milliseconds> sandbox_timeout_;
79+
};
80+
3281
class Fastly : public builtins::BuiltinNoConstructor<Fastly> {
3382
private:
3483
static bool log(JSContext *cx, unsigned argc, JS::Value *vp);
@@ -41,6 +90,7 @@ class Fastly : public builtins::BuiltinNoConstructor<Fastly> {
4190
static JS::PersistentRooted<JSString *> defaultBackend;
4291
static bool allowDynamicBackends;
4392
static host_api::BackendConfig defaultDynamicBackendConfig;
93+
static ReusableSandboxOptions reusableSandboxOptions;
4494

4595
static const JSPropertySpec properties[];
4696

@@ -61,6 +111,7 @@ class Fastly : public builtins::BuiltinNoConstructor<Fastly> {
61111
static bool allowDynamicBackends_get(JSContext *cx, unsigned argc, JS::Value *vp);
62112
static bool allowDynamicBackends_set(JSContext *cx, unsigned argc, JS::Value *vp);
63113
static bool inspect(JSContext *cx, unsigned argc, JS::Value *vp);
114+
static bool setReusableSandboxOptions(JSContext *cx, unsigned argc, JS::Value *vp);
64115
};
65116

66117
JS::Result<std::tuple<JS::UniqueChars, size_t>> convertBodyInit(JSContext *cx,

0 commit comments

Comments
 (0)