cannam@147: // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors cannam@147: // Licensed under the MIT License: cannam@147: // cannam@147: // Permission is hereby granted, free of charge, to any person obtaining a copy cannam@147: // of this software and associated documentation files (the "Software"), to deal cannam@147: // in the Software without restriction, including without limitation the rights cannam@147: // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cannam@147: // copies of the Software, and to permit persons to whom the Software is cannam@147: // furnished to do so, subject to the following conditions: cannam@147: // cannam@147: // The above copyright notice and this permission notice shall be included in cannam@147: // all copies or substantial portions of the Software. cannam@147: // cannam@147: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR cannam@147: // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, cannam@147: // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE cannam@147: // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER cannam@147: // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, cannam@147: // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN cannam@147: // THE SOFTWARE. cannam@147: cannam@147: #ifndef CAPNP_RPC_H_ cannam@147: #define CAPNP_RPC_H_ cannam@147: cannam@147: #if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS) cannam@147: #pragma GCC system_header cannam@147: #endif cannam@147: cannam@147: #include "capability.h" cannam@147: #include "rpc-prelude.h" cannam@147: cannam@147: namespace capnp { cannam@147: cannam@147: template cannam@147: class VatNetwork; cannam@147: template cannam@147: class SturdyRefRestorer; cannam@147: cannam@147: template cannam@147: class BootstrapFactory: public _::BootstrapFactoryBase { cannam@147: // Interface that constructs per-client bootstrap interfaces. Use this if you want each client cannam@147: // who connects to see a different bootstrap interface based on their (authenticated) VatId. cannam@147: // This allows an application to bootstrap off of the authentication performed at the VatNetwork cannam@147: // level. (Typically VatId is some sort of public key.) cannam@147: // cannam@147: // This is only useful for multi-party networks. For TwoPartyVatNetwork, there's no reason to cannam@147: // use a BootstrapFactory; just specify a single bootstrap capability in this case. cannam@147: cannam@147: public: cannam@147: virtual Capability::Client createFor(typename VatId::Reader clientId) = 0; cannam@147: // Create a bootstrap capability appropriate for exposing to the given client. VatNetwork will cannam@147: // have authenticated the client VatId before this is called. cannam@147: cannam@147: private: cannam@147: Capability::Client baseCreateFor(AnyStruct::Reader clientId) override; cannam@147: }; cannam@147: cannam@147: template cannam@147: class RpcSystem: public _::RpcSystemBase { cannam@147: // Represents the RPC system, which is the portal to objects available on the network. cannam@147: // cannam@147: // The RPC implementation sits on top of an implementation of `VatNetwork`. The `VatNetwork` cannam@147: // determines how to form connections between vats -- specifically, two-way, private, reliable, cannam@147: // sequenced datagram connections. The RPC implementation determines how to use such connections cannam@147: // to manage object references and make method calls. cannam@147: // cannam@147: // See `makeRpcServer()` and `makeRpcClient()` below for convenient syntax for setting up an cannam@147: // `RpcSystem` given a `VatNetwork`. cannam@147: // cannam@147: // See `ez-rpc.h` for an even simpler interface for setting up RPC in a typical two-party cannam@147: // client/server scenario. cannam@147: cannam@147: public: cannam@147: template cannam@147: RpcSystem( cannam@147: VatNetwork& network, cannam@147: kj::Maybe bootstrapInterface, cannam@147: kj::Maybe::Client> gateway = nullptr); cannam@147: cannam@147: template cannam@147: RpcSystem( cannam@147: VatNetwork& network, cannam@147: BootstrapFactory& bootstrapFactory, cannam@147: kj::Maybe::Client> gateway = nullptr); cannam@147: cannam@147: template cannam@147: RpcSystem( cannam@147: VatNetwork& network, cannam@147: SturdyRefRestorer& restorer); cannam@147: cannam@147: RpcSystem(RpcSystem&& other) = default; cannam@147: cannam@147: Capability::Client bootstrap(typename VatId::Reader vatId); cannam@147: // Connect to the given vat and return its bootstrap interface. cannam@147: cannam@147: Capability::Client restore(typename VatId::Reader hostId, AnyPointer::Reader objectId) cannam@147: KJ_DEPRECATED("Please transition to using a bootstrap interface instead."); cannam@147: // ** DEPRECATED ** cannam@147: // cannam@147: // Restores the given SturdyRef from the network and return the capability representing it. cannam@147: // cannam@147: // `hostId` identifies the host from which to request the ref, in the format specified by the cannam@147: // `VatNetwork` in use. `objectId` is the object ID in whatever format is expected by said host. cannam@147: // cannam@147: // This method will be removed in a future version of Cap'n Proto. Instead, please transition cannam@147: // to using bootstrap(), which is equivalent to calling restore() with a null `objectId`. cannam@147: // You may emulate the old concept of object IDs by exporting a bootstrap interface which has cannam@147: // methods that can be used to obtain other capabilities by ID. cannam@147: cannam@147: void setFlowLimit(size_t words); cannam@147: // Sets the incoming call flow limit. If more than `words` worth of call messages have not yet cannam@147: // received responses, the RpcSystem will not read further messages from the stream. This can be cannam@147: // used as a crude way to prevent a resource exhaustion attack (or bug) in which a peer makes an cannam@147: // excessive number of simultaneous calls that consume the receiver's RAM. cannam@147: // cannam@147: // There are some caveats. When over the flow limit, all messages are blocked, including returns. cannam@147: // If the outstanding calls are themselves waiting on calls going in the opposite direction, the cannam@147: // flow limit may prevent those calls from completing, leading to deadlock. However, a cannam@147: // sufficiently high limit should make this unlikely. cannam@147: // cannam@147: // Note that a call's parameter size counts against the flow limit until the call returns, even cannam@147: // if the recipient calls releaseParams() to free the parameter memory early. This is because cannam@147: // releaseParams() may simply indicate that the parameters have been forwarded to another cannam@147: // machine, but are still in-memory there. For illustration, say that Alice made a call to Bob cannam@147: // who forwarded the call to Carol. Bob has imposed a flow limit on Alice. Alice's calls are cannam@147: // being forwarded to Carol, so Bob never keeps the parameters in-memory for more than a brief cannam@147: // period. However, the flow limit counts all calls that haven't returned, even if Bob has cannam@147: // already freed the memory they consumed. You might argue that the right solution here is cannam@147: // instead for Carol to impose her own flow limit on Bob. This has a serious problem, though: cannam@147: // Bob might be forwarding requests to Carol on behalf of many different parties, not just Alice. cannam@147: // If Alice can pump enough data to hit the Bob -> Carol flow limit, then those other parties cannam@147: // will be disrupted. Thus, we can only really impose the limit on the Alice -> Bob link, which cannam@147: // only affects Alice. We need that one flow limit to limit Alice's impact on the whole system, cannam@147: // so it has to count all in-flight calls. cannam@147: // cannam@147: // In Sandstorm, flow limits are imposed by the supervisor on calls coming out of a grain, in cannam@147: // order to prevent a grain from inundating the system with in-flight calls. In practice, the cannam@147: // main time this happens is when a grain is pushing a large file download and doesn't implement cannam@147: // proper cooperative flow control. cannam@147: }; cannam@147: cannam@147: template cannam@147: RpcSystem makeRpcServer( cannam@147: VatNetwork& network, cannam@147: Capability::Client bootstrapInterface); cannam@147: // Make an RPC server. Typical usage (e.g. in a main() function): cannam@147: // cannam@147: // MyEventLoop eventLoop; cannam@147: // kj::WaitScope waitScope(eventLoop); cannam@147: // MyNetwork network; cannam@147: // MyMainInterface::Client bootstrap = makeMain(); cannam@147: // auto server = makeRpcServer(network, bootstrap); cannam@147: // kj::NEVER_DONE.wait(waitScope); // run forever cannam@147: // cannam@147: // See also ez-rpc.h, which has simpler instructions for the common case of a two-party cannam@147: // client-server RPC connection. cannam@147: cannam@147: template , cannam@147: typename ExternalRef = _::ExternalRefFromRealmGatewayClient> cannam@147: RpcSystem makeRpcServer( cannam@147: VatNetwork& network, cannam@147: Capability::Client bootstrapInterface, RealmGatewayClient gateway); cannam@147: // Make an RPC server for a VatNetwork that resides in a different realm from the application. cannam@147: // The given RealmGateway is used to translate SturdyRefs between the app's ("internal") format cannam@147: // and the network's ("external") format. cannam@147: cannam@147: template cannam@147: RpcSystem makeRpcServer( cannam@147: VatNetwork& network, cannam@147: BootstrapFactory& bootstrapFactory); cannam@147: // Make an RPC server that can serve different bootstrap interfaces to different clients via a cannam@147: // BootstrapInterface. cannam@147: cannam@147: template , cannam@147: typename ExternalRef = _::ExternalRefFromRealmGatewayClient> cannam@147: RpcSystem makeRpcServer( cannam@147: VatNetwork& network, cannam@147: BootstrapFactory& bootstrapFactory, RealmGatewayClient gateway); cannam@147: // Make an RPC server that can serve different bootstrap interfaces to different clients via a cannam@147: // BootstrapInterface and communicates with a different realm than the application is in via a cannam@147: // RealmGateway. cannam@147: cannam@147: template cannam@147: RpcSystem makeRpcServer( cannam@147: VatNetwork& network, cannam@147: SturdyRefRestorer& restorer) cannam@147: KJ_DEPRECATED("Please transition to using a bootstrap interface instead."); cannam@147: // ** DEPRECATED ** cannam@147: // cannam@147: // Create an RPC server which exports multiple main interfaces by object ID. The `restorer` object cannam@147: // can be used to look up objects by ID. cannam@147: // cannam@147: // Please transition to exporting only one interface, which is known as the "bootstrap" interface. cannam@147: // For backwards-compatibility with old clients, continue to implement SturdyRefRestorer, but cannam@147: // return the new bootstrap interface when the request object ID is null. When new clients connect cannam@147: // and request the bootstrap interface, they will get that interface. Eventually, once all clients cannam@147: // are updated to request only the bootstrap interface, stop implementing SturdyRefRestorer and cannam@147: // switch to passing the bootstrap capability itself as the second parameter to `makeRpcServer()`. cannam@147: cannam@147: template cannam@147: RpcSystem makeRpcClient( cannam@147: VatNetwork& network); cannam@147: // Make an RPC client. Typical usage (e.g. in a main() function): cannam@147: // cannam@147: // MyEventLoop eventLoop; cannam@147: // kj::WaitScope waitScope(eventLoop); cannam@147: // MyNetwork network; cannam@147: // auto client = makeRpcClient(network); cannam@147: // MyCapability::Client cap = client.restore(hostId, objId).castAs(); cannam@147: // auto response = cap.fooRequest().send().wait(waitScope); cannam@147: // handleMyResponse(response); cannam@147: // cannam@147: // See also ez-rpc.h, which has simpler instructions for the common case of a two-party cannam@147: // client-server RPC connection. cannam@147: cannam@147: template , cannam@147: typename ExternalRef = _::ExternalRefFromRealmGatewayClient> cannam@147: RpcSystem makeRpcClient( cannam@147: VatNetwork& network, cannam@147: RealmGatewayClient gateway); cannam@147: // Make an RPC client for a VatNetwork that resides in a different realm from the application. cannam@147: // The given RealmGateway is used to translate SturdyRefs between the app's ("internal") format cannam@147: // and the network's ("external") format. cannam@147: cannam@147: template cannam@147: class SturdyRefRestorer: public _::SturdyRefRestorerBase { cannam@147: // ** DEPRECATED ** cannam@147: // cannam@147: // In Cap'n Proto 0.4.x, applications could export multiple main interfaces identified by cannam@147: // object IDs. The callback used to map object IDs to objects was `SturdyRefRestorer`, as we cannam@147: // imagined this would eventually be used for restoring SturdyRefs as well. In practice, it was cannam@147: // never used for real SturdyRefs, only for exporting singleton objects under well-known names. cannam@147: // cannam@147: // The new preferred strategy is to export only a _single_ such interface, called the cannam@147: // "bootstrap interface". That interface can itself have methods for obtaining other objects, of cannam@147: // course, but that is up to the app. `SturdyRefRestorer` exists for backwards-compatibility. cannam@147: // cannam@147: // Hint: Use SturdyRefRestorer to define a server that exports services under cannam@147: // string names. cannam@147: cannam@147: public: cannam@147: virtual Capability::Client restore(typename SturdyRefObjectId::Reader ref) cannam@147: KJ_DEPRECATED( cannam@147: "Please transition to using bootstrap interfaces instead of SturdyRefRestorer.") = 0; cannam@147: // Restore the given object, returning a capability representing it. cannam@147: cannam@147: private: cannam@147: Capability::Client baseRestore(AnyPointer::Reader ref) override final; cannam@147: }; cannam@147: cannam@147: // ======================================================================================= cannam@147: // VatNetwork cannam@147: cannam@147: class OutgoingRpcMessage { cannam@147: // A message to be sent by a `VatNetwork`. cannam@147: cannam@147: public: cannam@147: virtual AnyPointer::Builder getBody() = 0; cannam@147: // Get the message body, which the caller may fill in any way it wants. (The standard RPC cannam@147: // implementation initializes it as a Message as defined in rpc.capnp.) cannam@147: cannam@147: virtual void send() = 0; cannam@147: // Send the message, or at least put it in a queue to be sent later. Note that the builder cannam@147: // returned by `getBody()` remains valid at least until the `OutgoingRpcMessage` is destroyed. cannam@147: }; cannam@147: cannam@147: class IncomingRpcMessage { cannam@147: // A message received from a `VatNetwork`. cannam@147: cannam@147: public: cannam@147: virtual AnyPointer::Reader getBody() = 0; cannam@147: // Get the message body, to be interpreted by the caller. (The standard RPC implementation cannam@147: // interprets it as a Message as defined in rpc.capnp.) cannam@147: }; cannam@147: cannam@147: template cannam@147: class VatNetwork: public _::VatNetworkBase { cannam@147: // Cap'n Proto RPC operates between vats, where a "vat" is some sort of host of objects. cannam@147: // Typically one Cap'n Proto process (in the Unix sense) is one vat. The RPC system is what cannam@147: // allows calls between objects hosted in different vats. cannam@147: // cannam@147: // The RPC implementation sits on top of an implementation of `VatNetwork`. The `VatNetwork` cannam@147: // determines how to form connections between vats -- specifically, two-way, private, reliable, cannam@147: // sequenced datagram connections. The RPC implementation determines how to use such connections cannam@147: // to manage object references and make method calls. cannam@147: // cannam@147: // The most common implementation of VatNetwork is TwoPartyVatNetwork (rpc-twoparty.h). Most cannam@147: // simple client-server apps will want to use it. (You may even want to use the EZ RPC cannam@147: // interfaces in `ez-rpc.h` and avoid all of this.) cannam@147: // cannam@147: // TODO(someday): Provide a standard implementation for the public internet. cannam@147: cannam@147: public: cannam@147: class Connection; cannam@147: cannam@147: struct ConnectionAndProvisionId { cannam@147: // Result of connecting to a vat introduced by another vat. cannam@147: cannam@147: kj::Own connection; cannam@147: // Connection to the new vat. cannam@147: cannam@147: kj::Own firstMessage; cannam@147: // An already-allocated `OutgoingRpcMessage` associated with `connection`. The RPC system will cannam@147: // construct this as an `Accept` message and send it. cannam@147: cannam@147: Orphan provisionId; cannam@147: // A `ProvisionId` already allocated inside `firstMessage`, which the RPC system will use to cannam@147: // build the `Accept` message. cannam@147: }; cannam@147: cannam@147: class Connection: public _::VatNetworkBase::Connection { cannam@147: // A two-way RPC connection. cannam@147: // cannam@147: // This object may represent a connection that doesn't exist yet, but is expected to exist cannam@147: // in the future. In this case, sent messages will automatically be queued and sent once the cannam@147: // connection is ready, so that the caller doesn't need to know the difference. cannam@147: cannam@147: public: cannam@147: // Level 0 features ---------------------------------------------- cannam@147: cannam@147: virtual typename VatId::Reader getPeerVatId() = 0; cannam@147: // Returns the connected vat's authenticated VatId. It is the VatNetwork's responsibility to cannam@147: // authenticate this, so that the caller can be assured that they are really talking to the cannam@147: // identified vat and not an imposter. cannam@147: cannam@147: virtual kj::Own newOutgoingMessage(uint firstSegmentWordSize) override = 0; cannam@147: // Allocate a new message to be sent on this connection. cannam@147: // cannam@147: // If `firstSegmentWordSize` is non-zero, it should be treated as a hint suggesting how large cannam@147: // to make the first segment. This is entirely a hint and the connection may adjust it up or cannam@147: // down. If it is zero, the connection should choose the size itself. cannam@147: cannam@147: virtual kj::Promise>> receiveIncomingMessage() override = 0; cannam@147: // Wait for a message to be received and return it. If the read stream cleanly terminates, cannam@147: // return null. If any other problem occurs, throw an exception. cannam@147: cannam@147: virtual kj::Promise shutdown() override KJ_WARN_UNUSED_RESULT = 0; cannam@147: // Waits until all outgoing messages have been sent, then shuts down the outgoing stream. The cannam@147: // returned promise resolves after shutdown is complete. cannam@147: cannam@147: private: cannam@147: AnyStruct::Reader baseGetPeerVatId() override; cannam@147: }; cannam@147: cannam@147: // Level 0 features ------------------------------------------------ cannam@147: cannam@147: virtual kj::Maybe> connect(typename VatId::Reader hostId) = 0; cannam@147: // Connect to a VatId. Note that this method immediately returns a `Connection`, even cannam@147: // if the network connection has not yet been established. Messages can be queued to this cannam@147: // connection and will be delivered once it is open. The caller must attempt to read from the cannam@147: // connection to verify that it actually succeeded; the read will fail if the connection cannam@147: // couldn't be opened. Some network implementations may actually start sending messages before cannam@147: // hearing back from the server at all, to avoid a round trip. cannam@147: // cannam@147: // Returns nullptr if `hostId` refers to the local host. cannam@147: cannam@147: virtual kj::Promise> accept() = 0; cannam@147: // Wait for the next incoming connection and return it. cannam@147: cannam@147: // Level 4 features ------------------------------------------------ cannam@147: // TODO(someday) cannam@147: cannam@147: private: cannam@147: kj::Maybe> cannam@147: baseConnect(AnyStruct::Reader hostId) override final; cannam@147: kj::Promise> baseAccept() override final; cannam@147: }; cannam@147: cannam@147: // ======================================================================================= cannam@147: // *************************************************************************************** cannam@147: // Inline implementation details start here cannam@147: // *************************************************************************************** cannam@147: // ======================================================================================= cannam@147: cannam@147: template cannam@147: Capability::Client BootstrapFactory::baseCreateFor(AnyStruct::Reader clientId) { cannam@147: return createFor(clientId.as()); cannam@147: } cannam@147: cannam@147: template cannam@147: kj::Maybe> cannam@147: VatNetwork:: cannam@147: baseConnect(AnyStruct::Reader ref) { cannam@147: auto maybe = connect(ref.as()); cannam@147: return maybe.map([](kj::Own& conn) -> kj::Own<_::VatNetworkBase::Connection> { cannam@147: return kj::mv(conn); cannam@147: }); cannam@147: } cannam@147: cannam@147: template cannam@147: kj::Promise> cannam@147: VatNetwork::baseAccept() { cannam@147: return accept().then( cannam@147: [](kj::Own&& connection) -> kj::Own<_::VatNetworkBase::Connection> { cannam@147: return kj::mv(connection); cannam@147: }); cannam@147: } cannam@147: cannam@147: template cannam@147: AnyStruct::Reader VatNetwork< cannam@147: SturdyRef, ProvisionId, RecipientId, ThirdPartyCapId, JoinResult>:: cannam@147: Connection::baseGetPeerVatId() { cannam@147: return getPeerVatId(); cannam@147: } cannam@147: cannam@147: template cannam@147: Capability::Client SturdyRefRestorer::baseRestore(AnyPointer::Reader ref) { cannam@147: #pragma GCC diagnostic push cannam@147: #pragma GCC diagnostic ignored "-Wdeprecated-declarations" cannam@147: return restore(ref.getAs()); cannam@147: #pragma GCC diagnostic pop cannam@147: } cannam@147: cannam@147: template cannam@147: template cannam@147: RpcSystem::RpcSystem( cannam@147: VatNetwork& network, cannam@147: kj::Maybe bootstrap, cannam@147: kj::Maybe::Client> gateway) cannam@147: : _::RpcSystemBase(network, kj::mv(bootstrap), kj::mv(gateway)) {} cannam@147: cannam@147: template cannam@147: template cannam@147: RpcSystem::RpcSystem( cannam@147: VatNetwork& network, cannam@147: BootstrapFactory& bootstrapFactory, cannam@147: kj::Maybe::Client> gateway) cannam@147: : _::RpcSystemBase(network, bootstrapFactory, kj::mv(gateway)) {} cannam@147: cannam@147: template cannam@147: template cannam@147: RpcSystem::RpcSystem( cannam@147: VatNetwork& network, cannam@147: SturdyRefRestorer& restorer) cannam@147: : _::RpcSystemBase(network, restorer) {} cannam@147: cannam@147: template cannam@147: Capability::Client RpcSystem::bootstrap(typename VatId::Reader vatId) { cannam@147: return baseBootstrap(_::PointerHelpers::getInternalReader(vatId)); cannam@147: } cannam@147: cannam@147: template cannam@147: Capability::Client RpcSystem::restore( cannam@147: typename VatId::Reader hostId, AnyPointer::Reader objectId) { cannam@147: return baseRestore(_::PointerHelpers::getInternalReader(hostId), objectId); cannam@147: } cannam@147: cannam@147: template cannam@147: inline void RpcSystem::setFlowLimit(size_t words) { cannam@147: baseSetFlowLimit(words); cannam@147: } cannam@147: cannam@147: template cannam@147: RpcSystem makeRpcServer( cannam@147: VatNetwork& network, cannam@147: Capability::Client bootstrapInterface) { cannam@147: return RpcSystem(network, kj::mv(bootstrapInterface)); cannam@147: } cannam@147: cannam@147: template cannam@147: RpcSystem makeRpcServer( cannam@147: VatNetwork& network, cannam@147: Capability::Client bootstrapInterface, RealmGatewayClient gateway) { cannam@147: return RpcSystem(network, kj::mv(bootstrapInterface), cannam@147: gateway.template castAs>()); cannam@147: } cannam@147: cannam@147: template cannam@147: RpcSystem makeRpcServer( cannam@147: VatNetwork& network, cannam@147: BootstrapFactory& bootstrapFactory) { cannam@147: return RpcSystem(network, bootstrapFactory); cannam@147: } cannam@147: cannam@147: template cannam@147: RpcSystem makeRpcServer( cannam@147: VatNetwork& network, cannam@147: BootstrapFactory& bootstrapFactory, RealmGatewayClient gateway) { cannam@147: return RpcSystem(network, bootstrapFactory, gateway.template castAs>()); cannam@147: } cannam@147: cannam@147: template cannam@147: RpcSystem makeRpcServer( cannam@147: VatNetwork& network, cannam@147: SturdyRefRestorer& restorer) { cannam@147: return RpcSystem(network, restorer); cannam@147: } cannam@147: cannam@147: template cannam@147: RpcSystem makeRpcClient( cannam@147: VatNetwork& network) { cannam@147: return RpcSystem(network, nullptr); cannam@147: } cannam@147: cannam@147: template cannam@147: RpcSystem makeRpcClient( cannam@147: VatNetwork& network, cannam@147: RealmGatewayClient gateway) { cannam@147: return RpcSystem(network, nullptr, gateway.template castAs>()); cannam@147: } cannam@147: cannam@147: } // namespace capnp cannam@147: cannam@147: #endif // CAPNP_RPC_H_