From c2c0196001c2bc94d14645272b931e39ee38c197 Mon Sep 17 00:00:00 2001 From: Michael FIG Date: Mon, 11 May 2020 21:54:24 -0600 Subject: [PATCH 1/6] fix: properly abort all communication when CapTP is disconnected This rejects promises with the abort exception. --- packages/captp/lib/captp.js | 10 ++++++++-- packages/captp/lib/index.js | 8 ++++++++ packages/cosmic-swingset/lib/ag-solo/vats/captp.js | 3 ++- packages/cosmic-swingset/lib/ag-solo/vats/vat-http.js | 6 ++++-- 4 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 packages/captp/lib/index.js diff --git a/packages/captp/lib/captp.js b/packages/captp/lib/captp.js index 4fb711299a7..817aa5bab73 100644 --- a/packages/captp/lib/captp.js +++ b/packages/captp/lib/captp.js @@ -9,8 +9,14 @@ import { isPromise } from '@agoric/produce-promise'; export { E, HandledPromise, Nat }; -export function makeCapTP(ourId, send, bootstrapObj = undefined) { +export function makeCapTP(ourId, rawSend, bootstrapObj = undefined) { let unplug = false; + function send(...args) { + if (unplug) { + throw unplug; + } + return rawSend(...args); + } // convertValToSlot and convertSlotToVal both perform side effects, // populating the c-lists (imports/exports/questions/answers) upon @@ -253,7 +259,6 @@ 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); } @@ -261,6 +266,7 @@ export function makeCapTP(ourId, send, bootstrapObj = undefined) { pr.rej(exception); } send(obj); + unplug = exception; }, }; diff --git a/packages/captp/lib/index.js b/packages/captp/lib/index.js new file mode 100644 index 00000000000..dc204a3e951 --- /dev/null +++ b/packages/captp/lib/index.js @@ -0,0 +1,8 @@ +import harden from '@agoric/harden'; +import Nat from '@agoric/nat'; + +export * from '@agoric/eventual-send'; +export * from '@agoric/marshal'; + +export * from './captp'; +export { harden, Nat }; diff --git a/packages/cosmic-swingset/lib/ag-solo/vats/captp.js b/packages/cosmic-swingset/lib/ag-solo/vats/captp.js index f906f0881f9..dcce6f3a7b4 100644 --- a/packages/cosmic-swingset/lib/ag-solo/vats/captp.js +++ b/packages/cosmic-swingset/lib/ag-solo/vats/captp.js @@ -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) { @@ -17,6 +17,7 @@ export const getCapTPHandler = (E, send, getBootstrapObject) => { origin, sendObj, getBootstrapObject, + { harden, ...powers }, ); chans.set(channelHandle, [dispatch, abort]); }, diff --git a/packages/cosmic-swingset/lib/ag-solo/vats/vat-http.js b/packages/cosmic-swingset/lib/ag-solo/vats/vat-http.js index 8237d81a736..c3f7d7f93b1 100644 --- a/packages/cosmic-swingset/lib/ag-solo/vats/vat-http.js +++ b/packages/cosmic-swingset/lib/ag-solo/vats/vat-http.js @@ -74,9 +74,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'); } From e16335e61d4df31f29469b35d7bd7cb5b8cacee2 Mon Sep 17 00:00:00 2001 From: Michael FIG Date: Tue, 12 May 2020 01:16:48 -0600 Subject: [PATCH 2/6] chore: silence lint on test files --- packages/captp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/captp/package.json b/packages/captp/package.json index b79d9ce4074..bbda5f4a7d3 100644 --- a/packages/captp/package.json +++ b/packages/captp/package.json @@ -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'" }, From 5c0c5097acd4dacf2b594d84606d0494d71f0216 Mon Sep 17 00:00:00 2001 From: Michael FIG Date: Wed, 29 Jul 2020 14:28:15 -0600 Subject: [PATCH 3/6] fix(captp): make more code paths fail on disconnect --- packages/captp/lib/captp.js | 15 ++++++++++++--- packages/captp/test/disco.js | 19 +++++++++++++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/captp/lib/captp.js b/packages/captp/lib/captp.js index 817aa5bab73..37ba9d072be 100644 --- a/packages/captp/lib/captp.js +++ b/packages/captp/lib/captp.js @@ -12,7 +12,7 @@ export { E, HandledPromise, Nat }; export function makeCapTP(ourId, rawSend, bootstrapObj = undefined) { let unplug = false; function send(...args) { - if (unplug) { + if (unplug !== false) { throw unplug; } return rawSend(...args); @@ -114,6 +114,9 @@ export function makeCapTP(ourId, rawSend, 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', @@ -124,6 +127,9 @@ export function makeCapTP(ourId, rawSend, 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({ @@ -271,7 +277,10 @@ export function makeCapTP(ourId, rawSend, bootstrapObj = undefined) { }; // 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', @@ -283,7 +292,7 @@ export function makeCapTP(ourId, rawSend, bootstrapObj = undefined) { // Return a dispatch function. const dispatch = obj => { - if (unplug) { + if (unplug !== false) { return false; } const fn = handler[obj.type]; diff --git a/packages/captp/test/disco.js b/packages/captp/test/disco.js index 1f1716a932d..daf338e1900 100644 --- a/packages/captp/test/disco.js +++ b/packages/captp/test/disco.js @@ -2,7 +2,7 @@ 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 { @@ -10,24 +10,35 @@ test('try disconnecting captp', async t => { 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 { From 274ed53fb99c0fb135b8beae984e3f0b731dbb81 Mon Sep 17 00:00:00 2001 From: Michael FIG Date: Wed, 29 Jul 2020 14:42:23 -0600 Subject: [PATCH 4/6] fix: supply default disconnected abort exception --- packages/captp/lib/captp.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/captp/lib/captp.js b/packages/captp/lib/captp.js index 37ba9d072be..d2804a15283 100644 --- a/packages/captp/lib/captp.js +++ b/packages/captp/lib/captp.js @@ -304,7 +304,9 @@ export function makeCapTP(ourId, rawSend, 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 }); } From 1a61a042af30138449ea6c97adf840964202b974 Mon Sep 17 00:00:00 2001 From: Michael FIG Date: Wed, 29 Jul 2020 14:53:57 -0600 Subject: [PATCH 5/6] chore: fix the lint --- packages/captp/lib/captp.js | 5 +---- packages/captp/lib/index.js | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/captp/lib/captp.js b/packages/captp/lib/captp.js index d2804a15283..95db6a5bb88 100644 --- a/packages/captp/lib/captp.js +++ b/packages/captp/lib/captp.js @@ -3,12 +3,9 @@ // 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 { HandledPromise } from '@agoric/eventual-send'; import { isPromise } from '@agoric/produce-promise'; -export { E, HandledPromise, Nat }; - export function makeCapTP(ourId, rawSend, bootstrapObj = undefined) { let unplug = false; function send(...args) { diff --git a/packages/captp/lib/index.js b/packages/captp/lib/index.js index dc204a3e951..1712f5cb845 100644 --- a/packages/captp/lib/index.js +++ b/packages/captp/lib/index.js @@ -1,8 +1,7 @@ -import harden from '@agoric/harden'; import Nat from '@agoric/nat'; export * from '@agoric/eventual-send'; export * from '@agoric/marshal'; export * from './captp'; -export { harden, Nat }; +export { Nat }; From c95282ec5b72260353441ec8dd2ad0eaba9cdfa8 Mon Sep 17 00:00:00 2001 From: Michael FIG Date: Wed, 29 Jul 2020 15:07:19 -0600 Subject: [PATCH 6/6] fix: shuffle around exports --- packages/captp/lib/captp.js | 4 +++- packages/captp/lib/index.js | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/captp/lib/captp.js b/packages/captp/lib/captp.js index 95db6a5bb88..8ae262b572b 100644 --- a/packages/captp/lib/captp.js +++ b/packages/captp/lib/captp.js @@ -3,9 +3,11 @@ // 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 { HandledPromise } from '@agoric/eventual-send'; +import { E, HandledPromise } from '@agoric/eventual-send'; import { isPromise } from '@agoric/produce-promise'; +export { E, HandledPromise }; + export function makeCapTP(ourId, rawSend, bootstrapObj = undefined) { let unplug = false; function send(...args) { diff --git a/packages/captp/lib/index.js b/packages/captp/lib/index.js index 1712f5cb845..14ecb8e46aa 100644 --- a/packages/captp/lib/index.js +++ b/packages/captp/lib/index.js @@ -1,6 +1,5 @@ import Nat from '@agoric/nat'; -export * from '@agoric/eventual-send'; export * from '@agoric/marshal'; export * from './captp';