@dxos/network-manager 0.1.56-main.c787da3 → 0.1.56-main.e47dfd1

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.
Files changed (37) hide show
  1. package/dist/lib/browser/{chunk-MIYEP22D.mjs → chunk-5R7TDXUU.mjs} +323 -229
  2. package/dist/lib/browser/chunk-5R7TDXUU.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +5 -1
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/testing/index.mjs +1 -1
  6. package/dist/lib/node/index.cjs +318 -222
  7. package/dist/lib/node/index.cjs.map +4 -4
  8. package/dist/lib/node/meta.json +1 -1
  9. package/dist/lib/node/testing/index.cjs +314 -222
  10. package/dist/lib/node/testing/index.cjs.map +4 -4
  11. package/dist/types/src/network-manager.d.ts +1 -1
  12. package/dist/types/src/network-manager.d.ts.map +1 -1
  13. package/dist/types/src/swarm/connection-limiter.d.ts +28 -0
  14. package/dist/types/src/swarm/connection-limiter.d.ts.map +1 -0
  15. package/dist/types/src/swarm/connection-limiter.test.d.ts +2 -0
  16. package/dist/types/src/swarm/connection-limiter.test.d.ts.map +1 -0
  17. package/dist/types/src/swarm/connection.d.ts +1 -1
  18. package/dist/types/src/swarm/connection.d.ts.map +1 -1
  19. package/dist/types/src/swarm/connection.test.d.ts +2 -0
  20. package/dist/types/src/swarm/connection.test.d.ts.map +1 -0
  21. package/dist/types/src/swarm/index.d.ts +1 -0
  22. package/dist/types/src/swarm/index.d.ts.map +1 -1
  23. package/dist/types/src/swarm/peer.d.ts +3 -1
  24. package/dist/types/src/swarm/peer.d.ts.map +1 -1
  25. package/dist/types/src/swarm/swarm.d.ts +3 -1
  26. package/dist/types/src/swarm/swarm.d.ts.map +1 -1
  27. package/package.json +17 -17
  28. package/src/network-manager.ts +18 -4
  29. package/src/swarm/connection-limiter.test.ts +44 -0
  30. package/src/swarm/connection-limiter.ts +71 -0
  31. package/src/swarm/connection.test.ts +72 -0
  32. package/src/swarm/connection.ts +1 -2
  33. package/src/swarm/index.ts +1 -0
  34. package/src/swarm/peer.ts +22 -7
  35. package/src/swarm/swarm.test.ts +140 -90
  36. package/src/swarm/swarm.ts +3 -1
  37. package/dist/lib/browser/chunk-MIYEP22D.mjs.map +0 -7
