xshell 0.0.58 → 0.0.60

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/net.d.ts CHANGED
@@ -101,70 +101,75 @@ on_open, on_close, on_error, on_message }: {
101
101
  data: ArrayBuffer | string;
102
102
  }, websocket: WebSocket): any;
103
103
  }): Promise<WebSocket>;
104
+ /** 接收到消息后的处理函数
105
+ 返回值可以是:
106
+ - 数组: 会自动被封装为 { id: 相同, data: 返回值, done: true } 这样的消息并调用 websocket.send 将其发送
107
+ - void: 什么都不做
108
+ - 以上的 promise */
109
+ export declare type MessageHandler = (message: Message, websocket?: WebSocket) => void | any[] | Promise<void | any[]>;
104
110
  /** 二进制消息格式
105
111
  - json.length (小端序): 4 字节
106
112
  - json 数据
107
113
  - binary 数据
108
114
  */
109
- export interface Message<T extends any[] = any[]> {
110
- /** 本次 rpc id */
115
+ export interface Message<TData extends any[] = any[]> {
116
+ /** rpc id: 在 rpc 系统中认为是唯一的。用来在单个 websocket 连接上复用多个 rpc 请求。多个相同 id 的 message 组成一个请求流 */
111
117
  id?: number;
112
- /** rpc 发起方指定被调用的 function name, 多个相同 id, func 的 message 组成一个请求流 */
118
+ /** 只在 rpc 发起时指定被调用的 function name,发起时 rpc 时必传 */
113
119
  func?: string;
114
- /** 等待执行,但不要序列化返回 func 的执行结果 (message 中无 args) */
115
- ignore?: boolean;
116
- /** 不等待 func 执行,remote 收到后直接确认返回 (message 中 done = true) */
117
- async?: boolean;
118
- /** 这个数组里面要么是对应的 JS 参数,要么是 Uint8Array 参数对应的 binary length
119
- args 可以是:
120
+ /** 通过这个 flag 主动表明这是发往对方的最后一个 message, 对方可以销毁 handler
121
+ 并非强制,可以不说明,由双方的函数自己约定
122
+ */
123
+ done?: boolean;
124
+ /** 通知对方这里产生的错误,本质上类似 data 也是一种数据,并不代表 rpc 的结束,后续可能继续有 rpc message 交换 */
125
+ error?: Error;
126
+ /** data 是一个数组, 作为:
120
127
  - rpc 发起方调用 func 的参数,或者请求流 message 携带的数据
121
- - 作为结果或者响应流的 message 数据,传给请求发起方
128
+ - 结果或者响应流的数据,传给请求发起方
129
+
130
+ 里面是可序列化为 json 的 js 变量,或者是 Uint8Array
131
+ Uint8Array 的参数处理后被替换为 Uint8Array.byteLength, 并将下标记录在 bins 中
132
+
133
+ 注意: 数组中如果有 undefined 值,传输到对面会变成 null 值
122
134
  */
123
- args?: T;
124
- /** bins: 哪几个 arg Uint8Array 类型的,如: [0, 3] */
135
+ data?: TData;
136
+ /** bins: data 中哪些下标对应的原始值是 Uint8Array 类型的,如: [0, 3] */
125
137
  bins?: number[];
126
- /** 被调方执行 func 产生的错误 */
127
- error?: Error;
128
- /** 如果请求或者响应是一个流,通过这个 flag 表明是最后一个 message, 并且可以销毁 handler 了 */
129
- done?: boolean;
130
138
  }
131
- /** 通过创建 Remote 对象对 WebSocket RPC 进行抽象
139
+ /** 通过创建 remote 对象对 websocket rpc 进行抽象
132
140
  调用方使用 remote.call 进行调用
133
- 被调方在创建 Remote 对象时传入 funcs 注册处理函数,并使用 Remote.handle 方法处理 WebSocket message
134
- */
141
+ 被调方在创建 remote 对象时传入 funcs 注册处理函数,并使用 remote.handle 方法处理 websocket message
142
+ 未连接时自动连接,断开后自动重连 */
135
143
  export declare class Remote {
136
144
  url: string;
137
- websocket: WebSocket;
138
- id: number;
139
- /** 被调方的 message 处理器 */
140
- funcs: Record<string, (message: Message, websocket?: WebSocket) => void | Promise<void>>;
141
- /** 调用方发起的 rpc 对应响应的 message 处理器 */
142
- handlers: ((message: Message) => any)[];
145
+ /** 主动发起 websocket 连接的客户端 (构造函数传 url) 有这个属性 */
146
+ websocket?: WebSocket;
147
+ /** 通过 rpc message.func 被调用的 rpc 函数 */
148
+ funcs: Record<string, MessageHandler>;
149
+ /** map<id, message handler>: 通过 rpc message.id 找到对应的 handler, unary rpc 接收方不需要设置 handlers, 发送方需要 */
150
+ handlers: Map<number, MessageHandler>;
143
151
  print: boolean;
144
- /** 在未连接时或连接断开后,调用 call 是否自动连接到 remote */
145
- autoconnect: boolean;
146
152
  pconnect: Promise<any>;
147
153
  get connected(): boolean;
148
- static parse<T extends any[] = any[]>(array_buffer: ArrayBuffer): Message<T>;
149
- static pack({ id, func, ignore, async: _async, done, error, args: _args, }: Message): Uint8Array;
150
- constructor({ url, funcs, websocket, autoconnect }?: {
154
+ static parse<TData extends any[] = any[]>(array_buffer: ArrayBuffer): Message<TData>;
155
+ static pack({ id, func, data, done, error }: Message): Uint8Array;
156
+ constructor({ url, funcs, websocket }?: {
151
157
  url?: string;
152
158
  funcs?: Remote['funcs'];
153
159
  websocket?: WebSocket;
154
- autoconnect?: boolean;
155
160
  });
161
+ /** 幂等,保证 websocket 已连接,否则抛出异常,不需要手动调用,在其它方法中已默认自动重连 */
156
162
  connect(): Promise<void>;
157
163
  disconnect(): void;
158
- send(message: Message, websocket?: WebSocket): void;
159
- /** 调用 remote 中的 func, 中间消息及返回结果可由 handler 处理,处理 done message 之后的返回值作为 call 函数的返回值
160
- 如果为 unary rpc, 可以不传 handler, await call 之后可以得到响应 message 的 args
161
- */
162
- call<T extends any[] = any[]>(message: Message, handler?: (message: Message<T>) => any): Promise<T>;
163
- /** 处理接收到的 WebSocket message
164
- 1. 被调用方接收 message 并开始处理
165
- 2. 调用方处理 message 响应
166
- */
164
+ /** 接收 websocket 连接的 remote 端必传 websocket 参数;发起端选传,如果传了必须等于 this.websocket
165
+ 发送或连接出错时自动清理 message.id 对应的 handler */
166
+ send(message: Message, websocket?: WebSocket): Promise<void>;
167
+ /** 处理接收到的 websocket message 并解析, 根据 id dispatch 到对应的 handler 进行处理
168
+ 如果 message.done == true 则清理 handler
169
+ 如果 handler 返回了值,则包装为 message 发送 */
167
170
  handle(event: {
168
171
  data: ArrayBuffer;
169
172
  }, websocket: WebSocket): Promise<void>;
173
+ /** 调用 remote 中的 func, 只适用于最简单的一元 rpc (请求, 响应) */
174
+ call<TReturn extends any[] = any[]>(func: string, args?: any[]): Promise<TReturn>;
170
175
  }
package/net.js CHANGED
@@ -6,7 +6,7 @@ import iconv from 'iconv-lite';
6
6
  import qs from 'qs';
7
7
  import { Cookie, MemoryCookieStore } from 'tough-cookie';
8
8
  import './prototype.js';
9
- import { inspect, output_width, concat } from './utils.js';
9
+ import { inspect, output_width, concat, assert, genid } from './utils.js';
10
10
  export var MyProxy;
11
11
  (function (MyProxy) {
12
12
  MyProxy["socks5"] = "http://localhost:10080";
@@ -263,16 +263,22 @@ on_open, on_close, on_error, on_message }) {
263
263
  websocket.binaryType = 'arraybuffer';
264
264
  return new Promise((resolve, reject) => {
265
265
  websocket.addEventListener('open', async (event) => {
266
- console.log(`${websocket.url} opened`);
266
+ console.log(new Date().toLocaleTimeString() + ': ' +
267
+ 'websocket connected: ' +
268
+ (protocols ?
269
+ typeof protocols === 'string' ? `protocol: ${protocols}, url: ` : `protocol: [${protocols.join(', ')}], url: `
270
+ :
271
+ '') +
272
+ websocket.url);
267
273
  await on_open?.(event, websocket);
268
274
  resolve(websocket);
269
275
  });
270
276
  websocket.addEventListener('close', event => {
271
- console.log(`${websocket.url} closed with code = ${event.code}, reason = '${event.reason}'`);
277
+ console.log(`${new Date().toLocaleTimeString()}: websocket closed: url: ${websocket.url}, code: ${event.code}, reason: ${event.reason}`);
272
278
  on_close?.(event, websocket);
273
279
  });
274
280
  websocket.addEventListener('error', event => {
275
- const message = `${websocket.url} errored`;
281
+ const message = `${new Date().toLocaleTimeString()} websocket errored: ${websocket.url}`;
276
282
  on_error?.(event, websocket);
277
283
  reject(Object.assign(new Error(message), { event }));
278
284
  });
@@ -281,21 +287,19 @@ on_open, on_close, on_error, on_message }) {
281
287
  });
282
288
  });
283
289
  }
