xshell 0.0.57 → 0.0.59

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
@@ -81,6 +81,12 @@ export declare function rpc(func: string, args?: any[], { url, async: _async, ig
81
81
  async?: boolean;
82
82
  ignore?: boolean;
83
83
  }): Promise<any>;
84
+ /** 连接 websocket url, 设置各种事件监听器
85
+ - url
86
+ - options:
87
+ - on_message: 根据 websocket frame 的 opcode 不同 (text frame 或 binary frame) event 中的 data 对应为 ArrayBuffer 或者 string
88
+ https://datatracker.ietf.org/doc/html/rfc6455#section-5.2
89
+ */
84
90
  export declare function connect_websocket(url: string | URL, { protocols, max_payload, // 8 GB
85
91
  on_open, on_close, on_error, on_message }: {
86
92
  protocols?: string | string[];
@@ -92,73 +98,80 @@ on_open, on_close, on_error, on_message }: {
92
98
  }, websocket: WebSocket): any;
93
99
  on_error?(event: any, websocket: WebSocket): any;
94
100
  on_message(event: {
95
- data: ArrayBuffer;
101
+ data: ArrayBuffer | string;
96
102
  }, websocket: WebSocket): any;
97
103
  }): Promise<WebSocket>;
104
+ /** 接收到消息后的处理函数
105
+ 返回值可以是:
106
+ - 数组: 会自动被封装为 { id: 相同, data: 返回值, done: true } 这样的消息并调用 websocket.send 将其发送
107
+ - void: 什么都不做
108
+ - 以上的 promise
109
+ */
110
+ export declare type MessageHandler = (message: Message, websocket?: WebSocket) => void | any[] | Promise<void | any[]>;
98
111
  /** 二进制消息格式
99
112
  - json.length (小端序): 4 字节
100
113
  - json 数据
101
114
  - binary 数据
102
115
  */
103
- export interface Message<T extends any[] = any[]> {
104
- /** 本次 rpc id */
116
+ export interface Message<TData extends any[] = any[]> {
117
+ /** rpc id: 在 rpc 系统中认为是唯一的。用来在单个 websocket 连接上复用多个 rpc 请求。多个相同 id 的 message 组成一个请求流 */
105
118
  id?: number;
106
- /** rpc 发起方指定被调用的 function name, 多个相同 id, func 的 message 组成一个请求流 */
119
+ /** 只在 rpc 发起时指定被调用的 function name,发起时 rpc 时必传 */
107
120
  func?: string;
108
- /** 等待执行,但不要序列化返回 func 的执行结果 (message 中无 args) */
109
- ignore?: boolean;
110
- /** 不等待 func 执行,remote 收到后直接确认返回 (message 中 done = true) */
111
- async?: boolean;
112
- /** 这个数组里面要么是对应的 JS 参数,要么是 Uint8Array 参数对应的 binary length
113
- args 可以是:
121
+ /** 通过这个 flag 主动表明这是发往对方的最后一个 message, 对方可以销毁 handler
122
+ 并非强制,可以不说明,由双方的函数自己约定
123
+ */
124
+ done?: boolean;
125
+ /** 通知对方这里产生的错误 */
126
+ error?: Error;
127
+ /** data 是一个数组, 作为:
114
128
  - rpc 发起方调用 func 的参数,或者请求流 message 携带的数据
115
- - 作为结果或者响应流的 message 数据,传给请求发起方
129
+ - 结果或者响应流的数据,传给请求发起方
130
+
131
+ 里面是可序列化为 json 的 js 变量,或者是 Uint8Array
132
+ Uint8Array 的参数处理后被替换为 Uint8Array.byteLength, 并将下标记录在 bins 中
116
133
  */
117
- args?: T;
118
- /** bins: 哪几个 arg Uint8Array 类型的,如: [0, 3] */
134
+ data?: TData;
135
+ /** bins: data 中哪些下标对应的原始值是 Uint8Array 类型的,如: [0, 3] */
119
136
  bins?: number[];
120
- /** 被调方执行 func 产生的错误 */
121
- error?: Error;
122
- /** 如果请求或者响应是一个流,通过这个 flag 表明是最后一个 message, 并且可以销毁 handler 了 */
123
- done?: boolean;
124
137
  }
125
- /** 通过创建 Remote 对象对 WebSocket RPC 进行抽象
138
+ /** 通过创建 remote 对象对 websocket rpc 进行抽象
126
139
  调用方使用 remote.call 进行调用
127
- 被调方在创建 Remote 对象时传入 funcs 注册处理函数,并使用 Remote.handle 方法处理 WebSocket message
140
+ 被调方在创建 remote 对象时传入 funcs 注册处理函数,并使用 remote.handle 方法处理 websocket message
141
+ 未连接时自动连接,断开后自动重连
128
142
  */
129
143
  export declare class Remote {
130
144
  url: string;
131
- websocket: WebSocket;
132
- id: number;
133
- /** 被调方的 message 处理器 */
134
- funcs: Record<string, (message: Message, websocket?: WebSocket) => void | Promise<void>>;
135
- /** 调用方发起的 rpc 对应响应的 message 处理器 */
136
- 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>;
137
151
  print: boolean;
138
- /** 在未连接时或连接断开后,调用 call 是否自动连接到 remote */
139
- autoconnect: boolean;
140
152
  pconnect: Promise<any>;
141
153
  get connected(): boolean;
142
- static parse<T extends any[] = any[]>(array_buffer: ArrayBuffer): Message<T>;
143
- static pack({ id, func, ignore, async: _async, done, error, args: _args, }: Message): Uint8Array;
144
- 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 }?: {
145
157
  url?: string;
146
158
  funcs?: Remote['funcs'];
147
159
  websocket?: WebSocket;
148
- autoconnect?: boolean;
149
160
  });
