capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

membrane-test.c++ (13663B)


      1 // Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors
      2 // Licensed under the MIT License:
      3 //
      4 // Permission is hereby granted, free of charge, to any person obtaining a copy
      5 // of this software and associated documentation files (the "Software"), to deal
      6 // in the Software without restriction, including without limitation the rights
      7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      8 // copies of the Software, and to permit persons to whom the Software is
      9 // furnished to do so, subject to the following conditions:
     10 //
     11 // The above copyright notice and this permission notice shall be included in
     12 // all copies or substantial portions of the Software.
     13 //
     14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     20 // THE SOFTWARE.
     21 
     22 #include "membrane.h"
     23 #include <kj/test.h>
     24 #include "test-util.h"
     25 #include <kj/function.h>
     26 #include <kj/async-io.h>
     27 #include "rpc-twoparty.h"
     28 
     29 namespace capnp {
     30 namespace _ {
     31 namespace {
     32 
     33 using Thing = test::TestMembrane::Thing;
     34 
     35 class ThingImpl final: public Thing::Server {
     36 public:
     37   ThingImpl(kj::StringPtr text): text(text) {}
     38 
     39 protected:
     40   kj::Promise<void> passThrough(PassThroughContext context) override {
     41     context.getResults().setText(text);
     42     return kj::READY_NOW;
     43   }
     44 
     45   kj::Promise<void> intercept(InterceptContext context) override {
     46     context.getResults().setText(text);
     47     return kj::READY_NOW;
     48   }
     49 
     50 private:
     51   kj::StringPtr text;
     52 };
     53 
     54 class TestMembraneImpl final: public test::TestMembrane::Server {
     55 protected:
     56   kj::Promise<void> makeThing(MakeThingContext context) override {
     57     context.getResults().setThing(kj::heap<ThingImpl>("inside"));
     58     return kj::READY_NOW;
     59   }
     60 
     61   kj::Promise<void> callPassThrough(CallPassThroughContext context) override {
     62     auto params = context.getParams();
     63     auto req = params.getThing().passThroughRequest();
     64     if (params.getTailCall()) {
     65       return context.tailCall(kj::mv(req));
     66     } else {
     67       return req.send().then(
     68           [KJ_CPCAP(context)](Response<test::TestMembrane::Result>&& result) mutable {
     69         context.setResults(result);
     70       });
     71     }
     72   }
     73 
     74   kj::Promise<void> callIntercept(CallInterceptContext context) override {
     75     auto params = context.getParams();
     76     auto req = params.getThing().interceptRequest();
     77     if (params.getTailCall()) {
     78       return context.tailCall(kj::mv(req));
     79     } else {
     80       return req.send().then(
     81           [KJ_CPCAP(context)](Response<test::TestMembrane::Result>&& result) mutable {
     82         context.setResults(result);
     83       });
     84     }
     85   }
     86 
     87   kj::Promise<void> loopback(LoopbackContext context) override {
     88     context.getResults().setThing(context.getParams().getThing());
     89     return kj::READY_NOW;
     90   }
     91 
     92   kj::Promise<void> waitForever(WaitForeverContext context) override {
     93     context.allowCancellation();
     94     return kj::NEVER_DONE;
     95   }
     96 };
     97 
     98 class MembranePolicyImpl: public MembranePolicy, public kj::Refcounted {
     99 public:
    100   MembranePolicyImpl() = default;
    101   MembranePolicyImpl(kj::Maybe<kj::Promise<void>> revokePromise)
    102       : revokePromise(revokePromise.map([](kj::Promise<void>& p) { return p.fork(); })) {}
    103 
    104   kj::Maybe<Capability::Client> inboundCall(uint64_t interfaceId, uint16_t methodId,
    105                                             Capability::Client target) override {
    106     if (interfaceId == capnp::typeId<Thing>() && methodId == 1) {
    107       return Capability::Client(kj::heap<ThingImpl>("inbound"));
    108     } else {
    109       return nullptr;
    110     }
    111   }
    112 
    113   kj::Maybe<Capability::Client> outboundCall(uint64_t interfaceId, uint16_t methodId,
    114                                              Capability::Client target) override {
    115     if (interfaceId == capnp::typeId<Thing>() && methodId == 1) {
    116       return Capability::Client(kj::heap<ThingImpl>("outbound"));
    117     } else {
    118       return nullptr;
    119     }
    120   }
    121 
    122   kj::Own<MembranePolicy> addRef() override {
    123     return kj::addRef(*this);
    124   }
    125 
    126   kj::Maybe<kj::Promise<void>> onRevoked() override {
    127     return revokePromise.map([](kj::ForkedPromise<void>& fork) {
    128       return fork.addBranch();
    129     });
    130   }
    131 
    132 private:
    133   kj::Maybe<kj::ForkedPromise<void>> revokePromise;
    134 };
    135 
    136 void testThingImpl(kj::WaitScope& waitScope, test::TestMembrane::Client membraned,
    137                    kj::Function<Thing::Client()> makeThing,
    138                    kj::StringPtr localPassThrough, kj::StringPtr localIntercept,
    139                    kj::StringPtr remotePassThrough, kj::StringPtr remoteIntercept) {
    140   KJ_EXPECT(makeThing().passThroughRequest().send().wait(waitScope).getText() == localPassThrough);
    141   KJ_EXPECT(makeThing().interceptRequest().send().wait(waitScope).getText() == localIntercept);
    142 
    143   {
    144     auto req = membraned.callPassThroughRequest();
    145     req.setThing(makeThing());
    146     req.setTailCall(false);
    147     KJ_EXPECT(req.send().wait(waitScope).getText() == remotePassThrough);
    148   }
    149   {
    150     auto req = membraned.callInterceptRequest();
    151     req.setThing(makeThing());
    152     req.setTailCall(false);
    153     KJ_EXPECT(req.send().wait(waitScope).getText() == remoteIntercept);
    154   }
    155   {
    156     auto req = membraned.callPassThroughRequest();
    157     req.setThing(makeThing());
    158     req.setTailCall(true);
    159     KJ_EXPECT(req.send().wait(waitScope).getText() == remotePassThrough);
    160   }
    161   {
    162     auto req = membraned.callInterceptRequest();
    163     req.setThing(makeThing());
    164     req.setTailCall(true);
    165     KJ_EXPECT(req.send().wait(waitScope).getText() == remoteIntercept);
    166   }
    167 }
    168 
    169 struct TestEnv {
    170   kj::EventLoop loop;
    171   kj::WaitScope waitScope;
    172   kj::Own<MembranePolicyImpl> policy;
    173   test::TestMembrane::Client membraned;
    174 
    175   TestEnv()
    176       : waitScope(loop),
    177         policy(kj::refcounted<MembranePolicyImpl>()),
    178         membraned(membrane(kj::heap<TestMembraneImpl>(), policy->addRef())) {}
    179 
    180   void testThing(kj::Function<Thing::Client()> makeThing,
    181                  kj::StringPtr localPassThrough, kj::StringPtr localIntercept,
    182                  kj::StringPtr remotePassThrough, kj::StringPtr remoteIntercept) {
    183     testThingImpl(waitScope, membraned, kj::mv(makeThing),
    184                   localPassThrough, localIntercept, remotePassThrough, remoteIntercept);
    185   }
    186 };
    187 
    188 KJ_TEST("call local object inside membrane") {
    189   TestEnv env;
    190   env.testThing([&]() {
    191     return env.membraned.makeThingRequest().send().wait(env.waitScope).getThing();
    192   }, "inside", "inbound", "inside", "inside");
    193 }
    194 
    195 KJ_TEST("call local promise inside membrane") {
    196   TestEnv env;
    197   env.testThing([&]() {
    198     return env.membraned.makeThingRequest().send().getThing();
    199   }, "inside", "inbound", "inside", "inside");
    200 }
    201 
    202 KJ_TEST("call local resolved promise inside membrane") {
    203   TestEnv env;
    204   env.testThing([&]() {
    205     auto thing = env.membraned.makeThingRequest().send().getThing();
    206     thing.whenResolved().wait(env.waitScope);
    207     return thing;
    208   }, "inside", "inbound", "inside", "inside");
    209 }
    210 
    211 KJ_TEST("call local object outside membrane") {
    212   TestEnv env;
    213   env.testThing([&]() {
    214     return kj::heap<ThingImpl>("outside");
    215   }, "outside", "outside", "outside", "outbound");
    216 }
    217 
    218 KJ_TEST("call local capability that has passed into and back out of membrane") {
    219   TestEnv env;
    220   env.testThing([&]() {
    221     auto req = env.membraned.loopbackRequest();
    222     req.setThing(kj::heap<ThingImpl>("outside"));
    223     return req.send().wait(env.waitScope).getThing();
    224   }, "outside", "outside", "outside", "outbound");
    225 }
    226 
    227 KJ_TEST("call local promise pointing into membrane that eventually resolves to outside") {
    228   TestEnv env;
    229   env.testThing([&]() {
    230     auto req = env.membraned.loopbackRequest();
    231     req.setThing(kj::heap<ThingImpl>("outside"));
    232     return req.send().getThing();
    233   }, "outside", "outside", "outside", "outbound");
    234 }
    235 
    236 KJ_TEST("apply membrane using copyOutOfMembrane() on struct") {
    237   TestEnv env;
    238 
    239   env.testThing([&]() {
    240     MallocMessageBuilder outsideBuilder;
    241     auto root = outsideBuilder.initRoot<test::TestContainMembrane>();
    242     root.setCap(kj::heap<ThingImpl>("inside"));
    243     MallocMessageBuilder insideBuilder;
    244     insideBuilder.adoptRoot(copyOutOfMembrane(
    245         root.asReader(), insideBuilder.getOrphanage(), env.policy->addRef()));
    246     return insideBuilder.getRoot<test::TestContainMembrane>().getCap();
    247   }, "inside", "inbound", "inside", "inside");
    248 }
    249 
    250 KJ_TEST("apply membrane using copyOutOfMembrane() on list") {
    251   TestEnv env;
    252 
    253   env.testThing([&]() {
    254     MallocMessageBuilder outsideBuilder;
    255     auto list = outsideBuilder.initRoot<test::TestContainMembrane>().initList(1);
    256     list.set(0, kj::heap<ThingImpl>("inside"));
    257     MallocMessageBuilder insideBuilder;
    258     insideBuilder.initRoot<test::TestContainMembrane>().adoptList(copyOutOfMembrane(
    259         list.asReader(), insideBuilder.getOrphanage(), env.policy->addRef()));
    260     return insideBuilder.getRoot<test::TestContainMembrane>().getList()[0];
    261   }, "inside", "inbound", "inside", "inside");
    262 }
    263 
    264 KJ_TEST("apply membrane using copyOutOfMembrane() on AnyPointer") {
    265   TestEnv env;
    266 
    267   env.testThing([&]() {
    268     MallocMessageBuilder outsideBuilder;
    269     auto ptr = outsideBuilder.initRoot<test::TestAnyPointer>().getAnyPointerField();
    270     ptr.setAs<test::TestMembrane::Thing>(kj::heap<ThingImpl>("inside"));
    271     MallocMessageBuilder insideBuilder;
    272     insideBuilder.initRoot<test::TestAnyPointer>().getAnyPointerField().adopt(copyOutOfMembrane(
    273         ptr.asReader(), insideBuilder.getOrphanage(), env.policy->addRef()));
    274     return insideBuilder.getRoot<test::TestAnyPointer>().getAnyPointerField()
    275         .getAs<test::TestMembrane::Thing>();
    276   }, "inside", "inbound", "inside", "inside");
    277 }
    278 
    279 struct TestRpcEnv {
    280   kj::EventLoop loop;
    281   kj::WaitScope waitScope;
    282   kj::TwoWayPipe pipe;
    283   TwoPartyClient client;
    284   TwoPartyClient server;
    285   test::TestMembrane::Client membraned;
    286 
    287   TestRpcEnv(kj::Maybe<kj::Promise<void>> revokePromise = nullptr)
    288       : waitScope(loop),
    289         pipe(kj::newTwoWayPipe()),
    290         client(*pipe.ends[0]),
    291         server(*pipe.ends[1],
    292                membrane(kj::heap<TestMembraneImpl>(),
    293                         kj::refcounted<MembranePolicyImpl>(kj::mv(revokePromise))),
    294                rpc::twoparty::Side::SERVER),
    295         membraned(client.bootstrap().castAs<test::TestMembrane>()) {}
    296 
    297   void testThing(kj::Function<Thing::Client()> makeThing,
    298                  kj::StringPtr localPassThrough, kj::StringPtr localIntercept,
    299                  kj::StringPtr remotePassThrough, kj::StringPtr remoteIntercept) {
    300     testThingImpl(waitScope, membraned, kj::mv(makeThing),
    301                   localPassThrough, localIntercept, remotePassThrough, remoteIntercept);
    302   }
    303 };
    304 
    305 KJ_TEST("call remote object inside membrane") {
    306   TestRpcEnv env;
    307   env.testThing([&]() {
    308     return env.membraned.makeThingRequest().send().wait(env.waitScope).getThing();
    309   }, "inside", "inbound", "inside", "inside");
    310 }
    311 
    312 KJ_TEST("call remote promise inside membrane") {
    313   TestRpcEnv env;
    314   env.testThing([&]() {
    315     return env.membraned.makeThingRequest().send().getThing();
    316   }, "inside", "inbound", "inside", "inside");
    317 }
    318 
    319 KJ_TEST("call remote resolved promise inside membrane") {
    320   TestEnv env;
    321   env.testThing([&]() {
    322     auto thing = env.membraned.makeThingRequest().send().getThing();
    323     thing.whenResolved().wait(env.waitScope);
    324     return thing;
    325   }, "inside", "inbound", "inside", "inside");
    326 }
    327 
    328 KJ_TEST("call remote object outside membrane") {
    329   TestRpcEnv env;
    330   env.testThing([&]() {
    331     return kj::heap<ThingImpl>("outside");
    332   }, "outside", "outside", "outside", "outbound");
    333 }
    334 
    335 KJ_TEST("call remote capability that has passed into and back out of membrane") {
    336   TestRpcEnv env;
    337   env.testThing([&]() {
    338     auto req = env.membraned.loopbackRequest();
    339     req.setThing(kj::heap<ThingImpl>("outside"));
    340     return req.send().wait(env.waitScope).getThing();
    341   }, "outside", "outside", "outside", "outbound");
    342 }
    343 
    344 KJ_TEST("call remote promise pointing into membrane that eventually resolves to outside") {
    345   TestRpcEnv env;
    346   env.testThing([&]() {
    347     auto req = env.membraned.loopbackRequest();
    348     req.setThing(kj::heap<ThingImpl>("outside"));
    349     return req.send().getThing();
    350   }, "outside", "outside", "outside", "outbound");
    351 }
    352 
    353 KJ_TEST("revoke membrane") {
    354   auto paf = kj::newPromiseAndFulfiller<void>();
    355 
    356   TestRpcEnv env(kj::mv(paf.promise));
    357 
    358   auto thing = env.membraned.makeThingRequest().send().wait(env.waitScope).getThing();
    359 
    360   auto callPromise = env.membraned.waitForeverRequest().send();
    361 
    362   KJ_EXPECT(!callPromise.poll(env.waitScope));
    363 
    364   paf.fulfiller->reject(KJ_EXCEPTION(DISCONNECTED, "foobar"));
    365 
    366   // TRICKY: We need to use .ignoreResult().wait() below because when compiling with
    367   //   -fno-exceptions, void waits throw recoverable exceptions while non-void waits necessarily
    368   //   throw fatal exceptions... but testing for fatal exceptions when exceptions are disabled
    369   //   involves fork()ing the process to run the code so if it has side effects on file descriptors
    370   //   then we'll get in a bad state...
    371 
    372   KJ_ASSERT(callPromise.poll(env.waitScope));
    373   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("foobar", callPromise.ignoreResult().wait(env.waitScope));
    374 
    375   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("foobar",
    376       env.membraned.makeThingRequest().send().ignoreResult().wait(env.waitScope));
    377 
    378   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("foobar",
    379       thing.passThroughRequest().send().ignoreResult().wait(env.waitScope));
    380 }
    381 
    382 }  // namespace
    383 }  // namespace _
    384 }  // namespace capnp