// Copyright (c) 2017-2022 Cloudflare, Inc. // Licensed under the Apache 2.0 license found in the LICENSE file or at: // https://opensource.org/licenses/Apache-2.0 #pragma once #include #include #include #include #include #include #include #include namespace workerd::api { using HibernationReader = rpc::HibernatableWebSocketEventDispatcher::HibernatableWebSocketEventParams::Reader; class HibernatableWebSocketEvent final: public ExtendableEvent { public: explicit HibernatableWebSocketEvent(); static jsg::Ref constructor(kj::String type) = delete; // When we call a close or error event, we need to move the owned websocket and the tags back into // the api::WebSocket to extend their lifetimes. This is because the HibernatableWebSocket, which // has owned these things for the entire duration of the connection, is free to go away after we // dispatch the final event. JS may still want to access the underlying kj::WebSocket or the tags, // so we have to transfer ownership to JS-land. struct ItemsForRelease { jsg::Ref webSocketRef; kj::Own ownedWebSocket; kj::Array tags; explicit ItemsForRelease( jsg::Ref ref, kj::Own owned, kj::Array tags); }; // Call this when transferring ownership of the kj::WebSocket and tags to the api::WebSocket. // // Gets a reference to the api::WebSocket, and moves the owned kj::WebSocket out of the // HibernatableWebSocket whose event we are currently delivering. ItemsForRelease prepareForRelease(jsg::Lock& lock, kj::StringPtr websocketId); // Should only be called once per event, see definition for details. jsg::Ref claimWebSocket(jsg::Lock& lock, kj::StringPtr websocketId); JSG_RESOURCE_TYPE(HibernatableWebSocketEvent) { JSG_INHERIT(ExtendableEvent); } private: Worker::Actor::HibernationManager& getHibernationManager(jsg::Lock& lock); }; class HibernatableWebSocketCustomEvent final: public WorkerInterface::CustomEvent, public kj::Refcounted { public: HibernatableWebSocketCustomEvent(uint16_t typeId, kj::Own params, kj::Maybe manager = kj::none); HibernatableWebSocketCustomEvent( uint16_t typeId, HibernatableSocketParams params, Worker::Actor::HibernationManager& manager); kj::Promise run(kj::Own incomingRequest, kj::Maybe entrypointName, kj::Maybe versionInfo, Frankenvalue props, kj::TaskSet& waitUntilTasks, bool isDynamicDispatch) override; kj::Promise sendRpc(capnp::HttpOverCapnpFactory& httpOverCapnpFactory, capnp::ByteStreamFactory& byteStreamFactory, rpc::EventDispatcher::Client dispatcher) override; uint16_t getType() override { return typeId; } tracing::EventInfo getEventInfo() const override; kj::Promise notSupported() override { KJ_UNIMPLEMENTED("hibernatable web socket event not supported"); } private: // Returns `params`, but if we have a HibernationReader we convert it to a // HibernatableSocketParams first. HibernatableSocketParams consumeParams(); // Peeks at params to extract the event type for tracing, without consuming them. tracing::HibernatableWebSocketEventInfo::Type getEventType() const; uint16_t typeId; kj::OneOf> params; kj::Maybe timeoutMs; kj::Maybe manager; }; #define EW_WEB_SOCKET_MESSAGE_ISOLATE_TYPES \ api::HibernatableWebSocketEvent, api::HibernatableWebSocketExportedHandler } // namespace workerd::api