Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
capnproto
Commits
c3b381ee
Commit
c3b381ee
authored
Jul 08, 2015
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add framework for capability membranes.
parent
cbd18b28
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
920 additions
and
4 deletions
+920
-4
Makefile.am
c++/Makefile.am
+3
-0
CMakeLists.txt
c++/src/capnp/CMakeLists.txt
+3
-0
capability.h
c++/src/capnp/capability.h
+6
-0
layout.c++
c++/src/capnp/layout.c++
+4
-0
layout.h
c++/src/capnp/layout.h
+3
-0
membrane-test.c++
c++/src/capnp/membrane-test.c++
+286
-0
membrane.c++
c++/src/capnp/membrane.c++
+438
-0
membrane.h
c++/src/capnp/membrane.h
+151
-0
rpc-twoparty.c++
c++/src/capnp/rpc-twoparty.c++
+6
-3
rpc-twoparty.h
c++/src/capnp/rpc-twoparty.h
+4
-1
test.capnp
c++/src/capnp/test.capnp
+16
-0
No files found.
c++/Makefile.am
View file @
c3b381ee
...
...
@@ -161,6 +161,7 @@ includecapnp_HEADERS = \
src/capnp/any.h
\
src/capnp/message.h
\
src/capnp/capability.h
\
src/capnp/membrane.h
\
src/capnp/schema.capnp.h
\
src/capnp/schema-lite.h
\
src/capnp/schema.h
\
...
...
@@ -255,6 +256,7 @@ libcapnp_rpc_la_LDFLAGS = -release $(SO_VERSION) -no-undefined
libcapnp_rpc_la_SOURCES
=
\
src/capnp/serialize-async.c++
\
src/capnp/capability.c++
\
src/capnp/membrane.c++
\
src/capnp/dynamic-capability.c++
\
src/capnp/rpc.c++
\
src/capnp/rpc.capnp.c++
\
...
...
@@ -373,6 +375,7 @@ heavy_tests = \
src/kj/parse/char-test.c++
\
src/kj/std/iostream-test.c++
\
src/capnp/capability-test.c++
\
src/capnp/membrane-test.c++
\
src/capnp/schema-test.c++
\
src/capnp/schema-loader-test.c++
\
src/capnp/schema-parser-test.c++
\
...
...
c++/src/capnp/CMakeLists.txt
View file @
c3b381ee
...
...
@@ -36,6 +36,7 @@ set(capnp_headers
any.h
message.h
capability.h
membrane.h
dynamic.h
schema.h
schema.capnp.h
...
...
@@ -62,6 +63,7 @@ install(FILES ${capnp_headers} ${capnp_schemas} DESTINATION "${INCLUDE_INSTALL_D
set
(
capnp-rpc_sources
serialize-async.c++
capability.c++
membrane.c++
dynamic-capability.c++
rpc.c++
rpc.capnp.c++
...
...
@@ -209,6 +211,7 @@ if(BUILD_TESTING)
add_executable
(
capnp-heavy-tests
endian-reverse-test.c++
capability-test.c++
membrane-test.c++
schema-test.c++
schema-loader-test.c++
schema-parser-test.c++
...
...
c++/src/capnp/capability.h
View file @
c3b381ee
...
...
@@ -139,6 +139,7 @@ private:
template
<
typename
,
typename
>
friend
class
Request
;
friend
class
ResponseHook
;
};
class
Capability
::
Client
{
...
...
@@ -488,6 +489,11 @@ class ResponseHook {
public
:
virtual
~
ResponseHook
()
noexcept
(
false
);
// Just here to make sure the type is dynamic.
template
<
typename
T
>
inline
static
kj
::
Own
<
ResponseHook
>
from
(
Response
<
T
>&&
response
)
{
return
kj
::
mv
(
response
.
hook
);
}
};
// class PipelineHook is declared in any.h because it is needed there.
...
...
c++/src/capnp/layout.c++
View file @
c3b381ee
...
...
@@ -2440,6 +2440,10 @@ kj::Maybe<Arena&> PointerReader::getArena() const {
return
segment
==
nullptr
?
nullptr
:
segment
->
getArena
();
}
CapTableReader
*
PointerReader
::
getCapTable
()
{
return
capTable
;
}
PointerReader
PointerReader
::
imbue
(
CapTableReader
*
capTable
)
const
{
auto
result
=
*
this
;
result
.
capTable
=
capTable
;
...
...
c++/src/capnp/layout.h
View file @
c3b381ee
...
...
@@ -411,6 +411,9 @@ public:
kj
::
Maybe
<
Arena
&>
getArena
()
const
;
// Get the arena containing this pointer.
CapTableReader
*
getCapTable
();
// Gets the capability context in which this object is operating.
PointerReader
imbue
(
CapTableReader
*
capTable
)
const
;
// Return a copy of this reader except using the given capability context.
...
...
c++/src/capnp/membrane-test.c++
0 → 100644
View file @
c3b381ee
// Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "membrane.h"
#include <kj/test.h>
#include "test-util.h"
#include <kj/function.h>
#include <kj/async-io.h>
#include "rpc-twoparty.h"
namespace
capnp
{
namespace
_
{
namespace
{
using
Thing
=
test
::
TestMembrane
::
Thing
;
class
ThingImpl
:
public
Thing
::
Server
{
public
:
ThingImpl
(
kj
::
StringPtr
text
)
:
text
(
text
)
{}
protected
:
kj
::
Promise
<
void
>
passThrough
(
PassThroughContext
context
)
override
{
context
.
getResults
().
setText
(
text
);
return
kj
::
READY_NOW
;
}
kj
::
Promise
<
void
>
intercept
(
InterceptContext
context
)
override
{
context
.
getResults
().
setText
(
text
);
return
kj
::
READY_NOW
;
}
private
:
kj
::
StringPtr
text
;
};
class
TestMembraneImpl
:
public
test
::
TestMembrane
::
Server
{
protected
:
kj
::
Promise
<
void
>
makeThing
(
MakeThingContext
context
)
override
{
context
.
getResults
().
setThing
(
kj
::
heap
<
ThingImpl
>
(
"inside"
));
return
kj
::
READY_NOW
;
}
kj
::
Promise
<
void
>
callPassThrough
(
CallPassThroughContext
context
)
override
{
auto
params
=
context
.
getParams
();
auto
req
=
params
.
getThing
().
passThroughRequest
();
if
(
params
.
getTailCall
())
{
return
context
.
tailCall
(
kj
::
mv
(
req
));
}
else
{
return
req
.
send
().
then
([
context
](
Response
<
test
::
TestMembrane
::
Result
>&&
result
)
mutable
{
context
.
setResults
(
result
);
});
}
}
kj
::
Promise
<
void
>
callIntercept
(
CallInterceptContext
context
)
override
{
auto
params
=
context
.
getParams
();
auto
req
=
params
.
getThing
().
interceptRequest
();
if
(
params
.
getTailCall
())
{
return
context
.
tailCall
(
kj
::
mv
(
req
));
}
else
{
return
req
.
send
().
then
([
context
](
Response
<
test
::
TestMembrane
::
Result
>&&
result
)
mutable
{
context
.
setResults
(
result
);
});
}
}
kj
::
Promise
<
void
>
loopback
(
LoopbackContext
context
)
override
{
context
.
getResults
().
setThing
(
context
.
getParams
().
getThing
());
return
kj
::
READY_NOW
;
}
};
class
MembranePolicyImpl
:
public
MembranePolicy
,
public
kj
::
Refcounted
{
public
:
kj
::
Maybe
<
Capability
::
Client
>
inboundCall
(
uint64_t
interfaceId
,
uint16_t
methodId
)
override
{
if
(
interfaceId
==
capnp
::
typeId
<
Thing
>
()
&&
methodId
==
1
)
{
return
Capability
::
Client
(
kj
::
heap
<
ThingImpl
>
(
"inbound"
));
}
else
{
return
nullptr
;
}
}
kj
::
Maybe
<
Capability
::
Client
>
outboundCall
(
uint64_t
interfaceId
,
uint16_t
methodId
)
override
{
if
(
interfaceId
==
capnp
::
typeId
<
Thing
>
()
&&
methodId
==
1
)
{
return
Capability
::
Client
(
kj
::
heap
<
ThingImpl
>
(
"outbound"
));
}
else
{
return
nullptr
;
}
}
kj
::
Own
<
MembranePolicy
>
addRef
()
override
{
return
kj
::
addRef
(
*
this
);
}
};
void
testThingImpl
(
kj
::
WaitScope
&
waitScope
,
test
::
TestMembrane
::
Client
membraned
,
kj
::
Function
<
Thing
::
Client
()
>
makeThing
,
kj
::
StringPtr
localPassThrough
,
kj
::
StringPtr
localIntercept
,
kj
::
StringPtr
remotePassThrough
,
kj
::
StringPtr
remoteIntercept
)
{
KJ_EXPECT
(
makeThing
().
passThroughRequest
().
send
().
wait
(
waitScope
).
getText
()
==
localPassThrough
);
KJ_EXPECT
(
makeThing
().
interceptRequest
().
send
().
wait
(
waitScope
).
getText
()
==
localIntercept
);
{
auto
req
=
membraned
.
callPassThroughRequest
();
req
.
setThing
(
makeThing
());
req
.
setTailCall
(
false
);
KJ_EXPECT
(
req
.
send
().
wait
(
waitScope
).
getText
()
==
remotePassThrough
);
}
{
auto
req
=
membraned
.
callInterceptRequest
();
req
.
setThing
(
makeThing
());
req
.
setTailCall
(
false
);
KJ_EXPECT
(
req
.
send
().
wait
(
waitScope
).
getText
()
==
remoteIntercept
);
}
{
auto
req
=
membraned
.
callPassThroughRequest
();
req
.
setThing
(
makeThing
());
req
.
setTailCall
(
true
);
KJ_EXPECT
(
req
.
send
().
wait
(
waitScope
).
getText
()
==
remotePassThrough
);
}
{
auto
req
=
membraned
.
callInterceptRequest
();
req
.
setThing
(
makeThing
());
req
.
setTailCall
(
true
);
KJ_EXPECT
(
req
.
send
().
wait
(
waitScope
).
getText
()
==
remoteIntercept
);
}
}
struct
TestEnv
{
kj
::
EventLoop
loop
;
kj
::
WaitScope
waitScope
;
test
::
TestMembrane
::
Client
membraned
;
TestEnv
()
:
waitScope
(
loop
),
membraned
(
membrane
(
kj
::
heap
<
TestMembraneImpl
>
(),
kj
::
refcounted
<
MembranePolicyImpl
>
()))
{}
void
testThing
(
kj
::
Function
<
Thing
::
Client
()
>
makeThing
,
kj
::
StringPtr
localPassThrough
,
kj
::
StringPtr
localIntercept
,
kj
::
StringPtr
remotePassThrough
,
kj
::
StringPtr
remoteIntercept
)
{
testThingImpl
(
waitScope
,
membraned
,
kj
::
mv
(
makeThing
),
localPassThrough
,
localIntercept
,
remotePassThrough
,
remoteIntercept
);
}
};
KJ_TEST
(
"call local object inside membrane"
)
{
TestEnv
env
;
env
.
testThing
([
&
]()
{
return
env
.
membraned
.
makeThingRequest
().
send
().
wait
(
env
.
waitScope
).
getThing
();
},
"inside"
,
"inbound"
,
"inside"
,
"inside"
);
}
KJ_TEST
(
"call local promise inside membrane"
)
{
TestEnv
env
;
env
.
testThing
([
&
]()
{
return
env
.
membraned
.
makeThingRequest
().
send
().
getThing
();
},
"inside"
,
"inbound"
,
"inside"
,
"inside"
);
}
KJ_TEST
(
"call local resolved promise inside membrane"
)
{
TestEnv
env
;
env
.
testThing
([
&
]()
{
auto
thing
=
env
.
membraned
.
makeThingRequest
().
send
().
getThing
();
thing
.
whenResolved
().
wait
(
env
.
waitScope
);
return
thing
;
},
"inside"
,
"inbound"
,
"inside"
,
"inside"
);
}
KJ_TEST
(
"call local object outside membrane"
)
{
TestEnv
env
;
env
.
testThing
([
&
]()
{
return
kj
::
heap
<
ThingImpl
>
(
"outside"
);
},
"outside"
,
"outside"
,
"outside"
,
"outbound"
);
}
KJ_TEST
(
"call local capability that has passed into and back out of membrane"
)
{
TestEnv
env
;
env
.
testThing
([
&
]()
{
auto
req
=
env
.
membraned
.
loopbackRequest
();
req
.
setThing
(
kj
::
heap
<
ThingImpl
>
(
"outside"
));
return
req
.
send
().
wait
(
env
.
waitScope
).
getThing
();
},
"outside"
,
"outside"
,
"outside"
,
"outbound"
);
}
KJ_TEST
(
"call local promise pointing into membrane that eventually resolves to outside"
)
{
TestEnv
env
;
env
.
testThing
([
&
]()
{
auto
req
=
env
.
membraned
.
loopbackRequest
();
req
.
setThing
(
kj
::
heap
<
ThingImpl
>
(
"outside"
));
return
req
.
send
().
getThing
();
},
"outside"
,
"outside"
,
"outside"
,
"outbound"
);
}
struct
TestRpcEnv
{
kj
::
AsyncIoContext
io
;
kj
::
TwoWayPipe
pipe
;
TwoPartyClient
client
;
TwoPartyClient
server
;
test
::
TestMembrane
::
Client
membraned
;
TestRpcEnv
()
:
io
(
kj
::
setupAsyncIo
()),
pipe
(
io
.
provider
->
newTwoWayPipe
()),
client
(
*
pipe
.
ends
[
0
]),
server
(
*
pipe
.
ends
[
1
],
membrane
(
kj
::
heap
<
TestMembraneImpl
>
(),
kj
::
refcounted
<
MembranePolicyImpl
>
()),
rpc
::
twoparty
::
Side
::
SERVER
),
membraned
(
client
.
bootstrap
().
castAs
<
test
::
TestMembrane
>
())
{}
void
testThing
(
kj
::
Function
<
Thing
::
Client
()
>
makeThing
,
kj
::
StringPtr
localPassThrough
,
kj
::
StringPtr
localIntercept
,
kj
::
StringPtr
remotePassThrough
,
kj
::
StringPtr
remoteIntercept
)
{
testThingImpl
(
io
.
waitScope
,
membraned
,
kj
::
mv
(
makeThing
),
localPassThrough
,
localIntercept
,
remotePassThrough
,
remoteIntercept
);
}
};
KJ_TEST
(
"call remote object inside membrane"
)
{
TestRpcEnv
env
;
env
.
testThing
([
&
]()
{
return
env
.
membraned
.
makeThingRequest
().
send
().
wait
(
env
.
io
.
waitScope
).
getThing
();
},
"inside"
,
"inbound"
,
"inside"
,
"inside"
);
}
KJ_TEST
(
"call remote promise inside membrane"
)
{
TestRpcEnv
env
;
env
.
testThing
([
&
]()
{
return
env
.
membraned
.
makeThingRequest
().
send
().
getThing
();
},
"inside"
,
"inbound"
,
"inside"
,
"inside"
);
}
KJ_TEST
(
"call remote resolved promise inside membrane"
)
{
TestEnv
env
;
env
.
testThing
([
&
]()
{
auto
thing
=
env
.
membraned
.
makeThingRequest
().
send
().
getThing
();
thing
.
whenResolved
().
wait
(
env
.
waitScope
);
return
thing
;
},
"inside"
,
"inbound"
,
"inside"
,
"inside"
);
}
KJ_TEST
(
"call remote object outside membrane"
)
{
TestRpcEnv
env
;
env
.
testThing
([
&
]()
{
return
kj
::
heap
<
ThingImpl
>
(
"outside"
);
},
"outside"
,
"outside"
,
"outside"
,
"outbound"
);
}
KJ_TEST
(
"call remote capability that has passed into and back out of membrane"
)
{
TestRpcEnv
env
;
env
.
testThing
([
&
]()
{
auto
req
=
env
.
membraned
.
loopbackRequest
();
req
.
setThing
(
kj
::
heap
<
ThingImpl
>
(
"outside"
));
return
req
.
send
().
wait
(
env
.
io
.
waitScope
).
getThing
();
},
"outside"
,
"outside"
,
"outside"
,
"outbound"
);
}
KJ_TEST
(
"call remote promise pointing into membrane that eventually resolves to outside"
)
{
TestRpcEnv
env
;
env
.
testThing
([
&
]()
{
auto
req
=
env
.
membraned
.
loopbackRequest
();
req
.
setThing
(
kj
::
heap
<
ThingImpl
>
(
"outside"
));
return
req
.
send
().
getThing
();
},
"outside"
,
"outside"
,
"outside"
,
"outbound"
);
}
}
// namespace
}
// namespace _
}
// namespace capnp
c++/src/capnp/membrane.c++
0 → 100644
View file @
c3b381ee
// Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "membrane.h"
#include <kj/debug.h>
namespace
capnp
{
namespace
{
static
const
char
DUMMY
=
0
;
static
constexpr
const
void
*
MEMBRANE_BRAND
=
&
DUMMY
;
kj
::
Own
<
ClientHook
>
membrane
(
kj
::
Own
<
ClientHook
>
inner
,
MembranePolicy
&
policy
,
bool
reverse
);
class
MembraneCapTableReader
final
:
public
_
::
CapTableReader
{
public
:
MembraneCapTableReader
(
MembranePolicy
&
policy
,
bool
reverse
)
:
policy
(
policy
),
reverse
(
reverse
)
{}
AnyPointer
::
Reader
imbue
(
AnyPointer
::
Reader
reader
)
{
KJ_REQUIRE
(
inner
==
nullptr
,
"can only call this once"
);
auto
pointerReader
=
_
::
PointerHelpers
<
AnyPointer
>::
getInternalReader
(
kj
::
mv
(
reader
));
inner
=
pointerReader
.
getCapTable
();
return
AnyPointer
::
Reader
(
pointerReader
.
imbue
(
this
));
}
kj
::
Maybe
<
kj
::
Own
<
ClientHook
>>
extractCap
(
uint
index
)
override
{
// The underlying message is inside the membrane, and we're pulling a cap out of it. Therefore,
// we want to wrap the extracted capability in the membrane.
return
inner
->
extractCap
(
index
).
map
([
this
](
kj
::
Own
<
ClientHook
>&&
cap
)
{
return
membrane
(
kj
::
mv
(
cap
),
policy
,
reverse
);
});
}
private
:
_
::
CapTableReader
*
inner
=
nullptr
;
MembranePolicy
&
policy
;
bool
reverse
;
};
class
MembraneCapTableBuilder
final
:
public
_
::
CapTableBuilder
{
public
:
MembraneCapTableBuilder
(
MembranePolicy
&
policy
,
bool
reverse
)
:
policy
(
policy
),
reverse
(
reverse
)
{}
AnyPointer
::
Builder
imbue
(
AnyPointer
::
Builder
builder
)
{
KJ_REQUIRE
(
inner
==
nullptr
,
"can only call this once"
);
auto
pointerBuilder
=
_
::
PointerHelpers
<
AnyPointer
>::
getInternalBuilder
(
kj
::
mv
(
builder
));
inner
=
pointerBuilder
.
getCapTable
();
return
AnyPointer
::
Builder
(
pointerBuilder
.
imbue
(
this
));
}
AnyPointer
::
Builder
unimbue
(
AnyPointer
::
Builder
builder
)
{
auto
pointerBuilder
=
_
::
PointerHelpers
<
AnyPointer
>::
getInternalBuilder
(
kj
::
mv
(
builder
));
KJ_REQUIRE
(
pointerBuilder
.
getCapTable
()
==
this
);
return
AnyPointer
::
Builder
(
pointerBuilder
.
imbue
(
inner
));
}
kj
::
Maybe
<
kj
::
Own
<
ClientHook
>>
extractCap
(
uint
index
)
override
{
// The underlying message is inside the membrane, and we're pulling a cap out of it. Therefore,
// we want to wrap the extracted capability in the membrane.
return
inner
->
extractCap
(
index
).
map
([
this
](
kj
::
Own
<
ClientHook
>&&
cap
)
{
return
membrane
(
kj
::
mv
(
cap
),
policy
,
reverse
);
});
}
uint
injectCap
(
kj
::
Own
<
ClientHook
>&&
cap
)
override
{
// The underlying message is inside the membrane, and we're inserting a cap from outside into
// it. Therefore we want to add a reverse membrane.
return
inner
->
injectCap
(
membrane
(
kj
::
mv
(
cap
),
policy
,
!
reverse
));
}
void
dropCap
(
uint
index
)
override
{
inner
->
dropCap
(
index
);
}
private
:
_
::
CapTableBuilder
*
inner
=
nullptr
;
MembranePolicy
&
policy
;
bool
reverse
;
};
class
MembranePipelineHook
final
:
public
PipelineHook
,
public
kj
::
Refcounted
{
public
:
MembranePipelineHook
(
kj
::
Own
<
PipelineHook
>&&
inner
,
kj
::
Own
<
MembranePolicy
>&&
policy
,
bool
reverse
)
:
inner
(
kj
::
mv
(
inner
)),
policy
(
kj
::
mv
(
policy
)),
reverse
(
reverse
)
{}
kj
::
Own
<
PipelineHook
>
addRef
()
override
{
return
kj
::
addRef
(
*
this
);
}
kj
::
Own
<
ClientHook
>
getPipelinedCap
(
kj
::
ArrayPtr
<
const
PipelineOp
>
ops
)
override
{
return
membrane
(
inner
->
getPipelinedCap
(
ops
),
*
policy
,
reverse
);
}
kj
::
Own
<
ClientHook
>
getPipelinedCap
(
kj
::
Array
<
PipelineOp
>&&
ops
)
override
{
return
membrane
(
inner
->
getPipelinedCap
(
kj
::
mv
(
ops
)),
*
policy
,
reverse
);
}
private
:
kj
::
Own
<
PipelineHook
>
inner
;
kj
::
Own
<
MembranePolicy
>
policy
;
bool
reverse
;
};
class
MembraneResponseHook
final
:
public
ResponseHook
{
public
:
MembraneResponseHook
(
kj
::
Own
<
ResponseHook
>&&
inner
,
kj
::
Own
<
MembranePolicy
>&&
policy
,
bool
reverse
)
:
inner
(
kj
::
mv
(
inner
)),
policy
(
kj
::
mv
(
policy
)),
capTable
(
*
this
->
policy
,
reverse
)
{}
AnyPointer
::
Reader
imbue
(
AnyPointer
::
Reader
reader
)
{
return
capTable
.
imbue
(
reader
);
}
private
:
kj
::
Own
<
ResponseHook
>
inner
;
kj
::
Own
<
MembranePolicy
>
policy
;
MembraneCapTableReader
capTable
;
};
class
MembraneRequestHook
final
:
public
RequestHook
{
public
:
MembraneRequestHook
(
kj
::
Own
<
RequestHook
>&&
inner
,
kj
::
Own
<
MembranePolicy
>&&
policy
,
bool
reverse
)
:
inner
(
kj
::
mv
(
inner
)),
policy
(
kj
::
mv
(
policy
)),
reverse
(
reverse
),
capTable
(
*
this
->
policy
,
reverse
)
{}
static
Request
<
AnyPointer
,
AnyPointer
>
wrap
(
Request
<
AnyPointer
,
AnyPointer
>&&
inner
,
MembranePolicy
&
policy
,
bool
reverse
)
{
AnyPointer
::
Builder
builder
=
inner
;
auto
innerHook
=
RequestHook
::
from
(
kj
::
mv
(
inner
));
if
(
innerHook
->
getBrand
()
==
MEMBRANE_BRAND
)
{
auto
&
otherMembrane
=
kj
::
downcast
<
MembraneRequestHook
>
(
*
innerHook
);
if
(
otherMembrane
.
policy
.
get
()
==
&
policy
&&
otherMembrane
.
reverse
==
!
reverse
)
{
// Request that passed across the membrane one way is now passing back the other way.
// Unwrap it rather than double-wrap it.
builder
=
otherMembrane
.
capTable
.
unimbue
(
builder
);
return
{
builder
,
kj
::
mv
(
otherMembrane
.
inner
)
};
}
}
auto
newHook
=
kj
::
heap
<
MembraneRequestHook
>
(
kj
::
mv
(
innerHook
),
policy
.
addRef
(),
reverse
);
builder
=
newHook
->
capTable
.
imbue
(
builder
);
return
{
builder
,
kj
::
mv
(
newHook
)
};
}
static
kj
::
Own
<
RequestHook
>
wrap
(
kj
::
Own
<
RequestHook
>&&
inner
,
MembranePolicy
&
policy
,
bool
reverse
)
{
if
(
inner
->
getBrand
()
==
MEMBRANE_BRAND
)
{
auto
&
otherMembrane
=
kj
::
downcast
<
MembraneRequestHook
>
(
*
inner
);
if
(
otherMembrane
.
policy
.
get
()
==
&
policy
&&
otherMembrane
.
reverse
==
!
reverse
)
{
// Request that passed across the membrane one way is now passing back the other way.
// Unwrap it rather than double-wrap it.
return
kj
::
mv
(
otherMembrane
.
inner
);
}
}
return
kj
::
heap
<
MembraneRequestHook
>
(
kj
::
mv
(
inner
),
policy
.
addRef
(),
reverse
);
}
RemotePromise
<
AnyPointer
>
send
()
override
{
auto
promise
=
inner
->
send
();
auto
newPipeline
=
AnyPointer
::
Pipeline
(
kj
::
refcounted
<
MembranePipelineHook
>
(
PipelineHook
::
from
(
kj
::
mv
(
promise
)),
policy
->
addRef
(),
reverse
));
bool
reverse
=
this
->
reverse
;
// for capture
auto
newPromise
=
promise
.
then
(
kj
::
mvCapture
(
policy
,
[
reverse
](
kj
::
Own
<
MembranePolicy
>&&
policy
,
Response
<
AnyPointer
>&&
response
)
{
AnyPointer
::
Reader
reader
=
response
;
auto
newRespHook
=
kj
::
heap
<
MembraneResponseHook
>
(
ResponseHook
::
from
(
kj
::
mv
(
response
)),
policy
->
addRef
(),
reverse
);
reader
=
newRespHook
->
imbue
(
reader
);
return
Response
<
AnyPointer
>
(
reader
,
kj
::
mv
(
newRespHook
));
}));
return
RemotePromise
<
AnyPointer
>
(
kj
::
mv
(
newPromise
),
kj
::
mv
(
newPipeline
));
}
const
void
*
getBrand
()
override
{
return
MEMBRANE_BRAND
;
}
private
:
kj
::
Own
<
RequestHook
>
inner
;
kj
::
Own
<
MembranePolicy
>
policy
;
bool
reverse
;
MembraneCapTableBuilder
capTable
;
};
class
MembraneCallContextHook
final
:
public
CallContextHook
,
public
kj
::
Refcounted
{
public
:
MembraneCallContextHook
(
kj
::
Own
<
CallContextHook
>&&
inner
,
kj
::
Own
<
MembranePolicy
>&&
policy
,
bool
reverse
)
:
inner
(
kj
::
mv
(
inner
)),
policy
(
kj
::
mv
(
policy
)),
reverse
(
reverse
),
paramsCapTable
(
*
this
->
policy
,
reverse
),
resultsCapTable
(
*
this
->
policy
,
reverse
)
{}
AnyPointer
::
Reader
getParams
()
override
{
KJ_REQUIRE
(
!
releasedParams
);
KJ_IF_MAYBE
(
p
,
params
)
{
return
*
p
;
}
else
{
auto
result
=
paramsCapTable
.
imbue
(
inner
->
getParams
());
params
=
result
;
return
result
;
}
}
void
releaseParams
()
override
{
KJ_REQUIRE
(
!
releasedParams
);
releasedParams
=
true
;
inner
->
releaseParams
();
}
AnyPointer
::
Builder
getResults
(
kj
::
Maybe
<
MessageSize
>
sizeHint
)
override
{
KJ_IF_MAYBE
(
r
,
results
)
{
return
*
r
;
}
else
{
auto
result
=
resultsCapTable
.
imbue
(
inner
->
getResults
(
sizeHint
));
results
=
result
;
return
result
;
}
}
kj
::
Promise
<
void
>
tailCall
(
kj
::
Own
<
RequestHook
>&&
request
)
override
{
return
inner
->
tailCall
(
MembraneRequestHook
::
wrap
(
kj
::
mv
(
request
),
*
policy
,
!
reverse
));
}
void
allowCancellation
()
override
{
inner
->
allowCancellation
();
}
kj
::
Promise
<
AnyPointer
::
Pipeline
>
onTailCall
()
override
{
return
inner
->
onTailCall
().
then
([
this
](
AnyPointer
::
Pipeline
&&
innerPipeline
)
{
return
AnyPointer
::
Pipeline
(
kj
::
refcounted
<
MembranePipelineHook
>
(
PipelineHook
::
from
(
kj
::
mv
(
innerPipeline
)),
policy
->
addRef
(),
reverse
));
});
}
ClientHook
::
VoidPromiseAndPipeline
directTailCall
(
kj
::
Own
<
RequestHook
>&&
request
)
override
{
auto
pair
=
inner
->
directTailCall
(
MembraneRequestHook
::
wrap
(
kj
::
mv
(
request
),
*
policy
,
!
reverse
));
return
{
kj
::
mv
(
pair
.
promise
),
kj
::
refcounted
<
MembranePipelineHook
>
(
kj
::
mv
(
pair
.
pipeline
),
policy
->
addRef
(),
reverse
)
};
}
kj
::
Own
<
CallContextHook
>
addRef
()
override
{
return
kj
::
addRef
(
*
this
);
}
private
:
kj
::
Own
<
CallContextHook
>
inner
;
kj
::
Own
<
MembranePolicy
>
policy
;
bool
reverse
;
MembraneCapTableReader
paramsCapTable
;
kj
::
Maybe
<
AnyPointer
::
Reader
>
params
;
bool
releasedParams
=
false
;
MembraneCapTableBuilder
resultsCapTable
;
kj
::
Maybe
<
AnyPointer
::
Builder
>
results
;
};
class
MembraneHook
final
:
public
ClientHook
,
public
kj
::
Refcounted
{
public
:
MembraneHook
(
kj
::
Own
<
ClientHook
>&&
inner
,
kj
::
Own
<
MembranePolicy
>&&
policy
,
bool
reverse
)
:
inner
(
kj
::
mv
(
inner
)),
policy
(
kj
::
mv
(
policy
)),
reverse
(
reverse
)
{}
static
kj
::
Own
<
ClientHook
>
wrap
(
ClientHook
&
cap
,
MembranePolicy
&
policy
,
bool
reverse
)
{
if
(
cap
.
getBrand
()
==
MEMBRANE_BRAND
)
{
auto
&
otherMembrane
=
kj
::
downcast
<
MembraneHook
>
(
cap
);
if
(
otherMembrane
.
policy
.
get
()
==
&
policy
&&
otherMembrane
.
reverse
==
!
reverse
)
{
// Capability that passed across the membrane one way is now passing back the other way.
// Unwrap it rather than double-wrap it.
return
otherMembrane
.
inner
->
addRef
();
}
}
return
kj
::
refcounted
<
MembraneHook
>
(
cap
.
addRef
(),
policy
.
addRef
(),
reverse
);
}
static
kj
::
Own
<
ClientHook
>
wrap
(
kj
::
Own
<
ClientHook
>
cap
,
MembranePolicy
&
policy
,
bool
reverse
)
{
if
(
cap
->
getBrand
()
==
MEMBRANE_BRAND
)
{
auto
&
otherMembrane
=
kj
::
downcast
<
MembraneHook
>
(
*
cap
);
if
(
otherMembrane
.
policy
.
get
()
==
&
policy
&&
otherMembrane
.
reverse
==
!
reverse
)
{
// Capability that passed across the membrane one way is now passing back the other way.
// Unwrap it rather than double-wrap it.
return
otherMembrane
.
inner
->
addRef
();
}
}
return
kj
::
refcounted
<
MembraneHook
>
(
kj
::
mv
(
cap
),
policy
.
addRef
(),
reverse
);
}
Request
<
AnyPointer
,
AnyPointer
>
newCall
(
uint64_t
interfaceId
,
uint16_t
methodId
,
kj
::
Maybe
<
MessageSize
>
sizeHint
)
override
{
KJ_IF_MAYBE
(
r
,
resolved
)
{
return
r
->
get
()
->
newCall
(
interfaceId
,
methodId
,
sizeHint
);
}
auto
redirect
=
reverse
?
policy
->
outboundCall
(
interfaceId
,
methodId
)
:
policy
->
inboundCall
(
interfaceId
,
methodId
);
KJ_IF_MAYBE
(
r
,
redirect
)
{
// The policy says that *if* this capability points into the membrane, then we want to
// redirect the call. However, if this capability is a promise, then it could resolve to
// something outside the membrane later. We have to wait before we actually redirect,
// otherwise behavior will differ depending on whether the promise is resolved.
KJ_IF_MAYBE
(
p
,
whenMoreResolved
())
{
return
newLocalPromiseClient
(
kj
::
mv
(
*
p
))
->
newCall
(
interfaceId
,
methodId
,
sizeHint
);
}
return
ClientHook
::
from
(
kj
::
mv
(
*
r
))
->
newCall
(
interfaceId
,
methodId
,
sizeHint
);
}
else
{
// For pass-through calls, we don't worry about promises, because if the capability resolves
// to something outside the membrane, then the call will pass back out of the membrane too.
return
MembraneRequestHook
::
wrap
(
inner
->
newCall
(
interfaceId
,
methodId
,
sizeHint
),
*
policy
,
reverse
);
}
}
VoidPromiseAndPipeline
call
(
uint64_t
interfaceId
,
uint16_t
methodId
,
kj
::
Own
<
CallContextHook
>&&
context
)
override
{
KJ_IF_MAYBE
(
r
,
resolved
)
{
return
r
->
get
()
->
call
(
interfaceId
,
methodId
,
kj
::
mv
(
context
));
}
auto
redirect
=
reverse
?
policy
->
outboundCall
(
interfaceId
,
methodId
)
:
policy
->
inboundCall
(
interfaceId
,
methodId
);
KJ_IF_MAYBE
(
r
,
redirect
)
{
// The policy says that *if* this capability points into the membrane, then we want to
// redirect the call. However, if this capability is a promise, then it could resolve to
// something outside the membrane later. We have to wait before we actually redirect,
// otherwise behavior will differ depending on whether the promise is resolved.
KJ_IF_MAYBE
(
p
,
whenMoreResolved
())
{
return
newLocalPromiseClient
(
kj
::
mv
(
*
p
))
->
call
(
interfaceId
,
methodId
,
kj
::
mv
(
context
));
}
return
ClientHook
::
from
(
kj
::
mv
(
*
r
))
->
call
(
interfaceId
,
methodId
,
kj
::
mv
(
context
));
}
else
{
// !reverse because calls to the CallContext go in the opposite direction.
auto
result
=
inner
->
call
(
interfaceId
,
methodId
,
kj
::
refcounted
<
MembraneCallContextHook
>
(
kj
::
mv
(
context
),
policy
->
addRef
(),
!
reverse
));
return
{
kj
::
mv
(
result
.
promise
),
kj
::
refcounted
<
MembranePipelineHook
>
(
kj
::
mv
(
result
.
pipeline
),
policy
->
addRef
(),
reverse
)
};
}
}
kj
::
Maybe
<
ClientHook
&>
getResolved
()
override
{
KJ_IF_MAYBE
(
r
,
resolved
)
{
return
**
r
;
}
KJ_IF_MAYBE
(
newInner
,
inner
->
getResolved
())
{
kj
::
Own
<
ClientHook
>
newResolved
=
wrap
(
*
newInner
,
*
policy
,
reverse
);
ClientHook
&
result
=
*
newResolved
;
resolved
=
kj
::
mv
(
newResolved
);
return
result
;
}
else
{
return
nullptr
;
}
}
kj
::
Maybe
<
kj
::
Promise
<
kj
::
Own
<
ClientHook
>>>
whenMoreResolved
()
override
{
KJ_IF_MAYBE
(
r
,
resolved
)
{
return
kj
::
Promise
<
kj
::
Own
<
ClientHook
>>
(
r
->
get
()
->
addRef
());
}
KJ_IF_MAYBE
(
promise
,
inner
->
whenMoreResolved
())
{
return
promise
->
then
([
this
](
kj
::
Own
<
ClientHook
>&&
newInner
)
{
kj
::
Own
<
ClientHook
>
newResolved
=
wrap
(
*
newInner
,
*
policy
,
reverse
);
if
(
resolved
==
nullptr
)
{
resolved
=
newResolved
->
addRef
();
}
return
newResolved
;
});
}
else
{
return
nullptr
;
}
}
kj
::
Own
<
ClientHook
>
addRef
()
override
{
return
kj
::
addRef
(
*
this
);
}
const
void
*
getBrand
()
override
{
return
MEMBRANE_BRAND
;
}
private
:
kj
::
Own
<
ClientHook
>
inner
;
kj
::
Own
<
MembranePolicy
>
policy
;
bool
reverse
;
kj
::
Maybe
<
kj
::
Own
<
ClientHook
>>
resolved
;
};
kj
::
Own
<
ClientHook
>
membrane
(
kj
::
Own
<
ClientHook
>
inner
,
MembranePolicy
&
policy
,
bool
reverse
)
{
return
MembraneHook
::
wrap
(
kj
::
mv
(
inner
),
policy
,
reverse
);
}
}
// namespace
Capability
::
Client
membrane
(
Capability
::
Client
inner
,
kj
::
Own
<
MembranePolicy
>
policy
)
{
return
Capability
::
Client
(
membrane
(
ClientHook
::
from
(
kj
::
mv
(
inner
)),
*
policy
,
false
));
}
Capability
::
Client
reverseMembrane
(
Capability
::
Client
inner
,
kj
::
Own
<
MembranePolicy
>
policy
)
{
return
Capability
::
Client
(
membrane
(
ClientHook
::
from
(
kj
::
mv
(
inner
)),
*
policy
,
true
));
}
}
// namespace capnp
c++/src/capnp/membrane.h
0 → 100644
View file @
c3b381ee
// Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CAPNP_MEMBRANE_H_
#define CAPNP_MEMBRANE_H_
// In capability theory, a "membrane" is a wrapper around a capability which (usually) forwards
// calls but recursively wraps capabilities in those calls in the same membrane. The purpose of a
// membrane is to enforce a barrier between two capabilities that cannot be bypassed by merely
// introducing new objects.
//
// The most common use case for a membrane is revocation: Say Alice wants to give Bob a capability
// to access Carol, but wants to be able to revoke this capability later. Alice can accomplish this
// by wrapping Carol in a revokable wrapper which passes through calls until such a time as Alice
// indicates it should be revoked, after which all calls through the wrapper will throw exceptions.
// However, a naive wrapper approach has a problem: if Bob makes a call to Carol and sends a new
// capability in that call, or if Carol returns a capability to Bob in the response to a call, then
// the two are now able to communicate using this new capability, which Alice cannot revoke. In
// order to avoid this problem, Alice must use not just a wrapper but a "membrane", which
// recursively wraps all objects that pass through it in either direction. Thus, all connections
// formed between Bob and Carol (originating from Alice's original introduction) can be revoked
// together by revoking the membrane.
//
// Note that when a capability is passed into a membrane and then passed back out, the result is
// the original capability, not a double-membraned capability. This means that in our revocation
// example, if Bob uses his capability to Carol to obtain another capability from her, then send
// it back to her, the capability Carol receives back will NOT be revoked when Bob's access to
// Carol is revoked. Thus Bob can create long-term irrevocable connections. In most practical use
// cases, this is what you want. APIs commonly rely on the fact that a capability obtained and then
// passed back can be recognized as the original capability.
//
// Mark Miller on membranes: http://www.eros-os.org/pipermail/e-lang/2003-January/008434.html
#include "capability.h"
namespace
capnp
{
class
MembranePolicy
{
// Applications may implement this interface to define a membrane policy, which allows some
// calls crossing the membrane to be blocked or redirected.
public
:
virtual
kj
::
Maybe
<
Capability
::
Client
>
inboundCall
(
uint64_t
interfaceId
,
uint16_t
methodId
)
=
0
;
// Given an inbound call (a call originating "outside" the membrane destined for an object
// "inside" the membrane), decides what to do with it. The policy may:
//
// - Return null to indicate that the call should proceed to the destination. All capabilities
// in the parameters or result will be properly wrapped in the same membrane.
// - Return a capability to have the call redirected to that capability. Note that the redirect
// capability will be treated as outside the membrane, so the params and results will not be
// auto-wrapped; however, the callee can easily wrap the returned capability in the membrane
// itself before returning to achieve this effect.
// - Throw an exception to cause the call to fail with that exception.
virtual
kj
::
Maybe
<
Capability
::
Client
>
outboundCall
(
uint64_t
interfaceId
,
uint16_t
methodId
)
=
0
;
// Like `inboundCall()`, but applies to calls originating *inside* the membrane and terminating
// outside.
//
// Note: It is strongly recommended that `outboundCall()` returns null in exactly the same cases
// that `inboundCall()` return null. Conversely, for any case where `inboundCall()` would
// redirect or throw, `outboundCall()` should also redirect or throw. Otherwise, you can run
// into inconsistent behavion when a promise is returned across a membrane, and that promise
// later resolves to a capability on the other side of the membrane: calls on the promise
// will enter and then exit the membrane, but calls on the eventual resolution will not cross
// the membrane at all, so it is important that these two cases behave the same.
virtual
kj
::
Own
<
MembranePolicy
>
addRef
()
=
0
;
// Return a new owned pointer to the same policy.
//
// Typically an implementation of MembranePolicy should also inherit kj::Refcounted and implement
// `addRef()` as `return kj::addRef(*this);`.
//
// Note that the membraning system considers two membranes created with the same MembranePolicy
// object actually to be the *same* membrane. This is relevant when an object passes into the
// membrane and then back out (or out and then back in): instead of double-wrapping the object,
// the wrapping will be removed.
};
Capability
::
Client
membrane
(
Capability
::
Client
inner
,
kj
::
Own
<
MembranePolicy
>
policy
);
// Wrap `inner` in a membrane specified by `filter`. `inner` is considered "inside" the membrane,
// while the returned capability should only be called from outside the membrane.
Capability
::
Client
reverseMembrane
(
Capability
::
Client
outer
,
kj
::
Own
<
MembranePolicy
>
policy
);
// Like `membrane` but treat the input capability as "outside" the membrane, and return a
// capability appropriate for use inside.
//
// Applications typically won't use this directly; the membraning code automatically sets up
// reverse membranes where needed.
template
<
typename
ClientType
>
ClientType
membrane
(
ClientType
inner
,
kj
::
Own
<
MembranePolicy
>
policy
);
template
<
typename
ClientType
>
ClientType
reverseMembrane
(
ClientType
inner
,
kj
::
Own
<
MembranePolicy
>
policy
);
// Convenience templates which return the same interface type as the input.
template
<
typename
ServerType
>
typename
ServerType
::
Serves
::
Client
membrane
(
kj
::
Own
<
ServerType
>
inner
,
kj
::
Own
<
MembranePolicy
>
policy
);
template
<
typename
ServerType
>
typename
ServerType
::
Serves
::
Client
reverseMembrane
(
kj
::
Own
<
ServerType
>
inner
,
kj
::
Own
<
MembranePolicy
>
policy
);
// Convenience templates which input a capability server type and return the appropriate client
// type.
// =======================================================================================
// inline implementation details
template
<
typename
ClientType
>
ClientType
membrane
(
ClientType
inner
,
kj
::
Own
<
MembranePolicy
>
policy
)
{
return
membrane
(
Capability
::
Client
(
kj
::
mv
(
inner
)),
kj
::
mv
(
policy
))
.
castAs
<
typename
ClientType
::
Calls
>
();
}
template
<
typename
ClientType
>
ClientType
reverseMembrane
(
ClientType
inner
,
kj
::
Own
<
MembranePolicy
>
policy
)
{
return
reverseMembrane
(
Capability
::
Client
(
kj
::
mv
(
inner
)),
kj
::
mv
(
policy
))
.
castAs
<
typename
ClientType
::
Calls
>
();
}
template
<
typename
ServerType
>
typename
ServerType
::
Serves
::
Client
membrane
(
kj
::
Own
<
ServerType
>
inner
,
kj
::
Own
<
MembranePolicy
>
policy
)
{
return
membrane
(
Capability
::
Client
(
kj
::
mv
(
inner
)),
kj
::
mv
(
policy
))
.
castAs
<
typename
ServerType
::
Serves
::
Client
>
();
}
template
<
typename
ServerType
>
typename
ServerType
::
Serves
::
Client
reverseMembrane
(
kj
::
Own
<
ServerType
>
inner
,
kj
::
Own
<
MembranePolicy
>
policy
)
{
return
reverseMembrane
(
Capability
::
Client
(
kj
::
mv
(
inner
)),
kj
::
mv
(
policy
))
.
castAs
<
typename
ServerType
::
Serves
::
Client
>
();
}
}
// namespace capnp
#endif // CAPNP_MEMBRANE_H_
c++/src/capnp/rpc-twoparty.c++
View file @
c3b381ee
...
...
@@ -182,14 +182,17 @@ TwoPartyClient::TwoPartyClient(kj::AsyncIoStream& connection)
TwoPartyClient
::
TwoPartyClient
(
kj
::
AsyncIoStream
&
connection
,
Capability
::
Client
bootstrapInterface
)
:
network
(
connection
,
rpc
::
twoparty
::
Side
::
CLIENT
),
Capability
::
Client
bootstrapInterface
,
rpc
::
twoparty
::
Side
side
)
:
network
(
connection
,
side
),
rpcSystem
(
network
,
bootstrapInterface
)
{}
Capability
::
Client
TwoPartyClient
::
bootstrap
()
{
MallocMessageBuilder
message
(
4
);
auto
vatId
=
message
.
getRoot
<
rpc
::
twoparty
::
VatId
>
();
vatId
.
setSide
(
rpc
::
twoparty
::
Side
::
SERVER
);
vatId
.
setSide
(
network
.
getSide
()
==
rpc
::
twoparty
::
Side
::
CLIENT
?
rpc
::
twoparty
::
Side
::
SERVER
:
rpc
::
twoparty
::
Side
::
CLIENT
);
return
rpcSystem
.
bootstrap
(
vatId
);
}
...
...
c++/src/capnp/rpc-twoparty.h
View file @
c3b381ee
...
...
@@ -59,6 +59,8 @@ public:
kj
::
Promise
<
void
>
onDisconnect
()
{
return
disconnectPromise
.
addBranch
();
}
// Returns a promise that resolves when the peer disconnects.
rpc
::
twoparty
::
Side
getSide
()
{
return
side
;
}
// implements VatNetwork -----------------------------------------------------
kj
::
Maybe
<
kj
::
Own
<
TwoPartyVatNetworkBase
::
Connection
>>
connect
(
...
...
@@ -136,7 +138,8 @@ class TwoPartyClient {
public
:
explicit
TwoPartyClient
(
kj
::
AsyncIoStream
&
connection
);
TwoPartyClient
(
kj
::
AsyncIoStream
&
connection
,
Capability
::
Client
bootstrapInterface
);
TwoPartyClient
(
kj
::
AsyncIoStream
&
connection
,
Capability
::
Client
bootstrapInterface
,
rpc
::
twoparty
::
Side
side
=
rpc
::
twoparty
::
Side
::
CLIENT
);
Capability
::
Client
bootstrap
();
// Get the server's bootstrap interface.
...
...
c++/src/capnp/test.capnp
View file @
c3b381ee
...
...
@@ -803,6 +803,22 @@ interface TestMoreStuff extends(TestCallOrder) {
# this can be used to test garbage collection.
}
interface TestMembrane {
makeThing @0 () -> (thing :Thing);
callPassThrough @1 (thing :Thing, tailCall :Bool) -> Result;
callIntercept @2 (thing :Thing, tailCall :Bool) -> Result;
loopback @3 (thing :Thing) -> (thing :Thing);
interface Thing {
passThrough @0 () -> Result;
intercept @1 () -> Result;
}
struct Result {
text @0 :Text;
}
}
struct TestTransferCap {
list @0 :List(Element);
struct Element {
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment