core-3nweb-client-lib 0.48.4 → 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.
- package/build/core/asmail/inbox/inbox-events.d.ts +1 -1
- package/build/core/asmail/inbox/inbox-events.js +2 -2
- package/build/core/storage/synced/remote-events.d.ts +2 -2
- package/build/core/storage/synced/remote-events.js +2 -2
- package/build/lib-client/3nstorage/storage-owner.d.ts +8 -3
- package/build/lib-client/3nstorage/storage-owner.js +6 -4
- package/build/lib-client/asmail/recipient.d.ts +7 -3
- package/build/lib-client/asmail/recipient.js +6 -4
- package/build/lib-client/request-utils.d.ts +28 -4
- package/build/lib-client/request-utils.js +2 -2
- package/build/lib-client/user-with-mid-session.d.ts +9 -4
- package/build/lib-client/user-with-mid-session.js +9 -9
- package/build/lib-client/xsp-fs/xsp-payload-v2.js +18 -8
- package/build/lib-common/ipc/ws-ipc.d.ts +1 -23
- package/build/lib-common/ipc/ws-ipc.js +2 -126
- package/build/lib-common-on-node/websocket-from-node.d.ts +11 -3
- package/build/lib-common-on-node/websocket-from-node.js +135 -1
- package/build/tests/libs-for-tests/core-runner.js +1 -1
- package/build/tests/libs-for-tests/setups.js +1 -1
- package/package.json +1 -1
|
@@ -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-
|
|
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,
|
|
60
|
+
const proc$ = (0, rxjs_1.from)(this.msgReceiver.openEventSource().then(({ client, watchHeartbeat }) => {
|
|
61
61
|
const channel = retrieval_1.msgRecievedCompletely.EVENT_NAME;
|
|
62
|
-
|
|
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-
|
|
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,
|
|
47
|
+
return (0, rxjs_1.from)(this.remoteStorage.openEventSource().then(({ client, watchHeartbeat }) => {
|
|
48
48
|
this.remoteStorage.connectedState.setState();
|
|
49
|
-
|
|
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 {
|
|
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<
|
|
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.
|
|
339
|
+
const { rep, req } = await this.openEventSrc(api.wsEventChannel.URL_END);
|
|
340
340
|
if (rep.status === api.wsEventChannel.SC.ok) {
|
|
341
|
-
|
|
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 {
|
|
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<
|
|
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.
|
|
204
|
+
const { rep, req } = await this.openEventSrc(api.wsEventChannel.URL_END);
|
|
205
205
|
if (rep.status === api.wsEventChannel.SC.ok) {
|
|
206
|
-
|
|
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
|
|
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
|
-
|
|
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>,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
method: 'GET'
|
|
227
|
-
|
|
228
|
-
|
|
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;
|
|
@@ -95,12 +95,9 @@ class ReadonlyPayloadV2 {
|
|
|
95
95
|
if (start === end) {
|
|
96
96
|
return undefined;
|
|
97
97
|
}
|
|
98
|
-
const startSecInd = this.contentSections
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
.findIndex(s => ((s.ofs + s.len) >= end));
|
|
102
|
-
(0, assert_1.assert)((startSecInd >= 0) && (endSecInd >= 0) &&
|
|
103
|
-
(startSecInd <= endSecInd));
|
|
98
|
+
const startSecInd = this.contentSections.findIndex(s => (s.ofs <= start));
|
|
99
|
+
const endSecInd = this.contentSections.findIndex(s => ((s.ofs + s.len) >= end));
|
|
100
|
+
(0, assert_1.assert)((startSecInd >= 0) && (endSecInd >= 0) && (startSecInd <= endSecInd));
|
|
104
101
|
return await this.syncProc.startOrChain((startSecInd === endSecInd) ?
|
|
105
102
|
async () => {
|
|
106
103
|
const s = this.contentSections[startSecInd];
|
|
@@ -790,8 +787,21 @@ function payloadLayoutException(msg, cause) {
|
|
|
790
787
|
}
|
|
791
788
|
async function sureReadOfBytesFrom(src, ofs, len) {
|
|
792
789
|
const bytes = await src.readAt(ofs, len);
|
|
793
|
-
(0, assert_1.assert)(!!bytes
|
|
794
|
-
|
|
790
|
+
(0, assert_1.assert)(!!bytes);
|
|
791
|
+
if (bytes.length === len) {
|
|
792
|
+
return bytes;
|
|
793
|
+
}
|
|
794
|
+
// somehow, on Android src.readAt(ofs, len) may produce a thing that is shorter than len, hence, loop below
|
|
795
|
+
len -= bytes.length;
|
|
796
|
+
ofs += bytes.length;
|
|
797
|
+
const chunks = [bytes];
|
|
798
|
+
while (len > 0) {
|
|
799
|
+
const chunk = await src.readAt(ofs, len);
|
|
800
|
+
(0, assert_1.assert)(!!chunk);
|
|
801
|
+
len -= chunk.length;
|
|
802
|
+
ofs += chunk.length;
|
|
803
|
+
}
|
|
804
|
+
return (0, buffer_utils_1.joinByteArrs)(chunks);
|
|
795
805
|
}
|
|
796
806
|
function noop() { }
|
|
797
807
|
// XXX we may add smaller bytes for packing section info: there is enough values
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { Observable } from 'rxjs';
|
|
2
|
-
import {
|
|
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
|
|
29
|
-
Object.defineProperty(exports, "makeEventfulServer", { enumerable: true, get: function () { return
|
|
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
|
|
2
|
-
import {
|
|
3
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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()}`,
|