161
+ /** 幂等,保证 websocket 已连接,否则抛出异常,不需要手动调用,在其它方法中已默认自动重连 */
150
162
  connect(): Promise<void>;
151
163
  disconnect(): void;
152
- send(message: Message, websocket?: WebSocket): void;
153
- /** 调用 remote 中的 func, 中间消息及返回结果可由 handler 处理,处理 done message 之后的返回值作为 call 函数的返回值
154
- 如果为 unary rpc, 可以不传 handler, await call 之后可以得到响应 message 的 args
164
+ /** 接收 websocket 连接的 remote 端必传 websocket 参数;发起端选传,如果传了必须等于 this.websocket
165
+ 发送或连接出错时自动清理 message.id 对应的 handler
155
166
  */
156
- call<T extends any[] = any[]>(message: Message, handler?: (message: Message<T>) => any): Promise<T>;
157
- /** 处理接收到的 WebSocket message
158
- 1. 被调用方接收 message 并开始处理
159
- 2. 调用方处理 message 响应
167
+ send(message: Message, websocket?: WebSocket): Promise<void>;
168
+ /** 处理接收到的 websocket message 并解析, 根据 id dispatch 到对应的 handler 进行处理
169
+ 如果 message.done == true 则清理 handler
170
+ 如果 handler 返回了值,则包装为 message 发送
160
171
  */
161
172
  handle(event: {
162
173
  data: ArrayBuffer;
163
174
  }, websocket: WebSocket): Promise<void>;
