Commit d07bf3b8 authored by Kenton Varda's avatar Kenton Varda

More capabilities core API WIP.

parent 398dac0d
......@@ -24,10 +24,13 @@
#define CAPNP_PRIVATE
#include "arena.h"
#include "message.h"
#include "capability.h"
#include <kj/debug.h>
#include <vector>
#include <string.h>
#include <stdio.h>
#include "capability.h"
#include "capability-context.h"
namespace capnp {
namespace _ { // private
......@@ -99,10 +102,42 @@ void BasicReaderArena::reportReadLimitReached() {
}
}
namespace {
class DummyClientHook final: public ClientHook {
public:
Request<ObjectPointer, TypelessAnswer> newCall(
uint64_t interfaceId, uint16_t methodId) const override {
KJ_FAIL_REQUIRE("Calling capability that was extracted from a message that had no "
"capability context.");
}
kj::Promise<void> whenResolved() const override {
return kj::READY_NOW;
}
kj::Own<ClientHook> addRef() const override {
return kj::heap<DummyClientHook>();
}
void* getBrand() const override {
return nullptr;
}
};
} // namespace
kj::Own<ClientHook> BasicReaderArena::extractCap(const _::StructReader& capDescriptor) {
KJ_FAIL_REQUIRE("Message contained a capability but is not imbued with a capability context.") {
return kj::heap<DummyClientHook>();
}
}
// =======================================================================================
ImbuedReaderArena::ImbuedReaderArena(Arena* base)
: base(base), segment0(nullptr, SegmentId(0), nullptr, nullptr) {}
ImbuedReaderArena::ImbuedReaderArena(Arena* base, CapExtractorBase* capExtractor)
: base(base), capExtractor(capExtractor),
segment0(nullptr, SegmentId(0), nullptr, nullptr) {}
ImbuedReaderArena::~ImbuedReaderArena() noexcept(false) {}
SegmentReader* ImbuedReaderArena::imbue(SegmentReader* baseSegment) {
......@@ -139,8 +174,6 @@ SegmentReader* ImbuedReaderArena::imbue(SegmentReader* baseSegment) {
return result;
}
// implements Arena ------------------------------------------------
SegmentReader* ImbuedReaderArena::tryGetSegment(SegmentId id) {
return imbue(base->tryGetSegment(id));
}
......@@ -149,6 +182,10 @@ void ImbuedReaderArena::reportReadLimitReached() {
return base->reportReadLimitReached();
}
kj::Own<ClientHook> ImbuedReaderArena::extractCap(const _::StructReader& capDescriptor) {
return capExtractor->extractCapInternal(capDescriptor);
}
// =======================================================================================
BasicBuilderArena::BasicBuilderArena(MessageBuilder* message)
......@@ -288,10 +325,19 @@ void BasicBuilderArena::reportReadLimitReached() {
}
}
kj::Own<ClientHook> BasicBuilderArena::extractCap(const _::StructReader& capDescriptor) {
KJ_FAIL_REQUIRE("Message contains no capabilities.");
}
void BasicBuilderArena::injectCap(_::PointerBuilder pointer, kj::Own<ClientHook>&& cap) {
KJ_FAIL_REQUIRE("Cannot inject capability into a builder that has not been imbued with a "
"capability context.");
}
// =======================================================================================
ImbuedBuilderArena::ImbuedBuilderArena(BuilderArena* base)
: base(base), segment0(nullptr) {}
ImbuedBuilderArena::ImbuedBuilderArena(BuilderArena* base, CapInjectorBase* capInjector)
: base(base), capInjector(capInjector), segment0(nullptr) {}
ImbuedBuilderArena::~ImbuedBuilderArena() noexcept(false) {}
SegmentBuilder* ImbuedBuilderArena::imbue(SegmentBuilder* baseSegment) {
......@@ -334,6 +380,10 @@ void ImbuedBuilderArena::reportReadLimitReached() {
base->reportReadLimitReached();
}
kj::Own<ClientHook> ImbuedBuilderArena::extractCap(const _::StructReader& capDescriptor) {
return capInjector->getInjectedCapInternal(capDescriptor);
}
SegmentBuilder* ImbuedBuilderArena::getSegment(SegmentId id) {
return imbue(base->getSegment(id));
}
......@@ -344,5 +394,9 @@ BuilderArena::AllocateResult ImbuedBuilderArena::allocate(WordCount amount) {
return result;
}
void ImbuedBuilderArena::injectCap(_::PointerBuilder pointer, kj::Own<ClientHook>&& cap) {
return capInjector->injectCapInternal(pointer, kj::mv(cap));
}
} // namespace _ (private)
} // namespace capnp
......@@ -34,10 +34,13 @@
#include <kj/mutex.h>
#include "common.h"
#include "message.h"
#include "layout.h"
namespace capnp {
class TypelessCapability;
class CapExtractorBase;
class CapInjectorBase;
class ClientHook;
namespace _ { // private
......@@ -182,7 +185,7 @@ public:
// the VALIDATE_INPUT() macro which may throw an exception; if it return normally, the caller
// will need to continue with default values.
virtual kj::Own<TypelessCapability> extractCap(const _::StructReader& capDescriptor);
virtual kj::Own<ClientHook> extractCap(const _::StructReader& capDescriptor) = 0;
// Given a StructReader for a capability descriptor embedded in the message, return the
// corresponding capability.
};
......@@ -196,6 +199,7 @@ public:
// implements Arena ------------------------------------------------
SegmentReader* tryGetSegment(SegmentId id) override;
void reportReadLimitReached() override;
kj::Own<ClientHook> extractCap(const _::StructReader& capDescriptor);
private:
MessageReader* message;
......@@ -210,7 +214,7 @@ private:
class ImbuedReaderArena final: public Arena {
public:
ImbuedReaderArena(Arena* base);
ImbuedReaderArena(Arena* base, CapExtractorBase* capExtractor);
~ImbuedReaderArena() noexcept(false);
KJ_DISALLOW_COPY(ImbuedReaderArena);
......@@ -219,9 +223,11 @@ public:
// implements Arena ------------------------------------------------
SegmentReader* tryGetSegment(SegmentId id) override;
void reportReadLimitReached() override;
kj::Own<ClientHook> extractCap(const _::StructReader& capDescriptor);
private:
Arena* base;
CapExtractorBase* capExtractor;
// Optimize for single-segment messages so that small messages are handled quickly.
SegmentReader segment0;
......@@ -248,7 +254,7 @@ public:
// the arena is guaranteed to succeed. Therefore callers should try to allocate from a specific
// segment first if there is one, then fall back to the arena.
virtual void injectCap(_::PointerBuilder pointer, kj::Own<TypelessCapability>&& cap) = 0;
virtual void injectCap(_::PointerBuilder pointer, kj::Own<ClientHook>&& cap) = 0;
// Add the capability to the message and initialize the given pointer as an interface pointer
// pointing to this cap.
};
......@@ -271,10 +277,12 @@ public:
// implements Arena ------------------------------------------------
SegmentReader* tryGetSegment(SegmentId id) override;
void reportReadLimitReached() override;
kj::Own<ClientHook> extractCap(const _::StructReader& capDescriptor);
// implements BuilderArena -----------------------------------------
SegmentBuilder* getSegment(SegmentId id) override;
AllocateResult allocate(WordCount amount) override;
void injectCap(_::PointerBuilder pointer, kj::Own<ClientHook>&& cap);
private:
MessageBuilder* message;
......@@ -294,7 +302,7 @@ class ImbuedBuilderArena final: public BuilderArena {
// A BuilderArena imbued with the ability to inject capabilities.
public:
ImbuedBuilderArena(BuilderArena* base);
ImbuedBuilderArena(BuilderArena* base, CapInjectorBase* capInjector);
~ImbuedBuilderArena() noexcept(false);
KJ_DISALLOW_COPY(ImbuedBuilderArena);
......@@ -304,13 +312,16 @@ public:
// implements Arena ------------------------------------------------
SegmentReader* tryGetSegment(SegmentId id) override;
void reportReadLimitReached() override;
kj::Own<ClientHook> extractCap(const _::StructReader& capDescriptor);
// implements BuilderArena -----------------------------------------
SegmentBuilder* getSegment(SegmentId id) override;
AllocateResult allocate(WordCount amount) override;
void injectCap(_::PointerBuilder pointer, kj::Own<ClientHook>&& cap);
private:
BuilderArena* base;
CapInjectorBase* capInjector;
ImbuedSegmentBuilder segment0;
......
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CAPNP_PRIVATE
#include "capability-context.h"
#include "arena.h"
#include <kj/debug.h>
namespace capnp {
CapReaderContext::CapReaderContext(CapExtractorBase& extractor): extractor(&extractor) {}
CapReaderContext::~CapReaderContext() noexcept(false) {
if (extractor == nullptr) {
kj::dtor(arena());
}
}
ObjectPointer::Reader CapReaderContext::imbue(ObjectPointer::Reader base) {
KJ_REQUIRE(extractor != nullptr, "imbue() can only be called once.");
KJ_IF_MAYBE(oldArena, base.reader.getArena()) {
kj::ctor(arena(), oldArena, extractor);
} else {
KJ_FAIL_REQUIRE("Cannot imbue unchecked message.");
}
extractor = nullptr;
return ObjectPointer::Reader(base.reader.imbue(arena()));
}
CapBuilderContext::CapBuilderContext(CapInjectorBase& injector): injector(&injector) {}
CapBuilderContext::~CapBuilderContext() noexcept(false) {
if (injector == nullptr) {
kj::dtor(arena());
}
}
ObjectPointer::Builder CapBuilderContext::imbue(ObjectPointer::Builder base) {
KJ_REQUIRE(injector != nullptr, "imbue() can only be called once.");
kj::ctor(arena(), &base.builder.getArena(), injector);
injector = nullptr;
return ObjectPointer::Builder(base.builder.imbue(arena()));
}
} // namespace capnp
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Classes for imbuing message readers/builders with a capability context.
//
// These classes are for use by RPC implementations. Application code need not know about them.
//
// Normally, MessageReader and MessageBuilder do not support interface pointers because they
// are not RPC-aware and so have no idea how to convert between a serialized CapabilityDescriptor
// and a live capability. To fix this, a reader/builder object needs to be "imbued" with a
// capability context. This creates a new reader/builder which points at the same object but has
// the ability to deal with interface fields. Use `CapReaderContext` and `CapBuilderContext` to
// accomplish this.
#ifndef CAPNP_CAPABILITY_CONTEXT_H_
#define CAPNP_CAPABILITY_CONTEXT_H_
#include "layout.h"
#include "object.h"
#include <kj/mutex.h>
namespace capnp {
class ClientHook;
namespace _ { // private
class ImbuedReaderArena;
class ImbuedBuilderArena;
} // namespace _ (private)
class CapExtractorBase {
// Non-template base class for CapExtractor<T>.
private:
virtual kj::Own<ClientHook> extractCapInternal(const _::StructReader& capDescriptor) = 0;
friend class _::ImbuedReaderArena;
};
class CapInjectorBase {
// Non-template base class for CapInjector<T>.
private:
virtual void injectCapInternal(_::PointerBuilder builder, kj::Own<ClientHook>&& cap) = 0;
virtual kj::Own<ClientHook> getInjectedCapInternal(const _::StructReader& capDescriptor) = 0;
friend class _::ImbuedBuilderArena;
};
template <typename CapDescriptor>
class CapExtractor: public CapExtractorBase {
// Callback used to read a capability from a message, implemented by the RPC system.
// `CapDescriptor` is the struct type which the RPC implementation uses to represent
// capabilities. (On the wire, an interface pointer actually points to a struct of this type.)
public:
virtual kj::Own<ClientHook> extractCap(typename CapDescriptor::Reader descriptor) = 0;
// Given the descriptor read off the wire, construct a live capability.
private:
kj::Own<ClientHook> extractCapInternal(const _::StructReader& capDescriptor) override final {
return extractCap(typename CapDescriptor::Reader(capDescriptor));
}
};
template <typename CapDescriptor>
class CapInjector: public CapInjectorBase {
// Callback used to write a capability into a message, implemented by the RPC system.
// `CapDescriptor` is the struct type which the RPC implementation uses to represent
// capabilities. (On the wire, an interface pointer actually points to a struct of this type.)
public:
virtual void injectCap(typename CapDescriptor::Builder descriptor, kj::Own<ClientHook>&& cap) = 0;
// Fill in the given descriptor so that it describes the given capability.
virtual kj::Own<ClientHook> getInjectedCap(typename CapDescriptor::Reader descriptor) = 0;
// Read back a cap that was previously injected with `injectCap`. This should return a new
// reference.
private:
void injectCapInternal(_::PointerBuilder builder, kj::Own<ClientHook>&& cap) override final {
injectCap(
typename CapDescriptor::Builder(builder.initCapDescriptor(_::structSize<CapDescriptor>())),
kj::mv(cap));
}
kj::Own<ClientHook> getInjectedCapInternal(const _::StructReader& capDescriptor) {
return getInjectedCap(typename CapDescriptor::Reader(capDescriptor));
}
};
// -------------------------------------------------------------------
class CapReaderContext {
// Class which can "imbue" reader objects from some other message with a capability context,
// so that interface pointers found in the message can be extracted and called.
//
// `imbue()` can only be called once per context.
public:
CapReaderContext(CapExtractorBase& extractor);
~CapReaderContext() noexcept(false);
ObjectPointer::Reader imbue(ObjectPointer::Reader base);
private:
CapExtractorBase* extractor; // becomes null once arena is allocated
void* arenaSpace[12 + sizeof(kj::MutexGuarded<void*>) / sizeof(void*)];
_::ImbuedReaderArena& arena() { return *reinterpret_cast<_::ImbuedReaderArena*>(arenaSpace); }
friend class _::ImbuedReaderArena;
};
class CapBuilderContext {
// Class which can "imbue" reader objects from some other message with a capability context,
// so that interface pointers found in the message can be set to point at live capabilities.
//
// `imbue()` can only be called once per context.
public:
CapBuilderContext(CapInjectorBase& injector);
~CapBuilderContext() noexcept(false);
ObjectPointer::Builder imbue(ObjectPointer::Builder base);
private:
CapInjectorBase* injector; // becomes null once arena is allocated
void* arenaSpace[12 + sizeof(kj::MutexGuarded<void*>) / sizeof(void*)];
_::ImbuedBuilderArena& arena() { return *reinterpret_cast<_::ImbuedBuilderArena*>(arenaSpace); }
friend class _::ImbuedBuilderArena;
};
} // namespace capnp
#endif // CAPNP_CAPABILITY_CONTEXT_H_
......@@ -24,19 +24,18 @@
#include "capability.h"
namespace capnp {
namespace _ { // private
CallResult::Pipeline CallResult::Pipeline::getPointerField(uint16_t pointerIndex) const {
auto newOps = kj::heapArray<PipelineManager::Op>(ops.size() + 1);
TypelessAnswer::Pipeline TypelessAnswer::Pipeline::getPointerField(
uint16_t pointerIndex) const {
auto newOps = kj::heapArray<PipelineOp>(ops.size() + 1);
for (auto i: kj::indices(ops)) {
newOps[i] = ops[i];
}
auto& newOp = newOps[ops.size()];
newOp.type = PipelineManager::Op::GET_POINTER_FIELD;
newOp.type = PipelineOp::GET_POINTER_FIELD;
newOp.pointerIndex = pointerIndex;
return Pipeline(call->addRef(), kj::mv(newOps));
return Pipeline(hook->addRef(), kj::mv(newOps));
}
} // namespace _ (private)
} // namespace capnp
This diff is collapsed.
......@@ -2104,6 +2104,14 @@ PointerReader PointerBuilder::asReader() const {
return PointerReader(segment, pointer, std::numeric_limits<int>::max());
}
BuilderArena& PointerBuilder::getArena() const {
return *segment->getArena();
}
PointerBuilder PointerBuilder::imbue(ImbuedBuilderArena& newArena) const {
return PointerBuilder(newArena.imbue(segment), pointer);
}
// =======================================================================================
// PointerReader
......@@ -2139,6 +2147,14 @@ bool PointerReader::isNull() const {
return pointer == nullptr || pointer->isNull();
}
kj::Maybe<Arena&> PointerReader::getArena() const {
return segment == nullptr ? nullptr : segment->getArena();
}
PointerReader PointerReader::imbue(ImbuedReaderArena& newArena) const {
return PointerReader(newArena.imbue(segment), pointer, nestingLimit);
}
// =======================================================================================
// StructBuilder
......
......@@ -39,7 +39,7 @@
namespace capnp {
class TypelessCapability;
class ClientHook;
namespace _ { // private
......@@ -54,7 +54,10 @@ struct WirePointer;
struct WireHelpers;
class SegmentReader;
class SegmentBuilder;
class Arena;
class BuilderArena;
class ImbuedReaderArena;
class ImbuedBuilderArena;
// =============================================================================
......@@ -284,7 +287,7 @@ public:
ListBuilder getList(FieldSize elementSize, const word* defaultValzue);
ListBuilder getStructList(StructSize elementSize, const word* defaultValue);
template <typename T> typename T::Builder getBlob(const void* defaultValue,ByteCount defaultSize);
kj::Own<TypelessCapability> getCapability();
kj::Own<ClientHook> getCapability();
// Get methods: Get the value. If it is null, initialize it to a copy of the default value.
// The default value is encoded as an "unchecked message" for structs, lists, and objects, or a
// simple byte array for blobs.
......@@ -300,7 +303,7 @@ public:
void setStruct(const StructReader& value);
void setList(const ListReader& value);
template <typename T> void setBlob(typename T::Reader value);
void setCapability(kj::Own<TypelessCapability>&& cap);
void setCapability(kj::Own<ClientHook>&& cap);
// Set methods: Initialize the pointer to a newly-allocated copy of the given value, discarding
// the existing object.
......@@ -321,6 +324,12 @@ public:
PointerReader asReader() const;
BuilderArena& getArena() const;
// Get the arena containing this pointer.
PointerBuilder imbue(ImbuedBuilderArena& newArena) const;
// Imbue the pointer with a capability context, returning the imbued pointer.
private:
SegmentBuilder* segment; // Memory segment in which the pointer resides.
WirePointer* pointer; // Pointer to the pointer.
......@@ -342,7 +351,7 @@ public:
ListReader getList(FieldSize expectedElementSize, const word* defaultValue) const;
template <typename T>
typename T::Reader getBlob(const void* defaultValue, ByteCount defaultSize) const;
kj::Own<TypelessCapability> getCapability();
kj::Own<ClientHook> getCapability();
// Get methods: Get the value. If it is null, return the default value instead.
// The default value is encoded as an "unchecked message" for structs, lists, and objects, or a
// simple byte array for blobs.
......@@ -352,6 +361,12 @@ public:
// word* can actually be passed to readUnchecked() to read the designated sub-object later. If
// this isn't an unchecked message, throws an exception.
kj::Maybe<Arena&> getArena() const;
// Get the arena containing this pointer.
PointerReader imbue(ImbuedReaderArena& newArena) const;
// Imbue the pointer with a capability context, returning the imbued pointer.
private:
SegmentReader* segment; // Memory segment in which the pointer resides.
const WirePointer* pointer; // Pointer to the pointer. null = treat as null pointer.
......
......@@ -63,6 +63,7 @@ struct ObjectPointer {
_::PointerReader reader;
friend struct ObjectPointer;
friend class Orphanage;
friend class CapReaderContext;
};
class Builder {
......@@ -143,6 +144,7 @@ struct ObjectPointer {
private:
_::PointerBuilder builder;
friend class CapBuilderContext;
};
};
......@@ -281,6 +283,38 @@ inline Orphan<T> Orphan<ObjectPointer>::releaseAs() {
return Orphan<T>(kj::mv(builder));
}
// Using ObjectPointer as the template type should work...
template <>
inline typename ObjectPointer::Reader ObjectPointer::Reader::getAs<ObjectPointer>() {
return *this;
}
template <>
inline typename ObjectPointer::Builder ObjectPointer::Builder::getAs<ObjectPointer>() {
return *this;
}
template <>
inline typename ObjectPointer::Builder ObjectPointer::Builder::initAs<ObjectPointer>() {
clear();
return *this;
}
template <>
inline void ObjectPointer::Builder::setAs<ObjectPointer>(ObjectPointer::Reader value) {
return builder.copyFrom(value.reader);
}
template <>
inline void ObjectPointer::Builder::adopt<ObjectPointer>(Orphan<ObjectPointer>&& orphan) {
builder.adopt(kj::mv(orphan.builder));
}
template <>
inline Orphan<ObjectPointer> ObjectPointer::Builder::disownAs<ObjectPointer>() {
return Orphan<ObjectPointer>(builder.disown());
}
template <>
inline Orphan<ObjectPointer> Orphan<ObjectPointer>::releaseAs() {
return kj::mv(*this);
}
} // namespace capnp
#endif // CAPNP_OBJECT_H_
......@@ -950,7 +950,7 @@ using JoinAnswer = Object;
#
# # Level 4 features -----------------------------------------------
#
# newJoiner(count :UInt32): NewJoinerResponse;
# newJoiner(count :UInt32) :NewJoinerResponse;
# # Prepare a new Join operation, which will eventually lead to forming a new direct connection
# # to the host of the joined capability. `count` is the number of capabilities to join.
#
......@@ -973,7 +973,7 @@ using JoinAnswer = Object;
# # message on it with the specified `ProvisionId` in order to receive the final capability.
# }
#
# acceptConnectionFromJoiner(parts: List(JoinKeyPart), paths :List(VatPath))
# acceptConnectionFromJoiner(parts :List(JoinKeyPart), paths :List(VatPath))
# :ConnectionAndProvisionId;
# # Called on a joined capability's host to receive the connection from the joiner, once all
# # key parts have arrived. The caller should expect to receive an `Accept` message over the
......@@ -1002,17 +1002,17 @@ using JoinAnswer = Object;
# sendToTarget :RecipientId;
# }
#
# connectToIntroduced(capId: ThirdPartyCapId) :ConnectionAndProvisionId;
# connectToIntroduced(capId :ThirdPartyCapId) :ConnectionAndProvisionId;
# # Given a ThirdPartyCapId received over this connection, connect to the third party. The
# # caller should then send an `Accept` message over the new connection.
#
# acceptIntroducedConnection(recipientId: RecipientId): Connection
# acceptIntroducedConnection(recipientId :RecipientId) :Connection;
# # Given a RecipientId received in a `Provide` message on this `Connection`, wait for the
# # recipient to connect, and return the connection formed. Usually, the first message received
# # on the new connection will be an `Accept` message.
# }
#
# sturct ConnectionAndProvisionId {
# struct ConnectionAndProvisionId {
# # **(level 3)**
#
# connection :Connection;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment