Skip to content

Commit

Permalink
Merge pull request #1347 from Agoric/mfig/captp
Browse files Browse the repository at this point in the history
More captp connection aborting when disconnected
  • Loading branch information
michaelfig authored Jul 30, 2020
2 parents b67d21a + c95282e commit 2c7ba13
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 16 deletions.
32 changes: 24 additions & 8 deletions packages/captp/lib/captp.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@
// This logic was mostly lifted from @agoric/swingset-vat liveSlots.js
// Defects in it are mfig's fault.
import { Remotable, makeMarshal, QCLASS } from '@agoric/marshal';
import Nat from '@agoric/nat';
import { HandledPromise, E } from '@agoric/eventual-send';
import { E, HandledPromise } from '@agoric/eventual-send';
import { isPromise } from '@agoric/produce-promise';

export { E, HandledPromise, Nat };
export { E, HandledPromise };

export function makeCapTP(ourId, send, bootstrapObj = undefined) {
export function makeCapTP(ourId, rawSend, bootstrapObj = undefined) {
let unplug = false;
function send(...args) {
if (unplug !== false) {
throw unplug;
}
return rawSend(...args);
}

// convertValToSlot and convertSlotToVal both perform side effects,
// populating the c-lists (imports/exports/questions/answers) upon
Expand Down Expand Up @@ -108,6 +113,9 @@ export function makeCapTP(ourId, send, bootstrapObj = undefined) {
// as also being questions / remote handled promises
const handler = {
get(_o, prop) {
if (unplug !== false) {
throw unplug;
}
const [questionID, pr] = makeQuestion();
send({
type: 'CTP_CALL',
Expand All @@ -118,6 +126,9 @@ export function makeCapTP(ourId, send, bootstrapObj = undefined) {
return harden(pr.p);
},
applyMethod(_o, prop, args) {
if (unplug !== false) {
throw unplug;
}
// Support: o~.[prop](...args) remote method invocation
const [questionID, pr] = makeQuestion();
send({
Expand Down Expand Up @@ -253,19 +264,22 @@ export function makeCapTP(ourId, send, bootstrapObj = undefined) {
// Pull the plug!
CTP_ABORT(obj) {
const { exception } = obj;
unplug = true;
for (const pr of questions.values()) {
pr.rej(exception);
}
for (const pr of imports.values()) {
pr.rej(exception);
}
send(obj);
unplug = exception;
},
};

// Get a reference to the other side's bootstrap object.
const getBootstrap = () => {
const getBootstrap = async () => {
if (unplug !== false) {
throw unplug;
}
const [questionID, pr] = makeQuestion();
send({
type: 'CTP_BOOTSTRAP',
Expand All @@ -277,7 +291,7 @@ export function makeCapTP(ourId, send, bootstrapObj = undefined) {

// Return a dispatch function.
const dispatch = obj => {
if (unplug) {
if (unplug !== false) {
return false;
}
const fn = handler[obj.type];
Expand All @@ -289,7 +303,9 @@ export function makeCapTP(ourId, send, bootstrapObj = undefined) {
};

// Abort a connection.
const abort = exception => dispatch({ type: 'CTP_ABORT', exception });
const abort = (
exception = Error(`disconnected from ${JSON.stringify(ourId)}`),
) => dispatch({ type: 'CTP_ABORT', exception });

return harden({ abort, dispatch, getBootstrap });
}
6 changes: 6 additions & 0 deletions packages/captp/lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Nat from '@agoric/nat';

export * from '@agoric/marshal';

export * from './captp';
export { Nat };
2 changes: 1 addition & 1 deletion packages/captp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"build": "exit 0",
"test": "tape -r esm 'test/**/*.js'",
"lint-fix": "eslint --fix '**/*.js'",
"lint-check": "eslint '**/*.js'",
"lint-check": "eslint 'lib/*.js'",
"lint-fix-jessie": "eslint -c '.eslintrc-jessie.js' --fix '**/*.js'",
"lint-check-jessie": "eslint -c '.eslintrc-jessie.js' '**/*.js'"
},
Expand Down
19 changes: 15 additions & 4 deletions packages/captp/test/disco.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,43 @@

import '@agoric/install-ses';
import { test } from 'tape-promise/tape';
import { makeCapTP } from '../lib/captp';
import { E, makeCapTP } from '../lib/captp';

test('try disconnecting captp', async t => {
try {
const objs = [];
const { getBootstrap, abort } = makeCapTP(
'us',
obj => objs.push(obj),
() => harden({}),
() =>
harden({
method() {
return 'hello';
},
}),
);
t.deepEqual(objs, [], 'expected no messages');
const bs = getBootstrap();
const ps = [];
ps.push(t.rejects(E.G(bs).prop, Error, 'rejected get after disconnect'));
ps.push(
t.rejects(E(bs).method(), Error, 'rejected method after disconnect'),
);
t.deepEqual(
objs,
[{ type: 'CTP_BOOTSTRAP', questionID: 1 }],
'expected bootstrap messages',
);
const pr = t.rejects(bs, Error, 'rejected after disconnect');
ps.push(t.rejects(bs, Error, 'rejected after disconnect'));
const abortMsg = { type: 'CTP_ABORT', exception: Error('disconnect') };
abort(abortMsg.exception);
await t.rejects(getBootstrap(), Error, 'rejected disconnected bootstrap');
t.deepEqual(
objs,
[{ type: 'CTP_BOOTSTRAP', questionID: 1 }, abortMsg],
'expected disconnect messages',
);
await pr;
await ps;
} catch (e) {
t.isNot(e, e, 'unexpected exception');
} finally {
Expand Down
3 changes: 2 additions & 1 deletion packages/cosmic-swingset/lib/ag-solo/vats/captp.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// in its own makeHardener, etc.
import { makeCapTP } from '@agoric/captp/lib/captp';

export const getCapTPHandler = (E, send, getBootstrapObject) => {
export const getCapTPHandler = (send, getBootstrapObject, powers) => {
const chans = new Map();
const handler = harden({
onOpen(_obj, meta) {
Expand All @@ -17,6 +17,7 @@ export const getCapTPHandler = (E, send, getBootstrapObject) => {
origin,
sendObj,
getBootstrapObject,
{ harden, ...powers },
);
chans.set(channelHandle, [dispatch, abort]);
},
Expand Down
6 changes: 4 additions & 2 deletions packages/cosmic-swingset/lib/ag-solo/vats/vat-http.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ export function buildRootObject(vatPowers) {

// Assign the captp handler.
// TODO: Break this out into a separate vat.
const captpHandler = getCapTPHandler(E, send, () =>
const captpHandler = getCapTPHandler(
send,
// Harden only our exported objects.
harden(exportedToCapTP),
() => harden(exportedToCapTP),
{ E, harden, ...vatPowers },
);
registerURLHandler(captpHandler, '/private/captp');
}
Expand Down

0 comments on commit 2c7ba13

Please sign in to comment.