284
- /** 通过创建 Remote 对象对 WebSocket RPC 进行抽象
290
+ /** 通过创建 remote 对象对 websocket rpc 进行抽象
285
291
  调用方使用 remote.call 进行调用
286
- 被调方在创建 Remote 对象时传入 funcs 注册处理函数,并使用 Remote.handle 方法处理 WebSocket message
287
- */
292
+ 被调方在创建 remote 对象时传入 funcs 注册处理函数,并使用 remote.handle 方法处理 websocket message
293
+ 未连接时自动连接,断开后自动重连 */
288
294
  export class Remote {
289
295
  url;
296
+ /** 主动发起 websocket 连接的客户端 (构造函数传 url) 有这个属性 */
290
297
  websocket;
291
- id = 0;
292
- /** 被调方的 message 处理器 */
293
- funcs = {};
294
- /** 调用方发起的 rpc 对应响应的 message 处理器 */
295
- handlers = [];
298
+ /** 通过 rpc message.func 被调用的 rpc 函数 */
299
+ funcs;
300
+ /** map<id, message handler>: 通过 rpc message.id 找到对应的 handler, unary rpc 接收方不需要设置 handlers, 发送方需要 */
301
+ handlers = new Map();
296
302
  print = false;
297
- /** 在未连接时或连接断开后,调用 call 是否自动连接到 remote */
298
- autoconnect = true;
299
303
  pconnect;
300
304
  get connected() {
301
305
  return this.websocket?.readyState === WebSocket.OPEN;
@@ -308,154 +312,151 @@ export class Remote {
308
312
  let message = JSON.parse(decoder.decode(buf.subarray(4, offset)));
309
313
  if (message.bins) {
310
314
  const { bins } = message;
311
- let { args } = message;
315
+ let { data } = message;
312
316
  for (const ibin of bins) {
313
- const len_buf = args[ibin];
314
- args[ibin] = buf.subarray(offset, offset + len_buf);
317
+ const len_buf = data[ibin];
318
+ data[ibin] = buf.subarray(offset, offset + len_buf);
315
319
  offset += len_buf;
316
320
  }
317
321
  }
322
+ if (message.error)
323
+ message.error = Object.assign(new Error(), message.error);
318
324
  return message;
319
325
  }
320
- static pack({ id, func, ignore, async: _async, done, error, args: _args = [], }) {
321
- let args = [..._args];
326
+ static pack({ id, func, data = [], done, error }) {
327
+ let data_ = new Array(data.length);
322
328
  let bins = [];
323
329
  let bufs = [];
324
- for (let i = 0; i < args.length; i++) {
325
- const arg = args[i];
326
- if (arg instanceof Uint8Array) {
330
+ for (let i = 0; i < data.length; i++) {
331
+ const item = data[i];
332
+ if (item instanceof Uint8Array) {
327
333
  bins.push(i);
328
- bufs.push(arg);
329
- args[i] = arg.length;
334
+ bufs.push(item);
335
+ data_[i] = item.length;
330
336
  }
337
+ else
338
+ data_[i] = item;
331
339
  }
332
340
  const data_json = {
333
341
  id,
334
342
  ...func ? { func } : {},
335
- ...ignore ? { ignore } : {},
336
- ..._async ? { async: _async } : {},
337
343
  ...done ? { done } : {},
338
344
  ...error ? { error } : {},
339
- ...args.length ? { args } : {},
345
+ ...data_.length ? { data: data_ } : {},
340
346
  ...bins.length ? { bins } : {},
341
347
  };
342
348
  const str_json = encoder.encode(JSON.stringify(data_json));
343
349
  let dv = new DataView(new ArrayBuffer(4));
344
350
  dv.setUint32(0, str_json.length, true);
345
- return concat([
346
- dv,
347
- str_json,
348
- ...bufs
349
- ]);
351
+ return concat([dv, str_json, ...bufs]);
350
352
  }
351
- constructor({ url, funcs, websocket, autoconnect } = {}) {
353
+ constructor({ url, funcs = {}, websocket } = {}) {
352
354
  this.url = url;
353
- if (funcs)
354
- this.funcs = funcs;
355
+ this.funcs = funcs;
355
356
  this.websocket = websocket;
356
- if (typeof autoconnect !== 'undefined')
357
- this.autoconnect = autoconnect;
358
357
  }
358
+ /** 幂等,保证 websocket 已连接,否则抛出异常,不需要手动调用,在其它方法中已默认自动重连 */
359
359
  async connect() {
360
- this.websocket = await connect_websocket(this.url, {
361
- on_message: this.handle.bind(this),
362
- on_close: () => {
363
- this.id = 0;
364
- this.handlers = [];
365
- },
360
+ if (this.connected)
361
+ return;
362
+ const ptail = this.pconnect;
363
+ let resolve;
364
+ this.pconnect = new Promise((_resolve, _reject) => {
365
+ resolve = _resolve;
366
366
  });
367
+ await ptail;
368
+ try {
369
+ if (!this.connected)
370
+ // 保存的 rpc 状态在 this.handlers, 与 websocket 无关,因此即使断开重连也不影响 rpc 的运行,即
371
+ // 底层连接断开后自动重连对上层应该是无感知的,除非再次连接时失败
372
+ this.websocket = await connect_websocket(this.url, { on_message: this.handle.bind(this) });
373
+ }
374
+ finally {
375
+ resolve();
376
+ }
367
377
  }
368
378
  disconnect() {
369
379
  this.websocket?.close();
370
- this.id = 0;
371
- this.handlers = [];
372
- }
373
- send(message, websocket = this.websocket) {
374
- if (websocket?.readyState !== WebSocket.OPEN)
375
- throw new Error(`${websocket?.url || 'websocket'} 已断开,无法调用 remote.send`);
376
- if (!('id' in message))
377
- message.id = this.id;
378
- websocket.send(Remote.pack(message));
379
380
  }
380
- /** 调用 remote 中的 func, 中间消息及返回结果可由 handler 处理,处理 done message 之后的返回值作为 call 函数的返回值
381
- 如果为 unary rpc, 可以不传 handler, await call 之后可以得到响应 message 的 args
382
- */
383
- async call(message, handler) {
384
- if (!this.connected)
385
- if (this.autoconnect) {
386
- // 临界区:保证多个 call 并发时只连接一次
387
- const ptail = this.pconnect;
388
- let resolve;
389
- this.pconnect = new Promise((_resolve, _reject) => {
390
- resolve = _resolve;
391
- });
392
- try {
393
- await ptail;
394
- }
395
- catch { }
396
- // 临界区结束,只有一个 call 调用运行到这里,可以开始连接 WebSocket
397
- if (!this.connected) {
398
- if (this.websocket)
399
- console.log(`${this.url} 已断开,尝试自动重连`);
400
- else
401
- console.log(`${this.url} 未连接,尝试自动连接`);
402
- try {
403
- await this.connect();
404
- }
405
- finally {
406
- resolve();
407
- }
408
- }
381
+ /** 接收 websocket 连接的 remote 端必传 websocket 参数;发起端选传,如果传了必须等于 this.websocket
382
+ 发送或连接出错时自动清理 message.id 对应的 handler */
383
+ async send(message, websocket) {
384
+ try {
385
+ if (this.url) {
386
+ assert(!websocket || websocket === this.websocket);
387
+ await this.connect();
388
+ websocket = this.websocket;
409
389
  }
410
- else
411
- throw new Error(`${this.url} 未连接或已断开,无法调用 remote.call`);
412
- return new Promise((resolve, reject) => {
413
- this.handlers[this.id] = async (message) => {
414
- const { error, done } = message;
415
- if (error) {
416
- reject(Object.assign(new Error(), error));
417
- return;
418
- }
419
- const result = handler ?
420
- await handler(message)
421
- :
422
- message.args;
423
- if (done)
424
- resolve(result);
425
- };
426
- this.send(message);
427
- this.id++;
428
- });
390
+ else {
391
+ assert(websocket);
392
+ assert(!websocket.url, `The websocket received by the server should have no url attribute, websocket.url: ${websocket.url}`);
393
+ if (websocket.readyState !== WebSocket.OPEN)
394
+ throw new Error(`remote.send(): websocket client disconnected`);
395
+ }
396
+ if (!message.id)
397
+ message.id = genid();
398
+ websocket.send(Remote.pack(message));
399
+ }
400
+ catch (error) {
401
+ if (message.id)
402
+ this.handlers.delete(message.id);
403
+ throw error;
404
+ }
429
405
  }
430
- /** 处理接收到的 WebSocket message
431
- 1. 被调用方接收 message 并开始处理
432
- 2. 调用方处理 message 响应
433
- */
406
+ /** 处理接收到的 websocket message 并解析, 根据 id dispatch 到对应的 handler 进行处理
407
+ 如果 message.done == true 则清理 handler
408
+ 如果 handler 返回了值,则包装为 message 发送 */
434
409
  async handle(event, websocket) {
435
410
  const message = Remote.parse(event.data);
436
- const { func, id, done } = message;
411
+ const { id, func, done } = message;
437
412
  if (this.print)
438
413
  console.log(message);
439
- if (func) // 作为被调方
440
- try {
441
- const handler = this.funcs[func];
442
- if (!handler)
443
- throw new Error(`找不到 rpc handler for '${func}'`);
444
- await handler(message, websocket);
445
- }
446
- catch (error) {
447
- this.send({
448
- id,
449
- error,
450
- done: true
451
- }, websocket);
452
- throw error;
453
- }
454
- else { // 作为发起方
455
- this.handlers[id](message);
414
+ let handler;
415
+ if (func)
416
+ handler = this.funcs[func];
417
+ else {
418
+ handler = this.handlers.get(id);
456
419
  if (done)
457
- this.handlers[id] = null;
420
+ this.handlers.delete(id);
458
421
  }
422
+ try {
423
+ if (handler) {
424
+ const data = await handler(message, websocket);
425
+ if (data)
426
+ await this.send({ id, data }, websocket);
427
+ }
428
+ else if (message.error)
429
+ throw message.error;
430
+ else
431
+ throw new Error(`rpc handler not found: ${func ? `func: ${func.quote()}` : `id: ${id}`}`);
432
+ }
433
+ catch (error) {
434
+ // handle 出错并不意味着 rpc 一定会结束,可能 error 是运行中的正常数据,所以不能清理 handler
435
+ if (websocket.readyState === WebSocket.OPEN &&
436
+ !message.error // 防止无限循环往对方发送 error, 只有在对方无错误时才可以发送
437
+ )
438
+ try {
439
+ await this.send({ id, error, /* 不能设置 done 清理对面 handler, 理由同上 */ }, websocket);
440
+ }
441
+ catch { }
442
+ // 再往上层抛出错误没有意义了,上层调用栈是 websocket.on('message') 之类的
443
+ console.log(error);
444
+ }
445
+ }
446
+ /** 调用 remote 中的 func, 只适用于最简单的一元 rpc (请求, 响应) */
447
+ async call(func, args) {
448
+ return new Promise(async (resolve, reject) => {
449
+ const id = genid();
450
+ this.handlers.set(id, (message) => {
451
+ const { error, data } = message;
452
+ if (error)
453
+ reject(error);
454
+ else
455
+ resolve(data);
456
+ this.handlers.delete(id);
457
+ });
458
+ await this.send({ id, func, data: args }); // 不需要 done: true, 因为对面的 remote.handlers 中不会有这个 id 的 handler
459
+ });
459
460
  }
460
461
  }
461
462
  //# sourceMappingURL=net.js.map