@@ -45,12 +45,12 @@ export type NetworkManagerOptions = {
45
45
  * Manages connection to the swarm.
46
46
  */
47
47
  export declare class NetworkManager {
48
- private readonly _swarms;
49
48
  private readonly _mappers;
50
49
  private readonly _transportFactory;
51
50
  private readonly _signalManager;
52
51
  private readonly _messenger;
53
52
  private readonly _signalConnection;
53
+ private readonly _connectionLimiter;
54
54
  private _connectionState;
55
55
  readonly connectionStateChanged: Event<ConnectionState>;
56
56
  private readonly _connectionLog?;
@@ -1 +1 @@
1
- {"version":3,"file":"network-manager.d.ts","sourceRoot":"","sources":["../../../src/network-manager.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAa,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE3D,OAAO,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAG7E,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG;IACzB;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,SAAS,CAAC;IAElB;;OAEG;IACH,gBAAgB,EAAE,oBAAoB,CAAC;IAEvC;;OAEG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF;;GAEG;AAEH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoD;IAC5E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0D;IAEnF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAmB;IACrD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IACvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAmB;IAErD,OAAO,CAAC,gBAAgB,CAA0B;IAClD,SAAgB,sBAAsB,yBAAgC;IAEtE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAgB;IAEhD,SAAgB,aAAa,cAAqB;IAElD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA8B;gBAE9C,EAAE,gBAAgB,EAAE,aAAa,EAAE,GAAG,EAAE,EAAE,qBAAqB;IAmB3E,IAAI,aAAa,8BAEhB;IAED,IAAI,eAAe,oBAElB;IAGD,IAAI,MAAM,gBAET;IAED,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS;IAItD,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,KAAK,GAAG,SAAS;IAIvC,IAAI;IAOJ,KAAK;IAWX;;OAEG;IACG,SAAS,CAAC,EACd,KAAK,EACL,MAAM,EACN,QAAQ,EACR,gBAAgB,EAAE,QAAQ,EAC1B,KAAK,GACN,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAgC1C;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,SAAS;IAuB3B,kBAAkB,CAAC,KAAK,EAAE,eAAe;CA0BhD"}
1
+ {"version":3,"file":"network-manager.d.ts","sourceRoot":"","sources":["../../../src/network-manager.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAa,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE3D,OAAO,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAG7E,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAqB,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG;IACzB;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,SAAS,CAAC;IAElB;;OAEG;IACH,gBAAgB,EAAE,oBAAoB,CAAC;IAEvC;;OAEG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF;;GAEG;AAEH,qBAAa,cAAc;IAKzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0D;IAEnF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAmB;IACrD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IACvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAmB;IACrD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoB;IAEvD,OAAO,CAAC,gBAAgB,CAA0B;IAClD,SAAgB,sBAAsB,yBAAgC;IAEtE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAgB;IAEhD,SAAgB,aAAa,cAAqB;IAElD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA8B;gBAE9C,EAAE,gBAAgB,EAAE,aAAa,EAAE,GAAG,EAAE,EAAE,qBAAqB;IAoB3E,IAAI,aAAa,8BAEhB;IAED,IAAI,eAAe,oBAElB;IAGD,IAAI,MAAM,gBAET;IAED,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS;IAItD,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,KAAK,GAAG,SAAS;IAIvC,IAAI;IAOJ,KAAK;IAWX;;OAEG;IACG,SAAS,CAAC,EACd,KAAK,EACL,MAAM,EACN,QAAQ,EACR,gBAAgB,EAAE,QAAQ,EAC1B,KAAK,GACN,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IA0C1C;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,SAAS;IAuB3B,kBAAkB,CAAC,KAAK,EAAE,eAAe;CA0BhD"}
@@ -0,0 +1,28 @@
1
+ import { DeferredTask } from '@dxos/async';
2
+ import { PublicKey } from '@dxos/keys';
3
+ export declare const MAX_CONCURRENT_INITIATING_CONNECTIONS = 3;
4
+ export type ConnectionLimiterOptions = {
5
+ maxConcurrentInitConnections?: number;
6
+ };
7
+ /**
8
+ * Limits the amount of concurrent connections with 'CONNECTING' state.
9
+ */
10
+ export declare class ConnectionLimiter {
11
+ private readonly _ctx;
12
+ private readonly _maxConcurrentInitConnections;
13
+ /**
14
+ * Queue of promises to resolve when initiating connections amount is below the limit.
15
+ */
16
+ private readonly _waitingPromises;
17
+ resolveWaitingPromises: DeferredTask;
18
+ constructor({ maxConcurrentInitConnections }?: ConnectionLimiterOptions);
19
+ /**
20
+ * @returns Promise that resolves in queue when connections amount with 'CONNECTING' state is below the limit.
21
+ */
22
+ connecting(sessionId: PublicKey): Promise<void>;
23
+ /**
24
+ * Rejects promise returned by `connecting` method.
25
+ */
26
+ doneConnecting(sessionId: PublicKey): void;
27
+ }
28
+ //# sourceMappingURL=connection-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-limiter.d.ts","sourceRoot":"","sources":["../../../../src/swarm/connection-limiter.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIvC,eAAO,MAAM,qCAAqC,IAAI,CAAC;AAEvD,MAAM,MAAM,wBAAwB,GAAG;IACrC,4BAA4B,CAAC,EAAE,MAAM,CAAC;CACvC,CAAC;AAEF;;GAEG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiB;IACtC,OAAO,CAAC,QAAQ,CAAC,6BAA6B,CAAC;IAC/C;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAE/B;IAEF,sBAAsB,eAMnB;gBAES,EAAE,4BAAoE,EAAE,GAAE,wBAA6B;IAInH;;OAEG;IACG,UAAU,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAarD;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,SAAS;CASpC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=connection-limiter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-limiter.test.d.ts","sourceRoot":"","sources":["../../../../src/swarm/connection-limiter.test.ts"],"names":[],"mappings":""}
@@ -61,7 +61,7 @@ export declare class Connection {
61
61
  /**
62
62
  * Create an underlying transport and prepares it for the connection.
63
63
  */
64
- openConnection(): void;
64
+ openConnection(): Promise<void>;
65
65
  close(): Promise<void>;
66
66
  private _sendSignal;
67
67
  private _flushSignalBuffer;
@@ -1 +1 @@
1
- {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../../../src/swarm/connection.ts"],"names":[],"mappings":"AAMA,OAAO,EAAgB,KAAK,EAAuB,MAAM,aAAa,CAAC;AAEvE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAKvC,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAahD;;GAEG;AACH,oBAAY,eAAe;IACzB;;;OAGG;IACH,OAAO,YAAY;IAEnB;;OAEG;IACH,UAAU,eAAe;IAEzB;;OAEG;IACH,SAAS,cAAc;IAEvB;;OAEG;IACH,OAAO,YAAY;IAEnB;;OAEG;IACH,MAAM,WAAW;CAClB;AAED;;;GAGG;AACH,qBAAa,UAAU;aAqBH,KAAK,EAAE,SAAS;aAChB,KAAK,EAAE,SAAS;aAChB,QAAQ,EAAE,SAAS;aACnB,SAAS,EAAE,SAAS;aACpB,SAAS,EAAE,OAAO;IAClC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IA3BpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiB;IAEtC,OAAO,CAAC,MAAM,CAA4C;IAC1D,OAAO,CAAC,UAAU,CAAwB;IAE1C,OAAO,CAAC,qBAAqB,CAAgB;IAC7C,OAAO,CAAC,qBAAqB,CAAgB;IAE7C,QAAQ,CAAC,YAAY,yBAAgC;IACrD,QAAQ,CAAC,MAAM,cAAqB;IAE7B,WAAW,SAA8B;IAEhD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAE7B;IAEH,OAAO,CAAC,gBAAgB,CAA6B;gBAGnC,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,SAAS,EAAE,wBAAwB;IAC1C,QAAQ,EAAE,SAAS,EACnB,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,OAAO,EACjB,gBAAgB,EAAE,eAAe,EACjC,SAAS,EAAE,YAAY,EACvB,iBAAiB,EAAE,gBAAgB;IAGtD,IAAI,KAAK,oBAER;IAED,IAAI,SAAS,0BAEZ;IAED,IAAI,QAAQ,iBAEX;IAED;;OAEG;IAEH,cAAc;IAkDR,KAAK;IA4BX,OAAO,CAAC,WAAW;YAKL,kBAAkB;IA8BhC;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,aAAa;IA2B/B,OAAO,CAAC,YAAY;CAMrB"}
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../../../src/swarm/connection.ts"],"names":[],"mappings":"AAMA,OAAO,EAAgB,KAAK,EAAuB,MAAM,aAAa,CAAC;AAEvE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAKvC,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAahD;;GAEG;AACH,oBAAY,eAAe;IACzB;;;OAGG;IACH,OAAO,YAAY;IAEnB;;OAEG;IACH,UAAU,eAAe;IAEzB;;OAEG;IACH,SAAS,cAAc;IAEvB;;OAEG;IACH,OAAO,YAAY;IAEnB;;OAEG;IACH,MAAM,WAAW;CAClB;AAED;;;GAGG;AACH,qBAAa,UAAU;aAqBH,KAAK,EAAE,SAAS;aAChB,KAAK,EAAE,SAAS;aAChB,QAAQ,EAAE,SAAS;aACnB,SAAS,EAAE,SAAS;aACpB,SAAS,EAAE,OAAO;IAClC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IA3BpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiB;IAEtC,OAAO,CAAC,MAAM,CAA4C;IAC1D,OAAO,CAAC,UAAU,CAAwB;IAE1C,OAAO,CAAC,qBAAqB,CAAgB;IAC7C,OAAO,CAAC,qBAAqB,CAAgB;IAE7C,QAAQ,CAAC,YAAY,yBAAgC;IACrD,QAAQ,CAAC,MAAM,cAAqB;IAE7B,WAAW,SAA8B;IAEhD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAE7B;IAEH,OAAO,CAAC,gBAAgB,CAA6B;gBAGnC,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,SAAS,EAAE,wBAAwB;IAC1C,QAAQ,EAAE,SAAS,EACnB,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,OAAO,EACjB,gBAAgB,EAAE,eAAe,EACjC,SAAS,EAAE,YAAY,EACvB,iBAAiB,EAAE,gBAAgB;IAGtD,IAAI,KAAK,oBAER;IAED,IAAI,SAAS,0BAEZ;IAED,IAAI,QAAQ,iBAEX;IAED;;OAEG;IACG,cAAc;IAkDd,KAAK;IA4BX,OAAO,CAAC,WAAW;YAKL,kBAAkB;IA8BhC;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,aAAa;IA2B/B,OAAO,CAAC,YAAY;CAMrB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=connection.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.test.d.ts","sourceRoot":"","sources":["../../../../src/swarm/connection.test.ts"],"names":[],"mappings":""}
@@ -1,4 +1,5 @@
1
1
  export * from './connection';
2
2
  export * from './swarm';
3
3
  export * from './swarm-mapper';
4
+ export * from './connection-limiter';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/swarm/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/swarm/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC"}
@@ -4,6 +4,7 @@ import { OfferMessage, SignalMessage, SignalMessenger } from '../signal';
4
4
  import { TransportFactory } from '../transport';
5
5
  import { WireProtocolProvider } from '../wire-protocol';
6
6
  import { Connection } from './connection';
7
+ import { ConnectionLimiter } from './connection-limiter';
7
8
  interface PeerCallbacks {
8
9
  /**
9
10
  * Connection attempt initiated.
@@ -45,6 +46,7 @@ export declare class Peer {
45
46
  private readonly _signalMessaging;
46
47
  private readonly _protocolProvider;
47
48
  private readonly _transportFactory;
49
+ private readonly _connectionLimiter;
48
50
  private readonly _callbacks;
49
51
  /**
50
52
  * Will be available to connect after this time.
@@ -60,7 +62,7 @@ export declare class Peer {
60
62
  */
61
63
  advertizing: boolean;
62
64
  initiating: boolean;
63
- constructor(id: PublicKey, topic: PublicKey, localPeerId: PublicKey, _signalMessaging: SignalMessenger, _protocolProvider: WireProtocolProvider, _transportFactory: TransportFactory, _callbacks: PeerCallbacks);
65
+ constructor(id: PublicKey, topic: PublicKey, localPeerId: PublicKey, _signalMessaging: SignalMessenger, _protocolProvider: WireProtocolProvider, _transportFactory: TransportFactory, _connectionLimiter: ConnectionLimiter, _callbacks: PeerCallbacks);
64
66
  /**
65
67
  * Respond to remote offer.
66
68
  */
@@ -1 +1 @@
1
- {"version":3,"file":"peer.d.ts","sourceRoot":"","sources":["../../../../src/swarm/peer.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,MAAM,EAAE,MAAM,uCAAuC,CAAC;AAE/D,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAmB,MAAM,cAAc,CAAC;AAE3D,UAAU,aAAa;IACrB;;OAEG;IACH,WAAW,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAE9C;;OAEG;IACH,WAAW,EAAE,MAAM,IAAI,CAAC;IAExB;;OAEG;IACH,cAAc,EAAE,MAAM,IAAI,CAAC;IAE3B;;OAEG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;IAEvB;;OAEG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;IAEvB;;OAEG;IACH,OAAO,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAEnD;;OAEG;IACH,eAAe,EAAE,MAAM,IAAI,CAAC;CAC7B;AAOD;;;GAGG;AACH,qBAAa,IAAI;aAqBG,EAAE,EAAE,SAAS;aACb,KAAK,EAAE,SAAS;aAChB,WAAW,EAAE,SAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU;IA1B7B;;OAEG;IACH,OAAO,CAAC,eAAe,CAAK;IACrB,kBAAkB,UAAQ;IACjC,OAAO,CAAC,mBAAmB,CAAC,CAAS;IAErC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiB;IACtC,OAAO,CAAC,cAAc,CAAC,CAAU;IAE1B,UAAU,CAAC,EAAE,UAAU,CAAC;IAE/B;;OAEG;IACI,WAAW,UAAS;IAEpB,UAAU,UAAS;gBAGR,EAAE,EAAE,SAAS,EACb,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,SAAS,EACrB,gBAAgB,EAAE,eAAe,EACjC,iBAAiB,EAAE,oBAAoB,EACvC,iBAAiB,EAAE,gBAAgB,EACnC,UAAU,EAAE,aAAa;IAG5C;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IA2CrD;;OAEG;IACG,kBAAkB;IAsCxB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IA2EnB,eAAe;IAcf,QAAQ,CAAC,OAAO,EAAE,aAAa;IAS/B,OAAO;CAOd"}
1
+ {"version":3,"file":"peer.d.ts","sourceRoot":"","sources":["../../../../src/swarm/peer.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,MAAM,EAAE,MAAM,uCAAuC,CAAC;AAE/D,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAmB,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,UAAU,aAAa;IACrB;;OAEG;IACH,WAAW,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAE9C;;OAEG;IACH,WAAW,EAAE,MAAM,IAAI,CAAC;IAExB;;OAEG;IACH,cAAc,EAAE,MAAM,IAAI,CAAC;IAE3B;;OAEG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;IAEvB;;OAEG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;IAEvB;;OAEG;IACH,OAAO,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAEnD;;OAEG;IACH,eAAe,EAAE,MAAM,IAAI,CAAC;CAC7B;AAOD;;;GAGG;AACH,qBAAa,IAAI;aAoBG,EAAE,EAAE,SAAS;aACb,KAAK,EAAE,SAAS;aAChB,WAAW,EAAE,SAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU;IA1B7B;;OAEG;IACH,OAAO,CAAC,eAAe,CAAK;IACrB,kBAAkB,UAAQ;IACjC,OAAO,CAAC,mBAAmB,CAAC,CAAS;IAErC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiB;IACtC,OAAO,CAAC,cAAc,CAAC,CAAU;IAE1B,UAAU,CAAC,EAAE,UAAU,CAAC;IAE/B;;OAEG;IACI,WAAW,UAAS;IAEpB,UAAU,UAAS;gBAER,EAAE,EAAE,SAAS,EACb,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,SAAS,EACrB,gBAAgB,EAAE,eAAe,EACjC,iBAAiB,EAAE,oBAAoB,EACvC,iBAAiB,EAAE,gBAAgB,EACnC,kBAAkB,EAAE,iBAAiB,EACrC,UAAU,EAAE,aAAa;IAG5C;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAiDrD;;OAEG;IACG,kBAAkB;IAyCxB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IA8EnB,eAAe;IAcf,QAAQ,CAAC,OAAO,EAAE,aAAa;IAU/B,OAAO;CAOd"}
@@ -9,6 +9,7 @@ import { TransportFactory } from '../transport';
9
9
  import { Topic } from '../types';
10
10
  import { WireProtocolProvider } from '../wire-protocol';
11
11
  import { Connection } from './connection';
12
+ import { ConnectionLimiter } from './connection-limiter';
12
13
  /**
13
14
  * A single peer's view of the swarm.
14
15
  * Manages a set of connections implemented by simple-peer instances.
@@ -22,6 +23,7 @@ export declare class Swarm {
22
23
  private readonly _messenger;
23
24
  private readonly _transportFactory;
24
25
  private readonly _label;
26
+ private readonly _connectionLimiter;
25
27
  private readonly _swarmMessenger;
26
28
  private _ctx;
27
29
  private _listeningHandle?;
@@ -30,7 +32,7 @@ export declare class Swarm {
30
32
  */
31
33
  readonly _instanceId: string;
32
34
  readonly errors: ErrorStream;
33
- constructor(_topic: PublicKey, _ownPeerId: PublicKey, _topology: Topology, _protocolProvider: WireProtocolProvider, _messenger: Messenger, _transportFactory: TransportFactory, _label: string | undefined);
35
+ constructor(_topic: PublicKey, _ownPeerId: PublicKey, _topology: Topology, _protocolProvider: WireProtocolProvider, _messenger: Messenger, _transportFactory: TransportFactory, _label: string | undefined, _connectionLimiter: ConnectionLimiter);
34
36
  get connections(): Connection[];
35
37
  get ownPeerId(): PublicKey;
36
38
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"swarm.d.ts","sourceRoot":"","sources":["../../../../src/swarm/swarm.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAmB,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE7D,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,uCAAuC,CAAC;AAG/D,OAAO,EAAkB,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAmB,QAAQ,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAmB,MAAM,cAAc,CAAC;AAQ3D;;;;GAIG;AACH,qBAAa,KAAK;IA0Cd,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM;IA1CzB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IAEjD,OAAO,CAAC,IAAI,CAAiB;IAE7B,OAAO,CAAC,gBAAgB,CAAC,CAA8B;IAEvD;;OAEG;IAEH,QAAQ,CAAC,WAAW,SAA8B;IAoBlD,QAAQ,CAAC,MAAM,cAAqB;gBAMjB,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,SAAS,EAC9B,SAAS,EAAE,QAAQ,EACV,iBAAiB,EAAE,oBAAoB,EACvC,UAAU,EAAE,SAAS,EACrB,iBAAiB,EAAE,gBAAgB,EACnC,MAAM,EAAE,MAAM,GAAG,SAAS;IAkB7C,IAAI,WAAW,iBAId;IAED,IACI,SAAS,cAEZ;IAED;;OAEG;IACH,IAAI,KAAK,IAAI,MAAM,GAAG,SAAS,CAE9B;IAED,IACI,KAAK,IAAI,KAAK,CAEjB;IAEK,IAAI;IAaJ,OAAO;IAWP,WAAW,CAAC,QAAQ,EAAE,QAAQ;IAiBpC,YAAY,CAAC,UAAU,EAAE,UAAU;IA8B7B,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAyB/C,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB/C,SAAS;IAOT,QAAQ;IAId,OAAO,CAAC,gBAAgB;YAiDV,YAAY;IAM1B,OAAO,CAAC,mBAAmB;IAuC3B;;OAEG;YACW,mBAAmB;YA0BnB,gBAAgB;CAQ/B"}
1
+ {"version":3,"file":"swarm.d.ts","sourceRoot":"","sources":["../../../../src/swarm/swarm.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAmB,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE7D,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,uCAAuC,CAAC;AAG/D,OAAO,EAAkB,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAmB,QAAQ,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAmB,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAQzD;;;;GAIG;AACH,qBAAa,KAAK;IA0Cd,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IA3CrC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IAEjD,OAAO,CAAC,IAAI,CAAiB;IAE7B,OAAO,CAAC,gBAAgB,CAAC,CAA8B;IAEvD;;OAEG;IAEH,QAAQ,CAAC,WAAW,SAA8B;IAoBlD,QAAQ,CAAC,MAAM,cAAqB;gBAMjB,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,SAAS,EAC9B,SAAS,EAAE,QAAQ,EACV,iBAAiB,EAAE,oBAAoB,EACvC,UAAU,EAAE,SAAS,EACrB,iBAAiB,EAAE,gBAAgB,EACnC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,kBAAkB,EAAE,iBAAiB;IAkBxD,IAAI,WAAW,iBAId;IAED,IACI,SAAS,cAEZ;IAED;;OAEG;IACH,IAAI,KAAK,IAAI,MAAM,GAAG,SAAS,CAE9B;IAED,IACI,KAAK,IAAI,KAAK,CAEjB;IAEK,IAAI;IAaJ,OAAO;IAWP,WAAW,CAAC,QAAQ,EAAE,QAAQ;IAiBpC,YAAY,CAAC,UAAU,EAAE,UAAU;IA8B7B,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAwB/C,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB/C,SAAS;IAOT,QAAQ;IAId,OAAO,CAAC,gBAAgB;YAkDV,YAAY;IAM1B,OAAO,CAAC,mBAAmB;IAuC3B;;OAEG;YACW,mBAAmB;YA0BnB,gBAAgB;CAQ/B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/network-manager",
3
- "version": "0.1.56-main.c787da3",
3
+ "version": "0.1.56-main.e47dfd1",
4
4
  "description": "Network Manager",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -26,21 +26,21 @@
26
26
  "tiny-invariant": "^1.3.1",
27
27
  "ws": "^7.4.4",
28
28
  "xor-distance": "^2.0.0",
29
- "@dxos/async": "0.1.56-main.c787da3",
30
- "@dxos/codec-protobuf": "0.1.56-main.c787da3",
31
- "@dxos/context": "0.1.56-main.c787da3",
32
- "@dxos/credentials": "0.1.56-main.c787da3",
33
- "@dxos/crypto": "0.1.56-main.c787da3",
34
- "@dxos/debug": "0.1.56-main.c787da3",
35
- "@dxos/errors": "0.1.56-main.c787da3",
36
- "@dxos/keys": "0.1.56-main.c787da3",
37
- "@dxos/log": "0.1.56-main.c787da3",
38
- "@dxos/messaging": "0.1.56-main.c787da3",
39
- "@dxos/node-std": "0.1.56-main.c787da3",
40
- "@dxos/protocols": "0.1.56-main.c787da3",
41
- "@dxos/rpc": "0.1.56-main.c787da3",
42
- "@dxos/teleport": "0.1.56-main.c787da3",
43
- "@dxos/util": "0.1.56-main.c787da3"
29
+ "@dxos/async": "0.1.56-main.e47dfd1",
30
+ "@dxos/codec-protobuf": "0.1.56-main.e47dfd1",
31
+ "@dxos/context": "0.1.56-main.e47dfd1",
32
+ "@dxos/credentials": "0.1.56-main.e47dfd1",
33
+ "@dxos/crypto": "0.1.56-main.e47dfd1",
34
+ "@dxos/debug": "0.1.56-main.e47dfd1",
35
+ "@dxos/errors": "0.1.56-main.e47dfd1",
36
+ "@dxos/keys": "0.1.56-main.e47dfd1",
37
+ "@dxos/log": "0.1.56-main.e47dfd1",
38
+ "@dxos/messaging": "0.1.56-main.e47dfd1",
39
+ "@dxos/node-std": "0.1.56-main.e47dfd1",
40
+ "@dxos/protocols": "0.1.56-main.e47dfd1",
41
+ "@dxos/rpc": "0.1.56-main.e47dfd1",
42
+ "@dxos/teleport": "0.1.56-main.e47dfd1",
43
+ "@dxos/util": "0.1.56-main.e47dfd1"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@swc-node/register": "1.5.4",
@@ -50,7 +50,7 @@
50
50
  "earljs": "~0.1.10",
51
51
  "fast-check": "~3.3.0",
52
52
  "typescript": "^5.0.4",
53
- "@dxos/signal": "0.1.56-main.c787da3"
53
+ "@dxos/signal": "0.1.56-main.e47dfd1"
54
54
  },
55
55
  "publishConfig": {
56
56
  "access": "public"
@@ -14,11 +14,10 @@ import { ComplexMap } from '@dxos/util';
14
14
 
15
15
  import { ConnectionLog } from './connection-log';
16
16
  import { SignalConnection } from './signal';
17
- import { Swarm, SwarmMapper } from './swarm';
17
+ import { Swarm, SwarmMapper, ConnectionLimiter } from './swarm';
18
18
  import { Topology } from './topology';
19
19
  import { TransportFactory } from './transport';
20
20
  import { WireProtocolProvider } from './wire-protocol';
21
-
22
21
  /**
23
22
  * Represents a single connection to a remote peer.
24
23
  */
@@ -66,13 +65,17 @@ export type NetworkManagerOptions = {
66
65
  */
67
66
  // TODO(dmaretskyi): Rename SwarmManager.
68
67
  export class NetworkManager {
69
- private readonly _swarms = new ComplexMap<PublicKey, Swarm>(PublicKey.hash);
68
+ /**
69
+ * @internal
70
+ */
71
+ readonly _swarms = new ComplexMap<PublicKey, Swarm>(PublicKey.hash);
70
72
  private readonly _mappers = new ComplexMap<PublicKey, SwarmMapper>(PublicKey.hash);
71
73
 
72
74
  private readonly _transportFactory: TransportFactory;
73
75
  private readonly _signalManager: SignalManager;
74
76
  private readonly _messenger: Messenger;
75
77
  private readonly _signalConnection: SignalConnection;
78
+ private readonly _connectionLimiter: ConnectionLimiter;
76
79
 
77
80
  private _connectionState = ConnectionState.ONLINE;
78
81
  public readonly connectionStateChanged = new Event<ConnectionState>();
@@ -95,6 +98,7 @@ export class NetworkManager {
95
98
  leave: (opts) => this._signalManager.leave(opts),
96
99
  };
97
100
 
101
+ this._connectionLimiter = new ConnectionLimiter();
98
102
  // TODO(burdon): Inject listener (generic pattern).
99
103
  if (log) {
100
104
  this._connectionLog = new ConnectionLog();
@@ -160,7 +164,17 @@ export class NetworkManager {
160
164
  }
161
165
 
162
166
  log('joining', { topic: PublicKey.from(topic), peerId, topology: topology.toString() }); // TODO(burdon): Log peerId.
163
- const swarm = new Swarm(topic, peerId, topology, protocol, this._messenger, this._transportFactory, label);
167
+ const swarm = new Swarm(
168
+ topic,
169
+ peerId,
170
+ topology,
171
+ protocol,
172
+ this._messenger,
173
+ this._transportFactory,
174
+ label,
175
+ this._connectionLimiter,
176
+ );
177
+
164
178
  swarm.errors.handle((error) => {
165
179
  log('swarm error', { error });
166
180
  });
@@ -0,0 +1,44 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+ import chai, { expect } from 'chai';
5
+ import chaiAsPromised from 'chai-as-promised';
6
+
7
+ import { asyncTimeout } from '@dxos/async';
8
+ import { PublicKey } from '@dxos/keys';
9
+ import { describe, test } from '@dxos/test';
10
+
11
+ import { ConnectionLimiter } from './connection-limiter';
12
+
13
+ chai.use(chaiAsPromised);
14
+
15
+ describe('ConnectionLimiter', () => {
16
+ function* setupPeers(
17
+ limiter: ConnectionLimiter,
18
+ ): Generator<{ sessionId: PublicKey; connecting: () => Promise<void>; doneConnecting: () => void }> {
19
+ while (true) {
20
+ const sessionId = PublicKey.random();
21
+ yield {
22
+ sessionId,
23
+ connecting: () => asyncTimeout(limiter.connecting(sessionId), 500),
24
+ doneConnecting: () => limiter.doneConnecting(sessionId),
25
+ };
26
+ }
27
+ }
28
+
29
+ test('resolves immediately when limit is not reached', async () => {
30
+ const limiter = new ConnectionLimiter({ maxConcurrentInitConnections: 2 });
31
+ const [first, second, third] = setupPeers(limiter);
32
+ await Promise.all([first.connecting(), second.connecting()]);
33
+ first.doneConnecting();
34
+ await third.connecting();
35
+ });
36
+
37
+ test('rejects if done is called', async () => {
38
+ const limiter = new ConnectionLimiter({ maxConcurrentInitConnections: 1 });
39
+ const [first] = setupPeers(limiter);
40
+ const testPromise = expect(first.connecting()).to.be.rejected;
41
+ first.doneConnecting();
42
+ await testPromise;
43
+ });
44
+ });
@@ -0,0 +1,71 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { DeferredTask } from '@dxos/async';
6
+ import { Context } from '@dxos/context';
7
+ import { CancelledError } from '@dxos/errors';
8
+ import { PublicKey } from '@dxos/keys';
9
+ import { invariant, log } from '@dxos/log';
10
+ import { ComplexMap } from '@dxos/util';
11
+
12
+ export const MAX_CONCURRENT_INITIATING_CONNECTIONS = 3;
13
+
14
+ export type ConnectionLimiterOptions = {
15
+ maxConcurrentInitConnections?: number;
16
+ };
17
+
18
+ /**
19
+ * Limits the amount of concurrent connections with 'CONNECTING' state.
20
+ */
21
+ export class ConnectionLimiter {
22
+ private readonly _ctx = new Context();
23
+ private readonly _maxConcurrentInitConnections;
24
+ /**
25
+ * Queue of promises to resolve when initiating connections amount is below the limit.
26
+ */
27
+ private readonly _waitingPromises = new ComplexMap<PublicKey, { resolve: () => void; reject: (err: Error) => void }>(
28
+ PublicKey.hash,
29
+ );
30
+
31
+ resolveWaitingPromises = new DeferredTask(this._ctx, async () => {
32
+ Array.from(this._waitingPromises.values())
33
+ .slice(0, this._maxConcurrentInitConnections)
34
+ .forEach(({ resolve }) => {
35
+ resolve();
36
+ });
37
+ });
38
+
39
+ constructor({ maxConcurrentInitConnections = MAX_CONCURRENT_INITIATING_CONNECTIONS }: ConnectionLimiterOptions = {}) {
40
+ this._maxConcurrentInitConnections = maxConcurrentInitConnections;
41
+ }
42
+
43
+ /**
44
+ * @returns Promise that resolves in queue when connections amount with 'CONNECTING' state is below the limit.
45
+ */
46
+ async connecting(sessionId: PublicKey): Promise<void> {
47
+ invariant(!this._waitingPromises.has(sessionId), 'Peer is already waiting for connection');
48
+ log('waiting', { sessionId });
49
+ await new Promise<void>((resolve, reject) => {
50
+ this._waitingPromises.set(sessionId, {
51
+ resolve,
52
+ reject,
53
+ });
54
+ this.resolveWaitingPromises.schedule();
55
+ });
56
+ log('allow', { sessionId });
57
+ }
58
+
59
+ /**
60
+ * Rejects promise returned by `connecting` method.
61
+ */
62
+ doneConnecting(sessionId: PublicKey) {
63
+ log('done', { sessionId });
64
+ if (!this._waitingPromises.has(sessionId)) {
65
+ return;
66
+ }
67
+ this._waitingPromises.get(sessionId)!.reject(new CancelledError());
68
+ this._waitingPromises.delete(sessionId);
69
+ this.resolveWaitingPromises.schedule();
70
+ }
71
+ }
@@ -0,0 +1,72 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { sleep } from '@dxos/async';
6
+ import { PublicKey } from '@dxos/keys';
7
+ import { log } from '@dxos/log';
8
+ import { describe, test } from '@dxos/test';
9
+
10
+ import { TestWireProtocol } from '../testing/test-wire-protocol';
11
+ import { createWebRTCTransportFactory } from '../transport';
12
+ import { Connection } from './connection';
13
+
14
+ describe('Connection', () => {
15
+ test('initiator opens after responder', async () => {
16
+ const [topic, peerId1, peerId2, sessionId] = PublicKey.randomSequence();
17
+ const protocol1 = new TestWireProtocol(peerId1);
18
+ const connection1 = new Connection(
19
+ topic,
20
+ peerId1,
21
+ peerId2,
22
+ sessionId,
23
+ true,
24
+ {
25
+ offer: async (msg) => {
26
+ log('offer', { msg });
27
+
28
+ return { accept: true };
29
+ },
30
+ signal: async (msg) => {
31
+ await connection2.signal(msg);
32
+ },
33
+ },
34
+ protocol1.factory({
35
+ initiator: true,
36
+ localPeerId: peerId1,
37
+ remotePeerId: peerId2,
38
+ topic,
39
+ }),
40
+ createWebRTCTransportFactory(),
41
+ );
42
+
43
+ const protocol2 = new TestWireProtocol(peerId2);
44
+ const connection2 = new Connection(
45
+ topic,
46
+ peerId2,
47
+ peerId1,
48
+ sessionId,
49
+ false,
50
+ {
51
+ offer: async (msg) => {
52
+ log('offer', { msg });
53
+
54
+ return { accept: true };
55
+ },
56
+ signal: async (msg) => {
57
+ await connection1.signal(msg);
58
+ },
59
+ },
60
+ protocol2.factory({ initiator: false, localPeerId: peerId2, remotePeerId: peerId1, topic }),
61
+ createWebRTCTransportFactory(),
62
+ );
63
+
64
+ await connection2.openConnection();
65
+ await sleep(100);
66
+ await connection1.openConnection();
67
+ await Promise.all([
68
+ protocol1.testConnection(peerId2, 'test message 1'),
69
+ protocol2.testConnection(peerId1, 'test message 2'),
70
+ ]);
71
+ });
72
+ });
@@ -109,8 +109,7 @@ export class Connection {
109
109
  /**
110
110
  * Create an underlying transport and prepares it for the connection.
111
111
  */
112
- // TODO(burdon): Make async?
113
- openConnection() {
112
+ async openConnection() {
114
113
  invariant(this._state === ConnectionState.INITIAL, 'Invalid state.');
115
114
  log.trace('dxos.mesh.connection.open-connection', trace.begin({ id: this._instanceId }));
116
115
 
@@ -5,3 +5,4 @@
5
5
  export * from './connection';
6
6
  export * from './swarm';
7
7
  export * from './swarm-mapper';
8
+ export * from './connection-limiter';
package/src/swarm/peer.ts CHANGED
@@ -6,6 +6,7 @@ import invariant from 'tiny-invariant';
6
6
 
7
7
  import { scheduleTask, synchronized } from '@dxos/async';
8
8
  import { Context } from '@dxos/context';
9
+ import { CancelledError } from '@dxos/errors';
9
10
  import { PublicKey } from '@dxos/keys';
10
11
  import { log } from '@dxos/log';
11
12
  import { Answer } from '@dxos/protocols/proto/dxos/mesh/swarm';
@@ -14,6 +15,7 @@ import { OfferMessage, SignalMessage, SignalMessenger } from '../signal';
14
15
  import { TransportFactory } from '../transport';
15
16
  import { WireProtocolProvider } from '../wire-protocol';
16
17
  import { Connection, ConnectionState } from './connection';
18
+ import { ConnectionLimiter } from './connection-limiter';
17
19
 
18
20
  interface PeerCallbacks {
19
21
  /**
@@ -80,7 +82,6 @@ export class Peer {
80
82
  public advertizing = false;
81
83
 
82
84
  public initiating = false;
83
-
84
85
  constructor(
85
86
  public readonly id: PublicKey,
86
87
  public readonly topic: PublicKey,
@@ -88,6 +89,7 @@ export class Peer {
88
89
  private readonly _signalMessaging: SignalMessenger,
89
90
  private readonly _protocolProvider: WireProtocolProvider,
90
91
  private readonly _transportFactory: TransportFactory,
92
+ private readonly _connectionLimiter: ConnectionLimiter,
91
93
  private readonly _callbacks: PeerCallbacks,
92
94
  ) {}
93
95
 
@@ -102,10 +104,11 @@ export class Peer {
102
104
  // Peer with the highest Id closes its connection, and accepts remote peer's offer.
103
105
  if (remoteId.toHex() < this.localPeerId.toHex()) {
104
106
  // TODO(burdon): Too verbose.
105
- log("closing local connection and accepting remote peer's offer", {
106
- id: this.id,
107
+ log('close local connection', {
108
+ localPeerId: this.id,
107
109
  topic: this.topic,
108
- peerId: this.localPeerId,
110
+ remotePeerId: this.localPeerId,
111
+ sessionId: this.connection?.sessionId,
109
112
  });
110
113
 
111
114
  if (this.connection) {
@@ -123,10 +126,15 @@ export class Peer {
123
126
  // Connection might have been already established.
124
127
  invariant(message.sessionId);
125
128
  const connection = this._createConnection(false, message.sessionId);
129
+
126
130
  try {
127
- connection.openConnection();
131
+ await this._connectionLimiter.connecting(message.sessionId);
132
+ await connection.openConnection();
128
133
  } catch (err: any) {
129
- log.warn('connection error', { topic: this.topic, peerId: this.localPeerId, remoteId: this.id, err });
134
+ if (!(err instanceof CancelledError)) {
135
+ log.warn('connection error', { topic: this.topic, peerId: this.localPeerId, remoteId: this.id, err });
136
+ }
137
+
130
138
  // Calls `onStateChange` with CLOSED state.
131
139
  await this.closeConnection();
132
140
  }
@@ -145,10 +153,13 @@ export class Peer {
145
153
  invariant(!this.connection, 'Already connected.');
146
154
  const sessionId = PublicKey.random();
147
155
  log('initiating...', { id: this.id, topic: this.topic, peerId: this.id, sessionId });
156
+
148
157
  const connection = this._createConnection(true, sessionId);
149
158
  this.initiating = true;
150
159
 
151
160
  try {
161
+ await this._connectionLimiter.connecting(sessionId);
162
+
152
163
  const answer = await this._signalMessaging.offer({
153
164
  author: this.localPeerId,
154
165
  recipient: this.id,
@@ -166,7 +177,7 @@ export class Peer {
166
177
  this._callbacks.onRejected();
167
178
  return;
168
179
  }
169
- connection.openConnection();
180
+ await connection.openConnection();
170
181
  this._callbacks.onAccepted();
171
182
  } catch (err: any) {
172
183
  log('initiation error', { err, topic: this.topic, peerId: this.localPeerId, remoteId: this.id });
@@ -214,6 +225,8 @@ export class Peer {
214
225
  this.availableToConnect = true;
215
226
  this._lastConnectionTime = Date.now();
216
227
  this._callbacks.onConnected();
228
+
229
+ this._connectionLimiter.doneConnecting(sessionId);
217
230
  break;
218
231
  }
219
232
 
@@ -231,6 +244,7 @@ export class Peer {
231
244
 
232
245
  this.connection = undefined;
233
246
  this._callbacks.onDisconnected();
247
+ this._connectionLimiter.doneConnecting(sessionId);
234
248
 
235
249
  scheduleTask(
236
250
  this._connectionCtx!,
@@ -276,6 +290,7 @@ export class Peer {
276
290
  log('dropping signal message for non-existent connection', { message });
277
291
  return;
278
292
  }
293
+
279
294
  await this.connection.signal(message);
280
295
  }
281
296