diff --git a/integration-tests/js-compute/fixtures/reusable-sandboxes/src/dynamic-backend.js b/integration-tests/js-compute/fixtures/reusable-sandboxes/src/dynamic-backend.js
new file mode 100644
index 0000000000..4581e8827d
--- /dev/null
+++ b/integration-tests/js-compute/fixtures/reusable-sandboxes/src/dynamic-backend.js
@@ -0,0 +1,32 @@
+///
+import { Backend } from 'fastly:backend';
+import { assert } from './assertions.js';
+import { isRunningLocally, routes } from './routes.js';
+
+routes.set('/backend/ephemeral1', async () => {
+ if (isRunningLocally()) {
+ return;
+ }
+ assert(!Backend.exists('ephemeral'));
+ new Backend({
+ name: 'ephemeral',
+ target: 'http-me.fastly.dev',
+ hostOverride: 'http-me.fastly.dev',
+ useSSL: true,
+ });
+ assert(Backend.exists('ephemeral'));
+});
+
+routes.set('/backend/ephemeral2', async () => {
+ if (isRunningLocally()) {
+ return;
+ }
+ assert(!Backend.exists('ephemeral'));
+ new Backend({
+ name: 'ephemeral',
+ target: 'http-me.fastly.dev',
+ hostOverride: 'http-me.fastly.dev',
+ useSSL: true,
+ });
+ assert(Backend.exists('ephemeral'));
+});
diff --git a/integration-tests/js-compute/fixtures/reusable-sandboxes/src/index.js b/integration-tests/js-compute/fixtures/reusable-sandboxes/src/index.js
index 00faeb4372..21dee146fd 100644
--- a/integration-tests/js-compute/fixtures/reusable-sandboxes/src/index.js
+++ b/integration-tests/js-compute/fixtures/reusable-sandboxes/src/index.js
@@ -9,6 +9,7 @@ import { setReusableSandboxOptions } from 'fastly:experimental';
setReusableSandboxOptions({ maxRequests: 9001 });
+import './dynamic-backend.js';
import './interleave.js';
addEventListener('fetch', (event) => {
diff --git a/integration-tests/js-compute/fixtures/reusable-sandboxes/tests.json b/integration-tests/js-compute/fixtures/reusable-sandboxes/tests.json
index 949059d38d..24b7b49e56 100644
--- a/integration-tests/js-compute/fixtures/reusable-sandboxes/tests.json
+++ b/integration-tests/js-compute/fixtures/reusable-sandboxes/tests.json
@@ -99,5 +99,18 @@
"QuuxName": "QuuxValue"
}
}
+ },
+
+ "session #3, request #1: GET /backend/ephemeral1": {
+ "downstream_request": {
+ "method": "GET",
+ "pathname": "/backend/ephemeral1"
+ }
+ },
+ "session #3, request #2: GET /backend/ephemeral2": {
+ "downstream_request": {
+ "method": "GET",
+ "pathname": "/backend/ephemeral2"
+ }
}
}
diff --git a/runtime/fastly/builtins/backend.cpp b/runtime/fastly/builtins/backend.cpp
index c3340f924c..fdb44fab57 100644
--- a/runtime/fastly/builtins/backend.cpp
+++ b/runtime/fastly/builtins/backend.cpp
@@ -1808,6 +1808,21 @@ void Backend::finalize(JS::GCContext *gcx, JSObject *obj) {
free(backend);
}
+bool Backend::restore_global_state(JSContext *cx) {
+ JS::Rooted props(cx, cx);
+ if (!JS_Enumerate(cx, Backend::backends, &props)) {
+ return false;
+ }
+ JS::RootedValue backend(cx);
+ for (uint32_t i = 0, len = props.length(); i < len; i++) {
+ if (!JS_GetPropertyById(cx, Backend::backends, props[i], &backend)) {
+ return false;
+ }
+ JS_DeletePropertyById(cx, Backend::backends, props[i]);
+ }
+ return true;
+}
+
bool set_default_backend_config(JSContext *cx, unsigned argc, JS::Value *vp) {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
if (!args.requireAtLeast(cx, "setDefaultDynamicBackendConfiguration", 1)) {
diff --git a/runtime/fastly/builtins/backend.h b/runtime/fastly/builtins/backend.h
index 176f04aabe..ffb0fbcd8b 100644
--- a/runtime/fastly/builtins/backend.h
+++ b/runtime/fastly/builtins/backend.h
@@ -1,13 +1,13 @@
#ifndef FASTLY_BACKEND_H
#define FASTLY_BACKEND_H
+#include "../host-api/host_api_fastly.h"
#include "builtin.h"
#include "extension-api.h"
namespace fastly::backend {
class Backend : public builtins::FinalizableBuiltinImpl {
-private:
public:
static constexpr const char *class_name = "Backend";
static const int ctor_length = 1;
@@ -23,6 +23,7 @@ class Backend : public builtins::FinalizableBuiltinImpl {
static JSString *name(JSContext *cx, JSObject *self);
static JSObject *create(JSContext *cx, JS::HandleObject request);
+ static bool restore_global_state(JSContext *cx);
// static methods
static bool exists(JSContext *cx, unsigned argc, JS::Value *vp);
diff --git a/runtime/fastly/builtins/fastly.cpp b/runtime/fastly/builtins/fastly.cpp
index 54d0233fd8..f0876db1c0 100644
--- a/runtime/fastly/builtins/fastly.cpp
+++ b/runtime/fastly/builtins/fastly.cpp
@@ -778,6 +778,14 @@ const JSPropertySpec Fastly::properties[] = {
#endif
JS_PS_END};
+bool Fastly::restore_builtin_state(JSContext *cx) {
+ Fastly::baseURL.reset();
+ Fastly::defaultBackend.reset();
+ Fastly::baseURL.init(cx);
+ Fastly::defaultBackend.init(cx);
+ return true;
+}
+
bool install(api::Engine *engine) {
ENGINE = engine;
diff --git a/runtime/fastly/builtins/fastly.h b/runtime/fastly/builtins/fastly.h
index 743d823620..0f5d4d2586 100644
--- a/runtime/fastly/builtins/fastly.h
+++ b/runtime/fastly/builtins/fastly.h
@@ -114,6 +114,7 @@ class Fastly : public builtins::BuiltinNoConstructor {
static bool allowDynamicBackends_set(JSContext *cx, unsigned argc, JS::Value *vp);
static bool inspect(JSContext *cx, unsigned argc, JS::Value *vp);
static bool setReusableSandboxOptions(JSContext *cx, unsigned argc, JS::Value *vp);
+ static bool restore_builtin_state(JSContext *cx);
};
JS::Result> convertBodyInit(JSContext *cx,
diff --git a/runtime/fastly/handler.cpp b/runtime/fastly/handler.cpp
index b5bfc6b8b1..aa1a4b9b4d 100644
--- a/runtime/fastly/handler.cpp
+++ b/runtime/fastly/handler.cpp
@@ -1,4 +1,5 @@
#include "../StarlingMonkey/builtins/web/performance.h"
+#include "./builtins/backend.h"
#include "./builtins/fastly.h"
#include "./builtins/fetch-event.h"
#include "./host-api/fastly.h"
@@ -17,12 +18,32 @@ namespace fastly::runtime {
api::Engine *ENGINE;
+int restore_builtin_state() {
+ JSContext *cx(ENGINE->cx());
+ if (!::fastly::backend::Backend::restore_global_state(cx)) {
+ if (ENGINE->debug_logging_enabled()) {
+ fprintf(stderr,
+ "Warning: Failed to restore Backend state processing next request. Exiting.\n");
+ }
+ return false;
+ }
+ if (!fastly::Fastly::restore_builtin_state(cx)) {
+ if (ENGINE->debug_logging_enabled()) {
+ fprintf(stderr,
+ "Warning: Failed to restore Backend state processing next request. Exiting.\n");
+ }
+ return false;
+ }
+ return true;
+}
+
// Install corresponds to Wizer time, so we configure the engine here
bool install(api::Engine *engine) {
#if defined(JS_GC_ZEAL) && defined(FASTLY_GC_FREQUENCY)
JS::SetGCZeal(engine->cx(), 2, FASTLY_GC_FREQUENCY);
#endif
ENGINE = engine;
+
return true;
}
@@ -100,6 +121,11 @@ bool handle_incoming(host_api::Request req) {
printf("Done. Total request processing time: %fms. Total compute time: %fms\n", diff / 1000,
total_compute / 1000);
}
+
+ if (!restore_builtin_state()) {
+ return false;
+ }
+
return true;
}