@litert/televoke 1.0.0-0 → 1.0.0-10
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/docs/Televoke_v2.md +378 -0
- package/lib/client/JsonApiClient.impl.js +2 -2
- package/lib/client/JsonApiClient.impl.js.map +1 -1
- package/lib/client/LegacyHttpClient.impl.d.ts +17 -16
- package/lib/client/LegacyHttpClient.impl.d.ts.map +1 -1
- package/lib/client/LegacyHttpClient.impl.js +25 -11
- package/lib/client/LegacyHttpClient.impl.js.map +1 -1
- package/lib/server/Server.decl.d.ts +2 -1
- package/lib/server/Server.decl.d.ts.map +1 -1
- package/lib/server/Server.impl.d.ts +1 -0
- package/lib/server/Server.impl.d.ts.map +1 -1
- package/lib/server/Server.impl.js +8 -1
- package/lib/server/Server.impl.js.map +1 -1
- package/lib/server/SimpleJsonApiRouter.d.ts +3 -1
- package/lib/server/SimpleJsonApiRouter.d.ts.map +1 -1
- package/lib/server/SimpleJsonApiRouter.js +18 -8
- package/lib/server/SimpleJsonApiRouter.js.map +1 -1
- package/lib/shared/BinaryStream.d.ts +5 -1
- package/lib/shared/BinaryStream.d.ts.map +1 -1
- package/lib/shared/BinaryStream.js +9 -2
- package/lib/shared/BinaryStream.js.map +1 -1
- package/lib/shared/BinaryStreamManager.js.map +1 -1
- package/lib/shared/Channel.impl.d.ts +1 -1
- package/lib/shared/Channel.impl.d.ts.map +1 -1
- package/lib/shared/Channel.impl.js +21 -6
- package/lib/shared/Channel.impl.js.map +1 -1
- package/lib/shared/Encodings/v1/EncoderV1.js.map +1 -1
- package/lib/shared/Encodings/v2/DecoderV2.d.ts.map +1 -1
- package/lib/shared/Encodings/v2/DecoderV2.js +3 -1
- package/lib/shared/Encodings/v2/DecoderV2.js.map +1 -1
- package/lib/shared/Encodings/v2/EncoderV2.d.ts.map +1 -1
- package/lib/shared/Encodings/v2/EncoderV2.js +7 -6
- package/lib/shared/Encodings/v2/EncoderV2.js.map +1 -1
- package/lib/shared/Encodings/v2/Encoding.v2.decl.d.ts +4 -0
- package/lib/shared/Encodings/v2/Encoding.v2.decl.d.ts.map +1 -1
- package/lib/shared/Errors.d.ts +3 -0
- package/lib/shared/Errors.d.ts.map +1 -1
- package/lib/shared/Errors.js +3 -0
- package/lib/shared/Errors.js.map +1 -1
- package/lib/shared/Shared.decl.d.ts +21 -4
- package/lib/shared/Shared.decl.d.ts.map +1 -1
- package/lib/shared/Utils.js.map +1 -1
- package/lib/transporters/http-listener/HttpListener.d.ts.map +1 -1
- package/lib/transporters/http-listener/HttpListener.js +1 -0
- package/lib/transporters/http-listener/HttpListener.js.map +1 -1
- package/lib/transporters/http-listener/HttpUnixSocketListener.js.map +1 -1
- package/lib/transporters/http-listener/HttpsListener.js.map +1 -1
- package/lib/transporters/legacy-http/LegacyHttp.Server.d.ts.map +1 -1
- package/lib/transporters/legacy-http/LegacyHttp.Server.js +22 -15
- package/lib/transporters/legacy-http/LegacyHttp.Server.js.map +1 -1
- package/lib/transporters/legacy-http/LegacyHttp.Transporter.js.map +1 -1
- package/lib/transporters/lwdfx/LwDFX.Tcp.Server.js.map +1 -1
- package/lib/transporters/lwdfx/LwDFX.Tls.Server.js.map +1 -1
- package/lib/transporters/lwdfx/LwDFX.Transporter.js.map +1 -1
- package/lib/transporters/lwdfx/LwDFX.UnixSocket.Server.js.map +1 -1
- package/lib/transporters/memory/Memory.Impl.js.map +1 -1
- package/lib/transporters/websocket/WebSocket.Server.d.ts +11 -1
- package/lib/transporters/websocket/WebSocket.Server.d.ts.map +1 -1
- package/lib/transporters/websocket/WebSocket.Server.js +4 -3
- package/lib/transporters/websocket/WebSocket.Server.js.map +1 -1
- package/lib/transporters/websocket/WebSocket.Transporter.js.map +1 -1
- package/package.json +8 -8
- package/src/lib/client/JsonApiClient.impl.ts +2 -2
- package/src/lib/client/LegacyHttpClient.impl.ts +51 -31
- package/src/lib/server/Server.decl.ts +3 -1
- package/src/lib/server/Server.impl.ts +10 -1
- package/src/lib/server/SimpleJsonApiRouter.ts +22 -10
- package/src/lib/shared/BinaryStream.ts +12 -2
- package/src/lib/shared/Channel.impl.ts +24 -7
- package/src/lib/shared/Encodings/v2/DecoderV2.ts +3 -1
- package/src/lib/shared/Encodings/v2/EncoderV2.ts +7 -6
- package/src/lib/shared/Encodings/v2/Encoding.v2.decl.ts +5 -0
- package/src/lib/shared/Errors.ts +3 -0
- package/src/lib/shared/Shared.decl.ts +23 -4
- package/src/lib/transporters/http-listener/HttpListener.ts +1 -0
- package/src/lib/transporters/legacy-http/LegacyHttp.Server.ts +38 -21
- package/src/lib/transporters/websocket/WebSocket.Server.ts +16 -1
|
@@ -36,7 +36,8 @@ class TvLegacyHttpClient extends EventEmitter implements C.IClient<any> {
|
|
|
36
36
|
private readonly _request: (opts: any, cb: (resp: $Http.IncomingMessage) => void) => $Http.ClientRequest,
|
|
37
37
|
private readonly _opts: $Https.RequestOptions,
|
|
38
38
|
private readonly _agent: $Http.Agent,
|
|
39
|
-
private readonly
|
|
39
|
+
private readonly _retryOnConnReset: boolean = true,
|
|
40
|
+
private readonly _apiNameWrapper?: (name: string) => string,
|
|
40
41
|
) {
|
|
41
42
|
|
|
42
43
|
super();
|
|
@@ -75,7 +76,26 @@ class TvLegacyHttpClient extends EventEmitter implements C.IClient<any> {
|
|
|
75
76
|
return Promise.reject(new Shared.errors.cmd_not_impl());
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
public invoke(api: any, ...args: any[]): Promise<any> {
|
|
79
|
+
public async invoke(api: any, ...args: any[]): Promise<any> {
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
|
|
83
|
+
return await this._invoke(api, ...args);
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
|
|
87
|
+
if (this._retryOnConnReset && (e as any)?.code === 'ECONNRESET') {
|
|
88
|
+
|
|
89
|
+
return this._invoke(api, ...args);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
|
|
93
|
+
throw e;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private _invoke(api: any, ...args: any[]): Promise<any> {
|
|
79
99
|
|
|
80
100
|
const CST = Date.now();
|
|
81
101
|
|
|
@@ -84,7 +104,7 @@ class TvLegacyHttpClient extends EventEmitter implements C.IClient<any> {
|
|
|
84
104
|
rid: 0,
|
|
85
105
|
cst: CST,
|
|
86
106
|
args,
|
|
87
|
-
api: this._apiNameWrapper
|
|
107
|
+
api: this._apiNameWrapper?.(api) ?? api
|
|
88
108
|
});
|
|
89
109
|
|
|
90
110
|
return new Promise((resolve, reject) => {
|
|
@@ -174,7 +194,7 @@ class TvLegacyHttpClient extends EventEmitter implements C.IClient<any> {
|
|
|
174
194
|
case v1.EResponseCode.MALFORMED_ARGUMENTS:
|
|
175
195
|
case v1.EResponseCode.SYSTEM_ERROR:
|
|
176
196
|
default:
|
|
177
|
-
reject(new Shared.errors.
|
|
197
|
+
reject(new Shared.errors.server_internal_error({
|
|
178
198
|
api, ...data
|
|
179
199
|
}));
|
|
180
200
|
break;
|
|
@@ -207,7 +227,7 @@ class TvLegacyHttpClient extends EventEmitter implements C.IClient<any> {
|
|
|
207
227
|
}
|
|
208
228
|
}
|
|
209
229
|
|
|
210
|
-
|
|
230
|
+
interface IHttpClientOptionsBase extends $Https.RequestOptions {
|
|
211
231
|
|
|
212
232
|
/**
|
|
213
233
|
* Whether to use HTTPS.
|
|
@@ -217,44 +237,43 @@ export interface IHttpClientNetworkOptions extends $Https.RequestOptions {
|
|
|
217
237
|
https?: boolean;
|
|
218
238
|
|
|
219
239
|
/**
|
|
220
|
-
*
|
|
240
|
+
* Wrap the API name before sending to server.
|
|
221
241
|
*
|
|
222
|
-
* @default
|
|
242
|
+
* @default null
|
|
223
243
|
*/
|
|
224
|
-
|
|
244
|
+
apiNameWrapper?: (name: string) => string;
|
|
225
245
|
|
|
226
246
|
/**
|
|
227
|
-
*
|
|
247
|
+
* Whether to retry on connection reset.
|
|
228
248
|
*
|
|
229
|
-
* @default
|
|
249
|
+
* @default true
|
|
230
250
|
*/
|
|
231
|
-
|
|
251
|
+
retryOnConnReset?: boolean;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface IHttpClientNetworkOptions extends IHttpClientOptionsBase {
|
|
232
255
|
|
|
233
256
|
/**
|
|
234
|
-
* The
|
|
257
|
+
* The hostname of HTTP server.
|
|
235
258
|
*
|
|
236
|
-
* @default '
|
|
259
|
+
* @default 'localhost'
|
|
237
260
|
*/
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
apiNameWrapper?: (name: string) => string;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export interface IHttpClientUnixSocketOptions extends $Https.RequestOptions {
|
|
261
|
+
hostname: string;
|
|
244
262
|
|
|
245
263
|
/**
|
|
246
|
-
*
|
|
264
|
+
* The port of HTTP server.
|
|
247
265
|
*
|
|
248
|
-
*
|
|
266
|
+
* @default 80 or 443
|
|
249
267
|
*/
|
|
250
|
-
|
|
268
|
+
port?: number;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export interface IHttpClientUnixSocketOptions extends IHttpClientOptionsBase {
|
|
251
272
|
|
|
252
273
|
/**
|
|
253
274
|
* The path of Unix domain socket to connect to.
|
|
254
275
|
*/
|
|
255
276
|
socketPath: string;
|
|
256
|
-
|
|
257
|
-
apiNameWrapper?: (name: string) => string;
|
|
258
277
|
}
|
|
259
278
|
|
|
260
279
|
export type IHttpClientOptions = IHttpClientNetworkOptions | IHttpClientUnixSocketOptions;
|
|
@@ -265,14 +284,15 @@ export function createLegacyHttpClient<TAPIs extends Shared.IObject>(opts: IHttp
|
|
|
265
284
|
opts.https ? $Https.request : $Http.request,
|
|
266
285
|
opts,
|
|
267
286
|
opts.https ? new $Https.Agent({
|
|
268
|
-
maxSockets: 0,
|
|
269
|
-
keepAlive: true,
|
|
270
|
-
keepAliveMsecs: DEFAULT_TIMEOUT
|
|
287
|
+
'maxSockets': 0,
|
|
288
|
+
'keepAlive': true,
|
|
289
|
+
'keepAliveMsecs': DEFAULT_TIMEOUT
|
|
271
290
|
}) : new $Http.Agent({
|
|
272
|
-
maxSockets: 0,
|
|
273
|
-
keepAlive: true,
|
|
274
|
-
keepAliveMsecs: DEFAULT_TIMEOUT
|
|
291
|
+
'maxSockets': 0,
|
|
292
|
+
'keepAlive': true,
|
|
293
|
+
'keepAliveMsecs': DEFAULT_TIMEOUT
|
|
275
294
|
}),
|
|
276
|
-
opts.
|
|
295
|
+
opts.retryOnConnReset,
|
|
296
|
+
opts.apiNameWrapper,
|
|
277
297
|
);
|
|
278
298
|
}
|
|
@@ -41,7 +41,7 @@ export type IRouteApiCallback = (response: string | Buffer | Array<string | Buff
|
|
|
41
41
|
|
|
42
42
|
export type IRouteLegacyApiCallback = (response: unknown | Shared.TvErrorResponse | Shared.TelevokeError) => void;
|
|
43
43
|
|
|
44
|
-
export interface IRouter {
|
|
44
|
+
export interface IRouter extends Shared.IEventListener<Shared.IDefaultEvents> {
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* The encoding of the API request/response.
|
|
@@ -82,6 +82,8 @@ export interface IRouter {
|
|
|
82
82
|
export interface IServerEvents extends Shared.IDefaultEvents {
|
|
83
83
|
|
|
84
84
|
channel(channel: IChannel): void;
|
|
85
|
+
|
|
86
|
+
warning(error: unknown, channel?: IChannel): void;
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
export interface IServer extends Shared.IEventListener<IServerEvents> {
|
|
@@ -35,11 +35,19 @@ export class TvServer extends EventEmitter implements dS.IServer, dT.IServer {
|
|
|
35
35
|
private readonly _streamManagerFactory: Shared.IStreamManagerFactory = Shared.createSharedStreamManagerFactory()
|
|
36
36
|
) {
|
|
37
37
|
super();
|
|
38
|
+
this.router.on('error', this._onRouterError);
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
private readonly _onRouterError = (e: unknown): void => {
|
|
42
|
+
|
|
43
|
+
this.emit('error', e);
|
|
44
|
+
};
|
|
45
|
+
|
|
40
46
|
public setRouter(router: dS.IRouter): void {
|
|
41
47
|
|
|
42
|
-
this.router
|
|
48
|
+
this.router?.off('error', this._onRouterError);
|
|
49
|
+
this.router = router
|
|
50
|
+
.on('error', this._onRouterError);
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
public registerChannel(transporter: dT.ITransporter): void {
|
|
@@ -60,6 +68,7 @@ export class TvServer extends EventEmitter implements dS.IServer, dT.IServer {
|
|
|
60
68
|
});
|
|
61
69
|
})
|
|
62
70
|
.on('error', (err) => this.emit('error', err))
|
|
71
|
+
.on('warning', (err) => this.emit('warning', err, this._channels[id]))
|
|
63
72
|
.on('close', () => { delete this._channels[id]; });
|
|
64
73
|
}
|
|
65
74
|
|
|
@@ -17,12 +17,13 @@
|
|
|
17
17
|
import type * as dS from './Server.decl';
|
|
18
18
|
import * as Shared from '../shared';
|
|
19
19
|
import { buffer2json } from '../shared/Utils';
|
|
20
|
+
import { EventEmitter } from 'node:events';
|
|
20
21
|
|
|
21
22
|
export type ISimpleJsonApiHandler = (ctx: dS.IRequestContext, ...args: any[]) => unknown;
|
|
22
23
|
|
|
23
24
|
export type ISimpleJsonMessageHandler = (ctx: dS.IRequestContext, msg: unknown) => unknown;
|
|
24
25
|
|
|
25
|
-
export class SimpleJsonApiRouter implements dS.IRouter {
|
|
26
|
+
export class SimpleJsonApiRouter extends EventEmitter implements dS.IRouter {
|
|
26
27
|
|
|
27
28
|
public get encoding(): string {
|
|
28
29
|
|
|
@@ -65,8 +66,18 @@ export class SimpleJsonApiRouter implements dS.IRouter {
|
|
|
65
66
|
if (result instanceof Promise) {
|
|
66
67
|
|
|
67
68
|
result.then(
|
|
68
|
-
(data) => {
|
|
69
|
-
|
|
69
|
+
(data) => {
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
|
|
73
|
+
cb(JSON.stringify(data ?? null));
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
|
|
77
|
+
this._processError(cb, e, name);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
(e) => { this._processError(cb, e, name); },
|
|
70
81
|
);
|
|
71
82
|
}
|
|
72
83
|
else {
|
|
@@ -76,11 +87,11 @@ export class SimpleJsonApiRouter implements dS.IRouter {
|
|
|
76
87
|
}
|
|
77
88
|
catch (e) {
|
|
78
89
|
|
|
79
|
-
this._processError(cb, e);
|
|
90
|
+
this._processError(cb, e, name);
|
|
80
91
|
}
|
|
81
92
|
}
|
|
82
93
|
|
|
83
|
-
private _processError(cb: dS.IRouteApiCallback, e: unknown): void {
|
|
94
|
+
private _processError(cb: dS.IRouteApiCallback, e: unknown, name: string): void {
|
|
84
95
|
|
|
85
96
|
if (e instanceof Shared.TelevokeError) {
|
|
86
97
|
|
|
@@ -90,11 +101,12 @@ export class SimpleJsonApiRouter implements dS.IRouter {
|
|
|
90
101
|
|
|
91
102
|
if (e instanceof Shared.TvErrorResponse) {
|
|
92
103
|
|
|
93
|
-
cb(new Shared.errors.app_error(JSON.stringify(e.data)));
|
|
104
|
+
cb(new Shared.errors.app_error(JSON.stringify(e.data ?? null)));
|
|
94
105
|
return;
|
|
95
106
|
}
|
|
96
107
|
|
|
97
|
-
cb(new Shared.errors.
|
|
108
|
+
cb(new Shared.errors.server_internal_error());
|
|
109
|
+
this.emit('error', new Shared.errors.unprocessable_error({ api: name }, e));
|
|
98
110
|
}
|
|
99
111
|
|
|
100
112
|
public processLegacyApi(
|
|
@@ -118,8 +130,8 @@ export class SimpleJsonApiRouter implements dS.IRouter {
|
|
|
118
130
|
if (result instanceof Promise) {
|
|
119
131
|
|
|
120
132
|
result.then(
|
|
121
|
-
(data) => { cb(data); },
|
|
122
|
-
(e) => { this._processError(cb, e); },
|
|
133
|
+
(data) => { cb(data ?? null); },
|
|
134
|
+
(e) => { this._processError(cb, e, name); },
|
|
123
135
|
);
|
|
124
136
|
}
|
|
125
137
|
else {
|
|
@@ -129,7 +141,7 @@ export class SimpleJsonApiRouter implements dS.IRouter {
|
|
|
129
141
|
}
|
|
130
142
|
catch (e) {
|
|
131
143
|
|
|
132
|
-
this._processError(cb, e);
|
|
144
|
+
this._processError(cb, e, name);
|
|
133
145
|
}
|
|
134
146
|
}
|
|
135
147
|
}
|
|
@@ -20,6 +20,11 @@ import { Readable } from 'node:stream';
|
|
|
20
20
|
|
|
21
21
|
export class TvBinaryReadStream extends Readable implements Shared.IBinaryReadStream {
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* The index of next chunk to read.
|
|
25
|
+
*/
|
|
26
|
+
public nextIndex: number = 0;
|
|
27
|
+
|
|
23
28
|
private _timer: NodeJS.Timeout | null = null;
|
|
24
29
|
|
|
25
30
|
public constructor(
|
|
@@ -87,7 +92,7 @@ export class TvBinaryReadStream extends Readable implements Shared.IBinaryReadSt
|
|
|
87
92
|
}, this._timeout);
|
|
88
93
|
}
|
|
89
94
|
|
|
90
|
-
public
|
|
95
|
+
public append(chunkSegments: Buffer[]): void {
|
|
91
96
|
|
|
92
97
|
if (this.readableEnded) {
|
|
93
98
|
|
|
@@ -99,7 +104,12 @@ export class TvBinaryReadStream extends Readable implements Shared.IBinaryReadSt
|
|
|
99
104
|
this._resetTimer();
|
|
100
105
|
}
|
|
101
106
|
|
|
102
|
-
|
|
107
|
+
for (const s of chunkSegments) {
|
|
108
|
+
|
|
109
|
+
super.push(s);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.nextIndex++;
|
|
103
113
|
}
|
|
104
114
|
|
|
105
115
|
public close(): void {
|
|
@@ -438,18 +438,34 @@ export abstract class AbstractTvChannelV2
|
|
|
438
438
|
break;
|
|
439
439
|
}
|
|
440
440
|
|
|
441
|
-
const
|
|
441
|
+
const chunkSegments = (packet as v2.IBinaryChunkRequestPacket).ct.body as Buffer[];
|
|
442
442
|
|
|
443
|
-
|
|
443
|
+
const chunkIndex = (packet as v2.IBinaryChunkRequestPacket).ct.index;
|
|
444
|
+
|
|
445
|
+
if (chunkIndex === 0xFFFFFFFF) {
|
|
446
|
+
|
|
447
|
+
stream.abort();
|
|
448
|
+
}
|
|
449
|
+
else if (chunkIndex !== stream.nextIndex) {
|
|
450
|
+
|
|
451
|
+
this._reply({
|
|
452
|
+
'cmd': v2.ECommand.BINARY_CHUNK,
|
|
453
|
+
'typ': v2.EPacketType.ERROR_RESPONSE,
|
|
454
|
+
'seq': packet.seq,
|
|
455
|
+
'ct': new E.errors.stream_index_mismatch({
|
|
456
|
+
'sid': (packet as v2.IBinaryChunkRequestPacket).ct.streamId,
|
|
457
|
+
'chId': this.id,
|
|
458
|
+
}),
|
|
459
|
+
});
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
else if (!chunkSegments[0]?.byteLength) {
|
|
444
463
|
|
|
445
464
|
stream.close();
|
|
446
465
|
}
|
|
447
466
|
else {
|
|
448
467
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
stream.push(chunk);
|
|
452
|
-
}
|
|
468
|
+
stream.append(chunkSegments);
|
|
453
469
|
}
|
|
454
470
|
|
|
455
471
|
this._reply({
|
|
@@ -546,7 +562,7 @@ export abstract class AbstractTvChannelV2
|
|
|
546
562
|
});
|
|
547
563
|
}
|
|
548
564
|
|
|
549
|
-
public sendBinaryChunk(streamId: number, chunk: Buffer | null): Promise<void> {
|
|
565
|
+
public sendBinaryChunk(streamId: number, index: number | false, chunk: Buffer | null): Promise<void> {
|
|
550
566
|
|
|
551
567
|
if (!this.writable) {
|
|
552
568
|
|
|
@@ -561,6 +577,7 @@ export abstract class AbstractTvChannelV2
|
|
|
561
577
|
'seq': seq,
|
|
562
578
|
'ct': {
|
|
563
579
|
streamId,
|
|
580
|
+
'index': index === false ? 0xFFFFFFFF : index,
|
|
564
581
|
'body': chunk ?? [],
|
|
565
582
|
}
|
|
566
583
|
} satisfies v2.IBinaryChunkRequestPacket));
|
|
@@ -234,7 +234,7 @@ class TvPushMessageRequestDecoder implements IPacketDecoder {
|
|
|
234
234
|
'cmd': CSv2.ECommand.PUSH_MESSAGE,
|
|
235
235
|
'typ': CSv2.EPacketType.REQUEST,
|
|
236
236
|
'seq': ctx.seq,
|
|
237
|
-
'ct':
|
|
237
|
+
'ct': readVarBuffer(ctx)
|
|
238
238
|
} satisfies dEnc2.IPushMessageRequestPacket;
|
|
239
239
|
}
|
|
240
240
|
}
|
|
@@ -244,12 +244,14 @@ class TvBinaryChunkRequestDecoder implements IPacketDecoder {
|
|
|
244
244
|
public decode(ctx: IDecodeContext): dEnc2.ICommandPacket {
|
|
245
245
|
|
|
246
246
|
const streamId = readSmallBytes(4, B4, ctx).readUInt32LE(0);
|
|
247
|
+
const chunkIndex = readSmallBytes(4, B4, ctx).readUInt32LE(0);
|
|
247
248
|
return {
|
|
248
249
|
'cmd': CSv2.ECommand.BINARY_CHUNK,
|
|
249
250
|
'typ': CSv2.EPacketType.REQUEST,
|
|
250
251
|
'seq': ctx.seq,
|
|
251
252
|
'ct': {
|
|
252
253
|
'streamId': streamId,
|
|
254
|
+
'index': chunkIndex,
|
|
253
255
|
'body': readVarBuffer(ctx),
|
|
254
256
|
}
|
|
255
257
|
} satisfies dEnc2.IBinaryChunkRequestPacket;
|
|
@@ -104,10 +104,11 @@ class BinaryChunkRequestEncoder implements IPacketEncoder {
|
|
|
104
104
|
|
|
105
105
|
public encode(packet: dEnc2.IBinaryChunkRequestPacket): v2.IDataChunkArray {
|
|
106
106
|
|
|
107
|
-
const leadSeg = Buffer.allocUnsafe(CS.HEADER_SIZE +
|
|
107
|
+
const leadSeg = Buffer.allocUnsafe(CS.HEADER_SIZE + 12);
|
|
108
108
|
|
|
109
109
|
writePacketHeader(leadSeg, packet.cmd, packet.typ, packet.seq);
|
|
110
110
|
leadSeg.writeUInt32LE(packet.ct.streamId, CS.HEADER_SIZE);
|
|
111
|
+
leadSeg.writeUInt32LE(packet.ct.index, CS.HEADER_SIZE + 4);
|
|
111
112
|
|
|
112
113
|
if (Array.isArray(packet.ct.body)) {
|
|
113
114
|
|
|
@@ -118,12 +119,12 @@ class BinaryChunkRequestEncoder implements IPacketEncoder {
|
|
|
118
119
|
bodyLen += Buffer.byteLength(p);
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
leadSeg.writeUInt32LE(bodyLen, CS.HEADER_SIZE +
|
|
122
|
+
leadSeg.writeUInt32LE(bodyLen, CS.HEADER_SIZE + 8);
|
|
122
123
|
|
|
123
124
|
return [leadSeg, ...packet.ct.body];
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
leadSeg.writeUInt32LE(Buffer.byteLength(packet.ct.body), CS.HEADER_SIZE +
|
|
127
|
+
leadSeg.writeUInt32LE(Buffer.byteLength(packet.ct.body), CS.HEADER_SIZE + 8);
|
|
127
128
|
|
|
128
129
|
return [leadSeg, packet.ct.body];
|
|
129
130
|
}
|
|
@@ -173,7 +174,7 @@ class PushMessageRequestEncoder implements IPacketEncoder {
|
|
|
173
174
|
|
|
174
175
|
public encode(packet: dEnc2.IPushMessageRequestPacket): v2.IDataChunkArray {
|
|
175
176
|
|
|
176
|
-
const leadSeg = Buffer.allocUnsafe(CS.HEADER_SIZE +
|
|
177
|
+
const leadSeg = Buffer.allocUnsafe(CS.HEADER_SIZE + 4);
|
|
177
178
|
|
|
178
179
|
writePacketHeader(leadSeg, packet.cmd, packet.typ, packet.seq);
|
|
179
180
|
|
|
@@ -186,12 +187,12 @@ class PushMessageRequestEncoder implements IPacketEncoder {
|
|
|
186
187
|
bodyLen += Buffer.byteLength(p);
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
leadSeg.
|
|
190
|
+
leadSeg.writeUInt32LE(bodyLen, CS.HEADER_SIZE);
|
|
190
191
|
|
|
191
192
|
return [leadSeg, ...packet.ct];
|
|
192
193
|
}
|
|
193
194
|
|
|
194
|
-
leadSeg.
|
|
195
|
+
leadSeg.writeUInt32LE(Buffer.byteLength(packet.ct), CS.HEADER_SIZE);
|
|
195
196
|
|
|
196
197
|
return [leadSeg, packet.ct];
|
|
197
198
|
}
|
package/src/lib/shared/Errors.ts
CHANGED
|
@@ -73,9 +73,12 @@ const errorCtors = {
|
|
|
73
73
|
...defineProtocolError('stream_not_found'),
|
|
74
74
|
...defineProtocolError('stream_aborted'),
|
|
75
75
|
...defineProtocolError('stream_closed'),
|
|
76
|
+
...defineProtocolError('stream_index_mismatch'),
|
|
76
77
|
...defineProtocolError('system_busy'),
|
|
78
|
+
...defineProtocolError('server_internal_error'),
|
|
77
79
|
...defineProtocolError('timeout'),
|
|
78
80
|
...defineProtocolError('unknown'),
|
|
81
|
+
...defineProtocolError('unprocessable_error'),
|
|
79
82
|
|
|
80
83
|
['app_error']: class AppError extends TelevokeError {
|
|
81
84
|
|
|
@@ -197,13 +197,28 @@ export interface ITransporter {
|
|
|
197
197
|
getAllProperties(): Record<string, unknown>;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
export interface IBinaryReadStream extends Readable {
|
|
200
|
+
export interface IBinaryReadStream extends Omit<Readable, 'push'> {
|
|
201
201
|
|
|
202
202
|
/**
|
|
203
203
|
* The unique id of binary stream.
|
|
204
204
|
*/
|
|
205
205
|
readonly id: number;
|
|
206
206
|
|
|
207
|
+
/**
|
|
208
|
+
* The expected index of next chunk to read.
|
|
209
|
+
*/
|
|
210
|
+
readonly nextIndex: number;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Append a binary data chunk to this binary stream, with the given index.
|
|
214
|
+
*
|
|
215
|
+
* @param chunk The binary data chunk to be appended.
|
|
216
|
+
* @param index The index of the chunk in the stream.
|
|
217
|
+
*
|
|
218
|
+
* @returns Whether the chunk is appended successfully.
|
|
219
|
+
*/
|
|
220
|
+
append(chunk: Buffer[]): void;
|
|
221
|
+
|
|
207
222
|
/**
|
|
208
223
|
* Abort the binary stream before it's completed, and release the resource.
|
|
209
224
|
*
|
|
@@ -332,13 +347,17 @@ export interface IChannelBase<TEvents extends IDefaultEvents> extends IEventList
|
|
|
332
347
|
/**
|
|
333
348
|
* Send a binary data chunk to an opened binary stream on the remote side.
|
|
334
349
|
*
|
|
335
|
-
* > **NOTE
|
|
336
|
-
* >
|
|
350
|
+
* > **NOTE**
|
|
351
|
+
* > - It is not recommended to send a large binary data chunk at once. A large binary chunk should be split
|
|
352
|
+
* > into several small chunks, and then send them one by one, in order.
|
|
353
|
+
* > - Set the `index` to `false` to close the binary stream by aborting.
|
|
354
|
+
* > - Set the `chunk` to `null` to close the binary stream by finishing.
|
|
337
355
|
*
|
|
338
356
|
* @param id The `id` field in `IBinaryReadStream`, opened by remote side.
|
|
357
|
+
* @param index The index of this chunk in the stream.
|
|
339
358
|
* @param chunks The binary data chunks to be sent.
|
|
340
359
|
*/
|
|
341
|
-
sendBinaryChunk(id: number, chunk: Buffer | null): Promise<void>;
|
|
360
|
+
sendBinaryChunk(id: number, index: number | false, chunk: Buffer | null): Promise<void>;
|
|
342
361
|
|
|
343
362
|
/**
|
|
344
363
|
* Close the channel.
|
|
@@ -27,7 +27,7 @@ const encoder = new v1.TvEncoderV1();
|
|
|
27
27
|
const INVALID_REQUEST_RESPONSE = Buffer.from(encoder.encodeApiErrorResponse(
|
|
28
28
|
'null',
|
|
29
29
|
Shared.Encodings.v1.EResponseCode.MALFORMED_ARGUMENTS,
|
|
30
|
-
'INVALID REQUEST',
|
|
30
|
+
'"INVALID REQUEST"',
|
|
31
31
|
0
|
|
32
32
|
));
|
|
33
33
|
|
|
@@ -76,6 +76,19 @@ class LegacyHttpGateway extends EventEmitter implements dT.IGateway {
|
|
|
76
76
|
.setLegacyApiProcessor(this._onRequest);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
private _sendResponse(resp: Http.ServerResponse, data: string): void {
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
|
|
83
|
+
resp.setHeader('content-length', Buffer.byteLength(data));
|
|
84
|
+
resp.end(data);
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
|
|
88
|
+
this.emit('error', e);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
79
92
|
public get running(): boolean {
|
|
80
93
|
|
|
81
94
|
return this._listener.running;
|
|
@@ -159,7 +172,6 @@ class LegacyHttpGateway extends EventEmitter implements dT.IGateway {
|
|
|
159
172
|
refuseBadRequest(resp);
|
|
160
173
|
this.emit('error', new Shared.errors.invalid_packet({
|
|
161
174
|
reason: 'invalid_json',
|
|
162
|
-
data: buf,
|
|
163
175
|
}, e));
|
|
164
176
|
return;
|
|
165
177
|
}
|
|
@@ -169,7 +181,6 @@ class LegacyHttpGateway extends EventEmitter implements dT.IGateway {
|
|
|
169
181
|
refuseBadRequest(resp);
|
|
170
182
|
this.emit('error', new Shared.errors.invalid_packet({
|
|
171
183
|
reason: 'malformed_json',
|
|
172
|
-
data: input,
|
|
173
184
|
}));
|
|
174
185
|
return;
|
|
175
186
|
}
|
|
@@ -185,48 +196,54 @@ class LegacyHttpGateway extends EventEmitter implements dT.IGateway {
|
|
|
185
196
|
|
|
186
197
|
if (result instanceof Shared.errors.app_error) {
|
|
187
198
|
|
|
188
|
-
|
|
199
|
+
this._sendResponse(resp, encoder.encodeApiErrorResponse(
|
|
189
200
|
input.rid,
|
|
190
201
|
v1.EResponseCode.FAILURE,
|
|
191
202
|
result.message,
|
|
192
203
|
recvAt
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
resp.setHeader('content-length', Buffer.byteLength(data));
|
|
196
|
-
resp.end(data);
|
|
204
|
+
));
|
|
197
205
|
}
|
|
198
206
|
else if (result instanceof Shared.errors.api_not_found) {
|
|
199
207
|
|
|
200
|
-
|
|
208
|
+
this._sendResponse(resp, encoder.encodeApiErrorResponse(
|
|
201
209
|
input.rid,
|
|
202
210
|
v1.EResponseCode.API_NOT_FOUND,
|
|
203
211
|
'null',
|
|
204
212
|
recvAt
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
resp.setHeader('content-length', Buffer.byteLength(data));
|
|
208
|
-
resp.end(data);
|
|
213
|
+
));
|
|
209
214
|
}
|
|
210
215
|
else {
|
|
211
216
|
|
|
212
|
-
|
|
217
|
+
this._sendResponse(resp, encoder.encodeApiErrorResponse(
|
|
213
218
|
input.rid,
|
|
214
219
|
v1.EResponseCode.SYSTEM_ERROR,
|
|
215
220
|
'null',
|
|
216
221
|
recvAt
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
resp.setHeader('content-length', Buffer.byteLength(data));
|
|
220
|
-
resp.end(data);
|
|
222
|
+
));
|
|
221
223
|
}
|
|
222
224
|
|
|
223
225
|
return;
|
|
224
226
|
}
|
|
225
227
|
|
|
226
|
-
|
|
228
|
+
let data: string;
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
|
|
232
|
+
data = encoder.encodeApiOkResponse(input.rid, result ?? null, recvAt);
|
|
233
|
+
}
|
|
234
|
+
catch (e) {
|
|
235
|
+
|
|
236
|
+
data = encoder.encodeApiErrorResponse(
|
|
237
|
+
input.rid,
|
|
238
|
+
v1.EResponseCode.SYSTEM_ERROR,
|
|
239
|
+
'null',
|
|
240
|
+
recvAt
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
this.emit('error', new Shared.errors.unprocessable_error({ api: input.api }, e));
|
|
244
|
+
}
|
|
227
245
|
|
|
228
|
-
|
|
229
|
-
resp.end(data);
|
|
246
|
+
this._sendResponse(resp, data);
|
|
230
247
|
|
|
231
248
|
}, input.api, input.args, new LegacyHttpTransporter(req));
|
|
232
249
|
});
|