175
+ /** 调用 remote 中的 func, 只适用于最简单的一元 rpc (请求, 响应) */
176
+ call<TReturn extends any[] = any[]>(func: string, args?: any[]): Promise<TReturn>;
164
177
  }
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";
@@ -84,7 +84,7 @@ export async function request(url, { queries, body, type = 'application/json', p
84
84
  resolveWithFullResponse: true,
85
85
  headers: {
86
86
  'accept-language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,ja-JP;q=0.6,ja;q=0.5',
87
- 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36',
87
+ 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36',
88
88
  ...body ? { 'content-type': type } : {},
89
89
  ...cookies ? {
90
90
  cookie: Object.entries(cookies)
@@ -247,23 +247,38 @@ export async function rpc(func, args, { url = 'http://localhost:8421/api/rpc', a
247
247
  }
248
248
  let decoder = new TextDecoder();
249
249
  let encoder = new TextEncoder();
250
+ /** 连接 websocket url, 设置各种事件监听器
251
+ - url
252
+ - options:
253
+ - on_message: 根据 websocket frame 的 opcode 不同 (text frame 或 binary frame) event 中的 data 对应为 ArrayBuffer 或者 string
254
+ https://datatracker.ietf.org/doc/html/rfc6455#section-5.2
255
+ */
250
256
  export async function connect_websocket(url, { protocols, max_payload = 2 ** 33, // 8 GB
251
257
  on_open, on_close, on_error, on_message }) {
252
- let websocket = new WebSocket(url, protocols, { maxPayload: max_payload });
258
+ let websocket = new WebSocket(url, protocols, {
259
+ maxPayload: max_payload,
260
+ skipUTF8Validation: true
261
+ });
253
262
  // https://stackoverflow.com/questions/11821096/what-is-the-difference-between-an-arraybuffer-and-a-blob/39951543
254
263
  websocket.binaryType = 'arraybuffer';
255
264
  return new Promise((resolve, reject) => {
256
265
  websocket.addEventListener('open', async (event) => {
257
- 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);
258
273
  await on_open?.(event, websocket);
259
274
  resolve(websocket);
260
275
  });
261
276
  websocket.addEventListener('close', event => {
262
- 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}`);
263
278
  on_close?.(event, websocket);
264
279
  });
265
280
  websocket.addEventListener('error', event => {
266
- const message = `${websocket.url} errored`;
281
+ const message = `${new Date().toLocaleTimeString()} websocket errored: ${websocket.url}`;
267
282
  on_error?.(event, websocket);
268
283
  reject(Object.assign(new Error(message), { event }));
269
284
  });
@@ -272,21 +287,20 @@ on_open, on_close, on_error, on_message }) {
272
287
  });
273
288
  });
274
289
  }
275
- /** 通过创建 Remote 对象对 WebSocket RPC 进行抽象
290
+ /** 通过创建 remote 对象对 websocket rpc 进行抽象
276
291
  调用方使用 remote.call 进行调用
277
- 被调方在创建 Remote 对象时传入 funcs 注册处理函数,并使用 Remote.handle 方法处理 WebSocket message
292
+ 被调方在创建 remote 对象时传入 funcs 注册处理函数,并使用 remote.handle 方法处理 websocket message
293
+ 未连接时自动连接,断开后自动重连
278
294
  */
279
295
  export class Remote {
280
296
  url;
297
+ /** 主动发起 websocket 连接的客户端 (构造函数传 url) 有这个属性 */
281
298
  websocket;
282
- id = 0;
283
- /** 被调方的 message 处理器 */
284
- funcs = {};
285
- /** 调用方发起的 rpc 对应响应的 message 处理器 */
286
- handlers = [];
299
+ /** 通过 rpc message.func 被调用的 rpc 函数 */
300
+ funcs;
301
+ /** map<id, message handler>: 通过 rpc message.id 找到对应的 handler, unary rpc 接收方不需要设置 handlers, 发送方需要 */
302
+ handlers = new Map();
287
303
  print = false;
288
- /** 在未连接时或连接断开后,调用 call 是否自动连接到 remote */
289
- autoconnect = true;
290
304
  pconnect;
291
305
  get connected() {
292
306
  return this.websocket?.readyState === WebSocket.OPEN;
@@ -299,154 +313,149 @@ export class Remote {
299
313
  let message = JSON.parse(decoder.decode(buf.subarray(4, offset)));
300
314
  if (message.bins) {
301
315
  const { bins } = message;
302
- let { args } = message;
316
+ let { data } = message;
303
317
  for (const ibin of bins) {
304
- const len_buf = args[ibin];
305
- args[ibin] = buf.subarray(offset, offset + len_buf);
318
+ const len_buf = data[ibin];
319
+ data[ibin] = buf.subarray(offset, offset + len_buf);
306
320
  offset += len_buf;
307
321
  }
308
322
  }
309
323
  return message;
310
324
  }
311
- static pack({ id, func, ignore, async: _async, done, error, args: _args = [], }) {
312
- let args = [..._args];
325
+ static pack({ id, func, data = [], done, error }) {
326
+ let data_ = new Array(data.length);
313
327
  let bins = [];
314
328
  let bufs = [];
315
- for (let i = 0; i < args.length; i++) {
316
- const arg = args[i];
317
- if (arg instanceof Uint8Array) {
329
+ for (let i = 0; i < data.length; i++) {
330
+ const item = data[i];
331
+ if (item instanceof Uint8Array) {
318
332
  bins.push(i);
319
- bufs.push(arg);
320
- args[i] = arg.length;
333
+ bufs.push(item);
334
+ data_[i] = item.length;
321
335
  }
336
+ else
337
+ data_[i] = item;
322
338
  }
323
339
  const data_json = {
324
340
  id,
325
341
  ...func ? { func } : {},
326
- ...ignore ? { ignore } : {},
327
- ..._async ? { async: _async } : {},
328
342
  ...done ? { done } : {},
329
343
  ...error ? { error } : {},
330
- ...args.length ? { args } : {},
344
+ ...data_.length ? { data: data_ } : {},
331
345
  ...bins.length ? { bins } : {},
332
346
  };
333
347
  const str_json = encoder.encode(JSON.stringify(data_json));
334
348
  let dv = new DataView(new ArrayBuffer(4));
335
349
  dv.setUint32(0, str_json.length, true);
336
- return concat([
337
- dv,
338
- str_json,
339
- ...bufs
340
- ]);
350
+ return concat([dv, str_json, ...bufs]);
341
351
  }
342
- constructor({ url, funcs, websocket, autoconnect } = {}) {
352
+ constructor({ url, funcs = {}, websocket } = {}) {
343
353
  this.url = url;
344
- if (funcs)
345
- this.funcs = funcs;
354
+ this.funcs = funcs;
346
355
  this.websocket = websocket;
347
- if (typeof autoconnect !== 'undefined')
348
- this.autoconnect = autoconnect;
349
356
  }
357
+ /** 幂等,保证 websocket 已连接,否则抛出异常,不需要手动调用,在其它方法中已默认自动重连 */
350
358
  async connect() {
351
- this.websocket = await connect_websocket(this.url, {
352
- on_message: this.handle.bind(this),
353
- on_close: () => {
354
- this.id = 0;
355
- this.handlers = [];
356
- },
359
+ if (this.connected)
360
+ return;
361
+ const ptail = this.pconnect;
362
+ let resolve;
363
+ this.pconnect = new Promise((_resolve, _reject) => {
364
+ resolve = _resolve;
357
365
  });
366
+ await ptail;
367
+ try {
368
+ if (!this.connected)
369
+ // 保存的 rpc 状态在 this.handlers, 与 websocket 无关,因此即使断开重连也不影响 rpc 的运行,即
370
+ // 底层连接断开后自动重连对上层应该是无感知的,除非再次连接时失败
371
+ this.websocket = await connect_websocket(this.url, { on_message: this.handle.bind(this) });
372
+ }
373
+ finally {
374
+ resolve();
375
+ }
358
376
  }
359
377
  disconnect() {
360
378
  this.websocket?.close();
361
- this.id = 0;
362
- this.handlers = [];
363
- }
364
- send(message, websocket = this.websocket) {
365
- if (websocket?.readyState !== WebSocket.OPEN)
366
- throw new Error(`${websocket?.url || 'websocket'} 已断开,无法调用 remote.send`);
367
- if (!('id' in message))
368
- message.id = this.id;
369
- websocket.send(Remote.pack(message));
370
379
  }
371
- /** 调用 remote 中的 func, 中间消息及返回结果可由 handler 处理,处理 done message 之后的返回值作为 call 函数的返回值
372
- 如果为 unary rpc, 可以不传 handler, await call 之后可以得到响应 message 的 args
380
+ /** 接收 websocket 连接的 remote 端必传 websocket 参数;发起端选传,如果传了必须等于 this.websocket
381
+ 发送或连接出错时自动清理 message.id 对应的 handler
373
382
  */
374
- async call(message, handler) {
375
- if (!this.connected)
376
- if (this.autoconnect) {
377
- // 临界区:保证多个 call 并发时只连接一次
378
- const ptail = this.pconnect;
379
- let resolve;
380
- this.pconnect = new Promise((_resolve, _reject) => {
381
- resolve = _resolve;
382
- });
383
- try {
384
- await ptail;
385
- }
386
- catch { }
387
- // 临界区结束,只有一个 call 调用运行到这里,可以开始连接 WebSocket
388
- if (!this.connected) {
389
- if (this.websocket)
390
- console.log(`${this.url} 已断开,尝试自动重连`);
391
- else
392
- console.log(`${this.url} 未连接,尝试自动连接`);
393
- try {
394
- await this.connect();
395
- }
396
- finally {
397
- resolve();
398
- }
399
- }
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;
400
389
  }
401
- else
402
- throw new Error(`${this.url} 未连接或已断开,无法调用 remote.call`);
403
- return new Promise((resolve, reject) => {
404
- this.handlers[this.id] = async (message) => {
405
- const { error, done } = message;
406
- if (error) {
407
- reject(Object.assign(new Error(), error));
408
- return;
409
- }
410
- const result = handler ?
411
- await handler(message)
412
- :
413
- message.args;
414
- if (done)
415
- resolve(result);
416
- };
417
- this.send(message);
418
- this.id++;
419
- });
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
+ }
420
405
  }
421
- /** 处理接收到的 WebSocket message
422
- 1. 被调用方接收 message 并开始处理
423
- 2. 调用方处理 message 响应
406
+ /** 处理接收到的 websocket message 并解析, 根据 id dispatch 到对应的 handler 进行处理
407
+ 如果 message.done == true 则清理 handler
408
+ 如果 handler 返回了值,则包装为 message 发送
424
409
  */
425
410
  async handle(event, websocket) {
426
411
  const message = Remote.parse(event.data);
427
- const { func, id, done } = message;
412
+ const { id, func, done } = message;
428
413
  if (this.print)
429
414
  console.log(message);
430
- if (func) // 作为被调方
431
- try {
432
- const handler = this.funcs[func];
433
- if (!handler)
434
- throw new Error(`找不到 rpc handler for '${func}'`);
435
- await handler(message, websocket);
436
- }
437
- catch (error) {
438
- this.send({
439
- id,
440
- error,
441
- done: true
442
- }, websocket);
443
- throw error;
444
- }
445
- else { // 作为发起方
446
- this.handlers[id](message);
415
+ let handler;
416
+ if (func)
417
+ handler = this.funcs[func];
418
+ else {
419
+ handler = this.handlers.get(id);
447
420
  if (done)
448
- this.handlers[id] = null;
421
+ this.handlers.delete(id);
422
+ }
423
+ try {
424
+ if (handler) {
425
+ const data = await handler(message, websocket);
426
+ if (data)
427
+ await this.send({ id, data }, websocket);
428
+ }
429
+ else if (message.error)
430
+ throw Object.assign(new Error(), message.error);
431
+ else
432
+ throw new Error(`rpc handler not found: ${func ? `func: ${func.quote()}` : `id: ${id}`}`);
449
433
  }
434
+ catch (error) {
435
+ if (websocket.readyState === WebSocket.OPEN &&
436
+ !message.error // 防止无限循环往对方发送 error, 只有在对方无错误时才可以发送
437
+ )
438
+ try {
439
+ await this.send({ id, error, done: true }, websocket);
440
+ }
441
+ catch { }
442
+ throw error;
443
+ }
444
+ }
445
+ /** 调用 remote 中的 func, 只适用于最简单的一元 rpc (请求, 响应) */
446
+ async call(func, args) {
447
+ return new Promise(async (resolve, reject) => {
448
+ const id = genid();
449
+ this.handlers.set(id, (message) => {
450
+ const { error, data } = message;
451
+ if (error)
452
+ reject(Object.assign(new Error(), error));
453
+ else
454
+ resolve(data);
455
+ this.handlers.delete(id);
456
+ });
457
+ await this.send({ id, func, data: args }); // 不需要 done: true, 因为对面的 remote.handlers 中不会有这个 id 的 handler
458
+ });
450
459
  }
451
460
  }
452
461
  //# sourceMappingURL=net.js.map