@opensumi/ide-connection 3.8.3-next-1741917543.0 → 3.8.3-next-1741920696.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/common/buffers/buffers.d.ts +3 -0
- package/lib/common/buffers/buffers.d.ts.map +1 -1
- package/lib/common/buffers/buffers.js +32 -1
- package/lib/common/buffers/buffers.js.map +1 -1
- package/lib/common/connection/drivers/frame-decoder.d.ts +9 -14
- package/lib/common/connection/drivers/frame-decoder.d.ts.map +1 -1
- package/lib/common/connection/drivers/frame-decoder.js +95 -39
- package/lib/common/connection/drivers/frame-decoder.js.map +1 -1
- package/lib/common/connection/drivers/reconnecting-websocket.d.ts +8 -2
- package/lib/common/connection/drivers/reconnecting-websocket.d.ts.map +1 -1
- package/lib/common/connection/drivers/reconnecting-websocket.js +56 -26
- package/lib/common/connection/drivers/reconnecting-websocket.js.map +1 -1
- package/lib/common/connection/drivers/stream.d.ts.map +1 -1
- package/lib/common/connection/drivers/stream.js +11 -4
- package/lib/common/connection/drivers/stream.js.map +1 -1
- package/lib/common/connection/drivers/ws-websocket.d.ts +8 -1
- package/lib/common/connection/drivers/ws-websocket.d.ts.map +1 -1
- package/lib/common/connection/drivers/ws-websocket.js +77 -7
- package/lib/common/connection/drivers/ws-websocket.js.map +1 -1
- package/lib/common/constants.d.ts +4 -0
- package/lib/common/constants.d.ts.map +1 -1
- package/lib/common/constants.js +5 -1
- package/lib/common/constants.js.map +1 -1
- package/lib/common/fury-extends/one-of.d.ts.map +1 -1
- package/lib/common/fury-extends/one-of.js +3 -0
- package/lib/common/fury-extends/one-of.js.map +1 -1
- package/lib/node/common-channel-handler.d.ts.map +1 -1
- package/lib/node/common-channel-handler.js +1 -2
- package/lib/node/common-channel-handler.js.map +1 -1
- package/package.json +6 -6
- package/src/common/buffers/buffers.ts +40 -0
- package/src/common/connection/drivers/frame-decoder.ts +103 -43
- package/src/common/connection/drivers/reconnecting-websocket.ts +66 -26
- package/src/common/connection/drivers/stream.ts +11 -4
- package/src/common/connection/drivers/ws-websocket.ts +93 -8
- package/src/common/constants.ts +5 -0
- package/src/common/fury-extends/one-of.ts +3 -0
- package/src/node/common-channel-handler.ts +1 -2
|
@@ -1,20 +1,74 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
1
2
|
import { IDisposable } from '@opensumi/ide-core-common';
|
|
2
3
|
import ReconnectingWebSocket, {
|
|
3
4
|
Options as ReconnectingWebSocketOptions,
|
|
4
5
|
UrlProvider,
|
|
5
6
|
} from '@opensumi/reconnecting-websocket';
|
|
6
7
|
|
|
8
|
+
import { chunkSize } from '../../constants';
|
|
9
|
+
|
|
7
10
|
import { BaseConnection } from './base';
|
|
11
|
+
import { LengthFieldBasedFrameDecoder } from './frame-decoder';
|
|
8
12
|
|
|
9
13
|
import type { ErrorEvent } from '@opensumi/reconnecting-websocket';
|
|
10
14
|
|
|
11
15
|
export class ReconnectingWebSocketConnection extends BaseConnection<Uint8Array> {
|
|
12
|
-
|
|
16
|
+
protected decoder = new LengthFieldBasedFrameDecoder();
|
|
17
|
+
private sendQueue: Array<{ data: Uint8Array; resolve: () => void; reject: (error: Error) => void }> = [];
|
|
18
|
+
private sending = false;
|
|
19
|
+
|
|
20
|
+
protected constructor(private socket: ReconnectingWebSocket) {
|
|
13
21
|
super();
|
|
22
|
+
|
|
23
|
+
if (socket.binaryType === 'arraybuffer') {
|
|
24
|
+
this.socket.addEventListener('message', this.arrayBufferHandler);
|
|
25
|
+
} else if (socket.binaryType === 'blob') {
|
|
26
|
+
throw new Error('blob is not implemented');
|
|
27
|
+
}
|
|
14
28
|
}
|
|
15
29
|
|
|
16
|
-
|
|
17
|
-
this.
|
|
30
|
+
private async processSendQueue() {
|
|
31
|
+
if (this.sending) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this.sending = true;
|
|
35
|
+
|
|
36
|
+
while (this.sendQueue.length > 0) {
|
|
37
|
+
const { data, resolve, reject } = this.sendQueue[0];
|
|
38
|
+
let handle: { get: () => Uint8Array; dispose: () => void } | null = null;
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
handle = LengthFieldBasedFrameDecoder.construct(data).dumpAndOwn();
|
|
42
|
+
const packet = handle.get();
|
|
43
|
+
|
|
44
|
+
for (let i = 0; i < packet.byteLength; i += chunkSize) {
|
|
45
|
+
await new Promise<void>((resolve) => {
|
|
46
|
+
const chunk = packet.subarray(i, Math.min(i + chunkSize, packet.byteLength));
|
|
47
|
+
this.socket.send(chunk);
|
|
48
|
+
resolve();
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
resolve();
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error('[ReconnectingWebSocket] Error sending data:', error);
|
|
55
|
+
reject(error);
|
|
56
|
+
} finally {
|
|
57
|
+
if (handle) {
|
|
58
|
+
handle.dispose();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
this.sendQueue.shift();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.sending = false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
send(data: Uint8Array): Promise<void> {
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
this.sendQueue.push({ data, resolve, reject });
|
|
70
|
+
this.processSendQueue();
|
|
71
|
+
});
|
|
18
72
|
}
|
|
19
73
|
|
|
20
74
|
isOpen(): boolean {
|
|
@@ -29,29 +83,8 @@ export class ReconnectingWebSocketConnection extends BaseConnection<Uint8Array>
|
|
|
29
83
|
},
|
|
30
84
|
};
|
|
31
85
|
}
|
|
32
|
-
|
|
33
86
|
onMessage(cb: (data: Uint8Array) => void): IDisposable {
|
|
34
|
-
|
|
35
|
-
let buffer: Promise<ArrayBuffer>;
|
|
36
|
-
if (e.data instanceof Blob) {
|
|
37
|
-
buffer = e.data.arrayBuffer();
|
|
38
|
-
} else if (e.data instanceof ArrayBuffer) {
|
|
39
|
-
buffer = Promise.resolve(e.data);
|
|
40
|
-
} else if (e.data?.constructor?.name === 'Buffer') {
|
|
41
|
-
// Compatibility with nodejs Buffer in test environment
|
|
42
|
-
buffer = Promise.resolve(e.data);
|
|
43
|
-
} else {
|
|
44
|
-
throw new Error('unknown message type, expect Blob or ArrayBuffer, received: ' + typeof e.data);
|
|
45
|
-
}
|
|
46
|
-
buffer.then((v) => cb(new Uint8Array(v, 0, v.byteLength)));
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
this.socket.addEventListener('message', handler);
|
|
50
|
-
return {
|
|
51
|
-
dispose: () => {
|
|
52
|
-
this.socket.removeEventListener('message', handler);
|
|
53
|
-
},
|
|
54
|
-
};
|
|
87
|
+
return this.decoder.onData(cb);
|
|
55
88
|
}
|
|
56
89
|
onceClose(cb: (code?: number, reason?: string) => void): IDisposable {
|
|
57
90
|
const disposable = this.onClose(wrapper);
|
|
@@ -91,8 +124,15 @@ export class ReconnectingWebSocketConnection extends BaseConnection<Uint8Array>
|
|
|
91
124
|
};
|
|
92
125
|
}
|
|
93
126
|
|
|
127
|
+
private arrayBufferHandler = (e: MessageEvent<ArrayBuffer>) => {
|
|
128
|
+
const buffer: ArrayBuffer = e.data;
|
|
129
|
+
this.decoder.push(new Uint8Array(buffer, 0, buffer.byteLength));
|
|
130
|
+
};
|
|
131
|
+
|
|
94
132
|
dispose(): void {
|
|
95
|
-
|
|
133
|
+
this.socket.removeEventListener('message', this.arrayBufferHandler);
|
|
134
|
+
this.sendQueue = [];
|
|
135
|
+
this.sending = false;
|
|
96
136
|
}
|
|
97
137
|
|
|
98
138
|
static forURL(url: UrlProvider, protocols?: string | string[], options?: ReconnectingWebSocketOptions) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
1
2
|
import { IDisposable } from '@opensumi/ide-core-common';
|
|
2
3
|
|
|
3
4
|
import { BaseConnection } from './base';
|
|
@@ -21,10 +22,16 @@ export class StreamConnection extends BaseConnection<Uint8Array> {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
send(data: Uint8Array): void {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
const handle = LengthFieldBasedFrameDecoder.construct(data).dumpAndOwn();
|
|
26
|
+
try {
|
|
27
|
+
this.writable.write(handle.get(), (error) => {
|
|
28
|
+
if (error) {
|
|
29
|
+
console.error('Failed to write data:', error);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
} finally {
|
|
33
|
+
handle.dispose();
|
|
34
|
+
}
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
onMessage(cb: (data: Uint8Array) => void): IDisposable {
|
|
@@ -1,24 +1,102 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
1
2
|
import { IDisposable } from '@opensumi/ide-core-common';
|
|
2
3
|
|
|
4
|
+
import { chunkSize } from '../../constants';
|
|
5
|
+
|
|
3
6
|
import { BaseConnection } from './base';
|
|
7
|
+
import { LengthFieldBasedFrameDecoder } from './frame-decoder';
|
|
4
8
|
|
|
5
9
|
import type WebSocket from 'ws';
|
|
6
10
|
|
|
11
|
+
interface SendQueueItem {
|
|
12
|
+
data: Uint8Array;
|
|
13
|
+
resolve: () => void;
|
|
14
|
+
reject: (error: Error) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
7
17
|
export class WSWebSocketConnection extends BaseConnection<Uint8Array> {
|
|
18
|
+
protected decoder = new LengthFieldBasedFrameDecoder();
|
|
19
|
+
private static readonly MAX_QUEUE_SIZE = 1000; // 限制队列长度
|
|
20
|
+
|
|
21
|
+
private sendQueue: SendQueueItem[] = [];
|
|
22
|
+
private pendingSize = 0;
|
|
23
|
+
private sending = false;
|
|
24
|
+
|
|
8
25
|
constructor(public socket: WebSocket) {
|
|
9
26
|
super();
|
|
27
|
+
this.socket.on('message', (data: Buffer) => {
|
|
28
|
+
this.decoder.push(data);
|
|
29
|
+
});
|
|
10
30
|
}
|
|
11
|
-
|
|
12
|
-
|
|
31
|
+
|
|
32
|
+
private async processSendQueue() {
|
|
33
|
+
if (this.sending) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
this.sending = true;
|
|
37
|
+
|
|
38
|
+
while (this.sendQueue.length > 0) {
|
|
39
|
+
const { data, resolve, reject } = this.sendQueue[0];
|
|
40
|
+
let handle: { get: () => Uint8Array; dispose: () => void } | null = null;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
handle = LengthFieldBasedFrameDecoder.construct(data).dumpAndOwn();
|
|
44
|
+
const packet = handle.get();
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < packet.byteLength; i += chunkSize) {
|
|
47
|
+
if (!this.isOpen()) {
|
|
48
|
+
throw new Error('Connection closed while sending');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
await new Promise<void>((resolve, reject) => {
|
|
52
|
+
const chunk = packet.subarray(i, Math.min(i + chunkSize, packet.byteLength));
|
|
53
|
+
this.socket.send(chunk, { binary: true }, (error?: Error) => {
|
|
54
|
+
if (error) {
|
|
55
|
+
reject(error);
|
|
56
|
+
} else {
|
|
57
|
+
resolve();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
resolve();
|
|
64
|
+
} catch (error) {
|
|
65
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
66
|
+
} finally {
|
|
67
|
+
if (handle) {
|
|
68
|
+
try {
|
|
69
|
+
handle.dispose();
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.warn('[WSWebSocket] Error disposing handle:', error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
this.pendingSize -= this.sendQueue[0].data.byteLength;
|
|
75
|
+
this.sendQueue.shift();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.sending = false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
send(data: Uint8Array): Promise<void> {
|
|
83
|
+
return new Promise<void>((resolve, reject) => {
|
|
84
|
+
// 检查队列大小限制
|
|
85
|
+
if (this.sendQueue.length >= WSWebSocketConnection.MAX_QUEUE_SIZE) {
|
|
86
|
+
reject(new Error('Send queue full'));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this.pendingSize += data.byteLength;
|
|
91
|
+
this.sendQueue.push({ data, resolve, reject });
|
|
92
|
+
this.processSendQueue().catch((error) => {
|
|
93
|
+
console.error('[WSWebSocket] Error processing queue:', error);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
13
96
|
}
|
|
14
97
|
|
|
15
98
|
onMessage(cb: (data: Uint8Array) => void): IDisposable {
|
|
16
|
-
this.
|
|
17
|
-
return {
|
|
18
|
-
dispose: () => {
|
|
19
|
-
this.socket.off('message', cb);
|
|
20
|
-
},
|
|
21
|
-
};
|
|
99
|
+
return this.decoder.onData(cb);
|
|
22
100
|
}
|
|
23
101
|
onceClose(cb: () => void): IDisposable {
|
|
24
102
|
this.socket.once('close', cb);
|
|
@@ -35,5 +113,12 @@ export class WSWebSocketConnection extends BaseConnection<Uint8Array> {
|
|
|
35
113
|
|
|
36
114
|
dispose(): void {
|
|
37
115
|
this.socket.removeAllListeners();
|
|
116
|
+
// 拒绝所有待发送的消息
|
|
117
|
+
while (this.sendQueue.length > 0) {
|
|
118
|
+
const { reject } = this.sendQueue.shift()!;
|
|
119
|
+
reject(new Error('Connection disposed'));
|
|
120
|
+
}
|
|
121
|
+
this.pendingSize = 0;
|
|
122
|
+
this.sending = false;
|
|
38
123
|
}
|
|
39
124
|
}
|
package/src/common/constants.ts
CHANGED
|
@@ -42,8 +42,7 @@ export class CommonChannelHandler extends BaseCommonChannelHandler implements We
|
|
|
42
42
|
...this.options.wsServerOptions,
|
|
43
43
|
});
|
|
44
44
|
this.wsServer.on('connection', (connection: WebSocket) => {
|
|
45
|
-
|
|
46
|
-
this.receiveConnection(wsConnection);
|
|
45
|
+
this.receiveConnection(new WSWebSocketConnection(connection));
|
|
47
46
|
});
|
|
48
47
|
}
|
|
49
48
|
|