core-3nweb-client-lib 0.48.5 → 0.48.6

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.
@@ -1,7 +1,7 @@
1
1
  import { MailRecipient } from '../../../lib-client/asmail/recipient';
2
2
  import { Observable } from 'rxjs';
3
3
  import { LogError } from '../../../lib-client/logging/log-to-file';
4
- import { ConnectionStatus } from '../../../lib-common/ipc/ws-ipc';
4
+ import type { ConnectionStatus } from '../../../lib-client/request-utils';
5
5
  type IncomingMessage = web3n.asmail.IncomingMessage;
6
6
  type InboxEventType = web3n.asmail.InboxEventType;
7
7
  type Observer<T> = web3n.Observer<T>;
@@ -57,9 +57,9 @@ class InboxEvents {
57
57
  Object.seal(this);
58
58
  }
59
59
  makeProc() {
60
- const proc$ = (0, rxjs_1.from)(this.msgReceiver.openEventSource().then(({ client, heartbeat }) => {
60
+ const proc$ = (0, rxjs_1.from)(this.msgReceiver.openEventSource().then(({ client, watchHeartbeat }) => {
61
61
  const channel = retrieval_1.msgRecievedCompletely.EVENT_NAME;
62
- heartbeat.subscribe({
62
+ watchHeartbeat({
63
63
  next: ev => {
64
64
  this.connectionEvents.next(toInboxConnectionStatus(ev));
65
65
  if (ev.type === 'heartbeat') {
@@ -2,8 +2,8 @@ import { Observable } from 'rxjs';
2
2
  import { StorageOwner } from '../../../lib-client/3nstorage/storage-owner';
3
3
  import { ObjFiles } from './obj-files';
4
4
  import { Storage } from '../../../lib-client/xsp-fs/common';
5
- import { LogError } from '../../../lib-client/logging/log-to-file';
6
- import { ConnectionStatus } from '../../../lib-common/ipc/ws-ipc';
5
+ import type { LogError } from '../../../lib-client/logging/log-to-file';
6
+ import type { ConnectionStatus } from '../../../lib-client/request-utils';
7
7
  export interface StorageConnectionStatus extends ConnectionStatus {
8
8
  service: 'storage';
9
9
  }
@@ -44,9 +44,9 @@ class RemoteEvents {
44
44
  Object.seal(this);
45
45
  }
46
46
  makeProc() {
47
- return (0, rxjs_1.from)(this.remoteStorage.openEventSource().then(({ client, heartbeat }) => {
47
+ return (0, rxjs_1.from)(this.remoteStorage.openEventSource().then(({ client, watchHeartbeat }) => {
48
48
  this.remoteStorage.connectedState.setState();
49
- heartbeat.subscribe({
49
+ watchHeartbeat({
50
50
  next: ev => {
51
51
  this.connectionEvents.next(toStorageConnectionStatus(ev));
52
52
  if (ev.type === 'heartbeat') {
@@ -1,11 +1,12 @@
1
- import { NetClient } from '../request-utils';
1
+ import { ConnectionStatus, NetClient } from '../request-utils';
2
2
  import * as api from '../../lib-common/service-api/3nstorage/owner';
3
3
  import { ServiceUser, IGetMailerIdSigner } from '../user-with-mid-session';
4
4
  import * as keyGen from '../key-derivation';
5
- import { makeSubscriber } from '../../lib-common/ipc/ws-ipc';
5
+ import { SubscribingClient } from '../../lib-common/ipc/ws-ipc';
6
6
  import { ObjId } from '../xsp-fs/common';
7
7
  export type FirstSaveReqOpts = api.PutObjFirstQueryOpts;
8
8
  export type FollowingSaveReqOpts = api.PutObjSecondQueryOpts;
9
+ type Observer<T> = web3n.Observer<T>;
9
10
  export declare class StorageOwner extends ServiceUser {
10
11
  maxChunkSize: number | undefined;
11
12
  private constructor();
@@ -86,5 +87,9 @@ export declare class StorageOwner extends ServiceUser {
86
87
  * @return a promise, resolvable, when an object is deleted.
87
88
  */
88
89
  deleteObj(objId: string): Promise<void>;
89
- openEventSource(): Promise<ReturnType<typeof makeSubscriber>>;
90
+ openEventSource(): Promise<{
91
+ client: SubscribingClient;
92
+ watchHeartbeat: (obs: Observer<ConnectionStatus>) => (() => void);
93
+ }>;
90
94
  }
95
+ export {};
@@ -23,9 +23,9 @@ const user_with_mid_session_1 = require("../user-with-mid-session");
23
23
  const service_locator_1 = require("../service-locator");
24
24
  const keyGen = require("../key-derivation");
25
25
  const exceptions_1 = require("../xsp-fs/exceptions");
26
- const ws_ipc_1 = require("../../lib-common/ipc/ws-ipc");
27
26
  const assert_1 = require("../../lib-common/assert");
28
27
  const http_1 = require("../../lib-common/exceptions/http");
28
+ const generic_ipc_1 = require("../../lib-common/ipc/generic-ipc");
29
29
  const storageAccessParams = {
30
30
  login: api.midLogin.MID_URL_PART,
31
31
  logout: api.closeSession.URL_END,
@@ -336,12 +336,14 @@ class StorageOwner extends user_with_mid_session_1.ServiceUser {
336
336
  }
337
337
  }
338
338
  async openEventSource() {
339
- const rep = await this.openWS(api.wsEventChannel.URL_END);
339
+ const { rep, req } = await this.openEventSrc(api.wsEventChannel.URL_END);
340
340
  if (rep.status === api.wsEventChannel.SC.ok) {
341
- return (0, ws_ipc_1.makeSubscriber)(rep.data, undefined);
341
+ const { comm, watch: watchHeartbeat } = rep.data;
342
+ const client = (0, generic_ipc_1.makeSubscribingClient)(undefined, comm);
343
+ return { client, watchHeartbeat };
342
344
  }
343
345
  else {
344
- throw (0, http_1.makeUnexpectedStatusHTTPException)(rep);
346
+ throw (0, http_1.makeUnexpectedStatusHTTPException)({ url: req.url, method: req.method, status: rep.status });
345
347
  }
346
348
  }
347
349
  }
@@ -1,8 +1,9 @@
1
- import { NetClient } from '../request-utils';
1
+ import { ConnectionStatus, NetClient } from '../request-utils';
2
2
  import * as api from '../../lib-common/service-api/asmail/retrieval';
3
3
  import { ServiceUser, IGetMailerIdSigner } from '../user-with-mid-session';
4
- import { makeSubscriber } from '../../lib-common/ipc/ws-ipc';
4
+ import { SubscribingClient } from '../../lib-common/ipc/ws-ipc';
5
5
  type InboxException = web3n.asmail.InboxException;
6
+ type Observer<T> = web3n.Observer<T>;
6
7
  export declare function makeMsgNotFoundException(msgId: string): InboxException;
7
8
  export declare function makeObjNotFoundException(msgId: string, objId: string): InboxException;
8
9
  export declare function makeMsgIsBrokenException(msgId: string): InboxException;
@@ -36,6 +37,9 @@ export declare class MailRecipient extends ServiceUser {
36
37
  */
37
38
  getObjSegs(msgId: string, objId: string, start: number, end: number): Promise<Uint8Array>;
38
39
  removeMsg(msgId: string): Promise<void>;
39
- openEventSource(): Promise<ReturnType<typeof makeSubscriber>>;
40
+ openEventSource(): Promise<{
41
+ client: SubscribingClient;
42
+ watchHeartbeat: (obs: Observer<ConnectionStatus>) => (() => void);
43
+ }>;
40
44
  }
41
45
  export {};
@@ -24,9 +24,9 @@ const request_utils_1 = require("../request-utils");
24
24
  const api = require("../../lib-common/service-api/asmail/retrieval");
25
25
  const user_with_mid_session_1 = require("../user-with-mid-session");
26
26
  const service_locator_1 = require("../service-locator");
27
- const ws_ipc_1 = require("../../lib-common/ipc/ws-ipc");
28
27
  const http_1 = require("../../lib-common/exceptions/http");
29
28
  const runtime_1 = require("../../lib-common/exceptions/runtime");
29
+ const generic_ipc_1 = require("../../lib-common/ipc/generic-ipc");
30
30
  function makeMsgNotFoundException(msgId) {
31
31
  const exc = {
32
32
  runtimeException: true,
@@ -201,12 +201,14 @@ class MailRecipient extends user_with_mid_session_1.ServiceUser {
201
201
  }
202
202
  }
203
203
  async openEventSource() {
204
- const rep = await this.openWS(api.wsEventChannel.URL_END);
204
+ const { rep, req } = await this.openEventSrc(api.wsEventChannel.URL_END);
205
205
  if (rep.status === api.wsEventChannel.SC.ok) {
206
- return (0, ws_ipc_1.makeSubscriber)(rep.data, undefined);
206
+ const { comm, watch: watchHeartbeat } = rep.data;
207
+ const client = (0, generic_ipc_1.makeSubscribingClient)(undefined, comm);
208
+ return { client, watchHeartbeat };
207
209
  }
208
210
  else {
209
- throw (0, http_1.makeUnexpectedStatusHTTPException)(rep);
211
+ throw (0, http_1.makeUnexpectedStatusHTTPException)({ url: req.url, method: req.method, status: rep.status });
210
212
  }
211
213
  }
212
214
  }
@@ -1,6 +1,6 @@
1
1
  import type * as https from 'https';
2
- import type * as WebSocket from 'ws';
3
2
  import type { ClientRequest, OutgoingHttpHeaders } from 'http';
3
+ import { Envelope, RawDuplex } from '../lib-common/ipc/generic-ipc';
4
4
  export declare const SESSION_ID_HEADER = "X-Session-Id";
5
5
  export declare const CONTENT_TYPE_HEADER = "Content-Type";
6
6
  export interface JSONHttpRequest extends XMLHttpRequest {
@@ -32,7 +32,15 @@ export type RequestFn<T> = (opts: RequestOpts, reqContentType?: ContentType, req
32
32
  export declare function processRequest<T>(requester: (opts: https.RequestOptions) => ClientRequest, httpsOpts: https.RequestOptions, opts: RequestOpts, reqBody: Uint8Array | undefined, attempt?: number): Promise<Reply<T>>;
33
33
  export declare function formHttpsReqOpts(opts: RequestOpts, reqContentType?: ContentType, reqBody?: Uint8Array): https.RequestOptions;
34
34
  export declare function extractIntHeader(rep: Reply<any>, headerName: string): number;
35
- export type OpenWebSocket = (url: string, sessionId: string) => Promise<Reply<WebSocket>>;
35
+ export type OpenServiceEventsSource = (req: RequestOpts) => Promise<{
36
+ status: number;
37
+ data: ServiceEventsSource;
38
+ }>;
39
+ export interface ServiceEventsSource {
40
+ comm: RawDuplex<Envelope>;
41
+ watch: (obs: Observer<ConnectionStatus>) => (() => void);
42
+ }
43
+ type Observer<T> = web3n.Observer<T>;
36
44
  export interface NetClient {
37
45
  /**
38
46
  * This makes a 'Content-Type: application/json' request with given json,
@@ -55,6 +63,22 @@ export interface NetClient {
55
63
  */
56
64
  doBodylessRequest<T>(opts: RequestOpts): Promise<Reply<T>>;
57
65
  reset(): void;
58
- openWebSocket: (url: string, sessionId: string) => Promise<Reply<WebSocket>>;
66
+ openEventSource: OpenServiceEventsSource;
67
+ }
68
+ export interface ConnectionStatus {
69
+ type: 'heartbeat' | 'heartbeat-skip' | 'disconnected' | 'connected';
70
+ url: string;
71
+ /**
72
+ * ping number is a number of millisecond between previous and current data receiving from server.
73
+ */
74
+ ping?: number;
75
+ /**
76
+ * This mirrors a "slow socket" exception, thrown to data sending process.
77
+ */
78
+ slowSocket?: true;
79
+ missingPongsFromServer?: number;
80
+ socketClosed?: true;
81
+ error?: any;
59
82
  }
60
- export declare function makeNetClient(request: RequestFn<unknown>, openWebSocket: NetClient['openWebSocket'], reset?: () => void): NetClient;
83
+ export declare function makeNetClient(request: RequestFn<unknown>, openEventSource: OpenServiceEventsSource, reset?: () => void): NetClient;
84
+ export {};
@@ -178,7 +178,7 @@ function extractIntHeader(rep, headerName) {
178
178
  }
179
179
  return intHeader;
180
180
  }
181
- function makeNetClient(request, openWebSocket, reset = () => { }) {
181
+ function makeNetClient(request, openEventSource, reset = () => { }) {
182
182
  const client = {
183
183
  doBinaryRequest(opts, bytes) {
184
184
  let reqBody;
@@ -202,7 +202,7 @@ function makeNetClient(request, openWebSocket, reset = () => { }) {
202
202
  return request(opts, 'application/json', reqBody);
203
203
  },
204
204
  reset,
205
- openWebSocket
205
+ openEventSource
206
206
  };
207
207
  return Object.freeze(client);
208
208
  }
@@ -2,8 +2,7 @@
2
2
  * This defines a base class for some service's client that logs in with
3
3
  * MailerId and uses respectively authenticated session.
4
4
  */
5
- import { Reply, RequestOpts, NetClient } from '../lib-client/request-utils';
6
- import type * as WebSocket from 'ws';
5
+ import { Reply, RequestOpts, NetClient, ServiceEventsSource } from '../lib-client/request-utils';
7
6
  import { MailerIdSigner } from '../lib-common/mailerid-sigs/user';
8
7
  import { AwaitableState } from '../lib-common/awaitable-state';
9
8
  export type IGetMailerIdSigner = () => Promise<MailerIdSigner>;
@@ -53,9 +52,15 @@ export declare abstract class ServiceUser {
53
52
  */
54
53
  logout(): Promise<void>;
55
54
  private callEnsuringLogin;
56
- private prepCallOpts;
55
+ protected prepCallOpts(opts: RequestOpts, isWS?: true): RequestOpts;
57
56
  protected doBodylessSessionRequest<T>(opts: RequestOpts): Promise<Reply<T>>;
58
57
  protected doJsonSessionRequest<T>(opts: RequestOpts, json: any): Promise<Reply<T>>;
59
58
  protected doBinarySessionRequest<T>(opts: RequestOpts, bytes: Uint8Array | Uint8Array[]): Promise<Reply<T>>;
60
- protected openWS(appPath: string): Promise<Reply<WebSocket>>;
59
+ protected openEventSrc(appPath: string): Promise<{
60
+ req: RequestOpts;
61
+ rep: {
62
+ status: number;
63
+ data: ServiceEventsSource;
64
+ };
65
+ }>;
61
66
  }
@@ -168,7 +168,7 @@ class ServiceUser {
168
168
  reply = await func();
169
169
  }
170
170
  else {
171
- // first attepmt
171
+ // first attempt
172
172
  const initSessionId = this.sessionId;
173
173
  const rep = await func();
174
174
  if (rep.status !== api.ERR_SC.needAuth) {
@@ -201,6 +201,7 @@ class ServiceUser {
201
201
  else if (!opts.url) {
202
202
  throw new Error(`Missing both appPath and ready url in request options.`);
203
203
  }
204
+ return opts;
204
205
  }
205
206
  doBodylessSessionRequest(opts) {
206
207
  return this.callEnsuringLogin(() => {
@@ -220,15 +221,14 @@ class ServiceUser {
220
221
  return this.net.doBinaryRequest(opts, bytes);
221
222
  });
222
223
  }
223
- openWS(appPath) {
224
- const opts = {
225
- appPath,
226
- method: 'GET'
227
- };
228
- return this.callEnsuringLogin(() => {
229
- this.prepCallOpts(opts, true);
230
- return this.net.openWebSocket(opts.url, opts.sessionId);
224
+ async openEventSrc(appPath) {
225
+ let opts;
226
+ const rep = await this.callEnsuringLogin(async () => {
227
+ const reqOpts = this.prepCallOpts({ appPath, method: 'GET' }, true);
228
+ const rep = await this.net.openEventSource(reqOpts);
229
+ return rep;
231
230
  });
231
+ return { req: opts, rep };
232
232
  }
233
233
  }
234
234
  exports.ServiceUser = ServiceUser;
@@ -1,7 +1,5 @@
1
1
  import { Observable } from 'rxjs';
2
- import { SubscribingClient } from './generic-ipc';
3
- import type * as WebSocket from 'ws';
4
- import type { ConnectException, HTTPException } from '../exceptions/http';
2
+ import type { ConnectionStatus } from '../../lib-client/request-utils';
5
3
  export { RequestEnvelope, RequestHandler, EventfulServer, makeEventfulServer, SubscribingClient } from './generic-ipc';
6
4
  export interface WSException extends web3n.RuntimeException {
7
5
  type: 'websocket';
@@ -9,27 +7,7 @@ export interface WSException extends web3n.RuntimeException {
9
7
  socketClosed?: true;
10
8
  }
11
9
  export declare function makeWSException(params: Partial<WSException>, flags?: Partial<WSException>): WSException;
12
- export interface ConnectionStatus {
13
- type: 'heartbeat' | 'heartbeat-skip' | 'disconnected' | 'connected';
14
- url: string;
15
- /**
16
- * ping number is a number of millisecond between previous and current data receiving from server.
17
- */
18
- ping?: number;
19
- /**
20
- * This mirrors a "slow socket" exception, thrown to data sending process.
21
- */
22
- slowSocket?: true;
23
- missingPongsFromServer?: number;
24
- socketClosed?: true;
25
- error?: any;
26
- }
27
- export declare function makeSubscriber(ws: WebSocket, ipcChannel: string | undefined): {
28
- client: SubscribingClient;
29
- heartbeat: Observable<ConnectionStatus>;
30
- };
31
10
  export declare function addToStatus<T extends ConnectionStatus>(status: ConnectionStatus, params: Partial<T>): T;
32
- export declare function shouldRestartWSAfterErr(exc: WSException | ConnectException | HTTPException): boolean;
33
11
  export declare class WebSocketListening {
34
12
  readonly restartWaitSecs: number;
35
13
  private readonly startWSProc;
@@ -18,138 +18,14 @@
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.WebSocketListening = exports.makeEventfulServer = void 0;
20
20
  exports.makeWSException = makeWSException;
21
- exports.makeSubscriber = makeSubscriber;
22
21
  exports.addToStatus = addToStatus;
23
- exports.shouldRestartWSAfterErr = shouldRestartWSAfterErr;
24
- const rxjs_1 = require("rxjs");
25
22
  const runtime_1 = require("../exceptions/runtime");
26
- const generic_ipc_1 = require("./generic-ipc");
27
23
  const sleep_1 = require("../processes/sleep");
28
- var generic_ipc_2 = require("./generic-ipc");
29
- Object.defineProperty(exports, "makeEventfulServer", { enumerable: true, get: function () { return generic_ipc_2.makeEventfulServer; } });
24
+ var generic_ipc_1 = require("./generic-ipc");
25
+ Object.defineProperty(exports, "makeEventfulServer", { enumerable: true, get: function () { return generic_ipc_1.makeEventfulServer; } });
30
26
  function makeWSException(params, flags) {
31
27
  return (0, runtime_1.makeRuntimeException)('websocket', params, flags !== null && flags !== void 0 ? flags : {});
32
28
  }
33
- const MAX_TXT_BUFFER = 64 * 1024;
34
- const CLIENT_SIDE_PING_PERIOD = 10 * 1000;
35
- /**
36
- * This creates a json communication point on a given web socket.
37
- * Point may have many listeners, allowing for single parsing of incoming
38
- * messages.
39
- * @param ws
40
- */
41
- function makeJsonCommPoint(ws) {
42
- const observers = new generic_ipc_1.MultiObserverWrap();
43
- const { heartbeat, healthyBeat, otherBeat } = makeHeartbeat(ws.url);
44
- let outstandingPongs = 0;
45
- let closedByPingProc = false;
46
- const pingRepeat = setInterval(() => {
47
- if (outstandingPongs >= 2) {
48
- ws.close();
49
- closedByPingProc = true;
50
- clearInterval(pingRepeat);
51
- return;
52
- }
53
- if (outstandingPongs > 0) {
54
- otherBeat('heartbeat-skip', { missingPongsFromServer: outstandingPongs });
55
- }
56
- ws.ping();
57
- outstandingPongs += 1;
58
- }, CLIENT_SIDE_PING_PERIOD);
59
- ws.on('message', data => {
60
- if (observers.done) {
61
- return;
62
- }
63
- let env;
64
- try {
65
- env = JSON.parse(data.toString('utf8'));
66
- }
67
- catch (err) {
68
- ws.close();
69
- clearInterval(pingRepeat);
70
- otherBeat('disconnected', err, true);
71
- observers.error(err);
72
- return;
73
- }
74
- observers.next(env);
75
- healthyBeat();
76
- });
77
- ws.on('close', (code, reason) => {
78
- clearInterval(pingRepeat);
79
- if (code === 1000) {
80
- otherBeat('disconnected', { socketClosed: true }, true);
81
- observers.complete();
82
- }
83
- else {
84
- otherBeat('disconnected', { error: { code, reason } }, true);
85
- observers.error(makeWSException({
86
- socketClosed: true,
87
- cause: { code, reason }
88
- }));
89
- }
90
- });
91
- ws.on('error', (err) => {
92
- otherBeat('disconnected', err, true);
93
- observers.error(makeWSException({ cause: err }));
94
- clearInterval(pingRepeat);
95
- ws.close();
96
- });
97
- ws.on('ping', () => {
98
- ws.pong();
99
- healthyBeat();
100
- });
101
- ws.on('pong', () => {
102
- healthyBeat();
103
- outstandingPongs = 0;
104
- });
105
- const comm = {
106
- subscribe: obs => observers.add(obs),
107
- postMessage(env) {
108
- if (ws.bufferedAmount > MAX_TXT_BUFFER) {
109
- otherBeat('heartbeat', { slowSocket: true });
110
- throw makeWSException({ socketSlow: true });
111
- }
112
- ws.send(JSON.stringify(env));
113
- }
114
- };
115
- otherBeat('connected', {});
116
- return { comm, heartbeat };
117
- }
118
- function makeHeartbeat(url) {
119
- const status = new rxjs_1.Subject();
120
- let lastInfo = Date.now();
121
- function healthyBeat() {
122
- const now = Date.now();
123
- status.next({
124
- type: 'heartbeat',
125
- url,
126
- ping: now - lastInfo
127
- });
128
- lastInfo = now;
129
- }
130
- function otherBeat(type, params, end = false) {
131
- status.next({
132
- type,
133
- url,
134
- ...params
135
- });
136
- if (end) {
137
- status.complete();
138
- }
139
- }
140
- return {
141
- heartbeat: status.asObservable().pipe((0, rxjs_1.share)()),
142
- healthyBeat,
143
- otherBeat
144
- };
145
- }
146
- function makeSubscriber(ws, ipcChannel) {
147
- const { comm, heartbeat } = makeJsonCommPoint(ws);
148
- return {
149
- client: (0, generic_ipc_1.makeSubscribingClient)(ipcChannel, comm),
150
- heartbeat
151
- };
152
- }
153
29
  function addToStatus(status, params) {
154
30
  for (const [field, value] of Object.entries(params)) {
155
31
  status[field] = value;
@@ -1,3 +1,11 @@
1
- import * as WebSocket from 'ws';
2
- import { Reply } from '../lib-client/request-utils';
3
- export declare function openSocketFromNode(url: string, sessionId: string): Promise<Reply<WebSocket>>;
1
+ import { ConnectionStatus, RequestOpts } from '../lib-client/request-utils';
2
+ import { type Envelope, type RawDuplex } from '../lib-common/ipc/generic-ipc';
3
+ type Observer<T> = web3n.Observer<T>;
4
+ export declare function openServiceEventsSrcFromNode(req: RequestOpts): Promise<{
5
+ status: number;
6
+ data: {
7
+ comm: RawDuplex<Envelope>;
8
+ watch: (obs: Observer<ConnectionStatus>) => (() => void);
9
+ };
10
+ }>;
11
+ export {};
@@ -16,12 +16,146 @@
16
16
  this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.openSocketFromNode = openSocketFromNode;
19
+ exports.openServiceEventsSrcFromNode = openServiceEventsSrcFromNode;
20
20
  const WebSocket = require("ws");
21
21
  const request_utils_1 = require("../lib-client/request-utils");
22
22
  const deferred_1 = require("../lib-common/processes/deferred");
23
23
  const http_1 = require("../lib-common/exceptions/http");
24
24
  const https_1 = require("https");
25
+ const generic_ipc_1 = require("../lib-common/ipc/generic-ipc");
26
+ const ws_ipc_1 = require("../lib-common/ipc/ws-ipc");
27
+ const rxjs_1 = require("rxjs");
28
+ async function openServiceEventsSrcFromNode(req) {
29
+ const { url, sessionId } = req;
30
+ const rep = await openSocketFromNode(url, sessionId);
31
+ if (rep.status !== 200) {
32
+ return rep;
33
+ }
34
+ const { comm, heartbeat } = makeJsonCommPoint(rep.data);
35
+ return {
36
+ status: rep.status,
37
+ data: {
38
+ comm,
39
+ watch: obs => {
40
+ const sub = heartbeat.subscribe(obs);
41
+ return () => sub.unsubscribe();
42
+ }
43
+ }
44
+ };
45
+ }
46
+ const MAX_TXT_BUFFER = 64 * 1024;
47
+ const CLIENT_SIDE_PING_PERIOD = 10 * 1000;
48
+ /**
49
+ * This creates a json communication point on a given web socket.
50
+ * Point may have many listeners, allowing for single parsing of incoming
51
+ * messages.
52
+ * @param ws
53
+ */
54
+ function makeJsonCommPoint(ws) {
55
+ const observers = new generic_ipc_1.MultiObserverWrap();
56
+ const { heartbeat, healthyBeat, otherBeat } = makeHeartbeat(ws.url);
57
+ let outstandingPongs = 0;
58
+ let closedByPingProc = false;
59
+ const pingRepeat = setInterval(() => {
60
+ if (outstandingPongs >= 2) {
61
+ ws.close();
62
+ closedByPingProc = true;
63
+ clearInterval(pingRepeat);
64
+ return;
65
+ }
66
+ if (outstandingPongs > 0) {
67
+ otherBeat('heartbeat-skip', { missingPongsFromServer: outstandingPongs });
68
+ }
69
+ ws.ping();
70
+ outstandingPongs += 1;
71
+ }, CLIENT_SIDE_PING_PERIOD);
72
+ ws.on('message', data => {
73
+ if (observers.done) {
74
+ return;
75
+ }
76
+ let env;
77
+ try {
78
+ env = JSON.parse(data.toString('utf8'));
79
+ }
80
+ catch (err) {
81
+ ws.close();
82
+ clearInterval(pingRepeat);
83
+ otherBeat('disconnected', err, true);
84
+ observers.error(err);
85
+ return;
86
+ }
87
+ observers.next(env);
88
+ healthyBeat();
89
+ });
90
+ ws.on('close', (code, reason) => {
91
+ clearInterval(pingRepeat);
92
+ if (code === 1000) {
93
+ otherBeat('disconnected', { socketClosed: true }, true);
94
+ observers.complete();
95
+ }
96
+ else {
97
+ otherBeat('disconnected', { error: { code, reason } }, true);
98
+ observers.error((0, ws_ipc_1.makeWSException)({
99
+ socketClosed: true,
100
+ cause: { code, reason }
101
+ }));
102
+ }
103
+ });
104
+ ws.on('error', (err) => {
105
+ otherBeat('disconnected', err, true);
106
+ observers.error((0, ws_ipc_1.makeWSException)({ cause: err }));
107
+ clearInterval(pingRepeat);
108
+ ws.close();
109
+ });
110
+ ws.on('ping', () => {
111
+ ws.pong();
112
+ healthyBeat();
113
+ });
114
+ ws.on('pong', () => {
115
+ healthyBeat();
116
+ outstandingPongs = 0;
117
+ });
118
+ const comm = {
119
+ subscribe: obs => observers.add(obs),
120
+ postMessage(env) {
121
+ if (ws.bufferedAmount > MAX_TXT_BUFFER) {
122
+ otherBeat('heartbeat', { slowSocket: true });
123
+ throw (0, ws_ipc_1.makeWSException)({ socketSlow: true });
124
+ }
125
+ ws.send(JSON.stringify(env));
126
+ }
127
+ };
128
+ otherBeat('connected', {});
129
+ return { comm, heartbeat };
130
+ }
131
+ function makeHeartbeat(url) {
132
+ const status = new rxjs_1.Subject();
133
+ let lastInfo = Date.now();
134
+ function healthyBeat() {
135
+ const now = Date.now();
136
+ status.next({
137
+ type: 'heartbeat',
138
+ url,
139
+ ping: now - lastInfo
140
+ });
141
+ lastInfo = now;
142
+ }
143
+ function otherBeat(type, params, end = false) {
144
+ status.next({
145
+ type,
146
+ url,
147
+ ...params
148
+ });
149
+ if (end) {
150
+ status.complete();
151
+ }
152
+ }
153
+ return {
154
+ heartbeat: status.asObservable().pipe((0, rxjs_1.share)()),
155
+ healthyBeat,
156
+ otherBeat
157
+ };
158
+ }
25
159
  function openSocketFromNode(url, sessionId) {
26
160
  if (!url.startsWith('wss://')) {
27
161
  throw new Error(`Url protocol must be wss`);
@@ -82,7 +82,7 @@ class CoreRunner {
82
82
  this.appCaps.close();
83
83
  this.appCaps = undefined;
84
84
  }
85
- this.runningCore = lib_index_1.Core.make({ dataDir: this.dataFolder, signUpUrl: this.signUpUrl }, () => (0, lib_index_1.makeNetClient)((0, request_from_node_1.makeRequestFromNode)(), websocket_from_node_1.openSocketFromNode), (0, service_locator_1.makeServiceLocator)({
85
+ this.runningCore = lib_index_1.Core.make({ dataDir: this.dataFolder, signUpUrl: this.signUpUrl }, () => (0, lib_index_1.makeNetClient)((0, request_from_node_1.makeRequestFromNode)(), websocket_from_node_1.openServiceEventsSrcFromNode), (0, service_locator_1.makeServiceLocator)({
86
86
  resolveTxt: domain => new Promise((resolve, reject) => (0, dns_1.resolveTxt)(domain, (err, texts) => {
87
87
  if (err) {
88
88
  reject(err);
@@ -262,7 +262,7 @@ function domainFromUserId(userId) {
262
262
  function serviceWithMailerIdLogin() {
263
263
  const testSrv = (new URL(SIGNUP_URL)).host;
264
264
  const serviceUrl = `https://${testSrv}/asmail/retrieval/login/mailerid`;
265
- const net = (0, request_utils_1.makeNetClient)((0, request_from_node_1.makeRequestFromNode)(), websocket_from_node_1.openSocketFromNode);
265
+ const net = (0, request_utils_1.makeNetClient)((0, request_from_node_1.makeRequestFromNode)(), websocket_from_node_1.openServiceEventsSrcFromNode);
266
266
  async function isSessionValid(sessionId) {
267
267
  const rep = await net.doBodylessRequest({
268
268
  url: `https://${testSrv}/asmail/retrieval/${retrieval_1.listMsgs.genUrlEnd()}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "core-3nweb-client-lib",
3
- "version": "0.48.5",
3
+ "version": "0.48.6",
4
4
  "description": "3NWeb client core library, embeddable into different environments",
5
5
  "main": "build/lib-index.js",
6
6
  "types": "build/lib-index.d.ts",