@libp2p/interface 0.1.6-9c67c5b3d → 0.1.6-bb6ceb192
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 +1 -1
- package/dist/src/connection/index.d.ts +13 -1
- package/dist/src/connection/index.d.ts.map +1 -1
- package/dist/src/connection/index.js.map +1 -1
- package/dist/src/stream-muxer/stream.d.ts +2 -149
- package/dist/src/stream-muxer/stream.d.ts.map +1 -1
- package/dist/src/stream-muxer/stream.js +8 -322
- package/dist/src/stream-muxer/stream.js.map +1 -1
- package/package.json +3 -9
- package/src/connection/index.ts +16 -1
- package/src/stream-muxer/stream.ts +9 -494
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[](http://libp2p.io/)
|
|
2
2
|
[](https://discuss.libp2p.io)
|
|
3
3
|
[](https://codecov.io/gh/libp2p/js-libp2p)
|
|
4
|
-
[](https://github.com/libp2p/js-libp2p/actions/workflows/main.yml?query=branch%3Amain)
|
|
5
5
|
|
|
6
6
|
> The interface implemented by a libp2p node
|
|
7
7
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AbortOptions } from '../index.js';
|
|
1
|
+
import type { AbortOptions, Logger } from '../index.js';
|
|
2
2
|
import type { PeerId } from '../peer-id/index.js';
|
|
3
3
|
import type { Multiaddr } from '@multiformats/multiaddr';
|
|
4
4
|
import type { Duplex, Source } from 'it-stream-types';
|
|
@@ -144,6 +144,10 @@ export interface Stream extends Duplex<AsyncGenerator<Uint8ArrayList>, Source<Ui
|
|
|
144
144
|
* The current status of the writable end of the stream
|
|
145
145
|
*/
|
|
146
146
|
writeStatus: WriteStatus;
|
|
147
|
+
/**
|
|
148
|
+
* The stream logger
|
|
149
|
+
*/
|
|
150
|
+
log: Logger;
|
|
147
151
|
}
|
|
148
152
|
export interface NewStreamOptions extends AbortOptions {
|
|
149
153
|
/**
|
|
@@ -226,6 +230,10 @@ export interface Connection {
|
|
|
226
230
|
* Immediately close the connection, any queued data will be discarded
|
|
227
231
|
*/
|
|
228
232
|
abort(err: Error): void;
|
|
233
|
+
/**
|
|
234
|
+
* The connection logger
|
|
235
|
+
*/
|
|
236
|
+
log: Logger;
|
|
229
237
|
}
|
|
230
238
|
export declare const symbol: unique symbol;
|
|
231
239
|
export declare function isConnection(other: any): other is Connection;
|
|
@@ -275,5 +283,9 @@ export interface MultiaddrConnection extends Duplex<AsyncGenerator<Uint8Array>,
|
|
|
275
283
|
* When connection lifecycle events occurred
|
|
276
284
|
*/
|
|
277
285
|
timeline: MultiaddrConnectionTimeline;
|
|
286
|
+
/**
|
|
287
|
+
* The multiaddr connection logger
|
|
288
|
+
*/
|
|
289
|
+
log: Logger;
|
|
278
290
|
}
|
|
279
291
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/connection/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/connection/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/connection/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/connection/index.ts"],"names":[],"mappings":"AA0RA,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAA;AAEtD,MAAM,UAAU,YAAY,CAAE,KAAU;IACtC,OAAO,KAAK,IAAI,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;AAChD,CAAC"}
|
|
@@ -1,150 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import type { AbortOptions } from '../index.js';
|
|
4
|
-
import type { Source } from 'it-stream-types';
|
|
5
|
-
interface Logger {
|
|
6
|
-
(formatter: any, ...args: any[]): void;
|
|
7
|
-
error(formatter: any, ...args: any[]): void;
|
|
8
|
-
trace(formatter: any, ...args: any[]): void;
|
|
9
|
-
enabled: boolean;
|
|
10
|
-
}
|
|
11
|
-
export interface AbstractStreamInit {
|
|
12
|
-
/**
|
|
13
|
-
* A unique identifier for this stream
|
|
14
|
-
*/
|
|
15
|
-
id: string;
|
|
16
|
-
/**
|
|
17
|
-
* The stream direction
|
|
18
|
-
*/
|
|
19
|
-
direction: Direction;
|
|
20
|
-
/**
|
|
21
|
-
* A Logger implementation used to log stream-specific information
|
|
22
|
-
*/
|
|
23
|
-
log: Logger;
|
|
24
|
-
/**
|
|
25
|
-
* User specific stream metadata
|
|
26
|
-
*/
|
|
27
|
-
metadata?: Record<string, unknown>;
|
|
28
|
-
/**
|
|
29
|
-
* Invoked when the stream ends
|
|
30
|
-
*/
|
|
31
|
-
onEnd?(err?: Error | undefined): void;
|
|
32
|
-
/**
|
|
33
|
-
* Invoked when the readable end of the stream is closed
|
|
34
|
-
*/
|
|
35
|
-
onCloseRead?(): void;
|
|
36
|
-
/**
|
|
37
|
-
* Invoked when the writable end of the stream is closed
|
|
38
|
-
*/
|
|
39
|
-
onCloseWrite?(): void;
|
|
40
|
-
/**
|
|
41
|
-
* Invoked when the the stream has been reset by the remote
|
|
42
|
-
*/
|
|
43
|
-
onReset?(): void;
|
|
44
|
-
/**
|
|
45
|
-
* Invoked when the the stream has errored
|
|
46
|
-
*/
|
|
47
|
-
onAbort?(err: Error): void;
|
|
48
|
-
/**
|
|
49
|
-
* How long to wait in ms for stream data to be written to the underlying
|
|
50
|
-
* connection when closing the writable end of the stream. (default: 500)
|
|
51
|
-
*/
|
|
52
|
-
closeTimeout?: number;
|
|
53
|
-
/**
|
|
54
|
-
* After the stream sink has closed, a limit on how long it takes to send
|
|
55
|
-
* a close-write message to the remote peer.
|
|
56
|
-
*/
|
|
57
|
-
sendCloseWriteTimeout?: number;
|
|
58
|
-
}
|
|
59
|
-
export declare abstract class AbstractStream implements Stream {
|
|
60
|
-
id: string;
|
|
61
|
-
direction: Direction;
|
|
62
|
-
timeline: StreamTimeline;
|
|
63
|
-
protocol?: string;
|
|
64
|
-
metadata: Record<string, unknown>;
|
|
65
|
-
source: AsyncGenerator<Uint8ArrayList, void, unknown>;
|
|
66
|
-
status: StreamStatus;
|
|
67
|
-
readStatus: ReadStatus;
|
|
68
|
-
writeStatus: WriteStatus;
|
|
69
|
-
private readonly sinkController;
|
|
70
|
-
private readonly sinkEnd;
|
|
71
|
-
private endErr;
|
|
72
|
-
private readonly streamSource;
|
|
73
|
-
private readonly onEnd?;
|
|
74
|
-
private readonly onCloseRead?;
|
|
75
|
-
private readonly onCloseWrite?;
|
|
76
|
-
private readonly onReset?;
|
|
77
|
-
private readonly onAbort?;
|
|
78
|
-
private readonly sendCloseWriteTimeout;
|
|
79
|
-
protected readonly log: Logger;
|
|
80
|
-
constructor(init: AbstractStreamInit);
|
|
81
|
-
sink(source: Source<Uint8ArrayList | Uint8Array>): Promise<void>;
|
|
82
|
-
protected onSourceEnd(err?: Error): void;
|
|
83
|
-
protected onSinkEnd(err?: Error): void;
|
|
84
|
-
close(options?: AbortOptions): Promise<void>;
|
|
85
|
-
closeRead(options?: AbortOptions): Promise<void>;
|
|
86
|
-
closeWrite(options?: AbortOptions): Promise<void>;
|
|
87
|
-
/**
|
|
88
|
-
* Close immediately for reading and writing and send a reset message (local
|
|
89
|
-
* error)
|
|
90
|
-
*/
|
|
91
|
-
abort(err: Error): void;
|
|
92
|
-
/**
|
|
93
|
-
* Receive a reset message - close immediately for reading and writing (remote
|
|
94
|
-
* error)
|
|
95
|
-
*/
|
|
96
|
-
reset(): void;
|
|
97
|
-
_closeSinkAndSource(err?: Error): void;
|
|
98
|
-
_closeSink(err?: Error): void;
|
|
99
|
-
_closeSource(err?: Error): void;
|
|
100
|
-
/**
|
|
101
|
-
* The remote closed for writing so we should expect to receive no more
|
|
102
|
-
* messages
|
|
103
|
-
*/
|
|
104
|
-
remoteCloseWrite(): void;
|
|
105
|
-
/**
|
|
106
|
-
* The remote closed for reading so we should not send any more
|
|
107
|
-
* messages
|
|
108
|
-
*/
|
|
109
|
-
remoteCloseRead(): void;
|
|
110
|
-
/**
|
|
111
|
-
* The underlying muxer has closed, no more messages can be sent or will
|
|
112
|
-
* be received, close immediately to free up resources
|
|
113
|
-
*/
|
|
114
|
-
destroy(): void;
|
|
115
|
-
/**
|
|
116
|
-
* When an extending class reads data from it's implementation-specific source,
|
|
117
|
-
* call this method to allow the stream consumer to read the data.
|
|
118
|
-
*/
|
|
119
|
-
sourcePush(data: Uint8ArrayList): void;
|
|
120
|
-
/**
|
|
121
|
-
* Returns the amount of unread data - can be used to prevent large amounts of
|
|
122
|
-
* data building up when the stream consumer is too slow.
|
|
123
|
-
*/
|
|
124
|
-
sourceReadableLength(): number;
|
|
125
|
-
/**
|
|
126
|
-
* Send a message to the remote muxer informing them a new stream is being
|
|
127
|
-
* opened
|
|
128
|
-
*/
|
|
129
|
-
abstract sendNewStream(options?: AbortOptions): void | Promise<void>;
|
|
130
|
-
/**
|
|
131
|
-
* Send a data message to the remote muxer
|
|
132
|
-
*/
|
|
133
|
-
abstract sendData(buf: Uint8ArrayList, options?: AbortOptions): void | Promise<void>;
|
|
134
|
-
/**
|
|
135
|
-
* Send a reset message to the remote muxer
|
|
136
|
-
*/
|
|
137
|
-
abstract sendReset(options?: AbortOptions): void | Promise<void>;
|
|
138
|
-
/**
|
|
139
|
-
* Send a message to the remote muxer, informing them no more data messages
|
|
140
|
-
* will be sent by this end of the stream
|
|
141
|
-
*/
|
|
142
|
-
abstract sendCloseWrite(options?: AbortOptions): void | Promise<void>;
|
|
143
|
-
/**
|
|
144
|
-
* Send a message to the remote muxer, informing them no more data messages
|
|
145
|
-
* will be read by this end of the stream
|
|
146
|
-
*/
|
|
147
|
-
abstract sendCloseRead(options?: AbortOptions): void | Promise<void>;
|
|
148
|
-
}
|
|
149
|
-
export {};
|
|
1
|
+
export type AbstractStreamInit = any;
|
|
2
|
+
export declare const AbstractStream: any;
|
|
150
3
|
//# sourceMappingURL=stream.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../src/stream-muxer/stream.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../src/stream-muxer/stream.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,kBAAkB,GAAG,GAAG,CAAA;AAEpC,eAAO,MAAM,cAAc,KAAmB,CAAA"}
|
|
@@ -1,323 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const DEFAULT_SEND_CLOSE_WRITE_TIMEOUT = 5000;
|
|
10
|
-
function isPromise(res) {
|
|
11
|
-
return res != null && typeof res.then === 'function';
|
|
12
|
-
}
|
|
13
|
-
export class AbstractStream {
|
|
14
|
-
id;
|
|
15
|
-
direction;
|
|
16
|
-
timeline;
|
|
17
|
-
protocol;
|
|
18
|
-
metadata;
|
|
19
|
-
source;
|
|
20
|
-
status;
|
|
21
|
-
readStatus;
|
|
22
|
-
writeStatus;
|
|
23
|
-
sinkController;
|
|
24
|
-
sinkEnd;
|
|
25
|
-
endErr;
|
|
26
|
-
streamSource;
|
|
27
|
-
onEnd;
|
|
28
|
-
onCloseRead;
|
|
29
|
-
onCloseWrite;
|
|
30
|
-
onReset;
|
|
31
|
-
onAbort;
|
|
32
|
-
sendCloseWriteTimeout;
|
|
33
|
-
log;
|
|
34
|
-
constructor(init) {
|
|
35
|
-
this.sinkController = new AbortController();
|
|
36
|
-
this.sinkEnd = defer();
|
|
37
|
-
this.log = init.log;
|
|
38
|
-
// stream status
|
|
39
|
-
this.status = 'open';
|
|
40
|
-
this.readStatus = 'ready';
|
|
41
|
-
this.writeStatus = 'ready';
|
|
42
|
-
this.id = init.id;
|
|
43
|
-
this.metadata = init.metadata ?? {};
|
|
44
|
-
this.direction = init.direction;
|
|
45
|
-
this.timeline = {
|
|
46
|
-
open: Date.now()
|
|
47
|
-
};
|
|
48
|
-
this.sendCloseWriteTimeout = init.sendCloseWriteTimeout ?? DEFAULT_SEND_CLOSE_WRITE_TIMEOUT;
|
|
49
|
-
this.onEnd = init.onEnd;
|
|
50
|
-
this.onCloseRead = init?.onCloseRead;
|
|
51
|
-
this.onCloseWrite = init?.onCloseWrite;
|
|
52
|
-
this.onReset = init?.onReset;
|
|
53
|
-
this.onAbort = init?.onAbort;
|
|
54
|
-
this.source = this.streamSource = pushable({
|
|
55
|
-
onEnd: (err) => {
|
|
56
|
-
if (err != null) {
|
|
57
|
-
this.log.trace('source ended with error', err);
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
this.log.trace('source ended');
|
|
61
|
-
}
|
|
62
|
-
this.onSourceEnd(err);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
// necessary because the libp2p upgrader wraps the sink function
|
|
66
|
-
this.sink = this.sink.bind(this);
|
|
67
|
-
}
|
|
68
|
-
async sink(source) {
|
|
69
|
-
if (this.writeStatus !== 'ready') {
|
|
70
|
-
throw new CodeError(`writable end state is "${this.writeStatus}" not "ready"`, ERR_SINK_INVALID_STATE);
|
|
71
|
-
}
|
|
72
|
-
try {
|
|
73
|
-
this.writeStatus = 'writing';
|
|
74
|
-
const options = {
|
|
75
|
-
signal: this.sinkController.signal
|
|
76
|
-
};
|
|
77
|
-
if (this.direction === 'outbound') { // If initiator, open a new stream
|
|
78
|
-
const res = this.sendNewStream(options);
|
|
79
|
-
if (isPromise(res)) {
|
|
80
|
-
await res;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
source = abortableSource(source, this.sinkController.signal, {
|
|
84
|
-
returnOnAbort: true
|
|
85
|
-
});
|
|
86
|
-
this.log.trace('sink reading from source');
|
|
87
|
-
for await (let data of source) {
|
|
88
|
-
data = data instanceof Uint8Array ? new Uint8ArrayList(data) : data;
|
|
89
|
-
const res = this.sendData(data, options);
|
|
90
|
-
if (isPromise(res)) { // eslint-disable-line max-depth
|
|
91
|
-
await res;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
this.log.trace('sink finished reading from source, write status is "%s"', this.writeStatus);
|
|
95
|
-
if (this.writeStatus === 'writing') {
|
|
96
|
-
this.writeStatus = 'closing';
|
|
97
|
-
this.log.trace('send close write to remote');
|
|
98
|
-
await this.sendCloseWrite({
|
|
99
|
-
signal: AbortSignal.timeout(this.sendCloseWriteTimeout)
|
|
100
|
-
});
|
|
101
|
-
this.writeStatus = 'closed';
|
|
102
|
-
}
|
|
103
|
-
this.onSinkEnd();
|
|
104
|
-
}
|
|
105
|
-
catch (err) {
|
|
106
|
-
this.log.trace('sink ended with error, calling abort with error', err);
|
|
107
|
-
this.abort(err);
|
|
108
|
-
throw err;
|
|
109
|
-
}
|
|
110
|
-
finally {
|
|
111
|
-
this.log.trace('resolve sink end');
|
|
112
|
-
this.sinkEnd.resolve();
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
onSourceEnd(err) {
|
|
116
|
-
if (this.timeline.closeRead != null) {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
this.timeline.closeRead = Date.now();
|
|
120
|
-
this.readStatus = 'closed';
|
|
121
|
-
if (err != null && this.endErr == null) {
|
|
122
|
-
this.endErr = err;
|
|
123
|
-
}
|
|
124
|
-
this.onCloseRead?.();
|
|
125
|
-
if (this.timeline.closeWrite != null) {
|
|
126
|
-
this.log.trace('source and sink ended');
|
|
127
|
-
this.timeline.close = Date.now();
|
|
128
|
-
if (this.status !== 'aborted' && this.status !== 'reset') {
|
|
129
|
-
this.status = 'closed';
|
|
130
|
-
}
|
|
131
|
-
if (this.onEnd != null) {
|
|
132
|
-
this.onEnd(this.endErr);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
this.log.trace('source ended, waiting for sink to end');
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
onSinkEnd(err) {
|
|
140
|
-
if (this.timeline.closeWrite != null) {
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
this.timeline.closeWrite = Date.now();
|
|
144
|
-
this.writeStatus = 'closed';
|
|
145
|
-
if (err != null && this.endErr == null) {
|
|
146
|
-
this.endErr = err;
|
|
147
|
-
}
|
|
148
|
-
this.onCloseWrite?.();
|
|
149
|
-
if (this.timeline.closeRead != null) {
|
|
150
|
-
this.log.trace('sink and source ended');
|
|
151
|
-
this.timeline.close = Date.now();
|
|
152
|
-
if (this.status !== 'aborted' && this.status !== 'reset') {
|
|
153
|
-
this.status = 'closed';
|
|
154
|
-
}
|
|
155
|
-
if (this.onEnd != null) {
|
|
156
|
-
this.onEnd(this.endErr);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
this.log.trace('sink ended, waiting for source to end');
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// Close for both Reading and Writing
|
|
164
|
-
async close(options) {
|
|
165
|
-
this.log.trace('closing gracefully');
|
|
166
|
-
this.status = 'closing';
|
|
167
|
-
await Promise.all([
|
|
168
|
-
this.closeRead(options),
|
|
169
|
-
this.closeWrite(options)
|
|
170
|
-
]);
|
|
171
|
-
this.status = 'closed';
|
|
172
|
-
this.log.trace('closed gracefully');
|
|
173
|
-
}
|
|
174
|
-
async closeRead(options = {}) {
|
|
175
|
-
if (this.readStatus === 'closing' || this.readStatus === 'closed') {
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
this.log.trace('closing readable end of stream with starting read status "%s"', this.readStatus);
|
|
179
|
-
const readStatus = this.readStatus;
|
|
180
|
-
this.readStatus = 'closing';
|
|
181
|
-
if (this.status !== 'reset' && this.status !== 'aborted' && this.timeline.closeRead == null) {
|
|
182
|
-
this.log.trace('send close read to remote');
|
|
183
|
-
await this.sendCloseRead(options);
|
|
184
|
-
}
|
|
185
|
-
if (readStatus === 'ready') {
|
|
186
|
-
this.log.trace('ending internal source queue');
|
|
187
|
-
this.streamSource.end();
|
|
188
|
-
}
|
|
189
|
-
this.log.trace('closed readable end of stream');
|
|
190
|
-
}
|
|
191
|
-
async closeWrite(options = {}) {
|
|
192
|
-
if (this.writeStatus === 'closing' || this.writeStatus === 'closed') {
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
this.log.trace('closing writable end of stream with starting write status "%s"', this.writeStatus);
|
|
196
|
-
if (this.writeStatus === 'ready') {
|
|
197
|
-
this.log.trace('sink was never sunk, sink an empty array');
|
|
198
|
-
await raceSignal(this.sink([]), options.signal);
|
|
199
|
-
}
|
|
200
|
-
if (this.writeStatus === 'writing') {
|
|
201
|
-
// stop reading from the source passed to `.sink` in the microtask queue
|
|
202
|
-
// - this lets any data queued by the user in the current tick get read
|
|
203
|
-
// before we exit
|
|
204
|
-
await new Promise((resolve, reject) => {
|
|
205
|
-
queueMicrotask(() => {
|
|
206
|
-
this.log.trace('aborting source passed to .sink');
|
|
207
|
-
this.sinkController.abort();
|
|
208
|
-
raceSignal(this.sinkEnd.promise, options.signal)
|
|
209
|
-
.then(resolve, reject);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
this.writeStatus = 'closed';
|
|
214
|
-
this.log.trace('closed writable end of stream');
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Close immediately for reading and writing and send a reset message (local
|
|
218
|
-
* error)
|
|
219
|
-
*/
|
|
220
|
-
abort(err) {
|
|
221
|
-
if (this.status === 'closed' || this.status === 'aborted' || this.status === 'reset') {
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
this.log('abort with error', err);
|
|
225
|
-
// try to send a reset message
|
|
226
|
-
this.log('try to send reset to remote');
|
|
227
|
-
const res = this.sendReset();
|
|
228
|
-
if (isPromise(res)) {
|
|
229
|
-
res.catch((err) => {
|
|
230
|
-
this.log.error('error sending reset message', err);
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
this.status = 'aborted';
|
|
234
|
-
this.timeline.abort = Date.now();
|
|
235
|
-
this._closeSinkAndSource(err);
|
|
236
|
-
this.onAbort?.(err);
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Receive a reset message - close immediately for reading and writing (remote
|
|
240
|
-
* error)
|
|
241
|
-
*/
|
|
242
|
-
reset() {
|
|
243
|
-
if (this.status === 'closed' || this.status === 'aborted' || this.status === 'reset') {
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
const err = new CodeError('stream reset', ERR_STREAM_RESET);
|
|
247
|
-
this.status = 'reset';
|
|
248
|
-
this.timeline.reset = Date.now();
|
|
249
|
-
this._closeSinkAndSource(err);
|
|
250
|
-
this.onReset?.();
|
|
251
|
-
}
|
|
252
|
-
_closeSinkAndSource(err) {
|
|
253
|
-
this._closeSink(err);
|
|
254
|
-
this._closeSource(err);
|
|
255
|
-
}
|
|
256
|
-
_closeSink(err) {
|
|
257
|
-
// if the sink function is running, cause it to end
|
|
258
|
-
if (this.writeStatus === 'writing') {
|
|
259
|
-
this.log.trace('end sink source');
|
|
260
|
-
this.sinkController.abort();
|
|
261
|
-
}
|
|
262
|
-
this.onSinkEnd(err);
|
|
263
|
-
}
|
|
264
|
-
_closeSource(err) {
|
|
265
|
-
// if the source is not ending, end it
|
|
266
|
-
if (this.readStatus !== 'closing' && this.readStatus !== 'closed') {
|
|
267
|
-
this.log.trace('ending source with %d bytes to be read by consumer', this.streamSource.readableLength);
|
|
268
|
-
this.readStatus = 'closing';
|
|
269
|
-
this.streamSource.end(err);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* The remote closed for writing so we should expect to receive no more
|
|
274
|
-
* messages
|
|
275
|
-
*/
|
|
276
|
-
remoteCloseWrite() {
|
|
277
|
-
if (this.readStatus === 'closing' || this.readStatus === 'closed') {
|
|
278
|
-
this.log('received remote close write but local source is already closed');
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
this.log.trace('remote close write');
|
|
282
|
-
this._closeSource();
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* The remote closed for reading so we should not send any more
|
|
286
|
-
* messages
|
|
287
|
-
*/
|
|
288
|
-
remoteCloseRead() {
|
|
289
|
-
if (this.writeStatus === 'closing' || this.writeStatus === 'closed') {
|
|
290
|
-
this.log('received remote close read but local sink is already closed');
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
this.log.trace('remote close read');
|
|
294
|
-
this._closeSink();
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* The underlying muxer has closed, no more messages can be sent or will
|
|
298
|
-
* be received, close immediately to free up resources
|
|
299
|
-
*/
|
|
300
|
-
destroy() {
|
|
301
|
-
if (this.status === 'closed' || this.status === 'aborted' || this.status === 'reset') {
|
|
302
|
-
this.log('received destroy but we are already closed');
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
this.log.trace('stream destroyed');
|
|
306
|
-
this._closeSinkAndSource();
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* When an extending class reads data from it's implementation-specific source,
|
|
310
|
-
* call this method to allow the stream consumer to read the data.
|
|
311
|
-
*/
|
|
312
|
-
sourcePush(data) {
|
|
313
|
-
this.streamSource.push(data);
|
|
314
|
-
}
|
|
315
|
-
/**
|
|
316
|
-
* Returns the amount of unread data - can be used to prevent large amounts of
|
|
317
|
-
* data building up when the stream consumer is too slow.
|
|
318
|
-
*/
|
|
319
|
-
sourceReadableLength() {
|
|
320
|
-
return this.streamSource.readableLength;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
1
|
+
// TODO: remove this file and the dep-check override in package.json for
|
|
2
|
+
// @libp2p/utils when yamux is updated.
|
|
3
|
+
// This is a hack to defeat TypeScript trying to inspect the types for
|
|
4
|
+
// @libp2p/utils/abstract-stream which depends on this module - making the
|
|
5
|
+
// import path only resolvable at runtime breaks the transpile time circular
|
|
6
|
+
// dependency
|
|
7
|
+
const s = await import('@libp2p/utils' + '/abstract-stream');
|
|
8
|
+
export const AbstractStream = s.AbstractStream;
|
|
323
9
|
//# sourceMappingURL=stream.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../../src/stream-muxer/stream.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../../src/stream-muxer/stream.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,uCAAuC;AACvC,sEAAsE;AACtE,0EAA0E;AAC1E,4EAA4E;AAC5E,aAAa;AACb,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,eAAe,GAAG,kBAAkB,CAAC,CAAA;AAI5D,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,cAAc,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libp2p/interface",
|
|
3
|
-
"version": "0.1.6-
|
|
3
|
+
"version": "0.1.6-bb6ceb192",
|
|
4
4
|
"description": "The interface implemented by a libp2p node",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
|
-
"homepage": "https://github.com/libp2p/js-libp2p/tree/
|
|
6
|
+
"homepage": "https://github.com/libp2p/js-libp2p/tree/main/packages/interface#readme",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/libp2p/js-libp2p.git"
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
"scripts": {
|
|
148
148
|
"clean": "aegir clean",
|
|
149
149
|
"lint": "aegir lint",
|
|
150
|
-
"dep-check": "aegir dep-check",
|
|
150
|
+
"dep-check": "aegir dep-check -i @libp2p/utils",
|
|
151
151
|
"build": "aegir build",
|
|
152
152
|
"test": "aegir test",
|
|
153
153
|
"test:chrome": "aegir test -t browser --cov",
|
|
@@ -159,20 +159,14 @@
|
|
|
159
159
|
},
|
|
160
160
|
"dependencies": {
|
|
161
161
|
"@multiformats/multiaddr": "^12.1.10",
|
|
162
|
-
"abortable-iterator": "^5.0.1",
|
|
163
162
|
"it-pushable": "^3.2.1",
|
|
164
163
|
"it-stream-types": "^2.0.1",
|
|
165
164
|
"multiformats": "^12.1.3",
|
|
166
|
-
"p-defer": "^4.0.0",
|
|
167
|
-
"race-signal": "^1.0.0",
|
|
168
165
|
"uint8arraylist": "^2.4.3"
|
|
169
166
|
},
|
|
170
167
|
"devDependencies": {
|
|
171
168
|
"@types/sinon": "^17.0.0",
|
|
172
169
|
"aegir": "^41.0.2",
|
|
173
|
-
"delay": "^6.0.0",
|
|
174
|
-
"it-all": "^3.0.3",
|
|
175
|
-
"it-drain": "^3.0.3",
|
|
176
170
|
"sinon": "^17.0.0",
|
|
177
171
|
"sinon-ts": "^2.0.0"
|
|
178
172
|
},
|
package/src/connection/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AbortOptions } from '../index.js'
|
|
1
|
+
import type { AbortOptions, Logger } from '../index.js'
|
|
2
2
|
import type { PeerId } from '../peer-id/index.js'
|
|
3
3
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
4
4
|
import type { Duplex, Source } from 'it-stream-types'
|
|
@@ -169,6 +169,11 @@ export interface Stream extends Duplex<AsyncGenerator<Uint8ArrayList>, Source<Ui
|
|
|
169
169
|
* The current status of the writable end of the stream
|
|
170
170
|
*/
|
|
171
171
|
writeStatus: WriteStatus
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* The stream logger
|
|
175
|
+
*/
|
|
176
|
+
log: Logger
|
|
172
177
|
}
|
|
173
178
|
|
|
174
179
|
export interface NewStreamOptions extends AbortOptions {
|
|
@@ -268,6 +273,11 @@ export interface Connection {
|
|
|
268
273
|
* Immediately close the connection, any queued data will be discarded
|
|
269
274
|
*/
|
|
270
275
|
abort(err: Error): void
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* The connection logger
|
|
279
|
+
*/
|
|
280
|
+
log: Logger
|
|
271
281
|
}
|
|
272
282
|
|
|
273
283
|
export const symbol = Symbol.for('@libp2p/connection')
|
|
@@ -329,4 +339,9 @@ export interface MultiaddrConnection extends Duplex<AsyncGenerator<Uint8Array>,
|
|
|
329
339
|
* When connection lifecycle events occurred
|
|
330
340
|
*/
|
|
331
341
|
timeline: MultiaddrConnectionTimeline
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* The multiaddr connection logger
|
|
345
|
+
*/
|
|
346
|
+
log: Logger
|
|
332
347
|
}
|
|
@@ -1,496 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import type { AbortOptions } from '../index.js'
|
|
9
|
-
import type { Source } from 'it-stream-types'
|
|
1
|
+
// TODO: remove this file and the dep-check override in package.json for
|
|
2
|
+
// @libp2p/utils when yamux is updated.
|
|
3
|
+
// This is a hack to defeat TypeScript trying to inspect the types for
|
|
4
|
+
// @libp2p/utils/abstract-stream which depends on this module - making the
|
|
5
|
+
// import path only resolvable at runtime breaks the transpile time circular
|
|
6
|
+
// dependency
|
|
7
|
+
const s = await import('@libp2p/utils' + '/abstract-stream')
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
interface Logger {
|
|
13
|
-
(formatter: any, ...args: any[]): void
|
|
14
|
-
error(formatter: any, ...args: any[]): void
|
|
15
|
-
trace(formatter: any, ...args: any[]): void
|
|
16
|
-
enabled: boolean
|
|
17
|
-
}
|
|
9
|
+
export type AbstractStreamInit = any
|
|
18
10
|
|
|
19
|
-
const
|
|
20
|
-
const ERR_SINK_INVALID_STATE = 'ERR_SINK_INVALID_STATE'
|
|
21
|
-
const DEFAULT_SEND_CLOSE_WRITE_TIMEOUT = 5000
|
|
22
|
-
|
|
23
|
-
export interface AbstractStreamInit {
|
|
24
|
-
/**
|
|
25
|
-
* A unique identifier for this stream
|
|
26
|
-
*/
|
|
27
|
-
id: string
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The stream direction
|
|
31
|
-
*/
|
|
32
|
-
direction: Direction
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* A Logger implementation used to log stream-specific information
|
|
36
|
-
*/
|
|
37
|
-
log: Logger
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* User specific stream metadata
|
|
41
|
-
*/
|
|
42
|
-
metadata?: Record<string, unknown>
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Invoked when the stream ends
|
|
46
|
-
*/
|
|
47
|
-
onEnd?(err?: Error | undefined): void
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Invoked when the readable end of the stream is closed
|
|
51
|
-
*/
|
|
52
|
-
onCloseRead?(): void
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Invoked when the writable end of the stream is closed
|
|
56
|
-
*/
|
|
57
|
-
onCloseWrite?(): void
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Invoked when the the stream has been reset by the remote
|
|
61
|
-
*/
|
|
62
|
-
onReset?(): void
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Invoked when the the stream has errored
|
|
66
|
-
*/
|
|
67
|
-
onAbort?(err: Error): void
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* How long to wait in ms for stream data to be written to the underlying
|
|
71
|
-
* connection when closing the writable end of the stream. (default: 500)
|
|
72
|
-
*/
|
|
73
|
-
closeTimeout?: number
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* After the stream sink has closed, a limit on how long it takes to send
|
|
77
|
-
* a close-write message to the remote peer.
|
|
78
|
-
*/
|
|
79
|
-
sendCloseWriteTimeout?: number
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function isPromise (res?: any): res is Promise<void> {
|
|
83
|
-
return res != null && typeof res.then === 'function'
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export abstract class AbstractStream implements Stream {
|
|
87
|
-
public id: string
|
|
88
|
-
public direction: Direction
|
|
89
|
-
public timeline: StreamTimeline
|
|
90
|
-
public protocol?: string
|
|
91
|
-
public metadata: Record<string, unknown>
|
|
92
|
-
public source: AsyncGenerator<Uint8ArrayList, void, unknown>
|
|
93
|
-
public status: StreamStatus
|
|
94
|
-
public readStatus: ReadStatus
|
|
95
|
-
public writeStatus: WriteStatus
|
|
96
|
-
|
|
97
|
-
private readonly sinkController: AbortController
|
|
98
|
-
private readonly sinkEnd: DeferredPromise<void>
|
|
99
|
-
private endErr: Error | undefined
|
|
100
|
-
private readonly streamSource: Pushable<Uint8ArrayList>
|
|
101
|
-
private readonly onEnd?: (err?: Error | undefined) => void
|
|
102
|
-
private readonly onCloseRead?: () => void
|
|
103
|
-
private readonly onCloseWrite?: () => void
|
|
104
|
-
private readonly onReset?: () => void
|
|
105
|
-
private readonly onAbort?: (err: Error) => void
|
|
106
|
-
private readonly sendCloseWriteTimeout: number
|
|
107
|
-
|
|
108
|
-
protected readonly log: Logger
|
|
109
|
-
|
|
110
|
-
constructor (init: AbstractStreamInit) {
|
|
111
|
-
this.sinkController = new AbortController()
|
|
112
|
-
this.sinkEnd = defer()
|
|
113
|
-
this.log = init.log
|
|
114
|
-
|
|
115
|
-
// stream status
|
|
116
|
-
this.status = 'open'
|
|
117
|
-
this.readStatus = 'ready'
|
|
118
|
-
this.writeStatus = 'ready'
|
|
119
|
-
|
|
120
|
-
this.id = init.id
|
|
121
|
-
this.metadata = init.metadata ?? {}
|
|
122
|
-
this.direction = init.direction
|
|
123
|
-
this.timeline = {
|
|
124
|
-
open: Date.now()
|
|
125
|
-
}
|
|
126
|
-
this.sendCloseWriteTimeout = init.sendCloseWriteTimeout ?? DEFAULT_SEND_CLOSE_WRITE_TIMEOUT
|
|
127
|
-
|
|
128
|
-
this.onEnd = init.onEnd
|
|
129
|
-
this.onCloseRead = init?.onCloseRead
|
|
130
|
-
this.onCloseWrite = init?.onCloseWrite
|
|
131
|
-
this.onReset = init?.onReset
|
|
132
|
-
this.onAbort = init?.onAbort
|
|
133
|
-
|
|
134
|
-
this.source = this.streamSource = pushable<Uint8ArrayList>({
|
|
135
|
-
onEnd: (err) => {
|
|
136
|
-
if (err != null) {
|
|
137
|
-
this.log.trace('source ended with error', err)
|
|
138
|
-
} else {
|
|
139
|
-
this.log.trace('source ended')
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
this.onSourceEnd(err)
|
|
143
|
-
}
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
// necessary because the libp2p upgrader wraps the sink function
|
|
147
|
-
this.sink = this.sink.bind(this)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async sink (source: Source<Uint8ArrayList | Uint8Array>): Promise<void> {
|
|
151
|
-
if (this.writeStatus !== 'ready') {
|
|
152
|
-
throw new CodeError(`writable end state is "${this.writeStatus}" not "ready"`, ERR_SINK_INVALID_STATE)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
try {
|
|
156
|
-
this.writeStatus = 'writing'
|
|
157
|
-
|
|
158
|
-
const options: AbortOptions = {
|
|
159
|
-
signal: this.sinkController.signal
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (this.direction === 'outbound') { // If initiator, open a new stream
|
|
163
|
-
const res = this.sendNewStream(options)
|
|
164
|
-
|
|
165
|
-
if (isPromise(res)) {
|
|
166
|
-
await res
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
source = abortableSource(source, this.sinkController.signal, {
|
|
171
|
-
returnOnAbort: true
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
this.log.trace('sink reading from source')
|
|
175
|
-
|
|
176
|
-
for await (let data of source) {
|
|
177
|
-
data = data instanceof Uint8Array ? new Uint8ArrayList(data) : data
|
|
178
|
-
|
|
179
|
-
const res = this.sendData(data, options)
|
|
180
|
-
|
|
181
|
-
if (isPromise(res)) { // eslint-disable-line max-depth
|
|
182
|
-
await res
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
this.log.trace('sink finished reading from source, write status is "%s"', this.writeStatus)
|
|
187
|
-
|
|
188
|
-
if (this.writeStatus === 'writing') {
|
|
189
|
-
this.writeStatus = 'closing'
|
|
190
|
-
|
|
191
|
-
this.log.trace('send close write to remote')
|
|
192
|
-
await this.sendCloseWrite({
|
|
193
|
-
signal: AbortSignal.timeout(this.sendCloseWriteTimeout)
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
this.writeStatus = 'closed'
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
this.onSinkEnd()
|
|
200
|
-
} catch (err: any) {
|
|
201
|
-
this.log.trace('sink ended with error, calling abort with error', err)
|
|
202
|
-
this.abort(err)
|
|
203
|
-
|
|
204
|
-
throw err
|
|
205
|
-
} finally {
|
|
206
|
-
this.log.trace('resolve sink end')
|
|
207
|
-
this.sinkEnd.resolve()
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
protected onSourceEnd (err?: Error): void {
|
|
212
|
-
if (this.timeline.closeRead != null) {
|
|
213
|
-
return
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
this.timeline.closeRead = Date.now()
|
|
217
|
-
this.readStatus = 'closed'
|
|
218
|
-
|
|
219
|
-
if (err != null && this.endErr == null) {
|
|
220
|
-
this.endErr = err
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
this.onCloseRead?.()
|
|
224
|
-
|
|
225
|
-
if (this.timeline.closeWrite != null) {
|
|
226
|
-
this.log.trace('source and sink ended')
|
|
227
|
-
this.timeline.close = Date.now()
|
|
228
|
-
|
|
229
|
-
if (this.status !== 'aborted' && this.status !== 'reset') {
|
|
230
|
-
this.status = 'closed'
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (this.onEnd != null) {
|
|
234
|
-
this.onEnd(this.endErr)
|
|
235
|
-
}
|
|
236
|
-
} else {
|
|
237
|
-
this.log.trace('source ended, waiting for sink to end')
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
protected onSinkEnd (err?: Error): void {
|
|
242
|
-
if (this.timeline.closeWrite != null) {
|
|
243
|
-
return
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
this.timeline.closeWrite = Date.now()
|
|
247
|
-
this.writeStatus = 'closed'
|
|
248
|
-
|
|
249
|
-
if (err != null && this.endErr == null) {
|
|
250
|
-
this.endErr = err
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
this.onCloseWrite?.()
|
|
254
|
-
|
|
255
|
-
if (this.timeline.closeRead != null) {
|
|
256
|
-
this.log.trace('sink and source ended')
|
|
257
|
-
this.timeline.close = Date.now()
|
|
258
|
-
|
|
259
|
-
if (this.status !== 'aborted' && this.status !== 'reset') {
|
|
260
|
-
this.status = 'closed'
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (this.onEnd != null) {
|
|
264
|
-
this.onEnd(this.endErr)
|
|
265
|
-
}
|
|
266
|
-
} else {
|
|
267
|
-
this.log.trace('sink ended, waiting for source to end')
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Close for both Reading and Writing
|
|
272
|
-
async close (options?: AbortOptions): Promise<void> {
|
|
273
|
-
this.log.trace('closing gracefully')
|
|
274
|
-
|
|
275
|
-
this.status = 'closing'
|
|
276
|
-
|
|
277
|
-
await Promise.all([
|
|
278
|
-
this.closeRead(options),
|
|
279
|
-
this.closeWrite(options)
|
|
280
|
-
])
|
|
281
|
-
|
|
282
|
-
this.status = 'closed'
|
|
283
|
-
|
|
284
|
-
this.log.trace('closed gracefully')
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
async closeRead (options: AbortOptions = {}): Promise<void> {
|
|
288
|
-
if (this.readStatus === 'closing' || this.readStatus === 'closed') {
|
|
289
|
-
return
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
this.log.trace('closing readable end of stream with starting read status "%s"', this.readStatus)
|
|
293
|
-
|
|
294
|
-
const readStatus = this.readStatus
|
|
295
|
-
this.readStatus = 'closing'
|
|
296
|
-
|
|
297
|
-
if (this.status !== 'reset' && this.status !== 'aborted' && this.timeline.closeRead == null) {
|
|
298
|
-
this.log.trace('send close read to remote')
|
|
299
|
-
await this.sendCloseRead(options)
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
if (readStatus === 'ready') {
|
|
303
|
-
this.log.trace('ending internal source queue')
|
|
304
|
-
this.streamSource.end()
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
this.log.trace('closed readable end of stream')
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
async closeWrite (options: AbortOptions = {}): Promise<void> {
|
|
311
|
-
if (this.writeStatus === 'closing' || this.writeStatus === 'closed') {
|
|
312
|
-
return
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
this.log.trace('closing writable end of stream with starting write status "%s"', this.writeStatus)
|
|
316
|
-
|
|
317
|
-
if (this.writeStatus === 'ready') {
|
|
318
|
-
this.log.trace('sink was never sunk, sink an empty array')
|
|
319
|
-
|
|
320
|
-
await raceSignal(this.sink([]), options.signal)
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
if (this.writeStatus === 'writing') {
|
|
324
|
-
// stop reading from the source passed to `.sink` in the microtask queue
|
|
325
|
-
// - this lets any data queued by the user in the current tick get read
|
|
326
|
-
// before we exit
|
|
327
|
-
await new Promise((resolve, reject) => {
|
|
328
|
-
queueMicrotask(() => {
|
|
329
|
-
this.log.trace('aborting source passed to .sink')
|
|
330
|
-
this.sinkController.abort()
|
|
331
|
-
raceSignal(this.sinkEnd.promise, options.signal)
|
|
332
|
-
.then(resolve, reject)
|
|
333
|
-
})
|
|
334
|
-
})
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
this.writeStatus = 'closed'
|
|
338
|
-
|
|
339
|
-
this.log.trace('closed writable end of stream')
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* Close immediately for reading and writing and send a reset message (local
|
|
344
|
-
* error)
|
|
345
|
-
*/
|
|
346
|
-
abort (err: Error): void {
|
|
347
|
-
if (this.status === 'closed' || this.status === 'aborted' || this.status === 'reset') {
|
|
348
|
-
return
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
this.log('abort with error', err)
|
|
352
|
-
|
|
353
|
-
// try to send a reset message
|
|
354
|
-
this.log('try to send reset to remote')
|
|
355
|
-
const res = this.sendReset()
|
|
356
|
-
|
|
357
|
-
if (isPromise(res)) {
|
|
358
|
-
res.catch((err) => {
|
|
359
|
-
this.log.error('error sending reset message', err)
|
|
360
|
-
})
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
this.status = 'aborted'
|
|
364
|
-
this.timeline.abort = Date.now()
|
|
365
|
-
this._closeSinkAndSource(err)
|
|
366
|
-
this.onAbort?.(err)
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Receive a reset message - close immediately for reading and writing (remote
|
|
371
|
-
* error)
|
|
372
|
-
*/
|
|
373
|
-
reset (): void {
|
|
374
|
-
if (this.status === 'closed' || this.status === 'aborted' || this.status === 'reset') {
|
|
375
|
-
return
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const err = new CodeError('stream reset', ERR_STREAM_RESET)
|
|
379
|
-
|
|
380
|
-
this.status = 'reset'
|
|
381
|
-
this.timeline.reset = Date.now()
|
|
382
|
-
this._closeSinkAndSource(err)
|
|
383
|
-
this.onReset?.()
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
_closeSinkAndSource (err?: Error): void {
|
|
387
|
-
this._closeSink(err)
|
|
388
|
-
this._closeSource(err)
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
_closeSink (err?: Error): void {
|
|
392
|
-
// if the sink function is running, cause it to end
|
|
393
|
-
if (this.writeStatus === 'writing') {
|
|
394
|
-
this.log.trace('end sink source')
|
|
395
|
-
this.sinkController.abort()
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
this.onSinkEnd(err)
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
_closeSource (err?: Error): void {
|
|
402
|
-
// if the source is not ending, end it
|
|
403
|
-
if (this.readStatus !== 'closing' && this.readStatus !== 'closed') {
|
|
404
|
-
this.log.trace('ending source with %d bytes to be read by consumer', this.streamSource.readableLength)
|
|
405
|
-
this.readStatus = 'closing'
|
|
406
|
-
this.streamSource.end(err)
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* The remote closed for writing so we should expect to receive no more
|
|
412
|
-
* messages
|
|
413
|
-
*/
|
|
414
|
-
remoteCloseWrite (): void {
|
|
415
|
-
if (this.readStatus === 'closing' || this.readStatus === 'closed') {
|
|
416
|
-
this.log('received remote close write but local source is already closed')
|
|
417
|
-
return
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
this.log.trace('remote close write')
|
|
421
|
-
this._closeSource()
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
* The remote closed for reading so we should not send any more
|
|
426
|
-
* messages
|
|
427
|
-
*/
|
|
428
|
-
remoteCloseRead (): void {
|
|
429
|
-
if (this.writeStatus === 'closing' || this.writeStatus === 'closed') {
|
|
430
|
-
this.log('received remote close read but local sink is already closed')
|
|
431
|
-
return
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
this.log.trace('remote close read')
|
|
435
|
-
this._closeSink()
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* The underlying muxer has closed, no more messages can be sent or will
|
|
440
|
-
* be received, close immediately to free up resources
|
|
441
|
-
*/
|
|
442
|
-
destroy (): void {
|
|
443
|
-
if (this.status === 'closed' || this.status === 'aborted' || this.status === 'reset') {
|
|
444
|
-
this.log('received destroy but we are already closed')
|
|
445
|
-
return
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
this.log.trace('stream destroyed')
|
|
449
|
-
|
|
450
|
-
this._closeSinkAndSource()
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* When an extending class reads data from it's implementation-specific source,
|
|
455
|
-
* call this method to allow the stream consumer to read the data.
|
|
456
|
-
*/
|
|
457
|
-
sourcePush (data: Uint8ArrayList): void {
|
|
458
|
-
this.streamSource.push(data)
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
/**
|
|
462
|
-
* Returns the amount of unread data - can be used to prevent large amounts of
|
|
463
|
-
* data building up when the stream consumer is too slow.
|
|
464
|
-
*/
|
|
465
|
-
sourceReadableLength (): number {
|
|
466
|
-
return this.streamSource.readableLength
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* Send a message to the remote muxer informing them a new stream is being
|
|
471
|
-
* opened
|
|
472
|
-
*/
|
|
473
|
-
abstract sendNewStream (options?: AbortOptions): void | Promise<void>
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* Send a data message to the remote muxer
|
|
477
|
-
*/
|
|
478
|
-
abstract sendData (buf: Uint8ArrayList, options?: AbortOptions): void | Promise<void>
|
|
479
|
-
|
|
480
|
-
/**
|
|
481
|
-
* Send a reset message to the remote muxer
|
|
482
|
-
*/
|
|
483
|
-
abstract sendReset (options?: AbortOptions): void | Promise<void>
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* Send a message to the remote muxer, informing them no more data messages
|
|
487
|
-
* will be sent by this end of the stream
|
|
488
|
-
*/
|
|
489
|
-
abstract sendCloseWrite (options?: AbortOptions): void | Promise<void>
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* Send a message to the remote muxer, informing them no more data messages
|
|
493
|
-
* will be read by this end of the stream
|
|
494
|
-
*/
|
|
495
|
-
abstract sendCloseRead (options?: AbortOptions): void | Promise<void>
|
|
496
|
-
}
|
|
11
|
+
export const AbstractStream = s.AbstractStream
|