@agoric/network 0.1.1-orchestration-dev-096c4e8.0 → 0.1.1-upgrade-16-dev-8879538.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -7
- package/package.json +21 -24
- package/src/bytes.d.ts +10 -8
- package/src/bytes.d.ts.map +1 -1
- package/src/bytes.js +39 -26
- package/src/index.d.ts +2 -0
- package/src/index.js +3 -0
- package/src/multiaddr.d.ts.map +1 -1
- package/src/network.d.ts +55 -79
- package/src/network.d.ts.map +1 -1
- package/src/network.js +999 -502
- package/src/router.d.ts +44 -20
- package/src/router.d.ts.map +1 -1
- package/src/router.js +19 -16
- package/src/shapes.d.ts +117 -0
- package/src/shapes.d.ts.map +1 -0
- package/src/shapes.js +191 -0
- package/src/types.d.ts +55 -42
- package/src/types.d.ts.map +1 -1
- package/src/types.js +65 -50
- package/exported.js +0 -1
- package/tsconfig.build.json +0 -6
- package/tsconfig.json +0 -14
- package/typedoc.json +0 -9
package/src/network.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
/// <reference types="@agoric/store/exported.js" />
|
|
4
|
+
|
|
3
5
|
import { E } from '@endo/far';
|
|
4
6
|
import { M } from '@endo/patterns';
|
|
5
7
|
import { Fail } from '@agoric/assert';
|
|
6
|
-
import { whileTrue } from '@agoric/internal';
|
|
7
8
|
import { toBytes } from './bytes.js';
|
|
9
|
+
import { Shape } from './shapes.js';
|
|
8
10
|
|
|
9
|
-
import '@agoric/store/exported.js';
|
|
10
11
|
/// <reference path="./types.js" />
|
|
12
|
+
/**
|
|
13
|
+
* @import {AttemptDescription, Bytes, Closable, CloseReason, Connection, ConnectionHandler, Endpoint, ListenHandler, Port, Protocol, ProtocolHandler, ProtocolImpl} from './types.js';
|
|
14
|
+
*/
|
|
11
15
|
|
|
12
16
|
/**
|
|
13
17
|
* Compatibility note: this must match what our peers use, so don't change it
|
|
@@ -15,136 +19,13 @@ import '@agoric/store/exported.js';
|
|
|
15
19
|
*/
|
|
16
20
|
export const ENDPOINT_SEPARATOR = '/';
|
|
17
21
|
|
|
18
|
-
const Shape1 = /** @type {const} */ ({
|
|
19
|
-
/**
|
|
20
|
-
* Data is string | Buffer | ArrayBuffer
|
|
21
|
-
* but only string is passable
|
|
22
|
-
*/
|
|
23
|
-
Data: M.string(),
|
|
24
|
-
Bytes: M.string(),
|
|
25
|
-
Endpoint: M.string(),
|
|
26
|
-
// TODO: match on "Vow" tag
|
|
27
|
-
// @endo/patterns supports it as of
|
|
28
|
-
// https://github.com/endojs/endo/pull/2091
|
|
29
|
-
// but that's not in agoric-sdk yet.
|
|
30
|
-
// For now, use M.any() to avoid:
|
|
31
|
-
// cannot check unrecognized tag "Vow": "[Vow]"
|
|
32
|
-
Vow: M.any(),
|
|
33
|
-
|
|
34
|
-
ConnectionHandler: M.remotable('ConnectionHandler'),
|
|
35
|
-
Connection: M.remotable('Connection'),
|
|
36
|
-
InboundAttempt: M.remotable('InboundAttempt'),
|
|
37
|
-
Listener: M.remotable('Listener'),
|
|
38
|
-
ListenHandler: M.remotable('ListenHandler'),
|
|
39
|
-
Port: M.remotable('Port'),
|
|
40
|
-
ProtocolHandler: M.remotable('ProtocolHandler'),
|
|
41
|
-
ProtocolImpl: M.remotable('ProtocolImpl'),
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const Shape2 = /** @type {const} */ ({
|
|
45
|
-
...Shape1,
|
|
46
|
-
Vow$: shape => M.or(shape, Shape1.Vow),
|
|
47
|
-
AttemptDescription: M.splitRecord(
|
|
48
|
-
{ handler: Shape1.ConnectionHandler },
|
|
49
|
-
{ remoteAddress: Shape1.Endpoint, localAddress: Shape1.Endpoint },
|
|
50
|
-
),
|
|
51
|
-
Opts: M.recordOf(M.string(), M.any()),
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
export const Shape = /** @type {const} */ harden({
|
|
55
|
-
...Shape2,
|
|
56
|
-
ConnectionI: M.interface('Connection', {
|
|
57
|
-
send: M.callWhen(Shape2.Data)
|
|
58
|
-
.optional(Shape2.Opts)
|
|
59
|
-
.returns(Shape2.Vow$(Shape2.Bytes)),
|
|
60
|
-
close: M.callWhen().returns(Shape2.Vow$(M.undefined())),
|
|
61
|
-
getLocalAddress: M.call().returns(Shape2.Endpoint),
|
|
62
|
-
getRemoteAddress: M.call().returns(Shape2.Endpoint),
|
|
63
|
-
}),
|
|
64
|
-
InboundAttemptI: M.interface('InboundAttempt', {
|
|
65
|
-
accept: M.callWhen(Shape2.AttemptDescription).returns(
|
|
66
|
-
Shape2.Vow$(Shape2.Connection),
|
|
67
|
-
),
|
|
68
|
-
getLocalAddress: M.call().returns(Shape2.Endpoint),
|
|
69
|
-
getRemoteAddress: M.call().returns(Shape2.Endpoint),
|
|
70
|
-
close: M.callWhen().returns(Shape2.Vow$(M.undefined())),
|
|
71
|
-
}),
|
|
72
|
-
PortI: M.interface('Port', {
|
|
73
|
-
getLocalAddress: M.call().returns(Shape2.Endpoint),
|
|
74
|
-
addListener: M.callWhen(Shape2.Listener).returns(
|
|
75
|
-
Shape2.Vow$(M.undefined()),
|
|
76
|
-
),
|
|
77
|
-
connect: M.callWhen(Shape2.Endpoint)
|
|
78
|
-
.optional(Shape2.ConnectionHandler)
|
|
79
|
-
.returns(Shape2.Vow$(Shape2.Connection)),
|
|
80
|
-
removeListener: M.callWhen(Shape2.Listener).returns(
|
|
81
|
-
Shape2.Vow$(M.undefined()),
|
|
82
|
-
),
|
|
83
|
-
revoke: M.callWhen().returns(M.undefined()),
|
|
84
|
-
}),
|
|
85
|
-
ProtocolHandlerI: M.interface('ProtocolHandler', {
|
|
86
|
-
onCreate: M.callWhen(M.remotable(), Shape2.ProtocolHandler).returns(
|
|
87
|
-
Shape2.Vow$(M.undefined()),
|
|
88
|
-
),
|
|
89
|
-
generatePortID: M.callWhen(Shape2.Endpoint, Shape2.ProtocolHandler).returns(
|
|
90
|
-
Shape2.Vow$(M.string()),
|
|
91
|
-
),
|
|
92
|
-
onBind: M.callWhen(
|
|
93
|
-
Shape2.Port,
|
|
94
|
-
Shape2.Endpoint,
|
|
95
|
-
Shape2.ProtocolHandler,
|
|
96
|
-
).returns(Shape2.Vow$(M.undefined())),
|
|
97
|
-
onListen: M.callWhen(
|
|
98
|
-
Shape2.Port,
|
|
99
|
-
Shape2.Endpoint,
|
|
100
|
-
Shape2.ListenHandler,
|
|
101
|
-
Shape2.ProtocolHandler,
|
|
102
|
-
).returns(Shape2.Vow$(M.undefined())),
|
|
103
|
-
onListenRemove: M.callWhen(
|
|
104
|
-
Shape2.Port,
|
|
105
|
-
Shape2.Endpoint,
|
|
106
|
-
Shape2.ListenHandler,
|
|
107
|
-
Shape2.ProtocolHandler,
|
|
108
|
-
).returns(Shape2.Vow$(M.undefined())),
|
|
109
|
-
onInstantiate: M.callWhen(
|
|
110
|
-
Shape2.Port,
|
|
111
|
-
Shape2.Endpoint,
|
|
112
|
-
Shape2.Endpoint,
|
|
113
|
-
Shape2.ProtocolHandler,
|
|
114
|
-
).returns(Shape2.Vow$(Shape2.Endpoint)),
|
|
115
|
-
onConnect: M.callWhen(
|
|
116
|
-
Shape2.Port,
|
|
117
|
-
Shape2.Endpoint,
|
|
118
|
-
Shape2.Endpoint,
|
|
119
|
-
Shape2.ConnectionHandler,
|
|
120
|
-
Shape2.ProtocolHandler,
|
|
121
|
-
).returns(Shape2.Vow$(Shape2.AttemptDescription)),
|
|
122
|
-
onRevoke: M.callWhen(
|
|
123
|
-
Shape2.Port,
|
|
124
|
-
Shape2.Endpoint,
|
|
125
|
-
Shape2.ProtocolHandler,
|
|
126
|
-
).returns(Shape2.Vow$(M.undefined())),
|
|
127
|
-
}),
|
|
128
|
-
ProtocolImplI: M.interface('ProtocolImpl', {
|
|
129
|
-
bind: M.callWhen(Shape2.Endpoint).returns(Shape2.Vow$(Shape2.Port)),
|
|
130
|
-
inbound: M.callWhen(Shape2.Endpoint, Shape2.Endpoint).returns(
|
|
131
|
-
Shape2.Vow$(Shape2.InboundAttempt),
|
|
132
|
-
),
|
|
133
|
-
outbound: M.callWhen(
|
|
134
|
-
Shape2.Port,
|
|
135
|
-
Shape2.Endpoint,
|
|
136
|
-
Shape2.ConnectionHandler,
|
|
137
|
-
).returns(Shape2.Vow$(Shape2.Connection)),
|
|
138
|
-
}),
|
|
139
|
-
});
|
|
140
|
-
|
|
141
22
|
/** @param {unknown} err */
|
|
142
23
|
export const rethrowUnlessMissing = err => {
|
|
143
24
|
// Ugly hack rather than being able to determine if the function
|
|
144
25
|
// exists.
|
|
145
26
|
if (
|
|
146
27
|
!(err instanceof TypeError) ||
|
|
147
|
-
!err.message.match(/target has no method|is not a function$/)
|
|
28
|
+
!String(err.message).match(/target has no method|is not a function$/)
|
|
148
29
|
) {
|
|
149
30
|
throw err;
|
|
150
31
|
}
|
|
@@ -169,10 +50,24 @@ export function getPrefixes(addr) {
|
|
|
169
50
|
return ret;
|
|
170
51
|
}
|
|
171
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Validate IBC port name
|
|
55
|
+
* @param {string} specifiedName
|
|
56
|
+
*/
|
|
57
|
+
function throwIfInvalidPortName(specifiedName) {
|
|
58
|
+
// Contains between 2 and 128 characters
|
|
59
|
+
// Can contain alphanumeric characters
|
|
60
|
+
// Valid symbols: ., ,, _, +, -, #, [, ], <, >
|
|
61
|
+
const portNameRegex = new RegExp('^[a-zA-Z0-9.,_+\\-#<>\\[\\]]{2,128}$');
|
|
62
|
+
if (!portNameRegex.test(specifiedName)) {
|
|
63
|
+
throw new Error(`Invalid IBC port name: ${specifiedName}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
172
67
|
/**
|
|
173
68
|
* @typedef {object} ConnectionOpts
|
|
174
69
|
* @property {Endpoint[]} addrs
|
|
175
|
-
* @property {ConnectionHandler[]} handlers
|
|
70
|
+
* @property {import('@agoric/vow').Remote<Required<ConnectionHandler>>[]} handlers
|
|
176
71
|
* @property {MapStore<number, Connection>} conns
|
|
177
72
|
* @property {WeakSetStore<Closable>} current
|
|
178
73
|
* @property {0|1} l
|
|
@@ -183,15 +78,12 @@ export function getPrefixes(addr) {
|
|
|
183
78
|
* @param {import('@agoric/base-zone').Zone} zone
|
|
184
79
|
* @param {ReturnType<import('@agoric/vow').prepareVowTools>} powers
|
|
185
80
|
*/
|
|
186
|
-
const prepareHalfConnection = (zone, {
|
|
187
|
-
const
|
|
81
|
+
const prepareHalfConnection = (zone, { watch }) => {
|
|
82
|
+
const makeHalfConnectionKit = zone.exoClassKit(
|
|
188
83
|
'Connection',
|
|
189
84
|
Shape.ConnectionI,
|
|
190
85
|
/** @param {ConnectionOpts} opts */
|
|
191
86
|
({ addrs, handlers, conns, current, l, r }) => {
|
|
192
|
-
/** @type {string | undefined} */
|
|
193
|
-
let closed;
|
|
194
|
-
|
|
195
87
|
return {
|
|
196
88
|
addrs,
|
|
197
89
|
handlers,
|
|
@@ -199,62 +91,97 @@ const prepareHalfConnection = (zone, { when }) => {
|
|
|
199
91
|
current,
|
|
200
92
|
l,
|
|
201
93
|
r,
|
|
202
|
-
|
|
94
|
+
/** @type {string | undefined} */
|
|
95
|
+
closed: undefined,
|
|
203
96
|
};
|
|
204
97
|
},
|
|
205
98
|
{
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
99
|
+
connection: {
|
|
100
|
+
getLocalAddress() {
|
|
101
|
+
const { addrs, l } = this.state;
|
|
102
|
+
return addrs[l];
|
|
103
|
+
},
|
|
104
|
+
getRemoteAddress() {
|
|
105
|
+
const { addrs, r } = this.state;
|
|
106
|
+
return addrs[r];
|
|
107
|
+
},
|
|
108
|
+
/** @param {Bytes} packetBytes */
|
|
109
|
+
async send(packetBytes) {
|
|
110
|
+
const { closed, handlers, r, conns } = this.state;
|
|
111
|
+
if (closed) {
|
|
112
|
+
throw Error(closed);
|
|
113
|
+
}
|
|
220
114
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
115
|
+
const innerVow = watch(
|
|
116
|
+
E(handlers[r]).onReceive(
|
|
117
|
+
conns.get(r),
|
|
118
|
+
toBytes(packetBytes),
|
|
119
|
+
handlers[r],
|
|
120
|
+
),
|
|
121
|
+
this.facets.openConnectionAckWatcher,
|
|
122
|
+
);
|
|
123
|
+
return watch(innerVow, this.facets.rethrowUnlessMissingWatcher);
|
|
124
|
+
},
|
|
125
|
+
async close() {
|
|
126
|
+
const { closed, current, conns, l, handlers } = this.state;
|
|
127
|
+
if (closed) {
|
|
128
|
+
throw Error(closed);
|
|
129
|
+
}
|
|
130
|
+
this.state.closed = 'Connection closed';
|
|
131
|
+
current.delete(conns.get(l));
|
|
132
|
+
const innerVow = watch(
|
|
133
|
+
E(this.state.handlers[l]).onClose(
|
|
134
|
+
conns.get(l),
|
|
135
|
+
undefined,
|
|
136
|
+
handlers[l],
|
|
137
|
+
),
|
|
138
|
+
this.facets.sinkWatcher,
|
|
139
|
+
);
|
|
226
140
|
|
|
227
|
-
|
|
141
|
+
return watch(innerVow, this.facets.rethrowUnlessMissingWatcher);
|
|
142
|
+
},
|
|
228
143
|
},
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
144
|
+
openConnectionAckWatcher: {
|
|
145
|
+
onFulfilled(ack) {
|
|
146
|
+
return toBytes(ack || '');
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
rethrowUnlessMissingWatcher: {
|
|
150
|
+
onRejected(e) {
|
|
151
|
+
rethrowUnlessMissing(e);
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
sinkWatcher: {
|
|
155
|
+
onFulfilled(_value) {
|
|
156
|
+
return undefined;
|
|
157
|
+
},
|
|
243
158
|
},
|
|
244
159
|
},
|
|
245
160
|
);
|
|
246
161
|
|
|
162
|
+
const makeHalfConnection = ({ addrs, handlers, conns, current, l, r }) => {
|
|
163
|
+
const { connection } = makeHalfConnectionKit({
|
|
164
|
+
addrs,
|
|
165
|
+
handlers,
|
|
166
|
+
conns,
|
|
167
|
+
current,
|
|
168
|
+
l,
|
|
169
|
+
r,
|
|
170
|
+
});
|
|
171
|
+
return harden(connection);
|
|
172
|
+
};
|
|
173
|
+
|
|
247
174
|
return makeHalfConnection;
|
|
248
175
|
};
|
|
249
176
|
|
|
250
177
|
/**
|
|
251
178
|
* @param {import('@agoric/zone').Zone} zone
|
|
252
|
-
* @param {ConnectionHandler} handler0
|
|
179
|
+
* @param {import('@agoric/vow').Remote<Required<ConnectionHandler>>} handler0
|
|
253
180
|
* @param {Endpoint} addr0
|
|
254
|
-
* @param {ConnectionHandler} handler1
|
|
181
|
+
* @param {import('@agoric/vow').Remote<Required<ConnectionHandler>>} handler1
|
|
255
182
|
* @param {Endpoint} addr1
|
|
256
183
|
* @param {(opts: ConnectionOpts) => Connection} makeConnection
|
|
257
|
-
* @param {WeakSetStore<Closable>} current
|
|
184
|
+
* @param {WeakSetStore<Closable>} [current]
|
|
258
185
|
*/
|
|
259
186
|
export const crossoverConnection = (
|
|
260
187
|
zone,
|
|
@@ -270,7 +197,7 @@ export const crossoverConnection = (
|
|
|
270
197
|
/** @type {MapStore<number, Connection>} */
|
|
271
198
|
const conns = detached.mapStore('addrToConnections');
|
|
272
199
|
|
|
273
|
-
/** @type {ConnectionHandler[]} */
|
|
200
|
+
/** @type {import('@agoric/vow').Remote<Required<ConnectionHandler>>[]} */
|
|
274
201
|
const handlers = harden([handler0, handler1]);
|
|
275
202
|
/** @type {Endpoint[]} */
|
|
276
203
|
const addrs = harden([addr0, addr1]);
|
|
@@ -308,17 +235,17 @@ export const crossoverConnection = (
|
|
|
308
235
|
* @param {(opts: ConnectionOpts) => Connection} makeConnection
|
|
309
236
|
* @param {ReturnType<import('@agoric/vow').prepareVowTools>} powers
|
|
310
237
|
*/
|
|
311
|
-
const prepareInboundAttempt = (zone, makeConnection, {
|
|
312
|
-
const
|
|
238
|
+
const prepareInboundAttempt = (zone, makeConnection, { watch }) => {
|
|
239
|
+
const makeInboundAttemptKit = zone.exoClassKit(
|
|
313
240
|
'InboundAttempt',
|
|
314
241
|
Shape.InboundAttemptI,
|
|
315
242
|
/**
|
|
316
243
|
* @param {object} opts
|
|
317
244
|
* @param {string} opts.localAddr
|
|
318
245
|
* @param {string} opts.remoteAddr
|
|
319
|
-
* @param {
|
|
246
|
+
* @param {MapStore<Port, SetStore<Closable>>} opts.currentConnections
|
|
320
247
|
* @param {string} opts.listenPrefix
|
|
321
|
-
* @param {MapStore<Endpoint, [Port, ListenHandler]>} opts.listening
|
|
248
|
+
* @param {MapStore<Endpoint, [Port, import('@agoric/vow').Remote<Required<ListenHandler>>]>} opts.listening
|
|
322
249
|
*/
|
|
323
250
|
({
|
|
324
251
|
localAddr,
|
|
@@ -327,7 +254,7 @@ const prepareInboundAttempt = (zone, makeConnection, { when }) => {
|
|
|
327
254
|
listenPrefix,
|
|
328
255
|
listening,
|
|
329
256
|
}) => {
|
|
330
|
-
/** @type {
|
|
257
|
+
/** @type {string | undefined} */
|
|
331
258
|
let consummated;
|
|
332
259
|
|
|
333
260
|
return {
|
|
@@ -340,75 +267,125 @@ const prepareInboundAttempt = (zone, makeConnection, { when }) => {
|
|
|
340
267
|
};
|
|
341
268
|
},
|
|
342
269
|
{
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
270
|
+
inboundAttempt: {
|
|
271
|
+
getLocalAddress() {
|
|
272
|
+
// Return address metadata.
|
|
273
|
+
return this.state.localAddr;
|
|
274
|
+
},
|
|
275
|
+
getRemoteAddress() {
|
|
276
|
+
return this.state.remoteAddr;
|
|
277
|
+
},
|
|
278
|
+
async close() {
|
|
279
|
+
const { consummated, localAddr, remoteAddr } = this.state;
|
|
280
|
+
const { listening, listenPrefix, currentConnections } = this.state;
|
|
353
281
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
282
|
+
if (consummated) {
|
|
283
|
+
throw Error(consummated);
|
|
284
|
+
}
|
|
285
|
+
this.state.consummated = 'Already closed';
|
|
358
286
|
|
|
359
|
-
|
|
287
|
+
const [port, listener] = listening.get(listenPrefix);
|
|
360
288
|
|
|
361
|
-
|
|
362
|
-
|
|
289
|
+
const current = currentConnections.get(port);
|
|
290
|
+
current.delete(this.facets.inboundAttempt);
|
|
363
291
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* @param {object} opts
|
|
370
|
-
* @param {string} [opts.localAddress]
|
|
371
|
-
* @param {string} [opts.remoteAddress]
|
|
372
|
-
* @param {ConnectionHandler} opts.handler
|
|
373
|
-
*/
|
|
374
|
-
async accept({ localAddress, remoteAddress, handler: rchandler }) {
|
|
375
|
-
const { consummated, localAddr, remoteAddr } = this.state;
|
|
376
|
-
const { listening, listenPrefix, currentConnections } = this.state;
|
|
377
|
-
if (consummated) {
|
|
378
|
-
throw Error(consummated);
|
|
379
|
-
}
|
|
380
|
-
this.state.consummated = 'Already accepted';
|
|
292
|
+
const innerVow = watch(
|
|
293
|
+
E(listener).onReject(port, localAddr, remoteAddr, listener),
|
|
294
|
+
this.facets.sinkWatcher,
|
|
295
|
+
);
|
|
381
296
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
297
|
+
return watch(innerVow, this.facets.rethrowUnlessMissingWatcher);
|
|
298
|
+
},
|
|
299
|
+
/**
|
|
300
|
+
* @param {object} opts
|
|
301
|
+
* @param {string} [opts.localAddress]
|
|
302
|
+
* @param {string} [opts.remoteAddress]
|
|
303
|
+
* @param {import('@agoric/vow').Remote<ConnectionHandler>} opts.handler
|
|
304
|
+
*/
|
|
305
|
+
async accept({ localAddress, remoteAddress, handler: rchandler }) {
|
|
306
|
+
const { consummated, localAddr, remoteAddr } = this.state;
|
|
307
|
+
const { listening, listenPrefix, currentConnections } = this.state;
|
|
308
|
+
if (consummated) {
|
|
309
|
+
throw Error(consummated);
|
|
310
|
+
}
|
|
385
311
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
312
|
+
if (localAddress === undefined) {
|
|
313
|
+
localAddress = localAddr;
|
|
314
|
+
}
|
|
315
|
+
this.state.consummated = `${localAddress} Already accepted`;
|
|
389
316
|
|
|
390
|
-
|
|
391
|
-
|
|
317
|
+
if (remoteAddress === undefined) {
|
|
318
|
+
remoteAddress = remoteAddr;
|
|
319
|
+
}
|
|
392
320
|
|
|
393
|
-
|
|
321
|
+
const [port, listener] = listening.get(listenPrefix);
|
|
322
|
+
const current = currentConnections.get(port);
|
|
394
323
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
324
|
+
current.delete(this.facets.inboundAttempt);
|
|
325
|
+
|
|
326
|
+
return watch(
|
|
327
|
+
E(listener).onAccept(port, localAddress, remoteAddress, listener),
|
|
328
|
+
this.facets.inboundAttemptAcceptWatcher,
|
|
329
|
+
{
|
|
330
|
+
localAddress,
|
|
331
|
+
rchandler,
|
|
332
|
+
remoteAddress,
|
|
333
|
+
current,
|
|
334
|
+
},
|
|
335
|
+
);
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
inboundAttemptAcceptWatcher: {
|
|
339
|
+
onFulfilled(lchandler, watchContext) {
|
|
340
|
+
const { localAddress, rchandler, remoteAddress, current } =
|
|
341
|
+
watchContext;
|
|
398
342
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
343
|
+
return crossoverConnection(
|
|
344
|
+
zone,
|
|
345
|
+
/** @type {import('@agoric/vow').Remote<Required<ConnectionHandler>>} */ (
|
|
346
|
+
lchandler
|
|
347
|
+
),
|
|
348
|
+
localAddress,
|
|
349
|
+
/** @type {import('@agoric/vow').Remote<Required<ConnectionHandler>>} */ (
|
|
350
|
+
rchandler
|
|
351
|
+
),
|
|
352
|
+
remoteAddress,
|
|
353
|
+
makeConnection,
|
|
354
|
+
current,
|
|
355
|
+
)[1];
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
rethrowUnlessMissingWatcher: {
|
|
359
|
+
onRejected(e) {
|
|
360
|
+
rethrowUnlessMissing(e);
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
sinkWatcher: {
|
|
364
|
+
onFulfilled(_value) {
|
|
365
|
+
return undefined;
|
|
366
|
+
},
|
|
408
367
|
},
|
|
409
368
|
},
|
|
410
369
|
);
|
|
411
370
|
|
|
371
|
+
const makeInboundAttempt = ({
|
|
372
|
+
localAddr,
|
|
373
|
+
remoteAddr,
|
|
374
|
+
currentConnections,
|
|
375
|
+
listenPrefix,
|
|
376
|
+
listening,
|
|
377
|
+
}) => {
|
|
378
|
+
const { inboundAttempt } = makeInboundAttemptKit({
|
|
379
|
+
localAddr,
|
|
380
|
+
remoteAddr,
|
|
381
|
+
currentConnections,
|
|
382
|
+
listenPrefix,
|
|
383
|
+
listening,
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
return harden(inboundAttempt);
|
|
387
|
+
};
|
|
388
|
+
|
|
412
389
|
return makeInboundAttempt;
|
|
413
390
|
};
|
|
414
391
|
|
|
@@ -423,49 +400,50 @@ const RevokeState = /** @type {const} */ ({
|
|
|
423
400
|
* @param {import('@agoric/zone').Zone} zone
|
|
424
401
|
* @param {ReturnType<import('@agoric/vow').prepareVowTools>} powers
|
|
425
402
|
*/
|
|
426
|
-
const preparePort = (zone,
|
|
403
|
+
const preparePort = (zone, powers) => {
|
|
427
404
|
const makeIncapable = zone.exoClass('Incapable', undefined, () => ({}), {});
|
|
428
405
|
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
406
|
+
const { watch, allVows } = powers;
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* @param {object} opts
|
|
410
|
+
* @param {Endpoint} opts.localAddr
|
|
411
|
+
* @param {MapStore<Endpoint, [Port, import('@agoric/vow').Remote<Required<ListenHandler>>]>} opts.listening
|
|
412
|
+
* @param {SetStore<import('@agoric/vow').Remote<Connection>>} opts.openConnections
|
|
413
|
+
* @param {MapStore<Port, SetStore<Closable>>} opts.currentConnections
|
|
414
|
+
* @param {MapStore<string, Port>} opts.boundPorts
|
|
415
|
+
* @param {import('@agoric/vow').Remote<ProtocolHandler>} opts.protocolHandler
|
|
416
|
+
* @param {import('@agoric/vow').Remote<ProtocolImpl>} opts.protocolImpl
|
|
417
|
+
*/
|
|
418
|
+
const initPort = ({
|
|
419
|
+
localAddr,
|
|
420
|
+
listening,
|
|
421
|
+
openConnections,
|
|
422
|
+
currentConnections,
|
|
423
|
+
boundPorts,
|
|
424
|
+
protocolHandler,
|
|
425
|
+
protocolImpl,
|
|
426
|
+
}) => {
|
|
427
|
+
return {
|
|
444
428
|
listening,
|
|
445
429
|
openConnections,
|
|
446
430
|
currentConnections,
|
|
447
431
|
boundPorts,
|
|
432
|
+
localAddr,
|
|
448
433
|
protocolHandler,
|
|
449
434
|
protocolImpl,
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
protocolHandler,
|
|
458
|
-
protocolImpl,
|
|
459
|
-
/** @type {RevokeState | undefined} */
|
|
460
|
-
revoked: undefined,
|
|
461
|
-
};
|
|
462
|
-
},
|
|
463
|
-
{
|
|
435
|
+
/** @type {RevokeState | undefined} */
|
|
436
|
+
revoked: undefined,
|
|
437
|
+
};
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
const makePortKit = zone.exoClassKit('Port', Shape.PortI, initPort, {
|
|
441
|
+
port: {
|
|
464
442
|
getLocalAddress() {
|
|
465
443
|
// Works even after revoke().
|
|
466
444
|
return this.state.localAddr;
|
|
467
445
|
},
|
|
468
|
-
/** @param {ListenHandler} listenHandler */
|
|
446
|
+
/** @param {import('@agoric/vow').Remote<ListenHandler>} listenHandler */
|
|
469
447
|
async addListener(listenHandler) {
|
|
470
448
|
const { revoked, listening, localAddr, protocolHandler } = this.state;
|
|
471
449
|
|
|
@@ -478,96 +456,196 @@ const preparePort = (zone, { when }) => {
|
|
|
478
456
|
if (lhandler === listenHandler) {
|
|
479
457
|
return;
|
|
480
458
|
}
|
|
481
|
-
listening.set(localAddr, [
|
|
459
|
+
listening.set(localAddr, [
|
|
460
|
+
this.facets.port,
|
|
461
|
+
/** @type {import('@agoric/vow').Remote<Required<ListenHandler>>} */ (
|
|
462
|
+
listenHandler
|
|
463
|
+
),
|
|
464
|
+
]);
|
|
482
465
|
E(lhandler).onRemove(lport, lhandler).catch(rethrowUnlessMissing);
|
|
483
466
|
} else {
|
|
484
|
-
listening.init(
|
|
467
|
+
listening.init(
|
|
468
|
+
localAddr,
|
|
469
|
+
harden([
|
|
470
|
+
this.facets.port,
|
|
471
|
+
/** @type {import('@agoric/vow').Remote<Required<ListenHandler>>} */ (
|
|
472
|
+
listenHandler
|
|
473
|
+
),
|
|
474
|
+
]),
|
|
475
|
+
);
|
|
485
476
|
}
|
|
486
477
|
|
|
487
478
|
// ASSUME: that the listener defines onAccept.
|
|
488
479
|
|
|
489
|
-
|
|
480
|
+
const innerVow = watch(
|
|
490
481
|
E(protocolHandler).onListen(
|
|
491
|
-
this.
|
|
482
|
+
this.facets.port,
|
|
492
483
|
localAddr,
|
|
493
484
|
listenHandler,
|
|
494
485
|
protocolHandler,
|
|
495
486
|
),
|
|
487
|
+
this.facets.portAddListenerWatcher,
|
|
488
|
+
{ listenHandler },
|
|
496
489
|
);
|
|
497
|
-
|
|
498
|
-
rethrowUnlessMissing,
|
|
499
|
-
);
|
|
490
|
+
return watch(innerVow, this.facets.rethrowUnlessMissingWatcher);
|
|
500
491
|
},
|
|
501
|
-
/** @param {ListenHandler} listenHandler */
|
|
492
|
+
/** @param {import('@agoric/vow').Remote<ListenHandler>} listenHandler */
|
|
502
493
|
async removeListener(listenHandler) {
|
|
503
494
|
const { listening, localAddr, protocolHandler } = this.state;
|
|
504
495
|
listening.has(localAddr) || Fail`Port ${localAddr} is not listening`;
|
|
505
496
|
listening.get(localAddr)[1] === listenHandler ||
|
|
506
497
|
Fail`Port ${localAddr} handler to remove is not listening`;
|
|
507
498
|
listening.delete(localAddr);
|
|
508
|
-
|
|
499
|
+
|
|
500
|
+
const innerVow = watch(
|
|
509
501
|
E(protocolHandler).onListenRemove(
|
|
510
|
-
this.
|
|
502
|
+
this.facets.port,
|
|
511
503
|
localAddr,
|
|
512
504
|
listenHandler,
|
|
513
505
|
protocolHandler,
|
|
514
506
|
),
|
|
507
|
+
this.facets.portRemoveListenerWatcher,
|
|
508
|
+
{ listenHandler },
|
|
515
509
|
);
|
|
516
|
-
|
|
517
|
-
rethrowUnlessMissing,
|
|
518
|
-
);
|
|
510
|
+
return watch(innerVow, this.facets.rethrowUnlessMissingWatcher);
|
|
519
511
|
},
|
|
520
512
|
/**
|
|
521
513
|
* @param {Endpoint} remotePort
|
|
522
|
-
* @param {ConnectionHandler} connectionHandler
|
|
514
|
+
* @param {import('@agoric/vow').Remote<ConnectionHandler>} [connectionHandler]
|
|
523
515
|
*/
|
|
524
516
|
async connect(
|
|
525
517
|
remotePort,
|
|
526
|
-
connectionHandler = /** @type {
|
|
518
|
+
connectionHandler = /** @type {import('@agoric/vow').Remote<ConnectionHandler>} */ (
|
|
519
|
+
makeIncapable()
|
|
520
|
+
),
|
|
527
521
|
) {
|
|
528
|
-
const { revoked, localAddr, protocolImpl
|
|
529
|
-
this.state;
|
|
522
|
+
const { revoked, localAddr, protocolImpl } = this.state;
|
|
530
523
|
|
|
531
524
|
!revoked || Fail`Port ${localAddr} is revoked`;
|
|
532
525
|
/** @type {Endpoint} */
|
|
533
526
|
const dst = harden(remotePort);
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
527
|
+
return watch(
|
|
528
|
+
E(protocolImpl).outbound(this.facets.port, dst, connectionHandler),
|
|
529
|
+
this.facets.portConnectWatcher,
|
|
530
|
+
{ revoked },
|
|
537
531
|
);
|
|
538
|
-
if (revoked) {
|
|
539
|
-
void E(conn).close();
|
|
540
|
-
} else {
|
|
541
|
-
openConnections.add(conn);
|
|
542
|
-
}
|
|
543
|
-
return conn;
|
|
544
532
|
},
|
|
545
533
|
async revoke() {
|
|
546
534
|
const { revoked, localAddr } = this.state;
|
|
547
|
-
const { protocolHandler
|
|
548
|
-
this.state;
|
|
535
|
+
const { protocolHandler } = this.state;
|
|
549
536
|
|
|
550
537
|
revoked !== RevokeState.REVOKED ||
|
|
551
538
|
Fail`Port ${localAddr} is already revoked`;
|
|
539
|
+
|
|
552
540
|
this.state.revoked = RevokeState.REVOKING;
|
|
553
|
-
|
|
554
|
-
|
|
541
|
+
|
|
542
|
+
const revokeVow = watch(
|
|
543
|
+
E(protocolHandler).onRevoke(
|
|
544
|
+
this.facets.port,
|
|
545
|
+
localAddr,
|
|
546
|
+
protocolHandler,
|
|
547
|
+
),
|
|
548
|
+
this.facets.portRevokeWatcher,
|
|
555
549
|
);
|
|
556
|
-
|
|
550
|
+
|
|
551
|
+
return watch(revokeVow, this.facets.portRevokeCleanupWatcher);
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
portAddListenerWatcher: {
|
|
555
|
+
onFulfilled(_value, watcherContext) {
|
|
556
|
+
const { listenHandler } = watcherContext;
|
|
557
|
+
return E(listenHandler).onListen(this.facets.port, listenHandler);
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
portRemoveListenerWatcher: {
|
|
561
|
+
onFulfilled(_value, watcherContext) {
|
|
562
|
+
const { listenHandler } = watcherContext;
|
|
563
|
+
return E(listenHandler).onRemove(this.facets.port, listenHandler);
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
portConnectWatcher: {
|
|
567
|
+
onFulfilled(conn, watchContext) {
|
|
568
|
+
const { revoked } = watchContext;
|
|
569
|
+
const { openConnections } = this.state;
|
|
570
|
+
|
|
571
|
+
if (revoked) {
|
|
572
|
+
void E(conn).close();
|
|
573
|
+
} else {
|
|
574
|
+
openConnections.add(conn);
|
|
575
|
+
}
|
|
576
|
+
return conn;
|
|
577
|
+
},
|
|
578
|
+
},
|
|
579
|
+
portRevokeWatcher: {
|
|
580
|
+
onFulfilled(_value) {
|
|
581
|
+
const { currentConnections, listening, localAddr } = this.state;
|
|
582
|
+
const port = this.facets.port;
|
|
557
583
|
|
|
558
584
|
// Clean up everything we did.
|
|
559
|
-
const values = [...currentConnections.get(
|
|
560
|
-
|
|
585
|
+
const values = [...currentConnections.get(port).values()];
|
|
586
|
+
|
|
587
|
+
/** @type {import('@agoric/vow').Specimen[]} */
|
|
588
|
+
const ps = [];
|
|
589
|
+
|
|
590
|
+
ps.push(
|
|
591
|
+
...values.map(conn =>
|
|
592
|
+
watch(E(conn).close(), this.facets.sinkWatcher),
|
|
593
|
+
),
|
|
594
|
+
);
|
|
595
|
+
|
|
561
596
|
if (listening.has(localAddr)) {
|
|
562
597
|
const listener = listening.get(localAddr)[1];
|
|
563
|
-
ps.push(
|
|
598
|
+
ps.push(port.removeListener(listener));
|
|
564
599
|
}
|
|
565
|
-
|
|
566
|
-
|
|
600
|
+
|
|
601
|
+
return watch(allVows(ps), this.facets.rethrowUnlessMissingWatcher);
|
|
602
|
+
},
|
|
603
|
+
},
|
|
604
|
+
sinkWatcher: {
|
|
605
|
+
onFulfilled() {
|
|
606
|
+
return undefined;
|
|
607
|
+
},
|
|
608
|
+
onRejected() {
|
|
609
|
+
return undefined;
|
|
610
|
+
},
|
|
611
|
+
},
|
|
612
|
+
portRevokeCleanupWatcher: {
|
|
613
|
+
onFulfilled(_value) {
|
|
614
|
+
const { currentConnections, boundPorts, localAddr } = this.state;
|
|
615
|
+
|
|
616
|
+
this.state.revoked = RevokeState.REVOKED;
|
|
617
|
+
|
|
618
|
+
currentConnections.delete(this.facets.port);
|
|
567
619
|
boundPorts.delete(localAddr);
|
|
568
620
|
},
|
|
569
621
|
},
|
|
570
|
-
|
|
622
|
+
rethrowUnlessMissingWatcher: {
|
|
623
|
+
onRejected(e) {
|
|
624
|
+
rethrowUnlessMissing(e);
|
|
625
|
+
},
|
|
626
|
+
},
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
const makePort = ({
|
|
630
|
+
localAddr,
|
|
631
|
+
listening,
|
|
632
|
+
openConnections,
|
|
633
|
+
currentConnections,
|
|
634
|
+
boundPorts,
|
|
635
|
+
protocolHandler,
|
|
636
|
+
protocolImpl,
|
|
637
|
+
}) => {
|
|
638
|
+
const { port } = makePortKit({
|
|
639
|
+
localAddr,
|
|
640
|
+
listening,
|
|
641
|
+
openConnections,
|
|
642
|
+
currentConnections,
|
|
643
|
+
boundPorts,
|
|
644
|
+
protocolHandler,
|
|
645
|
+
protocolImpl,
|
|
646
|
+
});
|
|
647
|
+
return harden(port);
|
|
648
|
+
};
|
|
571
649
|
|
|
572
650
|
return makePort;
|
|
573
651
|
};
|
|
@@ -578,29 +656,84 @@ const preparePort = (zone, { when }) => {
|
|
|
578
656
|
*/
|
|
579
657
|
const prepareBinder = (zone, powers) => {
|
|
580
658
|
const makeConnection = prepareHalfConnection(zone, powers);
|
|
581
|
-
|
|
659
|
+
|
|
660
|
+
const { watch } = powers;
|
|
661
|
+
|
|
582
662
|
const makeInboundAttempt = prepareInboundAttempt(
|
|
583
663
|
zone,
|
|
584
664
|
makeConnection,
|
|
585
665
|
powers,
|
|
586
666
|
);
|
|
667
|
+
|
|
587
668
|
const makePort = preparePort(zone, powers);
|
|
669
|
+
|
|
588
670
|
const detached = zone.detached();
|
|
589
671
|
|
|
590
|
-
const
|
|
672
|
+
const makeFullBinderKit = zone.exoClassKit(
|
|
591
673
|
'binder',
|
|
592
674
|
{
|
|
593
675
|
protocolImpl: Shape.ProtocolImplI,
|
|
594
676
|
binder: M.interface('Binder', {
|
|
595
|
-
|
|
677
|
+
bindPort: M.callWhen(Shape.Endpoint).returns(Shape.Vow$(Shape.Port)),
|
|
678
|
+
}),
|
|
679
|
+
binderInboundInstantiateWatcher: M.interface(
|
|
680
|
+
'BinderInboundInstantiateWatcher',
|
|
681
|
+
{
|
|
682
|
+
onFulfilled: M.call(M.any()).rest(M.any()).returns(M.any()),
|
|
683
|
+
},
|
|
684
|
+
),
|
|
685
|
+
binderInboundInstantiateCatchWatcher: M.interface(
|
|
686
|
+
'BinderInboundInstantiateCatchWatcher',
|
|
687
|
+
{
|
|
688
|
+
onRejected: M.call(M.any()).rest(M.any()).returns(M.any()),
|
|
689
|
+
},
|
|
690
|
+
),
|
|
691
|
+
binderOutboundInstantiateWatcher: M.interface(
|
|
692
|
+
'BinderOutboundInstantiateWatcher',
|
|
693
|
+
{
|
|
694
|
+
onFulfilled: M.call(M.any()).rest(M.any()).returns(M.any()),
|
|
695
|
+
},
|
|
696
|
+
),
|
|
697
|
+
binderOutboundConnectWatcher: M.interface(
|
|
698
|
+
'BinderOutboundConnectWatcher',
|
|
699
|
+
{
|
|
700
|
+
onFulfilled: M.call(M.any()).rest(M.any()).returns(M.any()),
|
|
701
|
+
},
|
|
702
|
+
),
|
|
703
|
+
binderOutboundCatchWatcher: M.interface('BinderOutboundCatchWatcher', {
|
|
704
|
+
onRejected: M.call(M.any()).rest(M.any()).returns(M.any()),
|
|
705
|
+
}),
|
|
706
|
+
binderOutboundInboundWatcher: M.interface(
|
|
707
|
+
'BinderOutboundInboundWatcher',
|
|
708
|
+
{
|
|
709
|
+
onFulfilled: M.call(M.any()).rest(M.any()).returns(M.any()),
|
|
710
|
+
},
|
|
711
|
+
),
|
|
712
|
+
binderOutboundAcceptWatcher: M.interface('BinderOutboundAcceptWatcher', {
|
|
713
|
+
onFulfilled: M.call(M.any()).rest(M.any()).returns(M.any()),
|
|
714
|
+
}),
|
|
715
|
+
binderBindGeneratePortWatcher: M.interface(
|
|
716
|
+
'BinderBindGeneratePortWatcher',
|
|
717
|
+
{
|
|
718
|
+
onFulfilled: M.call(M.any()).rest(M.any()).returns(M.any()),
|
|
719
|
+
},
|
|
720
|
+
),
|
|
721
|
+
binderPortWatcher: M.interface('BinderPortWatcher', {
|
|
722
|
+
onFulfilled: M.call(M.any()).rest(M.any()).returns(M.any()),
|
|
723
|
+
}),
|
|
724
|
+
binderBindWatcher: M.interface('BinderBindWatcher', {
|
|
725
|
+
onFulfilled: M.call(M.any()).rest(M.any()).returns(M.any()),
|
|
726
|
+
}),
|
|
727
|
+
rethrowUnlessMissingWatcher: M.interface('RethrowUnlessMissingWatcher', {
|
|
728
|
+
onRejected: M.call(M.any()).rest(M.any()).returns(M.any()),
|
|
596
729
|
}),
|
|
597
730
|
},
|
|
598
731
|
/**
|
|
599
732
|
* @param {object} opts
|
|
600
|
-
* @param {
|
|
733
|
+
* @param {MapStore<Port, SetStore<Closable>>} opts.currentConnections
|
|
601
734
|
* @param {MapStore<string, Port>} opts.boundPorts
|
|
602
|
-
* @param {MapStore<Endpoint, [Port, ListenHandler]>} opts.listening
|
|
603
|
-
* @param {ProtocolHandler} opts.protocolHandler
|
|
735
|
+
* @param {MapStore<Endpoint, [Port, import('@agoric/vow').Remote<Required<ListenHandler>>]>} opts.listening
|
|
736
|
+
* @param {import('@agoric/vow').Remote<ProtocolHandler>} opts.protocolHandler
|
|
604
737
|
*/
|
|
605
738
|
({ currentConnections, boundPorts, listening, protocolHandler }) => {
|
|
606
739
|
/** @type {SetStore<Connection>} */
|
|
@@ -613,8 +746,6 @@ const prepareBinder = (zone, powers) => {
|
|
|
613
746
|
revoked: RevokeState.NOT_REVOKED,
|
|
614
747
|
openConnections,
|
|
615
748
|
protocolHandler,
|
|
616
|
-
/** @type {Endpoint | undefined} */
|
|
617
|
-
localAddr: undefined,
|
|
618
749
|
};
|
|
619
750
|
},
|
|
620
751
|
{
|
|
@@ -624,49 +755,58 @@ const prepareBinder = (zone, powers) => {
|
|
|
624
755
|
* @param {Endpoint} remoteAddr
|
|
625
756
|
*/
|
|
626
757
|
async inbound(listenAddr, remoteAddr) {
|
|
627
|
-
const { listening, protocolHandler
|
|
758
|
+
const { listening, protocolHandler } = this.state;
|
|
759
|
+
|
|
760
|
+
const prefixes = getPrefixes(listenAddr);
|
|
761
|
+
let listenPrefixIndex = 0;
|
|
762
|
+
let listenPrefix;
|
|
628
763
|
|
|
629
|
-
|
|
630
|
-
|
|
764
|
+
while (listenPrefixIndex < prefixes.length) {
|
|
765
|
+
listenPrefix = prefixes[listenPrefixIndex];
|
|
631
766
|
if (!listening.has(listenPrefix)) {
|
|
767
|
+
listenPrefixIndex += 1;
|
|
632
768
|
continue;
|
|
633
769
|
}
|
|
634
|
-
const [port, _] = listening.get(listenPrefix);
|
|
635
|
-
let localAddr;
|
|
636
770
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
const localInstance = await when(
|
|
640
|
-
E(protocolHandler).onInstantiate(
|
|
641
|
-
port,
|
|
642
|
-
listenPrefix,
|
|
643
|
-
remoteAddr,
|
|
644
|
-
protocolHandler,
|
|
645
|
-
),
|
|
646
|
-
).catch(rethrowUnlessMissing);
|
|
647
|
-
localAddr = localInstance
|
|
648
|
-
? `${listenAddr}/${localInstance}`
|
|
649
|
-
: listenAddr;
|
|
650
|
-
})().catch(e => {
|
|
651
|
-
lastFailure = e;
|
|
652
|
-
});
|
|
653
|
-
if (!localAddr) {
|
|
654
|
-
continue;
|
|
655
|
-
}
|
|
656
|
-
// We have a legitimate inbound attempt.
|
|
657
|
-
const current = currentConnections.get(port);
|
|
658
|
-
const inboundAttempt = makeInboundAttempt({
|
|
659
|
-
localAddr,
|
|
660
|
-
remoteAddr,
|
|
661
|
-
currentConnections,
|
|
662
|
-
listenPrefix,
|
|
663
|
-
listening,
|
|
664
|
-
});
|
|
771
|
+
break;
|
|
772
|
+
}
|
|
665
773
|
|
|
666
|
-
|
|
667
|
-
|
|
774
|
+
if (listenPrefixIndex >= prefixes.length) {
|
|
775
|
+
throw Error(`No listeners for ${listenAddr}`);
|
|
668
776
|
}
|
|
669
|
-
|
|
777
|
+
|
|
778
|
+
const [port] = listening.get(/** @type {string} **/ (listenPrefix));
|
|
779
|
+
|
|
780
|
+
const innerVow = watch(
|
|
781
|
+
E(
|
|
782
|
+
/** @type {import('@agoric/vow').Remote<Required<ProtocolHandler>>} */ (
|
|
783
|
+
protocolHandler
|
|
784
|
+
),
|
|
785
|
+
).onInstantiate(
|
|
786
|
+
/** @type {Port} **/ (port),
|
|
787
|
+
prefixes[listenPrefixIndex],
|
|
788
|
+
remoteAddr,
|
|
789
|
+
protocolHandler,
|
|
790
|
+
),
|
|
791
|
+
this.facets.binderInboundInstantiateWatcher,
|
|
792
|
+
{
|
|
793
|
+
listenAddr,
|
|
794
|
+
remoteAddr,
|
|
795
|
+
port,
|
|
796
|
+
listenPrefixIndex,
|
|
797
|
+
},
|
|
798
|
+
);
|
|
799
|
+
|
|
800
|
+
return watch(
|
|
801
|
+
innerVow,
|
|
802
|
+
this.facets.binderInboundInstantiateCatchWatcher,
|
|
803
|
+
{
|
|
804
|
+
listenPrefixIndex,
|
|
805
|
+
listenAddr,
|
|
806
|
+
remoteAddr,
|
|
807
|
+
lastFailure: Error(`No listeners for ${listenAddr}`),
|
|
808
|
+
},
|
|
809
|
+
);
|
|
670
810
|
},
|
|
671
811
|
/**
|
|
672
812
|
* @param {Port} port
|
|
@@ -674,101 +814,312 @@ const prepareBinder = (zone, powers) => {
|
|
|
674
814
|
* @param {ConnectionHandler} lchandler
|
|
675
815
|
*/
|
|
676
816
|
async outbound(port, remoteAddr, lchandler) {
|
|
677
|
-
const { protocolHandler
|
|
817
|
+
const { protocolHandler } = this.state;
|
|
678
818
|
|
|
679
819
|
const localAddr = await E(port).getLocalAddress();
|
|
680
820
|
|
|
681
821
|
// Allocate a local address.
|
|
682
|
-
const
|
|
683
|
-
E(
|
|
822
|
+
const instantiateInnerVow = watch(
|
|
823
|
+
E(
|
|
824
|
+
/** @type {import('@agoric/vow').Remote<Required<ProtocolHandler>>} */ (
|
|
825
|
+
protocolHandler
|
|
826
|
+
),
|
|
827
|
+
).onInstantiate(port, localAddr, remoteAddr, protocolHandler),
|
|
828
|
+
this.facets.binderOutboundInstantiateWatcher,
|
|
829
|
+
{
|
|
684
830
|
port,
|
|
685
831
|
localAddr,
|
|
686
832
|
remoteAddr,
|
|
687
833
|
protocolHandler,
|
|
688
|
-
|
|
689
|
-
)
|
|
690
|
-
const initialLocalAddr = initialLocalInstance
|
|
691
|
-
? `${localAddr}/${initialLocalInstance}`
|
|
692
|
-
: localAddr;
|
|
834
|
+
},
|
|
835
|
+
);
|
|
693
836
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
837
|
+
const instantiateVow = watch(
|
|
838
|
+
instantiateInnerVow,
|
|
839
|
+
this.facets.rethrowUnlessMissingWatcher,
|
|
840
|
+
);
|
|
841
|
+
|
|
842
|
+
const attemptVow = watch(
|
|
843
|
+
instantiateVow,
|
|
844
|
+
this.facets.binderOutboundInboundWatcher,
|
|
845
|
+
{
|
|
846
|
+
localAddr,
|
|
847
|
+
remoteAddr,
|
|
848
|
+
},
|
|
849
|
+
);
|
|
850
|
+
const acceptedVow = watch(
|
|
851
|
+
attemptVow,
|
|
852
|
+
this.facets.binderOutboundAcceptWatcher,
|
|
853
|
+
{
|
|
854
|
+
handler: lchandler,
|
|
855
|
+
},
|
|
856
|
+
);
|
|
857
|
+
|
|
858
|
+
return watch(acceptedVow, this.facets.binderOutboundCatchWatcher, {
|
|
859
|
+
port,
|
|
860
|
+
remoteAddr,
|
|
861
|
+
lchandler,
|
|
862
|
+
localAddr,
|
|
704
863
|
});
|
|
705
|
-
|
|
706
|
-
|
|
864
|
+
},
|
|
865
|
+
async bindPort(localAddr) {
|
|
866
|
+
return this.facets.binder.bindPort(localAddr);
|
|
867
|
+
},
|
|
868
|
+
},
|
|
869
|
+
binder: {
|
|
870
|
+
/** @param {string} localAddr */
|
|
871
|
+
async bindPort(localAddr) {
|
|
872
|
+
const { protocolHandler } = this.state;
|
|
873
|
+
|
|
874
|
+
// Check if we are underspecified (ends in slash)
|
|
875
|
+
const underspecified = localAddr.endsWith(ENDPOINT_SEPARATOR);
|
|
876
|
+
|
|
877
|
+
const localAddrVow = watch(
|
|
878
|
+
E(protocolHandler).generatePortID(localAddr, protocolHandler),
|
|
879
|
+
this.facets.binderBindGeneratePortWatcher,
|
|
880
|
+
{
|
|
881
|
+
underspecified,
|
|
882
|
+
localAddr,
|
|
883
|
+
},
|
|
884
|
+
);
|
|
885
|
+
|
|
886
|
+
return watch(localAddrVow, this.facets.binderBindWatcher);
|
|
887
|
+
},
|
|
888
|
+
},
|
|
889
|
+
binderInboundInstantiateWatcher: {
|
|
890
|
+
onFulfilled(localInstance, watchContext) {
|
|
891
|
+
const { listenAddr, remoteAddr, port, listenPrefixIndex } =
|
|
892
|
+
watchContext;
|
|
893
|
+
const { listening, currentConnections } = this.state;
|
|
894
|
+
const prefixes = getPrefixes(listenAddr);
|
|
895
|
+
|
|
896
|
+
const localAddr = localInstance
|
|
897
|
+
? `${listenAddr}/${localInstance}`
|
|
898
|
+
: listenAddr;
|
|
899
|
+
const current = currentConnections.get(port);
|
|
900
|
+
const inboundAttempt = makeInboundAttempt({
|
|
901
|
+
localAddr,
|
|
902
|
+
remoteAddr,
|
|
903
|
+
currentConnections,
|
|
904
|
+
listenPrefix: prefixes[listenPrefixIndex],
|
|
905
|
+
listening,
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
current.add(inboundAttempt);
|
|
909
|
+
return inboundAttempt;
|
|
910
|
+
},
|
|
911
|
+
},
|
|
912
|
+
binderInboundInstantiateCatchWatcher: {
|
|
913
|
+
onRejected(e, watchContext) {
|
|
914
|
+
let { lastFailure, listenPrefixIndex } = watchContext;
|
|
915
|
+
|
|
916
|
+
try {
|
|
917
|
+
rethrowUnlessMissing(e);
|
|
918
|
+
} catch (innerE) {
|
|
919
|
+
lastFailure = innerE;
|
|
707
920
|
}
|
|
708
921
|
|
|
709
|
-
const {
|
|
710
|
-
|
|
922
|
+
const { listenAddr, remoteAddr } = watchContext;
|
|
923
|
+
|
|
924
|
+
const { listening, protocolHandler } = this.state;
|
|
925
|
+
|
|
926
|
+
const prefixes = getPrefixes(listenAddr);
|
|
927
|
+
|
|
928
|
+
let listenPrefix;
|
|
929
|
+
|
|
930
|
+
listenPrefixIndex += 1;
|
|
931
|
+
|
|
932
|
+
while (listenPrefixIndex < prefixes.length) {
|
|
933
|
+
listenPrefix = prefixes[listenPrefixIndex];
|
|
934
|
+
if (!listening.has(listenPrefix)) {
|
|
935
|
+
listenPrefixIndex += 1;
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
if (listenPrefixIndex >= prefixes.length) {
|
|
943
|
+
throw lastFailure;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
const [port] = listening.get(/** @type {string} */ (listenPrefix));
|
|
947
|
+
|
|
948
|
+
const innerVow = watch(
|
|
949
|
+
E(
|
|
950
|
+
/** @type {import('@agoric/vow').Remote<Required<ProtocolHandler>>} */ (
|
|
951
|
+
protocolHandler
|
|
952
|
+
),
|
|
953
|
+
).onInstantiate(
|
|
954
|
+
port,
|
|
955
|
+
prefixes[listenPrefixIndex],
|
|
956
|
+
remoteAddr,
|
|
957
|
+
protocolHandler,
|
|
958
|
+
),
|
|
959
|
+
this.facets.binderInboundInstantiateWatcher,
|
|
960
|
+
{
|
|
961
|
+
listenAddr,
|
|
962
|
+
remoteAddr,
|
|
963
|
+
port,
|
|
964
|
+
listenPrefixIndex,
|
|
965
|
+
},
|
|
966
|
+
);
|
|
967
|
+
|
|
968
|
+
return watch(
|
|
969
|
+
innerVow,
|
|
970
|
+
this.facets.binderInboundInstantiateCatchWatcher,
|
|
971
|
+
{
|
|
972
|
+
...watchContext,
|
|
973
|
+
lastFailure,
|
|
974
|
+
listenPrefixIndex,
|
|
975
|
+
},
|
|
976
|
+
);
|
|
977
|
+
},
|
|
978
|
+
},
|
|
979
|
+
binderOutboundInstantiateWatcher: {
|
|
980
|
+
onFulfilled(localInstance, watchContext) {
|
|
981
|
+
const { localAddr } = watchContext;
|
|
982
|
+
|
|
983
|
+
return localInstance ? `${localAddr}/${localInstance}` : localAddr;
|
|
984
|
+
},
|
|
985
|
+
},
|
|
986
|
+
binderOutboundConnectWatcher: {
|
|
987
|
+
onFulfilled(
|
|
988
|
+
{
|
|
711
989
|
handler: rchandler,
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
990
|
+
remoteAddress: negotiatedRemoteAddress,
|
|
991
|
+
localAddress: negotiatedLocalAddress,
|
|
992
|
+
},
|
|
993
|
+
watchContext,
|
|
994
|
+
) {
|
|
995
|
+
const {
|
|
996
|
+
lastFailure,
|
|
997
|
+
lchandler,
|
|
998
|
+
localAddr: requestedLocalAddress,
|
|
999
|
+
remoteAddr: requestedRemoteAddress,
|
|
1000
|
+
port,
|
|
1001
|
+
} = watchContext;
|
|
1002
|
+
|
|
1003
|
+
const { currentConnections } = this.state;
|
|
726
1004
|
|
|
727
1005
|
if (!rchandler) {
|
|
728
1006
|
throw lastFailure;
|
|
729
1007
|
}
|
|
730
1008
|
|
|
731
1009
|
const current = currentConnections.get(port);
|
|
1010
|
+
|
|
732
1011
|
return crossoverConnection(
|
|
733
1012
|
zone,
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
1013
|
+
/** @type {import('@agoric/vow').Remote<Required<ConnectionHandler>>} */ (
|
|
1014
|
+
lchandler
|
|
1015
|
+
),
|
|
1016
|
+
negotiatedLocalAddress || requestedLocalAddress,
|
|
1017
|
+
/** @type {import('@agoric/vow').Remote<Required<ConnectionHandler>>} */ (
|
|
1018
|
+
rchandler
|
|
1019
|
+
),
|
|
1020
|
+
negotiatedRemoteAddress || requestedRemoteAddress,
|
|
738
1021
|
makeConnection,
|
|
739
1022
|
current,
|
|
740
1023
|
)[0];
|
|
741
1024
|
},
|
|
742
|
-
|
|
743
|
-
|
|
1025
|
+
},
|
|
1026
|
+
binderOutboundCatchWatcher: {
|
|
1027
|
+
onRejected(e, watchContext) {
|
|
1028
|
+
let lastFailure;
|
|
1029
|
+
|
|
1030
|
+
try {
|
|
1031
|
+
rethrowUnlessMissing(e);
|
|
1032
|
+
} catch (innerE) {
|
|
1033
|
+
lastFailure = innerE;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
const { port, remoteAddr, lchandler, localAddr } = watchContext;
|
|
1037
|
+
|
|
1038
|
+
const { protocolHandler } = this.state;
|
|
1039
|
+
|
|
1040
|
+
const connectVow = watch(
|
|
1041
|
+
E(protocolHandler).onConnect(
|
|
1042
|
+
port,
|
|
1043
|
+
localAddr,
|
|
1044
|
+
remoteAddr,
|
|
1045
|
+
lchandler,
|
|
1046
|
+
protocolHandler,
|
|
1047
|
+
),
|
|
1048
|
+
);
|
|
1049
|
+
|
|
1050
|
+
return watch(connectVow, this.facets.binderOutboundConnectWatcher, {
|
|
1051
|
+
lastFailure,
|
|
1052
|
+
remoteAddr,
|
|
1053
|
+
localAddr,
|
|
1054
|
+
lchandler,
|
|
1055
|
+
port,
|
|
1056
|
+
});
|
|
744
1057
|
},
|
|
745
1058
|
},
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
1059
|
+
binderOutboundInboundWatcher: {
|
|
1060
|
+
onFulfilled(initialLocalAddress, watchContext) {
|
|
1061
|
+
const { remoteAddr, localAddr } = watchContext;
|
|
1062
|
+
|
|
1063
|
+
if (initialLocalAddress === undefined) {
|
|
1064
|
+
initialLocalAddress = localAddr;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// Attempt the loopback connection.
|
|
1068
|
+
return this.facets.protocolImpl.inbound(
|
|
1069
|
+
remoteAddr,
|
|
1070
|
+
initialLocalAddress,
|
|
1071
|
+
);
|
|
1072
|
+
},
|
|
1073
|
+
},
|
|
1074
|
+
binderOutboundAcceptWatcher: {
|
|
1075
|
+
onFulfilled(attempt, watchContext) {
|
|
1076
|
+
const { handler } = watchContext;
|
|
1077
|
+
return E(attempt).accept({ handler });
|
|
1078
|
+
},
|
|
1079
|
+
},
|
|
1080
|
+
binderBindGeneratePortWatcher: {
|
|
1081
|
+
onFulfilled(portID, watchContext) {
|
|
1082
|
+
const { localAddr, underspecified } = watchContext;
|
|
1083
|
+
const { protocolHandler, boundPorts } = this.state;
|
|
1084
|
+
|
|
1085
|
+
if (!underspecified) {
|
|
1086
|
+
return localAddr;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
const newAddr = `${localAddr}${portID}`;
|
|
1090
|
+
if (!boundPorts.has(newAddr)) {
|
|
1091
|
+
return newAddr;
|
|
1092
|
+
}
|
|
1093
|
+
return watch(
|
|
1094
|
+
E(protocolHandler).generatePortID(localAddr, protocolHandler),
|
|
1095
|
+
this.facets.binderBindGeneratePortWatcher,
|
|
1096
|
+
watchContext,
|
|
1097
|
+
);
|
|
1098
|
+
},
|
|
1099
|
+
},
|
|
1100
|
+
binderPortWatcher: {
|
|
1101
|
+
onFulfilled(_value, watchContext) {
|
|
1102
|
+
const { port, localAddr } = watchContext;
|
|
1103
|
+
const { boundPorts, currentConnections } = this.state;
|
|
1104
|
+
|
|
1105
|
+
boundPorts.init(localAddr, port);
|
|
1106
|
+
currentConnections.init(
|
|
1107
|
+
port,
|
|
1108
|
+
zone.detached().setStore('connections'),
|
|
1109
|
+
);
|
|
1110
|
+
return port;
|
|
1111
|
+
},
|
|
1112
|
+
},
|
|
1113
|
+
binderBindWatcher: {
|
|
1114
|
+
onFulfilled(localAddr) {
|
|
749
1115
|
const {
|
|
750
|
-
protocolHandler,
|
|
751
1116
|
boundPorts,
|
|
752
1117
|
listening,
|
|
753
1118
|
openConnections,
|
|
754
1119
|
currentConnections,
|
|
1120
|
+
protocolHandler,
|
|
755
1121
|
} = this.state;
|
|
756
1122
|
|
|
757
|
-
// Check if we are underspecified (ends in slash)
|
|
758
|
-
const underspecified = localAddr.endsWith(ENDPOINT_SEPARATOR);
|
|
759
|
-
for await (const _ of whileTrue(() => underspecified)) {
|
|
760
|
-
const portID = await when(
|
|
761
|
-
E(protocolHandler).generatePortID(localAddr, protocolHandler),
|
|
762
|
-
);
|
|
763
|
-
const newAddr = `${localAddr}${portID}`;
|
|
764
|
-
if (!boundPorts.has(newAddr)) {
|
|
765
|
-
localAddr = newAddr;
|
|
766
|
-
break;
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
this.state.localAddr = localAddr;
|
|
771
|
-
|
|
772
1123
|
if (boundPorts.has(localAddr)) {
|
|
773
1124
|
return boundPorts.get(localAddr);
|
|
774
1125
|
}
|
|
@@ -783,17 +1134,38 @@ const prepareBinder = (zone, powers) => {
|
|
|
783
1134
|
protocolImpl: this.facets.protocolImpl,
|
|
784
1135
|
});
|
|
785
1136
|
|
|
786
|
-
|
|
1137
|
+
return watch(
|
|
787
1138
|
E(protocolHandler).onBind(port, localAddr, protocolHandler),
|
|
1139
|
+
this.facets.binderPortWatcher,
|
|
1140
|
+
{
|
|
1141
|
+
port,
|
|
1142
|
+
localAddr,
|
|
1143
|
+
},
|
|
788
1144
|
);
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
1145
|
+
},
|
|
1146
|
+
},
|
|
1147
|
+
rethrowUnlessMissingWatcher: {
|
|
1148
|
+
onRejected(e) {
|
|
1149
|
+
rethrowUnlessMissing(e);
|
|
792
1150
|
},
|
|
793
1151
|
},
|
|
794
1152
|
},
|
|
795
1153
|
);
|
|
796
1154
|
|
|
1155
|
+
const makeBinderKit = ({
|
|
1156
|
+
currentConnections,
|
|
1157
|
+
boundPorts,
|
|
1158
|
+
listening,
|
|
1159
|
+
protocolHandler,
|
|
1160
|
+
}) => {
|
|
1161
|
+
const { protocolImpl, binder } = makeFullBinderKit({
|
|
1162
|
+
currentConnections,
|
|
1163
|
+
boundPorts,
|
|
1164
|
+
listening,
|
|
1165
|
+
protocolHandler,
|
|
1166
|
+
});
|
|
1167
|
+
return harden({ protocolImpl, binder });
|
|
1168
|
+
};
|
|
797
1169
|
return makeBinderKit;
|
|
798
1170
|
};
|
|
799
1171
|
|
|
@@ -805,7 +1177,7 @@ export const prepareNetworkProtocol = (zone, powers) => {
|
|
|
805
1177
|
const makeBinderKit = prepareBinder(zone, powers);
|
|
806
1178
|
|
|
807
1179
|
/**
|
|
808
|
-
* @param {ProtocolHandler} protocolHandler
|
|
1180
|
+
* @param {import('@agoric/vow').Remote<ProtocolHandler>} protocolHandler
|
|
809
1181
|
* @returns {Protocol}
|
|
810
1182
|
*/
|
|
811
1183
|
const makeNetworkProtocol = protocolHandler => {
|
|
@@ -817,7 +1189,7 @@ export const prepareNetworkProtocol = (zone, powers) => {
|
|
|
817
1189
|
/** @type {MapStore<string, Port>} */
|
|
818
1190
|
const boundPorts = detached.mapStore('addrToPort');
|
|
819
1191
|
|
|
820
|
-
/** @type {MapStore<Endpoint, [Port, ListenHandler]>} */
|
|
1192
|
+
/** @type {MapStore<Endpoint, [Port, import('@agoric/vow').Remote<Required<ListenHandler>>]>} */
|
|
821
1193
|
const listening = detached.mapStore('listening');
|
|
822
1194
|
|
|
823
1195
|
const { binder, protocolImpl } = makeBinderKit({
|
|
@@ -846,14 +1218,14 @@ export const prepareEchoConnectionKit = zone => {
|
|
|
846
1218
|
{
|
|
847
1219
|
handler: M.interface('ConnectionHandler', {
|
|
848
1220
|
onReceive: M.callWhen(
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
1221
|
+
Shape.Connection,
|
|
1222
|
+
Shape.Bytes,
|
|
1223
|
+
Shape.ConnectionHandler,
|
|
852
1224
|
)
|
|
853
|
-
.optional(
|
|
854
|
-
.returns(
|
|
855
|
-
onClose: M.callWhen(
|
|
856
|
-
.optional(M.any(),
|
|
1225
|
+
.optional(Shape.Opts)
|
|
1226
|
+
.returns(Shape.Data),
|
|
1227
|
+
onClose: M.callWhen(Shape.Connection)
|
|
1228
|
+
.optional(M.any(), Shape.ConnectionHandler)
|
|
857
1229
|
.returns(M.undefined()),
|
|
858
1230
|
}),
|
|
859
1231
|
listener: M.interface('Listener', {
|
|
@@ -869,10 +1241,9 @@ export const prepareEchoConnectionKit = zone => {
|
|
|
869
1241
|
}),
|
|
870
1242
|
},
|
|
871
1243
|
() => {
|
|
872
|
-
/** @type {string | undefined} */
|
|
873
|
-
let closed;
|
|
874
1244
|
return {
|
|
875
|
-
|
|
1245
|
+
/** @type {string | undefined} */
|
|
1246
|
+
closed: undefined,
|
|
876
1247
|
};
|
|
877
1248
|
},
|
|
878
1249
|
{
|
|
@@ -886,7 +1257,7 @@ export const prepareEchoConnectionKit = zone => {
|
|
|
886
1257
|
const { closed } = this.state;
|
|
887
1258
|
|
|
888
1259
|
if (closed) {
|
|
889
|
-
throw closed;
|
|
1260
|
+
throw Error(closed);
|
|
890
1261
|
}
|
|
891
1262
|
return bytes;
|
|
892
1263
|
},
|
|
@@ -907,7 +1278,7 @@ export const prepareEchoConnectionKit = zone => {
|
|
|
907
1278
|
},
|
|
908
1279
|
listener: {
|
|
909
1280
|
async onAccept(_port, _localAddr, _remoteAddr, _listenHandler) {
|
|
910
|
-
return
|
|
1281
|
+
return this.facets.handler;
|
|
911
1282
|
},
|
|
912
1283
|
async onListen(port, _listenHandler) {
|
|
913
1284
|
console.debug(`listening on echo port: ${port}`);
|
|
@@ -925,99 +1296,225 @@ export const prepareEchoConnectionKit = zone => {
|
|
|
925
1296
|
* @param {import('@agoric/base-zone').Zone} zone
|
|
926
1297
|
* @param {ReturnType<import('@agoric/vow').prepareVowTools>} powers
|
|
927
1298
|
*/
|
|
928
|
-
export function prepareLoopbackProtocolHandler(zone, {
|
|
1299
|
+
export function prepareLoopbackProtocolHandler(zone, { watch, allVows }) {
|
|
929
1300
|
const detached = zone.detached();
|
|
930
1301
|
|
|
931
|
-
|
|
1302
|
+
/** @param {string} [instancePrefix] */
|
|
1303
|
+
const initHandler = (instancePrefix = 'nonce/') => {
|
|
1304
|
+
/** @type {MapStore<string, [import('@agoric/vow').Remote<Port>, import('@agoric/vow').Remote<Required<ListenHandler>>]>} */
|
|
1305
|
+
const listeners = detached.mapStore('localAddr');
|
|
1306
|
+
|
|
1307
|
+
return {
|
|
1308
|
+
listeners,
|
|
1309
|
+
portNonce: 0n,
|
|
1310
|
+
instancePrefix,
|
|
1311
|
+
instanceNonce: 0n,
|
|
1312
|
+
};
|
|
1313
|
+
};
|
|
1314
|
+
|
|
1315
|
+
const makeLoopbackProtocolHandlerKit = zone.exoClassKit(
|
|
932
1316
|
'ProtocolHandler',
|
|
933
1317
|
Shape.ProtocolHandlerI,
|
|
934
1318
|
/** @param {string} [instancePrefix] */
|
|
935
|
-
|
|
936
|
-
/** @type {MapStore<string, [Port, ListenHandler]>} */
|
|
937
|
-
const listeners = detached.mapStore('localAddr');
|
|
938
|
-
|
|
939
|
-
return {
|
|
940
|
-
listeners,
|
|
941
|
-
portNonce: 0n,
|
|
942
|
-
instancePrefix,
|
|
943
|
-
instanceNonce: 0n,
|
|
944
|
-
};
|
|
945
|
-
},
|
|
1319
|
+
initHandler,
|
|
946
1320
|
{
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
1321
|
+
protocolHandler: {
|
|
1322
|
+
async onCreate(_impl, _protocolHandler) {
|
|
1323
|
+
// noop
|
|
1324
|
+
},
|
|
1325
|
+
async generatePortID(_localAddr, _protocolHandler) {
|
|
1326
|
+
this.state.portNonce += 1n;
|
|
1327
|
+
return `port${this.state.portNonce}`;
|
|
1328
|
+
},
|
|
1329
|
+
async onBind(_port, _localAddr, _protocolHandler) {
|
|
1330
|
+
// noop, for now; Maybe handle a bind?
|
|
1331
|
+
},
|
|
1332
|
+
/**
|
|
1333
|
+
* @param {*} _port
|
|
1334
|
+
* @param {Endpoint} localAddr
|
|
1335
|
+
* @param {Endpoint} remoteAddr
|
|
1336
|
+
* @returns {import('@agoric/vow').PromiseVow<AttemptDescription>}}
|
|
1337
|
+
*/
|
|
1338
|
+
async onConnect(_port, localAddr, remoteAddr) {
|
|
1339
|
+
const { listeners } = this.state;
|
|
1340
|
+
const [lport, lhandler] = listeners.get(remoteAddr);
|
|
1341
|
+
|
|
1342
|
+
const acceptVow = watch(
|
|
1343
|
+
E(lhandler).onAccept(lport, remoteAddr, localAddr, lhandler),
|
|
1344
|
+
this.facets.protocolHandlerAcceptWatcher,
|
|
1345
|
+
);
|
|
1346
|
+
|
|
1347
|
+
const instantiateInnerVow = watch(
|
|
1348
|
+
E(this.facets.protocolHandler).onInstantiate(
|
|
1349
|
+
lport,
|
|
1350
|
+
remoteAddr,
|
|
1351
|
+
localAddr,
|
|
1352
|
+
this.facets.protocolHandler,
|
|
1353
|
+
),
|
|
1354
|
+
this.facets.protocolHandlerInstantiateWatcher,
|
|
1355
|
+
);
|
|
1356
|
+
|
|
1357
|
+
const instantiateVow = watch(
|
|
1358
|
+
instantiateInnerVow,
|
|
1359
|
+
this.facets.rethrowUnlessMissingWatcher,
|
|
1360
|
+
);
|
|
1361
|
+
return watch(
|
|
1362
|
+
allVows([acceptVow, instantiateVow]),
|
|
1363
|
+
this.facets.protocolHandlerConnectWatcher,
|
|
1364
|
+
);
|
|
1365
|
+
},
|
|
1366
|
+
async onInstantiate(_port, _localAddr, _remote, _protocol) {
|
|
1367
|
+
const { instancePrefix } = this.state;
|
|
1368
|
+
this.state.instanceNonce += 1n;
|
|
1369
|
+
return `${instancePrefix}${this.state.instanceNonce}`;
|
|
1370
|
+
},
|
|
1371
|
+
async onListen(port, localAddr, listenHandler, _protocolHandler) {
|
|
1372
|
+
const { listeners } = this.state;
|
|
1373
|
+
|
|
1374
|
+
// This implementation has a simple last-one-wins replacement policy.
|
|
1375
|
+
// Other handlers might use different policies.
|
|
1376
|
+
if (listeners.has(localAddr)) {
|
|
1377
|
+
const lhandler = listeners.get(localAddr)[1];
|
|
1378
|
+
if (lhandler !== listenHandler) {
|
|
1379
|
+
listeners.set(
|
|
1380
|
+
localAddr,
|
|
1381
|
+
harden([
|
|
1382
|
+
port,
|
|
1383
|
+
/** @type {import('@agoric/vow').Remote<Required<ListenHandler>>} */ (
|
|
1384
|
+
listenHandler
|
|
1385
|
+
),
|
|
1386
|
+
]),
|
|
1387
|
+
);
|
|
1388
|
+
}
|
|
1389
|
+
} else {
|
|
1390
|
+
listeners.init(
|
|
1391
|
+
localAddr,
|
|
1392
|
+
harden([
|
|
1393
|
+
port,
|
|
1394
|
+
/** @type {import('@agoric/vow').Remote<Required<ListenHandler>>} */ (
|
|
1395
|
+
listenHandler
|
|
1396
|
+
),
|
|
1397
|
+
]),
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
},
|
|
1401
|
+
/**
|
|
1402
|
+
* @param {import('@agoric/vow').Remote<Port>} port
|
|
1403
|
+
* @param {Endpoint} localAddr
|
|
1404
|
+
* @param {import('@agoric/vow').Remote<ListenHandler>} listenHandler
|
|
1405
|
+
* @param {*} _protocolHandler
|
|
1406
|
+
*/
|
|
1407
|
+
async onListenRemove(port, localAddr, listenHandler, _protocolHandler) {
|
|
1408
|
+
const { listeners } = this.state;
|
|
1409
|
+
const [lport, lhandler] = listeners.get(localAddr);
|
|
1410
|
+
lport === port || Fail`Port does not match listener on ${localAddr}`;
|
|
1411
|
+
lhandler === listenHandler ||
|
|
1412
|
+
Fail`Listen handler does not match listener on ${localAddr}`;
|
|
1413
|
+
listeners.delete(localAddr);
|
|
1414
|
+
},
|
|
1415
|
+
async onRevoke(_port, _localAddr, _protocolHandler) {
|
|
1416
|
+
// This is an opportunity to clean up resources.
|
|
1417
|
+
},
|
|
953
1418
|
},
|
|
954
|
-
|
|
955
|
-
|
|
1419
|
+
protocolHandlerAcceptWatcher: {
|
|
1420
|
+
onFulfilled(rchandler) {
|
|
1421
|
+
return rchandler;
|
|
1422
|
+
},
|
|
956
1423
|
},
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
const { listeners } = this.state;
|
|
965
|
-
const [lport, lhandler] = listeners.get(remoteAddr);
|
|
966
|
-
const rchandler = await when(
|
|
967
|
-
E(lhandler).onAccept(lport, remoteAddr, localAddr, lhandler),
|
|
968
|
-
);
|
|
969
|
-
// console.log(`rchandler is`, rchandler);
|
|
970
|
-
const remoteInstance = await when(
|
|
971
|
-
E(protocolHandler).onInstantiate(
|
|
972
|
-
lport,
|
|
973
|
-
remoteAddr,
|
|
974
|
-
localAddr,
|
|
975
|
-
protocolHandler,
|
|
976
|
-
),
|
|
977
|
-
).catch(rethrowUnlessMissing);
|
|
978
|
-
return {
|
|
979
|
-
remoteInstance,
|
|
980
|
-
handler: rchandler,
|
|
981
|
-
};
|
|
982
|
-
},
|
|
983
|
-
async onInstantiate(_port, _localAddr, _remote, _protocol) {
|
|
984
|
-
const { instancePrefix } = this.state;
|
|
985
|
-
this.state.instanceNonce += 1n;
|
|
986
|
-
return `${instancePrefix}${this.state.instanceNonce}`;
|
|
987
|
-
},
|
|
988
|
-
async onListen(port, localAddr, listenHandler, _protocolHandler) {
|
|
989
|
-
const { listeners } = this.state;
|
|
990
|
-
|
|
991
|
-
// This implementation has a simple last-one-wins replacement policy.
|
|
992
|
-
// Other handlers might use different policies.
|
|
993
|
-
if (listeners.has(localAddr)) {
|
|
994
|
-
const lhandler = listeners.get(localAddr)[1];
|
|
995
|
-
if (lhandler !== listenHandler) {
|
|
996
|
-
listeners.set(localAddr, [port, listenHandler]);
|
|
997
|
-
}
|
|
998
|
-
} else {
|
|
999
|
-
listeners.init(localAddr, harden([port, listenHandler]));
|
|
1000
|
-
}
|
|
1424
|
+
protocolHandlerConnectWatcher: {
|
|
1425
|
+
onFulfilled(results) {
|
|
1426
|
+
return {
|
|
1427
|
+
remoteInstance: results[0],
|
|
1428
|
+
handler: results[1],
|
|
1429
|
+
};
|
|
1430
|
+
},
|
|
1001
1431
|
},
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
* @param {*} _protocolHandler
|
|
1007
|
-
*/
|
|
1008
|
-
async onListenRemove(port, localAddr, listenHandler, _protocolHandler) {
|
|
1009
|
-
const { listeners } = this.state;
|
|
1010
|
-
const [lport, lhandler] = listeners.get(localAddr);
|
|
1011
|
-
lport === port || Fail`Port does not match listener on ${localAddr}`;
|
|
1012
|
-
lhandler === listenHandler ||
|
|
1013
|
-
Fail`Listen handler does not match listener on ${localAddr}`;
|
|
1014
|
-
listeners.delete(localAddr);
|
|
1432
|
+
protocolHandlerInstantiateWatcher: {
|
|
1433
|
+
onFulfilled(remoteInstance) {
|
|
1434
|
+
return remoteInstance;
|
|
1435
|
+
},
|
|
1015
1436
|
},
|
|
1016
|
-
|
|
1017
|
-
|
|
1437
|
+
rethrowUnlessMissingWatcher: {
|
|
1438
|
+
onRejected(e) {
|
|
1439
|
+
rethrowUnlessMissing(e);
|
|
1440
|
+
},
|
|
1018
1441
|
},
|
|
1019
1442
|
},
|
|
1020
1443
|
);
|
|
1021
1444
|
|
|
1445
|
+
/** @param {string} [instancePrefix] */
|
|
1446
|
+
const makeLoopbackProtocolHandler = instancePrefix => {
|
|
1447
|
+
const { protocolHandler } = makeLoopbackProtocolHandlerKit(instancePrefix);
|
|
1448
|
+
return harden(protocolHandler);
|
|
1449
|
+
};
|
|
1450
|
+
|
|
1022
1451
|
return makeLoopbackProtocolHandler;
|
|
1023
1452
|
}
|
|
1453
|
+
|
|
1454
|
+
/**
|
|
1455
|
+
*
|
|
1456
|
+
* @param {import('@agoric/base-zone').Zone} zone
|
|
1457
|
+
* @param {ReturnType<import('@agoric/vow').prepareVowTools>} powers
|
|
1458
|
+
*/
|
|
1459
|
+
export const preparePortAllocator = (zone, { watch }) =>
|
|
1460
|
+
zone.exoClass(
|
|
1461
|
+
'PortAllocator',
|
|
1462
|
+
M.interface('PortAllocator', {
|
|
1463
|
+
allocateCustomIBCPort: M.callWhen()
|
|
1464
|
+
.optional(M.string())
|
|
1465
|
+
.returns(Shape.Vow$(Shape.Port)),
|
|
1466
|
+
allocateICAControllerPort: M.callWhen().returns(Shape.Vow$(Shape.Port)),
|
|
1467
|
+
allocateICQControllerPort: M.callWhen().returns(Shape.Vow$(Shape.Port)),
|
|
1468
|
+
allocateCustomLocalPort: M.callWhen()
|
|
1469
|
+
.optional(M.string())
|
|
1470
|
+
.returns(Shape.Vow$(Shape.Port)),
|
|
1471
|
+
}),
|
|
1472
|
+
({ protocol }) => ({ protocol, lastICAPortNum: 0n, lastICQPortNum: 0n }),
|
|
1473
|
+
{
|
|
1474
|
+
allocateCustomIBCPort(specifiedName = '') {
|
|
1475
|
+
const { state } = this;
|
|
1476
|
+
let localAddr = `/ibc-port/`;
|
|
1477
|
+
|
|
1478
|
+
if (specifiedName) {
|
|
1479
|
+
throwIfInvalidPortName(specifiedName);
|
|
1480
|
+
|
|
1481
|
+
localAddr = `/ibc-port/custom-${specifiedName}`;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
// Allocate an IBC port with a unique generated name.
|
|
1485
|
+
return watch(E(state.protocol).bindPort(localAddr));
|
|
1486
|
+
},
|
|
1487
|
+
allocateICAControllerPort() {
|
|
1488
|
+
const { state } = this;
|
|
1489
|
+
state.lastICAPortNum += 1n;
|
|
1490
|
+
return watch(
|
|
1491
|
+
E(state.protocol).bindPort(
|
|
1492
|
+
`/ibc-port/icacontroller-${state.lastICAPortNum}`,
|
|
1493
|
+
),
|
|
1494
|
+
);
|
|
1495
|
+
},
|
|
1496
|
+
allocateICQControllerPort() {
|
|
1497
|
+
const { state } = this;
|
|
1498
|
+
state.lastICQPortNum += 1n;
|
|
1499
|
+
return watch(
|
|
1500
|
+
E(state.protocol).bindPort(
|
|
1501
|
+
`/ibc-port/icqcontroller-${state.lastICQPortNum}`,
|
|
1502
|
+
),
|
|
1503
|
+
);
|
|
1504
|
+
},
|
|
1505
|
+
allocateCustomLocalPort(specifiedName = '') {
|
|
1506
|
+
const { state } = this;
|
|
1507
|
+
|
|
1508
|
+
let localAddr = `/local/`;
|
|
1509
|
+
|
|
1510
|
+
if (specifiedName) {
|
|
1511
|
+
throwIfInvalidPortName(specifiedName);
|
|
1512
|
+
|
|
1513
|
+
localAddr = `/local/custom-${specifiedName}`;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
// Allocate a local port with a unique generated name.
|
|
1517
|
+
return watch(E(state.protocol).bindPort(localAddr));
|
|
1518
|
+
},
|
|
1519
|
+
},
|
|
1520
|
+
);
|