Skip to content
Merged
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
79 changes: 68 additions & 11 deletions src/workerd/api/global-scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <workerd/api/fuzzilli.h>
#endif

#include <workerd/io/features.h>
#include <workerd/io/io-timers.h>
#include <workerd/jsg/jsg.h>

Expand Down Expand Up @@ -193,11 +194,24 @@ class TestController: public jsg::Object {
class ExecutionContext: public jsg::Object {
public:
ExecutionContext(jsg::Lock& js, jsg::JsValue exports)
: ExecutionContext(js, kj::mv(exports), /*props=*/js.obj(), /*versionInfo=*/kj::none) {}

ExecutionContext(jsg::Lock& js,
jsg::JsValue exports,
jsg::JsValue props,
kj::Maybe<Worker::VersionInfo> versionInfo)
: exports(js, exports),
props(js, js.obj()) {}
ExecutionContext(jsg::Lock& js, jsg::JsValue exports, jsg::JsValue props)
: exports(js, exports),
props(js, props) {}
props(js, props) {
auto featureFlags = FeatureFlags::get(js);
if (featureFlags.getEnableVersionApi()) {
version = kj::mv(versionInfo).map([&js, &featureFlags](auto v) {
return jsg::JsRef{js,
Comment thread
sohpeach marked this conversation as resolved.
v.toJs(js,
featureFlags.getEnableCtxVersionMetadata() ? PopulateVersionInfoMetadata::YES
: PopulateVersionInfoMetadata::NO)};
});
}
}

void waitUntil(kj::Promise<void> promise);
void passThroughOnException();
Expand All @@ -214,13 +228,27 @@ class ExecutionContext: public jsg::Object {
return props.getHandle(js);
}

jsg::JsValue getVersion(jsg::Lock& js) {
// TODO(soon): We should be able to assert for `version != kj::none` in the constructor when the
// `enable_version_api` compat flag is enabled, but currently dynamic workers and "reusable
// `ctx` object instantiation" do not pass information to populate `ctx.version` with,
// `ctx.version` is currently optional so we can return undefined for now.
KJ_IF_SOME(someVersion, version) {
return someVersion.getHandle(js);
}
return js.undefined();
}

JSG_RESOURCE_TYPE(ExecutionContext, CompatibilityFlags::Reader flags) {
JSG_METHOD(waitUntil);
JSG_METHOD(passThroughOnException);
if (flags.getEnableCtxExports()) {
JSG_LAZY_INSTANCE_PROPERTY(exports, getExports);
}
JSG_LAZY_INSTANCE_PROPERTY(props, getProps);
if (flags.getEnableVersionApi()) {
JSG_LAZY_INSTANCE_PROPERTY(version, getVersion);
}

if (flags.getWorkerdExperimental()) {
// TODO(soon): Before making this generally available we need to:
Expand All @@ -236,28 +264,57 @@ class ExecutionContext: public jsg::Object {
JSG_METHOD(abort);
}

// TODO(soon): This is getting unwieldy.
if (flags.getEnableCtxExports()) {
JSG_TS_OVERRIDE(<Props = unknown> {
readonly props: Props;
readonly exports: Cloudflare.Exports;
});
if (flags.getEnableVersionApi()) {
JSG_TS_OVERRIDE(<Props = unknown> {
readonly props: Props;
readonly exports: Cloudflare.Exports;
readonly version?: {
readonly metadata?: { readonly id: string; };
readonly cohort?: string;
readonly key?: string;
readonly override?: string;
};
});
} else {
JSG_TS_OVERRIDE(<Props = unknown> {
readonly props: Props;
readonly exports: Cloudflare.Exports;
});
}
} else {
JSG_TS_OVERRIDE(<Props = unknown> {
readonly props: Props;
});
if (flags.getEnableVersionApi()) {
JSG_TS_OVERRIDE(<Props = unknown> {
readonly props: Props;
readonly version?: {
readonly metadata?: { readonly id: string; };
readonly cohort?: string;
readonly key?: string;
readonly override?: string;
};
});
} else {
JSG_TS_OVERRIDE(<Props = unknown> {
readonly props: Props;
});
}
}
Comment thread
sohpeach marked this conversation as resolved.
}

void visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
tracker.trackField("props", props);
tracker.trackField("version", version);
}

private:
jsg::JsRef<jsg::JsValue> exports;
jsg::JsRef<jsg::JsValue> props;
kj::Maybe<jsg::JsRef<jsg::JsValue>> version;

void visitForGc(jsg::GcVisitor& visitor) {
visitor.visit(props);
visitor.visit(version);
Comment thread
sohpeach marked this conversation as resolved.
}
};

Expand Down
15 changes: 10 additions & 5 deletions src/workerd/api/hibernatable-web-socket.c++
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ jsg::Ref<WebSocket> HibernatableWebSocketEvent::claimWebSocket(
kj::Promise<WorkerInterface::CustomEvent::Result> HibernatableWebSocketCustomEvent::run(
kj::Own<IoContext_IncomingRequest> incomingRequest,
kj::Maybe<kj::StringPtr> entrypointName,
kj::Maybe<Worker::VersionInfo> versionInfo,
Frankenvalue props,
kj::TaskSet& waitUntilTasks) {
// Mark the request as delivered because we're about to run some JS.
Expand All @@ -82,29 +83,33 @@ kj::Promise<WorkerInterface::CustomEvent::Result> HibernatableWebSocketCustomEve
try {
co_await context.run(
[entrypointName = entrypointName, &context, eventParameters = kj::mv(eventParameters),
props = kj::mv(props)](Worker::Lock& lock) mutable {
versionInfo = kj::mv(versionInfo), props = kj::mv(props)](Worker::Lock& lock) mutable {
KJ_SWITCH_ONEOF(eventParameters.eventType) {
KJ_CASE_ONEOF(text, HibernatableSocketParams::Text) {
return lock.getGlobalScope().sendHibernatableWebSocketMessage(context,
kj::mv(text.message), eventParameters.eventTimeoutMs,
kj::mv(eventParameters.websocketId), lock,
lock.getExportedHandler(entrypointName, kj::mv(props), context.getActor()));
lock.getExportedHandler(
entrypointName, kj::mv(versionInfo), kj::mv(props), context.getActor()));
}
KJ_CASE_ONEOF(data, HibernatableSocketParams::Data) {
return lock.getGlobalScope().sendHibernatableWebSocketMessage(context,
kj::mv(data.message), eventParameters.eventTimeoutMs,
kj::mv(eventParameters.websocketId), lock,
lock.getExportedHandler(entrypointName, kj::mv(props), context.getActor()));
lock.getExportedHandler(
entrypointName, kj::mv(versionInfo), kj::mv(props), context.getActor()));
}
KJ_CASE_ONEOF(close, HibernatableSocketParams::Close) {
return lock.getGlobalScope().sendHibernatableWebSocketClose(context, kj::mv(close),
eventParameters.eventTimeoutMs, kj::mv(eventParameters.websocketId), lock,
lock.getExportedHandler(entrypointName, kj::mv(props), context.getActor()));
lock.getExportedHandler(
entrypointName, kj::mv(versionInfo), kj::mv(props), context.getActor()));
}
KJ_CASE_ONEOF(e, HibernatableSocketParams::Error) {
return lock.getGlobalScope().sendHibernatableWebSocketError(context, kj::mv(e.error),
eventParameters.eventTimeoutMs, kj::mv(eventParameters.websocketId), lock,
lock.getExportedHandler(entrypointName, kj::mv(props), context.getActor()));
lock.getExportedHandler(
entrypointName, kj::mv(versionInfo), kj::mv(props), context.getActor()));
}
KJ_UNREACHABLE;
}
Expand Down
1 change: 1 addition & 0 deletions src/workerd/api/hibernatable-web-socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class HibernatableWebSocketCustomEvent final: public WorkerInterface::CustomEven

kj::Promise<Result> run(kj::Own<IoContext_IncomingRequest> incomingRequest,
kj::Maybe<kj::StringPtr> entrypointName,
kj::Maybe<Worker::VersionInfo> versionInfo,
Frankenvalue props,
kj::TaskSet& waitUntilTasks) override;

Expand Down
7 changes: 5 additions & 2 deletions src/workerd/api/queue.c++
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ tracing::EventInfo QueueCustomEvent::getEventInfo() const {
kj::Promise<WorkerInterface::CustomEvent::Result> QueueCustomEvent::run(
kj::Own<IoContext_IncomingRequest> incomingRequest,
kj::Maybe<kj::StringPtr> entrypointName,
kj::Maybe<Worker::VersionInfo> versionInfo,
Frankenvalue props,
kj::TaskSet& waitUntilTasks) {
// This method has three main chunks of logic:
Expand All @@ -594,14 +595,16 @@ kj::Promise<WorkerInterface::CustomEvent::Result> QueueCustomEvent::run(
// 2. This is where we call into the worker's queue event handler
auto runProm = context.run(
[this, entrypointName = entrypointName, &context, queueEvent = kj::addRef(*queueEventHolder),
&metrics = incomingRequest->getMetrics(),
&metrics = incomingRequest->getMetrics(), versionInfo = kj::mv(versionInfo),
props = kj::mv(props)](Worker::Lock& lock) mutable {
jsg::AsyncContextFrame::StorageScope traceScope = context.makeAsyncTraceScope(lock);

auto& typeHandler = lock.getWorker().getIsolate().getApi().getQueueTypeHandler(lock);
auto startResp = startQueueEvent(lock.getGlobalScope(), context, kj::mv(params),
context.addObject(result), lock,
lock.getExportedHandler(entrypointName, kj::mv(props), context.getActor()), typeHandler);
lock.getExportedHandler(
entrypointName, kj::mv(versionInfo), kj::mv(props), context.getActor()),
typeHandler);
queueEvent->event = kj::mv(startResp.event);
queueEvent->exportedHandlerProm = kj::mv(startResp.exportedHandlerProm);
queueEvent->isServiceWorkerHandler = startResp.isServiceWorkerHandler;
Expand Down
2 changes: 2 additions & 0 deletions src/workerd/api/queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <workerd/io/trace.h>
#include <workerd/io/worker-interface.capnp.h>
#include <workerd/io/worker-interface.h>
#include <workerd/io/worker.h>
#include <workerd/jsg/jsg.h>

#include <kj/async.h>
Expand Down Expand Up @@ -341,6 +342,7 @@ class QueueCustomEvent final: public WorkerInterface::CustomEvent, public kj::Re

kj::Promise<Result> run(kj::Own<IoContext_IncomingRequest> incomingRequest,
kj::Maybe<kj::StringPtr> entrypointName,
kj::Maybe<Worker::VersionInfo> versionInfo,
Frankenvalue props,
kj::TaskSet& waitUntilTasks) override;

Expand Down
9 changes: 6 additions & 3 deletions src/workerd/api/trace.c++
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ jsg::Ref<TraceMetrics> UnsafeTraceMetrics::fromTrace(jsg::Lock& js, jsg::Ref<Tra
namespace {
kj::Promise<void> sendTracesToExportedHandler(kj::Own<IoContext::IncomingRequest> incomingRequest,
kj::Maybe<kj::StringPtr> entrypointNamePtr,
kj::Maybe<Worker::VersionInfo> versionInfo,
Frankenvalue props,
kj::ArrayPtr<kj::Own<Trace>> traces) {
// Mark the request as delivered because we're about to run some JS.
Expand All @@ -614,10 +615,11 @@ kj::Promise<void> sendTracesToExportedHandler(kj::Own<IoContext::IncomingRequest
try {
co_await context.run(
[&context, nonEmptyTraces = nonEmptyTraces.asPtr(), entrypointName = kj::mv(entrypointName),
props = kj::mv(props)](Worker::Lock& lock) mutable {
versionInfo = kj::mv(versionInfo), props = kj::mv(props)](Worker::Lock& lock) mutable {
jsg::AsyncContextFrame::StorageScope traceScope = context.makeAsyncTraceScope(lock);

auto handler = lock.getExportedHandler(entrypointName, kj::mv(props), context.getActor());
auto handler = lock.getExportedHandler(
entrypointName, kj::mv(versionInfo), kj::mv(props), context.getActor());
return lock.getGlobalScope().sendTraces(nonEmptyTraces, lock, handler);
});
} catch (kj::Exception& e) {
Expand Down Expand Up @@ -645,11 +647,12 @@ tracing::EventInfo TraceCustomEvent::getEventInfo() const {

auto TraceCustomEvent::run(kj::Own<IoContext::IncomingRequest> incomingRequest,
kj::Maybe<kj::StringPtr> entrypointNamePtr,
kj::Maybe<Worker::VersionInfo> versionInfo,
Frankenvalue props,
kj::TaskSet& waitUntilTasks) -> kj::Promise<Result> {
// Don't bother to wait around for the handler to run, just hand it off to the waitUntil tasks.
waitUntilTasks.add(sendTracesToExportedHandler(
kj::mv(incomingRequest), entrypointNamePtr, kj::mv(props), traces));
kj::mv(incomingRequest), entrypointNamePtr, kj::mv(versionInfo), kj::mv(props), traces));

// Reporting a proper outcome and return event here would be nice, but for that we'd need to await
// running the tail handler...
Expand Down
1 change: 1 addition & 0 deletions src/workerd/api/trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,7 @@ class TraceCustomEvent final: public WorkerInterface::CustomEvent {

kj::Promise<Result> run(kj::Own<IoContext::IncomingRequest> incomingRequest,
kj::Maybe<kj::StringPtr> entrypointName,
kj::Maybe<Worker::VersionInfo> versionInfo,
Frankenvalue props,
kj::TaskSet& waitUntilTasks) override;

Expand Down
14 changes: 9 additions & 5 deletions src/workerd/api/worker-rpc.c++
Original file line number Diff line number Diff line change
Expand Up @@ -1971,6 +1971,7 @@ class EntrypointJsRpcTarget final: public JsRpcTargetBase {
public:
EntrypointJsRpcTarget(IoContext& ioCtx,
kj::Maybe<kj::StringPtr> entrypointName,
kj::Maybe<Worker::VersionInfo> versionInfo,
Frankenvalue props,
kj::Maybe<kj::String> wrapperModule,
kj::Maybe<kj::Own<BaseTracer>> tracer)
Expand All @@ -1979,6 +1980,7 @@ class EntrypointJsRpcTarget final: public JsRpcTargetBase {
// Most of the time we don't really have to clone this but it's hard to fully prove, so
// let's be safe.
entrypointName(entrypointName.map([](kj::StringPtr s) { return kj::str(s); })),
versionInfo(kj::mv(versionInfo)),
props(kj::mv(props)),
wrapperModule(kj::mv(wrapperModule)),
tracer(kj::mv(tracer)) {}
Expand All @@ -1997,9 +1999,9 @@ class EntrypointJsRpcTarget final: public JsRpcTargetBase {
TargetInfo getTargetInfo(Worker::Lock& lock, IoContext& ioCtx) override {
jsg::Lock& js = lock;

auto handler =
KJ_REQUIRE_NONNULL(lock.getExportedHandler(entrypointName, kj::mv(props), ioCtx.getActor()),
"Failed to get handler to worker.");
auto handler = KJ_REQUIRE_NONNULL(lock.getExportedHandler(entrypointName, kj::mv(versionInfo),
kj::mv(props), ioCtx.getActor()),
"Failed to get handler to worker.");

if (handler->missingSuperclass && wrapperModule == kj::none) {
// JS RPC is not enabled on the server side, we cannot call any methods.
Expand Down Expand Up @@ -2063,6 +2065,7 @@ class EntrypointJsRpcTarget final: public JsRpcTargetBase {
private:
IoContext& ioCtx;
kj::Maybe<kj::String> entrypointName;
kj::Maybe<Worker::VersionInfo> versionInfo;
Frankenvalue props;
kj::Maybe<kj::String> wrapperModule;
kj::Maybe<kj::Own<BaseTracer>> tracer;
Expand Down Expand Up @@ -2152,6 +2155,7 @@ class JsRpcSessionCustomEvent::ServerTopLevelMembrane final: public capnp::Membr
kj::Promise<WorkerInterface::CustomEvent::Result> JsRpcSessionCustomEvent::run(
kj::Own<IoContext::IncomingRequest> incomingRequest,
kj::Maybe<kj::StringPtr> entrypointName,
kj::Maybe<Worker::VersionInfo> versionInfo,
Frankenvalue props,
kj::TaskSet& waitUntilTasks) {
IoContext& ioctx = incomingRequest->getContext();
Expand All @@ -2164,8 +2168,8 @@ kj::Promise<WorkerInterface::CustomEvent::Result> JsRpcSessionCustomEvent::run(
waitUntilTasks.add(incomingRequest->drain().attach(kj::mv(incomingRequest)));
});

EntrypointJsRpcTarget target(ioctx, entrypointName, kj::mv(props), kj::mv(wrapperModule),
mapAddRef(incomingRequest->getWorkerTracer()));
EntrypointJsRpcTarget target(ioctx, entrypointName, kj::mv(versionInfo), kj::mv(props),
kj::mv(wrapperModule), mapAddRef(incomingRequest->getWorkerTracer()));
capnp::RevocableServer<rpc::JsRpcTarget> revcableTarget(target);

try {
Expand Down
1 change: 1 addition & 0 deletions src/workerd/api/worker-rpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ class JsRpcSessionCustomEvent final: public WorkerInterface::CustomEvent {

kj::Promise<Result> run(kj::Own<IoContext::IncomingRequest> incomingRequest,
kj::Maybe<kj::StringPtr> entrypointName,
kj::Maybe<Worker::VersionInfo> versionInfo,
Frankenvalue props,
kj::TaskSet& waitUntilTasks) override;

Expand Down
10 changes: 9 additions & 1 deletion src/workerd/io/compatibility-date.capnp
Original file line number Diff line number Diff line change
Expand Up @@ -1424,7 +1424,8 @@ struct CompatibilityFlags @0x8f8c1b68151b6cef {
$compatEnableFlag("enable_version_api")
$experimental;
# Enables version-related APIs. This currently only enables the `version` option in loopback
# bindings to specify a requested version. The behaviour of this flag will change in the future.
# bindings to specify a requested version and exposes `ctx.version`. The behaviour of this flag
# will change in the future.

websocketBinaryTypeDefault @166 :Bool
$compatEnableFlag("websocket_standard_binary_type")
Expand Down Expand Up @@ -1494,4 +1495,11 @@ struct CompatibilityFlags @0x8f8c1b68151b6cef {
# WebSocket readyState is CLOSED (3) when the close event fires. Previously,
# no close reply was sent and readyState was CLOSING (2). The WebSocket spec
# requires readyState to be CLOSED when the close event is dispatched.

enableCtxVersionMetadata @172 :Bool
$compatEnableFlag("enable_ctx_version_metadata")
$experimental;
# When paired with `enable_version_api`, also exposes `ctx.version.metadata`. This is a separate
# flag as we haven't decided on the exact behaviour of `ctx.version.metadata`, but the rest of
# `ctx.version` is much more well defined. The behaviour of this flag will change in the future.
}
Loading
Loading