@olane/o-client-limited 0.7.52 → 0.7.54
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/dist/src/connection/index.d.ts +1 -0
- package/dist/src/connection/index.d.ts.map +1 -1
- package/dist/src/connection/index.js +1 -0
- package/dist/src/connection/o-limited-connection-manager.js +1 -1
- package/dist/src/connection/o-limited-connection.d.ts +20 -9
- package/dist/src/connection/o-limited-connection.d.ts.map +1 -1
- package/dist/src/connection/o-limited-connection.js +46 -11
- package/dist/src/connection/o-limited.stream-manager.d.ts +26 -9
- package/dist/src/connection/o-limited.stream-manager.d.ts.map +1 -1
- package/dist/src/connection/o-limited.stream-manager.js +118 -33
- package/dist/src/o-limited.tool.d.ts.map +1 -1
- package/dist/src/o-limited.tool.js +1 -0
- package/dist/test/bidirectional-communication.spec.js +100 -26
- package/dist/test/helpers/limited-test-tool.d.ts +2 -0
- package/dist/test/helpers/limited-test-tool.d.ts.map +1 -1
- package/dist/test/helpers/limited-test-tool.js +32 -1
- package/dist/test/helpers/receiver-test-tool.d.ts +2 -0
- package/dist/test/helpers/receiver-test-tool.d.ts.map +1 -1
- package/dist/test/helpers/receiver-test-tool.js +36 -2
- package/dist/test/helpers/relay-test-tool.d.ts +27 -0
- package/dist/test/helpers/relay-test-tool.d.ts.map +1 -0
- package/dist/test/helpers/relay-test-tool.js +75 -0
- package/package.json +8 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/connection/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/connection/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC"}
|
|
@@ -18,7 +18,7 @@ export class oLimitedConnectionManager extends oNodeConnectionManager {
|
|
|
18
18
|
address: address,
|
|
19
19
|
p2pConnection: p2pConnection,
|
|
20
20
|
callerAddress: callerAddress,
|
|
21
|
-
runOnLimitedConnection:
|
|
21
|
+
runOnLimitedConnection: true,
|
|
22
22
|
requestHandler: config.requestHandler,
|
|
23
23
|
});
|
|
24
24
|
return connection;
|
|
@@ -1,29 +1,40 @@
|
|
|
1
1
|
import { oNodeConnection } from '@olane/o-node';
|
|
2
2
|
import type { oNodeConnectionConfig } from '@olane/o-node';
|
|
3
3
|
import { oLimitedStreamManager } from './o-limited.stream-manager.js';
|
|
4
|
+
import type { oRequest, oResponse } from '@olane/o-core';
|
|
4
5
|
/**
|
|
5
|
-
* oLimitedConnection extends oNodeConnection with
|
|
6
|
+
* oLimitedConnection extends oNodeConnection with dual persistent streams for limited connections.
|
|
6
7
|
*
|
|
7
|
-
* This is optimized for limited connections where
|
|
8
|
-
* (
|
|
8
|
+
* This is optimized for limited connections where the caller cannot be dialed
|
|
9
|
+
* (browser clients, mobile clients, resource-constrained environments).
|
|
9
10
|
*
|
|
10
11
|
* Stream Architecture:
|
|
11
|
-
* - 1 dedicated reader stream for receiving
|
|
12
|
-
*
|
|
12
|
+
* - 1 dedicated reader stream for receiving ALL inbound data from receiver
|
|
13
|
+
* (receiver-initiated requests AND responses to limited client's outbound requests)
|
|
14
|
+
* - 1 dedicated writer stream for sending ALL outbound data to receiver
|
|
15
|
+
* (responses to receiver's requests AND limited client's outbound requests)
|
|
16
|
+
* - No ephemeral streams - all communication uses persistent streams for efficiency
|
|
13
17
|
*
|
|
14
18
|
* Features:
|
|
15
|
-
* - Automatic reader stream creation and maintenance
|
|
19
|
+
* - Automatic reader/writer stream creation and maintenance
|
|
16
20
|
* - Single retry on reader failure
|
|
17
|
-
* - Stream reuse for outbound requests
|
|
18
|
-
* -
|
|
21
|
+
* - Stream reuse for all outbound requests (no ephemeral streams)
|
|
22
|
+
* - Response routing via _streamId and _responseConnectionId in request params
|
|
23
|
+
* - Event emission for monitoring (reader-started, writer-started, reader-failed, reader-recovered)
|
|
19
24
|
*
|
|
20
25
|
* Default Behavior:
|
|
21
26
|
* - Uses 'reuse' stream policy by default
|
|
22
|
-
* - Automatically initializes
|
|
27
|
+
* - Automatically initializes persistent streams on connection start
|
|
23
28
|
*/
|
|
24
29
|
export declare class oLimitedConnection extends oNodeConnection {
|
|
25
30
|
streamManager: oLimitedStreamManager;
|
|
26
31
|
constructor(config: oNodeConnectionConfig);
|
|
32
|
+
/**
|
|
33
|
+
* Override transmit to inject response routing params
|
|
34
|
+
* Adds _streamId and _responseConnectionId to enable receiver to route responses
|
|
35
|
+
* to the persistent reader stream
|
|
36
|
+
*/
|
|
37
|
+
transmit(request: oRequest): Promise<oResponse>;
|
|
27
38
|
/**
|
|
28
39
|
* Override close to cleanup stream manager properly
|
|
29
40
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"o-limited-connection.d.ts","sourceRoot":"","sources":["../../../src/connection/o-limited-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EACL,qBAAqB,EAEtB,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"o-limited-connection.d.ts","sourceRoot":"","sources":["../../../src/connection/o-limited-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EACL,qBAAqB,EAEtB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,kBAAmB,SAAQ,eAAe;IAC7C,aAAa,EAAE,qBAAqB,CAAC;gBAEjC,MAAM,EAAE,qBAAqB;IAsCzC;;;;OAIG;IACG,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IA6BrD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B"}
|
|
@@ -2,24 +2,28 @@ import { oNodeConnection } from '@olane/o-node';
|
|
|
2
2
|
import { oLimitedStreamManager, } from './o-limited.stream-manager.js';
|
|
3
3
|
import { StreamManagerEvent } from '@olane/o-node';
|
|
4
4
|
/**
|
|
5
|
-
* oLimitedConnection extends oNodeConnection with
|
|
5
|
+
* oLimitedConnection extends oNodeConnection with dual persistent streams for limited connections.
|
|
6
6
|
*
|
|
7
|
-
* This is optimized for limited connections where
|
|
8
|
-
* (
|
|
7
|
+
* This is optimized for limited connections where the caller cannot be dialed
|
|
8
|
+
* (browser clients, mobile clients, resource-constrained environments).
|
|
9
9
|
*
|
|
10
10
|
* Stream Architecture:
|
|
11
|
-
* - 1 dedicated reader stream for receiving
|
|
12
|
-
*
|
|
11
|
+
* - 1 dedicated reader stream for receiving ALL inbound data from receiver
|
|
12
|
+
* (receiver-initiated requests AND responses to limited client's outbound requests)
|
|
13
|
+
* - 1 dedicated writer stream for sending ALL outbound data to receiver
|
|
14
|
+
* (responses to receiver's requests AND limited client's outbound requests)
|
|
15
|
+
* - No ephemeral streams - all communication uses persistent streams for efficiency
|
|
13
16
|
*
|
|
14
17
|
* Features:
|
|
15
|
-
* - Automatic reader stream creation and maintenance
|
|
18
|
+
* - Automatic reader/writer stream creation and maintenance
|
|
16
19
|
* - Single retry on reader failure
|
|
17
|
-
* - Stream reuse for outbound requests
|
|
18
|
-
* -
|
|
20
|
+
* - Stream reuse for all outbound requests (no ephemeral streams)
|
|
21
|
+
* - Response routing via _streamId and _responseConnectionId in request params
|
|
22
|
+
* - Event emission for monitoring (reader-started, writer-started, reader-failed, reader-recovered)
|
|
19
23
|
*
|
|
20
24
|
* Default Behavior:
|
|
21
25
|
* - Uses 'reuse' stream policy by default
|
|
22
|
-
* - Automatically initializes
|
|
26
|
+
* - Automatically initializes persistent streams on connection start
|
|
23
27
|
*/
|
|
24
28
|
export class oLimitedConnection extends oNodeConnection {
|
|
25
29
|
constructor(config) {
|
|
@@ -29,8 +33,7 @@ export class oLimitedConnection extends oNodeConnection {
|
|
|
29
33
|
reusePolicy,
|
|
30
34
|
});
|
|
31
35
|
// Replace the base streamManager with our limited version
|
|
32
|
-
const protocol = this.nextHopAddress.protocol +
|
|
33
|
-
(reusePolicy === 'reuse' ? '/reuse' : '');
|
|
36
|
+
const protocol = this.nextHopAddress.protocol + (reusePolicy === 'reuse' ? '/reuse' : '');
|
|
34
37
|
const limitedConfig = {
|
|
35
38
|
p2pConnection: this.p2pConnection,
|
|
36
39
|
protocol,
|
|
@@ -42,6 +45,9 @@ export class oLimitedConnection extends oNodeConnection {
|
|
|
42
45
|
this.streamManager.on(StreamManagerEvent.ReaderStarted, (data) => {
|
|
43
46
|
this.logger.info('Reader stream started', data);
|
|
44
47
|
});
|
|
48
|
+
this.streamManager.on(StreamManagerEvent.WriterStarted, (data) => {
|
|
49
|
+
this.logger.info('Writer stream started', data);
|
|
50
|
+
});
|
|
45
51
|
this.streamManager.on(StreamManagerEvent.ReaderFailed, (data) => {
|
|
46
52
|
this.logger.warn('Reader stream failed', data);
|
|
47
53
|
});
|
|
@@ -49,6 +55,35 @@ export class oLimitedConnection extends oNodeConnection {
|
|
|
49
55
|
this.logger.info('Reader stream recovered', data);
|
|
50
56
|
});
|
|
51
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Override transmit to inject response routing params
|
|
60
|
+
* Adds _streamId and _responseConnectionId to enable receiver to route responses
|
|
61
|
+
* to the persistent reader stream
|
|
62
|
+
*/
|
|
63
|
+
async transmit(request) {
|
|
64
|
+
// Wait for stream manager initialization to ensure streams are available
|
|
65
|
+
if (!this.streamManager.isInitialized) {
|
|
66
|
+
await this.streamManager.initialize();
|
|
67
|
+
}
|
|
68
|
+
// Inject response routing params to route responses to reader stream
|
|
69
|
+
// Writer stream sends the request, reader stream receives the response
|
|
70
|
+
if (this.streamManager.readerStream) {
|
|
71
|
+
request.params._streamId = this.streamManager.readerStream.p2pStream.id;
|
|
72
|
+
request.params._responseConnectionId = this.p2pConnection.id;
|
|
73
|
+
if (request.params.payload) {
|
|
74
|
+
request.params.payload.params._streamId = request.params._streamId;
|
|
75
|
+
}
|
|
76
|
+
this.logger.debug('Injected response routing params', {
|
|
77
|
+
streamId: request.params._streamId,
|
|
78
|
+
responseConnectionId: request.params._responseConnectionId,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
this.logger.warn('Reader stream not available, response routing params not injected');
|
|
83
|
+
}
|
|
84
|
+
// Call parent transmit with modified request
|
|
85
|
+
return await super.transmit(request);
|
|
86
|
+
}
|
|
52
87
|
/**
|
|
53
88
|
* Override close to cleanup stream manager properly
|
|
54
89
|
*/
|
|
@@ -21,16 +21,19 @@ export interface oLimitedStreamManagerConfig extends StreamManagerConfig {
|
|
|
21
21
|
/**
|
|
22
22
|
* Limited Stream Manager handles stream lifecycle for limited connections
|
|
23
23
|
* Features:
|
|
24
|
-
* - Maintains dedicated reader stream for receiving
|
|
25
|
-
* -
|
|
24
|
+
* - Maintains dedicated reader stream for receiving all inbound data from receiver
|
|
25
|
+
* (both receiver-initiated requests and responses to limited client's outbound requests)
|
|
26
|
+
* - Maintains dedicated writer stream for sending all outbound data to receiver
|
|
27
|
+
* (both responses to receiver's requests and limited client's outbound requests)
|
|
28
|
+
* - No ephemeral streams - all communication uses persistent streams
|
|
26
29
|
* - Single retry on reader failure
|
|
27
30
|
* - No health checks (error handling on use)
|
|
28
31
|
*/
|
|
29
32
|
export declare class oLimitedStreamManager extends oNodeStreamManager {
|
|
30
|
-
|
|
33
|
+
readerStream?: oNodeStream;
|
|
34
|
+
writerStream?: oNodeStream;
|
|
31
35
|
private isReaderLoopActive;
|
|
32
36
|
private readerLoopAbortController?;
|
|
33
|
-
private outboundStream?;
|
|
34
37
|
private limitedConfig;
|
|
35
38
|
private closing;
|
|
36
39
|
constructor(config: oLimitedStreamManagerConfig);
|
|
@@ -44,7 +47,12 @@ export declare class oLimitedStreamManager extends oNodeStreamManager {
|
|
|
44
47
|
*/
|
|
45
48
|
private createReaderStream;
|
|
46
49
|
/**
|
|
47
|
-
*
|
|
50
|
+
* Creates and initializes the dedicated writer stream
|
|
51
|
+
*/
|
|
52
|
+
private createWriterStream;
|
|
53
|
+
/**
|
|
54
|
+
* Sends stream initialization message to identify the stream role
|
|
55
|
+
* Waits for acknowledgment from receiver before proceeding
|
|
48
56
|
*/
|
|
49
57
|
private sendStreamInitMessage;
|
|
50
58
|
/**
|
|
@@ -61,16 +69,25 @@ export declare class oLimitedStreamManager extends oNodeStreamManager {
|
|
|
61
69
|
*/
|
|
62
70
|
private recoverReaderStream;
|
|
63
71
|
/**
|
|
64
|
-
* Override getOrCreateStream to
|
|
65
|
-
*
|
|
72
|
+
* Override getOrCreateStream to use persistent writer stream for outbound requests
|
|
73
|
+
* This eliminates ephemeral stream creation for limited connections
|
|
66
74
|
* Auto-initializes on first use
|
|
67
75
|
*/
|
|
68
76
|
getOrCreateStream(protocol: string, remoteAddress: any, config?: StreamHandlerConfig): Promise<oNodeStream>;
|
|
69
77
|
/**
|
|
70
|
-
* Override releaseStream to
|
|
71
|
-
*
|
|
78
|
+
* Override releaseStream to protect persistent streams from being closed
|
|
79
|
+
* Persistent streams (reader/writer) should never be released via this method
|
|
80
|
+
* Only closes streams that are not the dedicated reader or writer streams
|
|
72
81
|
*/
|
|
73
82
|
releaseStream(streamId: string): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Override getStreamById to check persistent streams first
|
|
85
|
+
* Provides access to reader and writer streams by ID
|
|
86
|
+
*
|
|
87
|
+
* @param streamId - The ID of the stream to retrieve
|
|
88
|
+
* @returns The libp2p Stream or undefined if not found
|
|
89
|
+
*/
|
|
90
|
+
getStreamById(streamId: string): Stream | undefined;
|
|
74
91
|
/**
|
|
75
92
|
* Close the stream manager and cleanup all resources
|
|
76
93
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"o-limited.stream-manager.d.ts","sourceRoot":"","sources":["../../../src/connection/o-limited.stream-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EACL,kBAAkB,EAElB,WAAW,EACX,mBAAmB,
|
|
1
|
+
{"version":3,"file":"o-limited.stream-manager.d.ts","sourceRoot":"","sources":["../../../src/connection/o-limited.stream-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EACL,kBAAkB,EAElB,WAAW,EACX,mBAAmB,EAKpB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAGzD;;GAEG;AACH,MAAM,WAAW,2BAA4B,SAAQ,mBAAmB;IACtE;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,aAAa,EAAE,GAAG,CAAC;IAEnB;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CACjE;AAED;;;;;;;;;;GAUG;AACH,qBAAa,qBAAsB,SAAQ,kBAAkB;IACpD,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,YAAY,CAAC,EAAE,WAAW,CAAC;IAClC,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,yBAAyB,CAAC,CAAkB;IACpD,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,OAAO,CAAkB;gBAErB,MAAM,EAAE,2BAA2B;IAK/C;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAsDjC;;OAEG;YACW,kBAAkB;IA4BhC;;OAEG;YACW,kBAAkB;IAyBhC;;;OAGG;YACW,qBAAqB;IA0EnC;;;OAGG;YACW,eAAe;IAe7B;;OAEG;YACW,aAAa;IAqB3B;;OAEG;YACW,mBAAmB;IAgCjC;;;;OAIG;IACG,iBAAiB,CACrB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,GAAG,EAClB,MAAM,GAAE,mBAAwB,GAC/B,OAAO,CAAC,WAAW,CAAC;IAqBvB;;;;OAIG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAepD;;;;;;OAMG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAcnD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAqC7B"}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { oError, oErrorCodes } from '@olane/o-core';
|
|
2
|
-
import { oNodeStreamManager, StreamManagerEvent, } from '@olane/o-node';
|
|
2
|
+
import { oNodeStreamManager, StreamManagerEvent, isStreamInitAckMessage, } from '@olane/o-node';
|
|
3
|
+
import { lpStream } from '@olane/o-config';
|
|
3
4
|
/**
|
|
4
5
|
* Limited Stream Manager handles stream lifecycle for limited connections
|
|
5
6
|
* Features:
|
|
6
|
-
* - Maintains dedicated reader stream for receiving
|
|
7
|
-
* -
|
|
7
|
+
* - Maintains dedicated reader stream for receiving all inbound data from receiver
|
|
8
|
+
* (both receiver-initiated requests and responses to limited client's outbound requests)
|
|
9
|
+
* - Maintains dedicated writer stream for sending all outbound data to receiver
|
|
10
|
+
* (both responses to receiver's requests and limited client's outbound requests)
|
|
11
|
+
* - No ephemeral streams - all communication uses persistent streams
|
|
8
12
|
* - Single retry on reader failure
|
|
9
13
|
* - No health checks (error handling on use)
|
|
10
14
|
*/
|
|
@@ -40,8 +44,9 @@ export class oLimitedStreamManager extends oNodeStreamManager {
|
|
|
40
44
|
this.logger.debug('Registered InboundRequest handler for limited connection');
|
|
41
45
|
}
|
|
42
46
|
try {
|
|
43
|
-
// Create dedicated reader
|
|
47
|
+
// Create dedicated reader and writer streams
|
|
44
48
|
await this.createReaderStream();
|
|
49
|
+
await this.createWriterStream();
|
|
45
50
|
this.isInitialized = true;
|
|
46
51
|
this.logger.info('Limited stream manager initialized', {
|
|
47
52
|
remotePeer: this.limitedConfig.p2pConnection.remotePeer.toString(),
|
|
@@ -60,7 +65,7 @@ export class oLimitedStreamManager extends oNodeStreamManager {
|
|
|
60
65
|
// Create reader stream using parent's createStream method
|
|
61
66
|
this.readerStream = await this.createStream(this.limitedConfig.protocol, this.limitedConfig.remoteAddress, {});
|
|
62
67
|
// Send self-identification message
|
|
63
|
-
await this.sendStreamInitMessage(this.readerStream.p2pStream);
|
|
68
|
+
await this.sendStreamInitMessage(this.readerStream.p2pStream, 'reader');
|
|
64
69
|
// Start background reader loop
|
|
65
70
|
await this.startReaderLoop();
|
|
66
71
|
this.eventEmitter.emit(StreamManagerEvent.ReaderStarted, {
|
|
@@ -76,21 +81,80 @@ export class oLimitedStreamManager extends oNodeStreamManager {
|
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
/**
|
|
79
|
-
*
|
|
84
|
+
* Creates and initializes the dedicated writer stream
|
|
80
85
|
*/
|
|
81
|
-
async
|
|
86
|
+
async createWriterStream() {
|
|
87
|
+
try {
|
|
88
|
+
// Create writer stream using parent's createStream method
|
|
89
|
+
this.writerStream = await this.createStream(this.limitedConfig.protocol, this.limitedConfig.remoteAddress, {});
|
|
90
|
+
// Send self-identification message
|
|
91
|
+
await this.sendStreamInitMessage(this.writerStream.p2pStream, 'writer');
|
|
92
|
+
this.eventEmitter.emit(StreamManagerEvent.WriterStarted, {
|
|
93
|
+
streamId: this.writerStream.p2pStream.id,
|
|
94
|
+
});
|
|
95
|
+
this.logger.info('Writer stream created', {
|
|
96
|
+
streamId: this.writerStream.p2pStream.id,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
this.logger.error('Failed to create writer stream:', error);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Sends stream initialization message to identify the stream role
|
|
106
|
+
* Waits for acknowledgment from receiver before proceeding
|
|
107
|
+
*/
|
|
108
|
+
async sendStreamInitMessage(stream, role) {
|
|
82
109
|
const initMessage = {
|
|
83
110
|
type: 'stream-init',
|
|
84
|
-
role
|
|
111
|
+
role,
|
|
85
112
|
timestamp: Date.now(),
|
|
86
113
|
connectionId: this.limitedConfig.p2pConnection.id,
|
|
87
114
|
};
|
|
88
115
|
const messageBytes = new TextEncoder().encode(JSON.stringify(initMessage));
|
|
89
116
|
await this.sendLengthPrefixed(stream, messageBytes, {});
|
|
90
|
-
this.logger.debug('Sent stream-init message', {
|
|
117
|
+
this.logger.debug('Sent stream-init message, waiting for ack', {
|
|
91
118
|
streamId: stream.id,
|
|
92
|
-
role
|
|
119
|
+
role,
|
|
93
120
|
});
|
|
121
|
+
// Wait for acknowledgment with timeout
|
|
122
|
+
const ackTimeout = 5000; // 5 seconds
|
|
123
|
+
const abortController = new AbortController();
|
|
124
|
+
const timeoutId = setTimeout(() => {
|
|
125
|
+
abortController.abort();
|
|
126
|
+
}, ackTimeout);
|
|
127
|
+
try {
|
|
128
|
+
const lp = lpStream(stream);
|
|
129
|
+
// Read acknowledgment message
|
|
130
|
+
const ackBytes = await lp.read({ signal: abortController.signal });
|
|
131
|
+
const ackDecoded = new TextDecoder().decode(ackBytes.subarray());
|
|
132
|
+
const ackMessage = this.extractAndParseJSON(ackDecoded);
|
|
133
|
+
// Validate acknowledgment
|
|
134
|
+
if (!isStreamInitAckMessage(ackMessage)) {
|
|
135
|
+
throw new oError(oErrorCodes.INTERNAL_ERROR, `Invalid stream-init-ack message received: ${JSON.stringify(ackMessage)}`);
|
|
136
|
+
}
|
|
137
|
+
if (ackMessage.status === 'error') {
|
|
138
|
+
throw new oError(oErrorCodes.INTERNAL_ERROR, `Stream initialization failed: ${ackMessage.error || 'Unknown error'}`);
|
|
139
|
+
}
|
|
140
|
+
if (ackMessage.role !== role) {
|
|
141
|
+
throw new oError(oErrorCodes.INTERNAL_ERROR, `Stream role mismatch: expected ${role}, got ${ackMessage.role}`);
|
|
142
|
+
}
|
|
143
|
+
this.logger.info('Received stream-init-ack', {
|
|
144
|
+
streamId: stream.id,
|
|
145
|
+
role,
|
|
146
|
+
ackStreamId: ackMessage.streamId,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
if (error.name === 'AbortError') {
|
|
151
|
+
throw new oError(oErrorCodes.TIMEOUT, `Stream initialization acknowledgment timeout after ${ackTimeout}ms`);
|
|
152
|
+
}
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
clearTimeout(timeoutId);
|
|
157
|
+
}
|
|
94
158
|
}
|
|
95
159
|
/**
|
|
96
160
|
* Starts the background reader loop
|
|
@@ -163,8 +227,8 @@ export class oLimitedStreamManager extends oNodeStreamManager {
|
|
|
163
227
|
}
|
|
164
228
|
}
|
|
165
229
|
/**
|
|
166
|
-
* Override getOrCreateStream to
|
|
167
|
-
*
|
|
230
|
+
* Override getOrCreateStream to use persistent writer stream for outbound requests
|
|
231
|
+
* This eliminates ephemeral stream creation for limited connections
|
|
168
232
|
* Auto-initializes on first use
|
|
169
233
|
*/
|
|
170
234
|
async getOrCreateStream(protocol, remoteAddress, config = {}) {
|
|
@@ -172,28 +236,49 @@ export class oLimitedStreamManager extends oNodeStreamManager {
|
|
|
172
236
|
if (!this.isInitialized) {
|
|
173
237
|
await this.initialize();
|
|
174
238
|
}
|
|
175
|
-
//
|
|
176
|
-
if (this.
|
|
177
|
-
this.
|
|
178
|
-
|
|
179
|
-
this.logger.debug('Reusing existing outbound stream', {
|
|
180
|
-
streamId: this.outboundStream.p2pStream.id,
|
|
239
|
+
// Use persistent writer stream for all outbound requests
|
|
240
|
+
if (this.writerStream && this.writerStream.p2pStream.status === 'open') {
|
|
241
|
+
this.logger.debug('Reusing writer stream for outbound request', {
|
|
242
|
+
streamId: this.writerStream.p2pStream.id,
|
|
181
243
|
});
|
|
182
|
-
return this.
|
|
244
|
+
return this.writerStream;
|
|
183
245
|
}
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
this.outboundStream = await this.createStream(protocol, remoteAddress, config);
|
|
187
|
-
return this.outboundStream;
|
|
246
|
+
// Writer stream should always be available after initialization
|
|
247
|
+
throw new oError(oErrorCodes.INTERNAL_ERROR, 'Writer stream not available or not open');
|
|
188
248
|
}
|
|
189
249
|
/**
|
|
190
|
-
* Override releaseStream to
|
|
191
|
-
*
|
|
250
|
+
* Override releaseStream to protect persistent streams from being closed
|
|
251
|
+
* Persistent streams (reader/writer) should never be released via this method
|
|
252
|
+
* Only closes streams that are not the dedicated reader or writer streams
|
|
192
253
|
*/
|
|
193
254
|
async releaseStream(streamId) {
|
|
194
|
-
//
|
|
195
|
-
this.
|
|
196
|
-
|
|
255
|
+
// Check if this is a persistent stream - don't close those
|
|
256
|
+
if ((this.readerStream && this.readerStream.p2pStream.id === streamId) ||
|
|
257
|
+
(this.writerStream && this.writerStream.p2pStream.id === streamId)) {
|
|
258
|
+
this.logger.debug('Skipping release of persistent stream', { streamId });
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
// Close any other streams (should not occur in normal operation)
|
|
262
|
+
this.logger.debug('Releasing non-persistent stream', { streamId });
|
|
263
|
+
await super.releaseStream(streamId);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Override getStreamById to check persistent streams first
|
|
267
|
+
* Provides access to reader and writer streams by ID
|
|
268
|
+
*
|
|
269
|
+
* @param streamId - The ID of the stream to retrieve
|
|
270
|
+
* @returns The libp2p Stream or undefined if not found
|
|
271
|
+
*/
|
|
272
|
+
getStreamById(streamId) {
|
|
273
|
+
// Check our persistent streams first
|
|
274
|
+
if (this.writerStream?.p2pStream.id === streamId) {
|
|
275
|
+
return this.writerStream.p2pStream;
|
|
276
|
+
}
|
|
277
|
+
if (this.readerStream?.p2pStream.id === streamId) {
|
|
278
|
+
return this.readerStream.p2pStream;
|
|
279
|
+
}
|
|
280
|
+
// Fall back to parent implementation (checks tracked ephemeral streams)
|
|
281
|
+
return super.getStreamById(streamId);
|
|
197
282
|
}
|
|
198
283
|
/**
|
|
199
284
|
* Close the stream manager and cleanup all resources
|
|
@@ -219,15 +304,15 @@ export class oLimitedStreamManager extends oNodeStreamManager {
|
|
|
219
304
|
}
|
|
220
305
|
this.readerStream = undefined;
|
|
221
306
|
}
|
|
222
|
-
// Close
|
|
223
|
-
if (this.
|
|
307
|
+
// Close writer stream
|
|
308
|
+
if (this.writerStream) {
|
|
224
309
|
try {
|
|
225
|
-
await this.
|
|
310
|
+
await this.writerStream.p2pStream.close();
|
|
226
311
|
}
|
|
227
312
|
catch (error) {
|
|
228
|
-
this.logger.warn('Error closing
|
|
313
|
+
this.logger.warn('Error closing writer stream:', error);
|
|
229
314
|
}
|
|
230
|
-
this.
|
|
315
|
+
this.writerStream = undefined;
|
|
231
316
|
}
|
|
232
317
|
this.eventEmitter.emit(StreamManagerEvent.ManagerClosed, undefined);
|
|
233
318
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"o-limited.tool.d.ts","sourceRoot":"","sources":["../../src/o-limited.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,SAAS,EACV,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,qBAAa,YAAa,SAAQ,SAAS;gBAC7B,MAAM,EAAE,WAAW;
|
|
1
|
+
{"version":3,"file":"o-limited.tool.d.ts","sourceRoot":"","sources":["../../src/o-limited.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,SAAS,EACV,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,qBAAa,YAAa,SAAQ,SAAS;gBAC7B,MAAM,EAAE,WAAW;IAWzB,OAAO,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC;IAkBhE,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ7C"}
|
|
@@ -1,50 +1,123 @@
|
|
|
1
1
|
import { expect } from 'chai';
|
|
2
2
|
import { TestEnvironment, createConnectionSpy, } from '@olane/o-node/test/helpers';
|
|
3
|
-
import { oNodeAddress } from '@olane/o-node';
|
|
3
|
+
import { oNodeAddress, oNodeTransport } from '@olane/o-node';
|
|
4
4
|
import { LimitedTestTool, ReceiverTestTool } from './helpers/index.js';
|
|
5
|
+
import { RelayTestTool } from './helpers/relay-test-tool.js';
|
|
5
6
|
describe('Bidirectional Communication', () => {
|
|
6
7
|
const env = new TestEnvironment();
|
|
7
8
|
let caller;
|
|
8
9
|
let receiver;
|
|
10
|
+
let relay;
|
|
9
11
|
afterEach(async () => {
|
|
10
12
|
await env.cleanup();
|
|
11
13
|
});
|
|
12
|
-
describe('Caller → Receiver', () => {
|
|
13
|
-
|
|
14
|
+
// describe('Caller → Receiver', () => {
|
|
15
|
+
// it('should send request and receive response', async () => {
|
|
16
|
+
// caller = await env.createNode<any>(LimitedTestTool, {
|
|
17
|
+
// address: new oNodeAddress('o://caller'),
|
|
18
|
+
// });
|
|
19
|
+
// receiver = await env.createNode<any>(ReceiverTestTool, {
|
|
20
|
+
// address: new oNodeAddress('o://receiver'),
|
|
21
|
+
// });
|
|
22
|
+
// const response = await caller.use(
|
|
23
|
+
// new oNodeAddress(
|
|
24
|
+
// receiver.address.toString(),
|
|
25
|
+
// receiver.address.libp2pTransports,
|
|
26
|
+
// ),
|
|
27
|
+
// {
|
|
28
|
+
// method: 'echo',
|
|
29
|
+
// params: { message: 'hello-receiver' },
|
|
30
|
+
// },
|
|
31
|
+
// );
|
|
32
|
+
// expect(response.result.success).to.be.true;
|
|
33
|
+
// expect(response.result.data.message).to.equal('hello-receiver');
|
|
34
|
+
// expect(receiver.receivedRequests).to.have.lengthOf(1);
|
|
35
|
+
// });
|
|
36
|
+
// it('should handle multiple sequential requests', async () => {
|
|
37
|
+
// caller = await env.createNode<any>(LimitedTestTool, {
|
|
38
|
+
// address: new oNodeAddress('o://caller'),
|
|
39
|
+
// });
|
|
40
|
+
// receiver = await env.createNode<any>(ReceiverTestTool, {
|
|
41
|
+
// address: new oNodeAddress('o://receiver'),
|
|
42
|
+
// });
|
|
43
|
+
// const receiverAddr = new oNodeAddress(
|
|
44
|
+
// receiver.address.toString(),
|
|
45
|
+
// receiver.address.libp2pTransports,
|
|
46
|
+
// );
|
|
47
|
+
// for (let i = 0; i < 5; i++) {
|
|
48
|
+
// const response = await caller.use(receiverAddr, {
|
|
49
|
+
// method: 'echo',
|
|
50
|
+
// params: { message: `request-${i}` },
|
|
51
|
+
// });
|
|
52
|
+
// expect(response.result.success).to.be.true;
|
|
53
|
+
// expect(response.result.data.message).to.equal(`request-${i}`);
|
|
54
|
+
// }
|
|
55
|
+
// expect(receiver.receivedRequests).to.have.lengthOf(5);
|
|
56
|
+
// });
|
|
57
|
+
// });
|
|
58
|
+
describe('Receiver → Caller', () => {
|
|
59
|
+
it('should fail when receiver tries to dial limited caller directly', async () => {
|
|
14
60
|
caller = await env.createNode(LimitedTestTool, {
|
|
15
61
|
address: new oNodeAddress('o://caller'),
|
|
62
|
+
runOnLimitedConnection: true,
|
|
16
63
|
});
|
|
17
64
|
receiver = await env.createNode(ReceiverTestTool, {
|
|
18
65
|
address: new oNodeAddress('o://receiver'),
|
|
66
|
+
runOnLimitedConnection: true,
|
|
67
|
+
network: {
|
|
68
|
+
listeners: ['/p2p-circuit'],
|
|
69
|
+
},
|
|
19
70
|
});
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
71
|
+
relay = await env.createNode(RelayTestTool, {
|
|
72
|
+
address: new oNodeAddress('o://relay'),
|
|
73
|
+
runOnLimitedConnection: true,
|
|
23
74
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
28
|
-
it('should handle multiple sequential requests', async () => {
|
|
29
|
-
caller = await env.createNode(LimitedTestTool, {
|
|
30
|
-
address: new oNodeAddress('o://caller'),
|
|
75
|
+
await receiver.use(relay.address, {
|
|
76
|
+
method: 'ping',
|
|
77
|
+
params: {},
|
|
31
78
|
});
|
|
32
|
-
|
|
33
|
-
|
|
79
|
+
const receiverAddr = new oNodeAddress(receiver.address.toString(), receiver.p2pNode
|
|
80
|
+
.getMultiaddrs()
|
|
81
|
+
.filter((t) => t.toString().includes('p2p-circuit'))
|
|
82
|
+
.map((ma) => new oNodeTransport(ma.toString())));
|
|
83
|
+
console.log('Receiver connected to relay', receiverAddr.libp2pTransports);
|
|
84
|
+
const callerAddr = new oNodeAddress(caller.address.toString(), caller.address.libp2pTransports);
|
|
85
|
+
// First: Caller establishes connection to receiver (this should succeed)
|
|
86
|
+
const initialResponse = await caller.use(receiverAddr, {
|
|
87
|
+
method: 'echo',
|
|
88
|
+
params: { message: 'initial' },
|
|
34
89
|
});
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
90
|
+
console.log('Initial response from receiver:', initialResponse);
|
|
91
|
+
expect(initialResponse.result.success).to.be.true;
|
|
92
|
+
// Now: Receiver attempts to create a stream directly to caller - should fail
|
|
93
|
+
// Get the underlying libp2p connection from receiver side
|
|
94
|
+
const receiverConn = receiver.getFirstConnection();
|
|
95
|
+
const receiverP2pConnection = receiverConn.p2pConnection;
|
|
96
|
+
console.log('receiverP2pConnection:', receiverP2pConnection.streams[0].protocol);
|
|
97
|
+
expect(receiverP2pConnection.direction).to.equal('inbound');
|
|
98
|
+
expect(receiverP2pConnection).to.exist;
|
|
99
|
+
// Get caller's protocol string (e.g., "/o/caller")
|
|
100
|
+
const callerProtocol = caller.address.protocol;
|
|
101
|
+
// Try to manually spawn a new stream to the limited caller
|
|
102
|
+
// This should fail because LimitedTestTool has no protocol handlers registered
|
|
103
|
+
let errorThrown = false;
|
|
104
|
+
try {
|
|
105
|
+
await receiverP2pConnection.newStream('/o/caller', {
|
|
106
|
+
runOnLimitedConnection: true,
|
|
107
|
+
maxOutboundStreams: 1000,
|
|
40
108
|
});
|
|
41
|
-
|
|
42
|
-
|
|
109
|
+
console.log('Created new stream!');
|
|
110
|
+
// Should not reach here
|
|
111
|
+
expect.fail('Should have thrown UnsupportedProtocolError');
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
console.log('Error thrown as expected:', error);
|
|
115
|
+
errorThrown = true;
|
|
116
|
+
// libp2p throws UnsupportedProtocolError when peer has no handler for protocol
|
|
117
|
+
expect(error.name).to.equal('UnsupportedProtocolError');
|
|
43
118
|
}
|
|
44
|
-
expect(
|
|
119
|
+
expect(errorThrown).to.be.true;
|
|
45
120
|
});
|
|
46
|
-
});
|
|
47
|
-
describe('Receiver → Caller', () => {
|
|
48
121
|
it('should send request via reader stream and receive response', async () => {
|
|
49
122
|
caller = await env.createNode(LimitedTestTool, {
|
|
50
123
|
address: new oNodeAddress('o://caller'),
|
|
@@ -63,13 +136,14 @@ describe('Bidirectional Communication', () => {
|
|
|
63
136
|
const callerConn = caller.getFirstConnection();
|
|
64
137
|
const receiverConn = receiver.getFirstConnection();
|
|
65
138
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
139
|
+
// the following code will not work since the stream listeners are setup after the connections are made
|
|
66
140
|
// if (callerConn) {
|
|
67
141
|
// caller.setupEventListeners(callerConn);
|
|
68
142
|
// }
|
|
69
143
|
// if (receiverConn) {
|
|
70
144
|
// receiver.setupStreamListeners(receiverConn);
|
|
71
145
|
// }
|
|
72
|
-
//
|
|
146
|
+
// Wait for receiver to identify reader stream
|
|
73
147
|
// await env.waitFor(() => receiver.identifiedStreams.length > 0, 5000, 100);
|
|
74
148
|
// expect(receiver.identifiedStreams).to.have.lengthOf(1);
|
|
75
149
|
// expect(receiver.identifiedStreams[0].role).to.equal('reader');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { oRequest } from '@olane/o-core';
|
|
2
2
|
import { oLimitedTool } from '../../src/o-limited.tool.js';
|
|
3
|
+
import { Libp2pConfig } from '@olane/o-config';
|
|
3
4
|
/**
|
|
4
5
|
* Test tool that uses limited connections
|
|
5
6
|
* Tracks events and call counts for verification
|
|
@@ -12,6 +13,7 @@ export declare class LimitedTestTool extends oLimitedTool {
|
|
|
12
13
|
}>;
|
|
13
14
|
private eventListenersSetup;
|
|
14
15
|
initialize(): Promise<void>;
|
|
16
|
+
configure(): Promise<Libp2pConfig>;
|
|
15
17
|
/**
|
|
16
18
|
* Set up event listeners for stream manager events
|
|
17
19
|
* Call this after first connection is established
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limited-test-tool.d.ts","sourceRoot":"","sources":["../../../test/helpers/limited-test-tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAI9C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"limited-test-tool.d.ts","sourceRoot":"","sources":["../../../test/helpers/limited-test-tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAI9C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAEL,YAAY,EAKb,MAAM,iBAAiB,CAAC;AAGzB;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,YAAY;IACxC,SAAS,SAAK;IACd,mBAAmB,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE,CAAC,CAAM;IACpE,OAAO,CAAC,mBAAmB,CAAS;IAE9B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;IA+BxC;;;OAGG;IACH,mBAAmB,CAAC,UAAU,EAAE,GAAG,GAAG,IAAI;IAiD1C;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAUjD;;;OAGG;IACG,kBAAkB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAczD;;OAEG;IACH,eAAe,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE,CAAC;IAIrD;;OAEG;IACH,kBAAkB,IAAI,GAAG;CAO1B"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { oLimitedConnectionManager } from '../../src/connection/o-limited-connection-manager.js';
|
|
2
2
|
import { StreamManagerEvent } from '@olane/o-node';
|
|
3
3
|
import { oLimitedTool } from '../../src/o-limited.tool.js';
|
|
4
|
+
import { identify, memory, ping, webSockets, webTransport, } from '@olane/o-config';
|
|
5
|
+
import { circuitRelayTransport } from '@libp2p/circuit-relay-v2';
|
|
4
6
|
/**
|
|
5
7
|
* Test tool that uses limited connections
|
|
6
8
|
* Tracks events and call counts for verification
|
|
@@ -20,6 +22,35 @@ export class LimitedTestTool extends oLimitedTool {
|
|
|
20
22
|
runOnLimitedConnection: true,
|
|
21
23
|
});
|
|
22
24
|
}
|
|
25
|
+
async configure() {
|
|
26
|
+
const config = await super.configure();
|
|
27
|
+
config.transports = [
|
|
28
|
+
memory(),
|
|
29
|
+
webSockets(),
|
|
30
|
+
webTransport(),
|
|
31
|
+
circuitRelayTransport({
|
|
32
|
+
reservationCompletionTimeout: 30000,
|
|
33
|
+
}),
|
|
34
|
+
];
|
|
35
|
+
config.services = {
|
|
36
|
+
identify: identify(),
|
|
37
|
+
ping: ping({
|
|
38
|
+
maxOutboundStreams: 1000,
|
|
39
|
+
maxInboundStreams: 1000,
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
config.connectionGater = {
|
|
43
|
+
...config.connectionGater,
|
|
44
|
+
denyDialPeer: (peerId) => {
|
|
45
|
+
// as a leader, we can dial anything
|
|
46
|
+
return false;
|
|
47
|
+
},
|
|
48
|
+
denyDialMultiaddr: (multiaddr) => {
|
|
49
|
+
return false;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
return config;
|
|
53
|
+
}
|
|
23
54
|
/**
|
|
24
55
|
* Set up event listeners for stream manager events
|
|
25
56
|
* Call this after first connection is established
|
|
@@ -93,6 +124,6 @@ export class LimitedTestTool extends oLimitedTool {
|
|
|
93
124
|
*/
|
|
94
125
|
getFirstConnection() {
|
|
95
126
|
const firstEntry = Array.from(this.connectionManager?.cachedConnections?.values() || []);
|
|
96
|
-
return firstEntry?.[0];
|
|
127
|
+
return (firstEntry?.[0])[0];
|
|
97
128
|
}
|
|
98
129
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { oNodeTool } from '@olane/o-node';
|
|
2
2
|
import type { oRequest } from '@olane/o-core';
|
|
3
|
+
import { Libp2pConfig } from '@olane/o-config';
|
|
3
4
|
/**
|
|
4
5
|
* Standard receiver tool (uses base connection manager)
|
|
5
6
|
* Tracks received requests and identified streams
|
|
@@ -12,6 +13,7 @@ export declare class ReceiverTestTool extends oNodeTool {
|
|
|
12
13
|
}>;
|
|
13
14
|
private eventListenersSetup;
|
|
14
15
|
constructor(config: any);
|
|
16
|
+
configure(): Promise<Libp2pConfig>;
|
|
15
17
|
/**
|
|
16
18
|
* Set up event listeners for stream identification
|
|
17
19
|
* Call this after tool is started
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"receiver-test-tool.d.ts","sourceRoot":"","sources":["../../../test/helpers/receiver-test-tool.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"receiver-test-tool.d.ts","sourceRoot":"","sources":["../../../test/helpers/receiver-test-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,SAAS,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG9C,OAAO,EAEL,YAAY,EAKb,MAAM,iBAAiB,CAAC;AAGzB;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,SAAS;IACtC,gBAAgB,EAAE,QAAQ,EAAE,CAAM;IAClC,iBAAiB,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IACzE,OAAO,CAAC,mBAAmB,CAAS;gBAExB,MAAM,EAAE,GAAG;IAOjB,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;IAyBxC;;;OAGG;IACH,oBAAoB,CAAC,UAAU,EAAE,GAAG,GAAG,IAAI;IAmB3C;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAWjD;;;OAGG;IACG,iBAAiB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAiBxD;;OAEG;IACH,kBAAkB,IAAI,GAAG;CAY1B"}
|
|
@@ -1,16 +1,45 @@
|
|
|
1
1
|
import { oNodeTool } from '@olane/o-node';
|
|
2
2
|
import { StreamManagerEvent } from '@olane/o-node';
|
|
3
|
+
import { identify, memory, ping, tcp, webSockets, } from '@olane/o-config';
|
|
4
|
+
import { circuitRelayTransport } from '@libp2p/circuit-relay-v2';
|
|
3
5
|
/**
|
|
4
6
|
* Standard receiver tool (uses base connection manager)
|
|
5
7
|
* Tracks received requests and identified streams
|
|
6
8
|
*/
|
|
7
9
|
export class ReceiverTestTool extends oNodeTool {
|
|
8
10
|
constructor(config) {
|
|
9
|
-
super(
|
|
11
|
+
super({
|
|
12
|
+
...config,
|
|
13
|
+
runOnLimitedConnection: true,
|
|
14
|
+
});
|
|
10
15
|
this.receivedRequests = [];
|
|
11
16
|
this.identifiedStreams = [];
|
|
12
17
|
this.eventListenersSetup = false;
|
|
13
18
|
}
|
|
19
|
+
async configure() {
|
|
20
|
+
const config = await super.configure();
|
|
21
|
+
config.transports = [
|
|
22
|
+
webSockets(),
|
|
23
|
+
circuitRelayTransport({
|
|
24
|
+
reservationCompletionTimeout: 30000,
|
|
25
|
+
}),
|
|
26
|
+
tcp(),
|
|
27
|
+
memory(),
|
|
28
|
+
];
|
|
29
|
+
config.services = {
|
|
30
|
+
identify: identify({
|
|
31
|
+
maxOutboundStreams: 1000,
|
|
32
|
+
maxInboundStreams: 1000,
|
|
33
|
+
runOnLimitedConnection: true,
|
|
34
|
+
}),
|
|
35
|
+
ping: ping({
|
|
36
|
+
maxOutboundStreams: 1000,
|
|
37
|
+
maxInboundStreams: 1000,
|
|
38
|
+
runOnLimitedConnection: true,
|
|
39
|
+
}),
|
|
40
|
+
};
|
|
41
|
+
return config;
|
|
42
|
+
}
|
|
14
43
|
/**
|
|
15
44
|
* Set up event listeners for stream identification
|
|
16
45
|
* Call this after tool is started
|
|
@@ -32,6 +61,7 @@ export class ReceiverTestTool extends oNodeTool {
|
|
|
32
61
|
* Simple echo method
|
|
33
62
|
*/
|
|
34
63
|
async _tool_echo(request) {
|
|
64
|
+
this.logger.info('Echo request received');
|
|
35
65
|
this.receivedRequests.push(request);
|
|
36
66
|
return {
|
|
37
67
|
message: request.params.message,
|
|
@@ -62,6 +92,10 @@ export class ReceiverTestTool extends oNodeTool {
|
|
|
62
92
|
*/
|
|
63
93
|
getFirstConnection() {
|
|
64
94
|
const firstEntry = Array.from(this.connectionManager?.cachedConnections?.values() || []);
|
|
65
|
-
|
|
95
|
+
// filter out relay addresses if there are some
|
|
96
|
+
const nonRelayConnections = firstEntry?.filter((entry) => {
|
|
97
|
+
return entry.some((conn) => conn.nextHopAddress.value !== 'o://relay');
|
|
98
|
+
});
|
|
99
|
+
return (nonRelayConnections?.[0])[0];
|
|
66
100
|
}
|
|
67
101
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { oNodeTool } from '@olane/o-node';
|
|
2
|
+
import type { oRequest } from '@olane/o-core';
|
|
3
|
+
import { Libp2pConfig } from '@olane/o-config';
|
|
4
|
+
/**
|
|
5
|
+
* Standard receiver tool (uses base connection manager)
|
|
6
|
+
* Tracks received requests and identified streams
|
|
7
|
+
*/
|
|
8
|
+
export declare class RelayTestTool extends oNodeTool {
|
|
9
|
+
receivedRequests: oRequest[];
|
|
10
|
+
identifiedStreams: Array<{
|
|
11
|
+
streamId: string;
|
|
12
|
+
role: string;
|
|
13
|
+
}>;
|
|
14
|
+
private eventListenersSetup;
|
|
15
|
+
constructor(config: any);
|
|
16
|
+
configure(): Promise<Libp2pConfig>;
|
|
17
|
+
/**
|
|
18
|
+
* Set up event listeners for stream identification
|
|
19
|
+
* Call this after tool is started
|
|
20
|
+
*/
|
|
21
|
+
setupStreamListeners(connection: any): void;
|
|
22
|
+
/**
|
|
23
|
+
* Get the first connection (for accessing stream manager)
|
|
24
|
+
*/
|
|
25
|
+
getFirstConnection(): any;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=relay-test-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay-test-tool.d.ts","sourceRoot":"","sources":["../../../test/helpers/relay-test-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EAAY,YAAY,EAAQ,MAAM,iBAAiB,CAAC;AAG/D;;;GAGG;AACH,qBAAa,aAAc,SAAQ,SAAS;IACnC,gBAAgB,EAAE,QAAQ,EAAE,CAAM;IAClC,iBAAiB,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IACzE,OAAO,CAAC,mBAAmB,CAAS;gBAExB,MAAM,EAAE,GAAG;IAOjB,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;IAmCxC;;;OAGG;IACH,oBAAoB,CAAC,UAAU,EAAE,GAAG,GAAG,IAAI;IAmB3C;;OAEG;IACH,kBAAkB,IAAI,GAAG;CAM1B"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { oNodeTool } from '@olane/o-node';
|
|
2
|
+
import { StreamManagerEvent } from '@olane/o-node';
|
|
3
|
+
import { identify, ping } from '@olane/o-config';
|
|
4
|
+
import { circuitRelayServer } from '@libp2p/circuit-relay-v2';
|
|
5
|
+
/**
|
|
6
|
+
* Standard receiver tool (uses base connection manager)
|
|
7
|
+
* Tracks received requests and identified streams
|
|
8
|
+
*/
|
|
9
|
+
export class RelayTestTool extends oNodeTool {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
super({
|
|
12
|
+
...config,
|
|
13
|
+
runOnLimitedConnection: true,
|
|
14
|
+
});
|
|
15
|
+
this.receivedRequests = [];
|
|
16
|
+
this.identifiedStreams = [];
|
|
17
|
+
this.eventListenersSetup = false;
|
|
18
|
+
}
|
|
19
|
+
async configure() {
|
|
20
|
+
const config = await super.configure();
|
|
21
|
+
config.services = {
|
|
22
|
+
identify: identify({
|
|
23
|
+
maxOutboundStreams: Infinity,
|
|
24
|
+
maxInboundStreams: Infinity,
|
|
25
|
+
runOnLimitedConnection: true,
|
|
26
|
+
}),
|
|
27
|
+
ping: ping({
|
|
28
|
+
maxOutboundStreams: Infinity,
|
|
29
|
+
maxInboundStreams: Infinity,
|
|
30
|
+
runOnLimitedConnection: true,
|
|
31
|
+
}),
|
|
32
|
+
relay: circuitRelayServer({
|
|
33
|
+
reservations: {
|
|
34
|
+
maxReservations: Infinity, // need to figure this out
|
|
35
|
+
reservationClearInterval: 60000 * 5, // 15 minutes
|
|
36
|
+
defaultDurationLimit: 60000 * 15, // 15 minutes
|
|
37
|
+
defaultDataLimit: BigInt(1024 * 1024 * 10000), // 10GB
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
};
|
|
41
|
+
config.listeners = config.listeners?.filter((l) => l.indexOf('/memory/') === -1);
|
|
42
|
+
config.connectionManager = {
|
|
43
|
+
...(config?.connectionManager || {}),
|
|
44
|
+
maxConnections: 500,
|
|
45
|
+
maxParallelDials: 100,
|
|
46
|
+
maxDialQueueLength: 100,
|
|
47
|
+
maxPeerAddrsToDial: 25,
|
|
48
|
+
};
|
|
49
|
+
return config;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Set up event listeners for stream identification
|
|
53
|
+
* Call this after tool is started
|
|
54
|
+
*/
|
|
55
|
+
setupStreamListeners(connection) {
|
|
56
|
+
if (this.eventListenersSetup || !connection?.streamManager) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.eventListenersSetup = true;
|
|
60
|
+
// Listen for stream identification events
|
|
61
|
+
connection.streamManager.on(StreamManagerEvent.StreamIdentified, (data) => {
|
|
62
|
+
this.identifiedStreams.push({
|
|
63
|
+
streamId: data.streamId,
|
|
64
|
+
role: data.role,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get the first connection (for accessing stream manager)
|
|
70
|
+
*/
|
|
71
|
+
getFirstConnection() {
|
|
72
|
+
const firstEntry = Array.from(this.connectionManager?.cachedConnections?.values() || []);
|
|
73
|
+
return (firstEntry?.[0])[0];
|
|
74
|
+
}
|
|
75
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@olane/o-client-limited",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.54",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@eslint/eslintrc": "^3.3.1",
|
|
37
37
|
"@eslint/js": "^9.29.0",
|
|
38
|
+
"@libp2p/circuit-relay-v2": "^4.0.5",
|
|
38
39
|
"@tsconfig/node20": "^20.1.6",
|
|
39
40
|
"@types/jest": "^30.0.0",
|
|
40
41
|
"@typescript-eslint/eslint-plugin": "^8.34.1",
|
|
@@ -53,13 +54,13 @@
|
|
|
53
54
|
"typescript": "5.4.5"
|
|
54
55
|
},
|
|
55
56
|
"dependencies": {
|
|
56
|
-
"@olane/o-config": "0.7.
|
|
57
|
-
"@olane/o-core": "0.7.
|
|
58
|
-
"@olane/o-node": "0.7.
|
|
59
|
-
"@olane/o-protocol": "0.7.
|
|
60
|
-
"@olane/o-tool": "0.7.
|
|
57
|
+
"@olane/o-config": "0.7.54",
|
|
58
|
+
"@olane/o-core": "0.7.54",
|
|
59
|
+
"@olane/o-node": "0.7.54",
|
|
60
|
+
"@olane/o-protocol": "0.7.54",
|
|
61
|
+
"@olane/o-tool": "0.7.54",
|
|
61
62
|
"debug": "^4.4.1",
|
|
62
63
|
"dotenv": "^16.5.0"
|
|
63
64
|
},
|
|
64
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "8e527a1481c6aeaf0840ea30198500a9baef1e98"
|
|
65
66
|
}
|