noibu-react-native 0.1.2 → 0.1.3
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/dist/api/clientConfig.d.ts +4 -5
- package/dist/api/clientConfig.js +13 -15
- package/dist/api/metroplexSocket.d.ts +155 -0
- package/dist/api/metroplexSocket.js +665 -814
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +8 -2
- package/dist/entry/init.d.ts +1 -1
- package/dist/entry/init.js +2 -2
- package/dist/monitors/appNavigationMonitor.js +3 -2
- package/dist/monitors/inputMonitor.js +5 -0
- package/dist/monitors/integrations/react-native-navigation-integration.d.ts +1 -2
- package/dist/pageVisit/pageVisit.js +5 -0
- package/dist/types/NavigationIntegration.d.ts +1 -2
- package/dist/types/globals.d.ts +2 -0
- package/dist/utils/function.d.ts +2 -3
- package/dist/utils/function.js +10 -9
- package/dist/utils/log.js +5 -3
- package/package.json +5 -4
|
@@ -11,848 +11,699 @@ import { unwrapNoibuWrapped } from '../utils/object.js';
|
|
|
11
11
|
import { noibuLog } from '../utils/log.js';
|
|
12
12
|
|
|
13
13
|
/** @module MetroplexSocket */
|
|
14
|
-
|
|
15
14
|
/** Manages the socket to Metroplex */
|
|
16
15
|
class MetroplexSocket {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
16
|
+
_initialURL;
|
|
17
|
+
ackedOnce;
|
|
18
|
+
connectionCount;
|
|
19
|
+
connectionPromise = null;
|
|
20
|
+
connectionURL;
|
|
21
|
+
currentConnectionAttempts;
|
|
22
|
+
forceClosed;
|
|
23
|
+
helpCodeCb;
|
|
24
|
+
initialReferingURL;
|
|
25
|
+
static instance;
|
|
26
|
+
instanceId;
|
|
27
|
+
isRetryLoopDisabled;
|
|
28
|
+
latestReceivedSeqNumStoredTime;
|
|
29
|
+
latestReceivedSeqNumber;
|
|
30
|
+
messageSequenceNum;
|
|
31
|
+
metroRetryFrequencyMS;
|
|
32
|
+
metroplexTypeLock;
|
|
33
|
+
pageVisitInfoSent;
|
|
34
|
+
postURL;
|
|
35
|
+
previousMessageType;
|
|
36
|
+
retryMessageQueue;
|
|
37
|
+
retryMetroplexInterval;
|
|
38
|
+
scriptInstanceId;
|
|
39
|
+
sessionLength;
|
|
40
|
+
sessionStartTime;
|
|
41
|
+
sessionTimestamp;
|
|
42
|
+
socket;
|
|
43
|
+
socketInstanceId;
|
|
44
|
+
socketCloseCodes;
|
|
45
|
+
socketOpens;
|
|
46
|
+
/**
|
|
47
|
+
* Creates an instance of metroplex
|
|
48
|
+
* id of script, to make sure only a single socket is open
|
|
49
|
+
*/
|
|
50
|
+
constructor(scriptInstanceId) {
|
|
51
|
+
const socketBase = GET_METROPLEX_BASE_SOCKET_URL();
|
|
52
|
+
// this flag is set to true when we manually need to close the
|
|
53
|
+
// socket. It happens currently during the pagehide event and
|
|
54
|
+
// if we havent sent a message to our backend in some time.
|
|
55
|
+
this.forceClosed = false;
|
|
56
|
+
// the socket used to transmit all data to metroplex
|
|
57
|
+
this.socket = null;
|
|
58
|
+
// the unique instance id of the socket
|
|
59
|
+
this.socketInstanceId = null;
|
|
60
|
+
// previous message type sent to metroplex
|
|
61
|
+
this.previousMessageType = '';
|
|
62
|
+
// how many times we tried reconnecting to metroplex
|
|
63
|
+
this.currentConnectionAttempts = 0;
|
|
64
|
+
// how many successful connections to metroplex
|
|
65
|
+
this.connectionCount = 0;
|
|
66
|
+
// sessiont start time, used to calculate accurate end time
|
|
67
|
+
this.sessionStartTime = safePerformanceNow();
|
|
68
|
+
// promise that will resolve once the socket is connected and metroplex has acknowledged
|
|
69
|
+
this.connectionPromise = null;
|
|
70
|
+
// Whether or not we have sent the page visit information after connecting the socket
|
|
71
|
+
this.pageVisitInfoSent = false;
|
|
72
|
+
// socket connection url
|
|
73
|
+
this.connectionURL = `${socketBase}/${METROPLEX_FRAG_ROUTE}`;
|
|
74
|
+
// post endpoint for the same events we would send to the socket
|
|
75
|
+
this.postURL = GET_METROPLEX_POST_URL();
|
|
76
|
+
// sequence number of the message sent to metroplex
|
|
77
|
+
this.messageSequenceNum = 0;
|
|
78
|
+
// the latest received sequence number from metroplex
|
|
79
|
+
this.latestReceivedSeqNumber = -1;
|
|
80
|
+
// set to true only when we start running behind on messages in metroplex
|
|
81
|
+
this.isRetryLoopDisabled = false;
|
|
82
|
+
// messages that need to be resent to metroplex since they are lacking confirmation
|
|
83
|
+
this.retryMessageQueue = [];
|
|
84
|
+
// this map will hold all types that noibujs should not send to metroplex
|
|
85
|
+
this.metroplexTypeLock = {};
|
|
86
|
+
// setting initial URL at the start in order to guarentee that the
|
|
87
|
+
// current page visit has the real initial onURL. Fragments and SPA's
|
|
88
|
+
// can change the URL without reloading the page.
|
|
89
|
+
this._initialURL = ClientConfig.getInstance().globalUrl;
|
|
90
|
+
noibuLog('metroplex constructor', {
|
|
91
|
+
initialURL: this._initialURL,
|
|
92
|
+
});
|
|
93
|
+
this.initialReferingURL = '';
|
|
94
|
+
this.sessionTimestamp = new Date();
|
|
95
|
+
// latest time that we received a confirmation message from metroplex
|
|
96
|
+
this.latestReceivedSeqNumStoredTime = new Date();
|
|
97
|
+
// unique instance id of Metroplex Socket
|
|
98
|
+
this.instanceId = uuid.v4();
|
|
99
|
+
// unique script instance id
|
|
100
|
+
this.scriptInstanceId = scriptInstanceId;
|
|
101
|
+
// length of the session based on page visit events
|
|
102
|
+
this.sessionLength = 0;
|
|
103
|
+
// track socket closure codes for debug
|
|
104
|
+
this.socketCloseCodes = [];
|
|
105
|
+
// track socket open times
|
|
106
|
+
this.socketOpens = [];
|
|
107
|
+
// flag to indicate Metroplex has acknowledged at least once
|
|
108
|
+
this.ackedOnce = false;
|
|
109
|
+
// retry frequency in ms
|
|
110
|
+
this.metroRetryFrequencyMS = METROPLEX_RETRY_FREQUENCY;
|
|
111
|
+
this.helpCodeCb = null;
|
|
112
|
+
this.retryMetroplexInterval = null;
|
|
103
113
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Adds the seq num field to the given payload depending on whether its
|
|
118
|
-
* a page visit part or video frag
|
|
119
|
-
* @param {} type
|
|
120
|
-
* @param {} payload
|
|
121
|
-
*/
|
|
122
|
-
_addSeqNumToPayload(type, payload) {
|
|
123
|
-
switch (type) {
|
|
124
|
-
case PV_METROPLEX_TYPE:
|
|
125
|
-
this._setSeqNumInPayloadAndIncrementSeqNum(
|
|
126
|
-
PAGE_VISIT_PART_ATT_NAME,
|
|
127
|
-
payload,
|
|
128
|
-
);
|
|
129
|
-
break;
|
|
130
|
-
case VIDEO_METROPLEX_TYPE:
|
|
131
|
-
this._setSeqNumInPayloadAndIncrementSeqNum(
|
|
132
|
-
PAGE_VISIT_VID_FRAG_ATT_NAME,
|
|
133
|
-
payload,
|
|
134
|
-
);
|
|
135
|
-
break;
|
|
136
|
-
case HTTP_DATA_METROPLEX_TYPE:
|
|
137
|
-
this._setSeqNumInPayloadAndIncrementSeqNum(
|
|
138
|
-
PAGE_VISIT_HTTP_DATA_ATT_NAME,
|
|
139
|
-
payload,
|
|
140
|
-
);
|
|
141
|
-
break;
|
|
142
|
-
case META_DATA_METROPLEX_TYPE:
|
|
143
|
-
this._setSeqNumInPayloadAndIncrementSeqNum(
|
|
144
|
-
PAGE_VISIT_META_DATA_ATT_NAME,
|
|
145
|
-
payload,
|
|
146
|
-
);
|
|
147
|
-
break;
|
|
114
|
+
/**
|
|
115
|
+
* gets the singleton instance
|
|
116
|
+
* @returns {MetroplexSocket}
|
|
117
|
+
*/
|
|
118
|
+
static getInstance(scriptInstanceId) {
|
|
119
|
+
if (!this.instance) {
|
|
120
|
+
this.instance = new MetroplexSocket(scriptInstanceId);
|
|
121
|
+
this.instance.start();
|
|
122
|
+
}
|
|
123
|
+
return this.instance;
|
|
148
124
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
* @param {} payload
|
|
156
|
-
*/
|
|
157
|
-
_setSeqNumInPayloadAndIncrementSeqNum(payloadKey, payload) {
|
|
158
|
-
// eslint-disable-next-line no-param-reassign
|
|
159
|
-
payload[payloadKey][SEQ_NUM_ATT_NAME] = this.messageSequenceNum;
|
|
160
|
-
this.messageSequenceNum += 1;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/** requests help code and saves a callback to be called on response */
|
|
164
|
-
requestHelpCode(cb) {
|
|
165
|
-
this.helpCodeCb = cb;
|
|
166
|
-
return MetroplexSocket.getInstance().sendMessage(WORK_REQUEST_ATT_NAME, {
|
|
167
|
-
[WORK_REQUEST_ATT_NAME]: HELP_CODE_ATT_NAME,
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Immediately sends a message to Metroplex over the web socket
|
|
173
|
-
* Queues the message if the connection isn't open yet.
|
|
174
|
-
* @param {} type
|
|
175
|
-
* @param {} payload
|
|
176
|
-
* @returns {Promise<boolean>} true if message was sent succefully, false otherwise
|
|
177
|
-
*/
|
|
178
|
-
async sendMessage(type, payload) {
|
|
179
|
-
noibuLog('sendMessage start', { type });
|
|
180
|
-
// if we have a lock on this specific type, we dont send it
|
|
181
|
-
if (
|
|
182
|
-
type in this.metroplexTypeLock ||
|
|
183
|
-
ClientConfig.getInstance().isClientDisabled
|
|
184
|
-
) {
|
|
185
|
-
noibuLog('sendMessage quitting due to', {
|
|
186
|
-
typeInLock: type in this.metroplexTypeLock,
|
|
187
|
-
isClientDisabled: ClientConfig.getInstance().isClientDisabled,
|
|
188
|
-
});
|
|
189
|
-
return false;
|
|
125
|
+
/** Starts off the socket connection */
|
|
126
|
+
start() {
|
|
127
|
+
// Connect the WS
|
|
128
|
+
this.connectSocket();
|
|
129
|
+
// Set up the offload events immediately
|
|
130
|
+
this._setupOffloadEvents();
|
|
190
131
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
132
|
+
/**
|
|
133
|
+
* Adds the seq num field to the given payload depending on whether its
|
|
134
|
+
* a page visit part or video frag
|
|
135
|
+
*/
|
|
136
|
+
_addSeqNumToPayload(type, payload) {
|
|
137
|
+
switch (type) {
|
|
138
|
+
case PV_METROPLEX_TYPE:
|
|
139
|
+
this._setSeqNumInPayloadAndIncrementSeqNum(PAGE_VISIT_PART_ATT_NAME, payload);
|
|
140
|
+
break;
|
|
141
|
+
case VIDEO_METROPLEX_TYPE:
|
|
142
|
+
this._setSeqNumInPayloadAndIncrementSeqNum(PAGE_VISIT_VID_FRAG_ATT_NAME, payload);
|
|
143
|
+
break;
|
|
144
|
+
case HTTP_DATA_METROPLEX_TYPE:
|
|
145
|
+
this._setSeqNumInPayloadAndIncrementSeqNum(PAGE_VISIT_HTTP_DATA_ATT_NAME, payload);
|
|
146
|
+
break;
|
|
147
|
+
case META_DATA_METROPLEX_TYPE:
|
|
148
|
+
this._setSeqNumInPayloadAndIncrementSeqNum(PAGE_VISIT_META_DATA_ATT_NAME, payload);
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
205
151
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
152
|
+
/**
|
|
153
|
+
* sets the seq num in the payload for the given key and increments the
|
|
154
|
+
* global seq number
|
|
155
|
+
*/
|
|
156
|
+
_setSeqNumInPayloadAndIncrementSeqNum(payloadKey, payload) {
|
|
157
|
+
// eslint-disable-next-line no-param-reassign
|
|
158
|
+
payload[payloadKey][SEQ_NUM_ATT_NAME] = this.messageSequenceNum;
|
|
159
|
+
this.messageSequenceNum += 1;
|
|
211
160
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (type === PV_METROPLEX_TYPE && payload[PAGE_VISIT_PART_ATT_NAME]) {
|
|
219
|
-
const events = payload[PAGE_VISIT_PART_ATT_NAME][PV_EVENTS_ATT_NAME]
|
|
220
|
-
? payload[PAGE_VISIT_PART_ATT_NAME][PV_EVENTS_ATT_NAME]
|
|
221
|
-
: [];
|
|
222
|
-
|
|
223
|
-
this._updateLatestPvTimestamp(events);
|
|
161
|
+
/** requests help code and saves a callback to be called on response */
|
|
162
|
+
requestHelpCode(cb) {
|
|
163
|
+
this.helpCodeCb = cb;
|
|
164
|
+
return MetroplexSocket.getInstance().sendMessage(WORK_REQUEST_ATT_NAME, {
|
|
165
|
+
[WORK_REQUEST_ATT_NAME]: HELP_CODE_ATT_NAME,
|
|
166
|
+
});
|
|
224
167
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Immediately sends a message to Metroplex over the web socket
|
|
170
|
+
* Queues the message if the connection isn't open yet.
|
|
171
|
+
* returns true if message was sent succefully, false otherwise
|
|
172
|
+
*/
|
|
173
|
+
async sendMessage(type, payload) {
|
|
174
|
+
noibuLog('sendMessage start', { type, payload: JSON.stringify(payload) });
|
|
175
|
+
// if we have a lock on this specific type, we dont send it
|
|
176
|
+
if (type in this.metroplexTypeLock ||
|
|
177
|
+
ClientConfig.getInstance().isClientDisabled) {
|
|
178
|
+
noibuLog('sendMessage quitting due to', {
|
|
179
|
+
typeInLock: type in this.metroplexTypeLock,
|
|
180
|
+
isClientDisabled: ClientConfig.getInstance().isClientDisabled,
|
|
181
|
+
});
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
const payloadData = payload;
|
|
185
|
+
if (type !== WORK_REQUEST_ATT_NAME) {
|
|
186
|
+
// Increasing the message sequence number for every message we send to metroplex
|
|
187
|
+
// and move the data to the retry queue.
|
|
188
|
+
this._addSeqNumToPayload(type, payloadData);
|
|
189
|
+
// push the message to the retry queue immediately in case socket isnt connected
|
|
190
|
+
this.retryMessageQueue.push({ payload: payloadData, type });
|
|
191
|
+
StoredPageVisit.getInstance().checkAndStoreRetryQueue(this.retryMessageQueue, await this.getPageInformation());
|
|
192
|
+
}
|
|
193
|
+
// send the socket message if we are connected and have sent page visit info
|
|
194
|
+
if (this.isConnected() && this.pageVisitInfoSent) {
|
|
195
|
+
// sending the data to metroplex
|
|
196
|
+
await this._sendSocketMessage(payloadData);
|
|
197
|
+
}
|
|
198
|
+
this.previousMessageType = type;
|
|
199
|
+
// Only update the last message send if its a page visit with user action
|
|
200
|
+
// ensure this is done regardless of whether socket is connected or not,
|
|
201
|
+
// so that pagehide will post PV if user is active but socket is not
|
|
202
|
+
if (type === PV_METROPLEX_TYPE && payload[PAGE_VISIT_PART_ATT_NAME]) {
|
|
203
|
+
const events = payload[PAGE_VISIT_PART_ATT_NAME][PV_EVENTS_ATT_NAME]
|
|
204
|
+
? payload[PAGE_VISIT_PART_ATT_NAME][PV_EVENTS_ATT_NAME]
|
|
205
|
+
: [];
|
|
206
|
+
await this._updateLatestPvTimestamp(events);
|
|
207
|
+
}
|
|
208
|
+
return true;
|
|
239
209
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
/** returns true if we are connecting to the socket */
|
|
248
|
-
isConnecting() {
|
|
249
|
-
return this.socket !== null && this.socket.readyState === 0;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/** close will close the socket opened for video frag transmission */
|
|
253
|
-
close() {
|
|
254
|
-
this.forceClosed = true;
|
|
255
|
-
// closing the video frag socket only if it was used in the past by
|
|
256
|
-
// the sessionRecorder
|
|
257
|
-
if (this.isConnected() || this.isConnecting()) {
|
|
258
|
-
this.socket.close(1000);
|
|
210
|
+
/** Updates the latest pv message sent timestamp if events contain any user steps
|
|
211
|
+
*/
|
|
212
|
+
async _updateLatestPvTimestamp(events) {
|
|
213
|
+
const userstepsEvents = events.filter(ev => ev[TYPE_ATT_NAME] === USERSTEP_EVENT_TYPE);
|
|
214
|
+
if (userstepsEvents.length > 0) {
|
|
215
|
+
await ClientConfig.getInstance().updateLastActiveTime(new Date());
|
|
216
|
+
}
|
|
259
217
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
* @param {} callback
|
|
264
|
-
* @param {} forceOpen
|
|
265
|
-
*/
|
|
266
|
-
async handleConnect(callback, forceOpen) {
|
|
267
|
-
// if we are connected or about to connect, we do not reset
|
|
268
|
-
// the socket information
|
|
269
|
-
if (!forceOpen && (this.isConnected() || this.isConnecting())) {
|
|
270
|
-
return;
|
|
218
|
+
/** returns true if the socket is either connecting or connected to metroplex */
|
|
219
|
+
isConnected() {
|
|
220
|
+
return this.socket !== null && this.socket.readyState === 1;
|
|
271
221
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
headers: {
|
|
276
|
-
'User-Agent': await getUserAgent(), // must pass useragent explicitly
|
|
277
|
-
},
|
|
278
|
-
});
|
|
279
|
-
this.socketInstanceId = uuid.v4();
|
|
280
|
-
|
|
281
|
-
this.socket.onerror = e => {
|
|
282
|
-
// use for metrics
|
|
283
|
-
// there is no useful error message/description from this event
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
// once the socket closes
|
|
287
|
-
this.socket.onclose = event => {
|
|
288
|
-
// Reset this to prevent sending messages before the PVI is sent on reconnect
|
|
289
|
-
this.pageVisitInfoSent = false;
|
|
290
|
-
|
|
291
|
-
// we do not reconnect if the page is unloading
|
|
292
|
-
if (this.forceClosed) {
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// track socket closure codes and times
|
|
297
|
-
this.socketCloseCodes.push(
|
|
298
|
-
`${!isDateOverwritten() ? new Date().toISOString() : ''}:${event.code}`,
|
|
299
|
-
);
|
|
300
|
-
|
|
301
|
-
// we are already in the process of reconnecting
|
|
302
|
-
// we do not attempt to reconnect yet
|
|
303
|
-
if (this.isConnecting()) {
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Clear the interval that resends unconfirmed msgs so we don't try send while the socket
|
|
308
|
-
// is and starting back up, after which it will be restarted
|
|
309
|
-
clearInterval(this.retryMetroplexInterval);
|
|
310
|
-
|
|
311
|
-
// if we tried reconnecting too many times we abandon
|
|
312
|
-
if (
|
|
313
|
-
this.currentConnectionAttempts >=
|
|
314
|
-
GET_MAX_METROPLEX_RECONNECTION_NUMBER()
|
|
315
|
-
) {
|
|
316
|
-
// if we tried beyond the threshold, we block ourselves a short
|
|
317
|
-
// while while fix the issue
|
|
318
|
-
ClientConfig.getInstance().lockClientUntilNextPage(
|
|
319
|
-
'Too many reconnection attempts, locking until next page',
|
|
320
|
-
);
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// if we tried reconnecting too many times we abandon
|
|
325
|
-
if (this.connectionCount >= MAX_METROPLEX_CONNECTION_COUNT) {
|
|
326
|
-
// if we tried beyond the threshold, we block ourselves a short
|
|
327
|
-
// while while fix the issue
|
|
328
|
-
ClientConfig.getInstance().lockClientUntilNextPage(
|
|
329
|
-
'Too many connections, locking until next page',
|
|
330
|
-
);
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// try to reconnect on close but after a certain delay based off unsuccessful connections
|
|
335
|
-
setTimeout(() => {
|
|
336
|
-
this.handleConnect(callback, false);
|
|
337
|
-
}, this.currentConnectionAttempts ** 2 * GET_METROPLEX_CONSECUTIVE_CONNECTION_DELAY());
|
|
338
|
-
};
|
|
339
|
-
// everytime we get a message from the socket
|
|
340
|
-
this.socket.onmessage = event => {
|
|
341
|
-
this._onSocketMessage(event, callback);
|
|
342
|
-
};
|
|
343
|
-
// the moment we open the socket
|
|
344
|
-
this.socket.onopen = () => {
|
|
345
|
-
// track socket open codes and times
|
|
346
|
-
this.socketOpens.push(
|
|
347
|
-
`${!isDateOverwritten() ? new Date().toISOString() : ''}`,
|
|
348
|
-
);
|
|
349
|
-
this._onSocketOpen();
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* connectSocket will establish a websocket connection to the metroplex
|
|
355
|
-
* service
|
|
356
|
-
*/
|
|
357
|
-
connectSocket() {
|
|
358
|
-
if (this.isConnected() || this.isConnecting()) {
|
|
359
|
-
// self resolving promise since we are already ready to send
|
|
360
|
-
// requests, if we did reach state 1, we already sent initial
|
|
361
|
-
// request, thus not sending it again.
|
|
362
|
-
return this.connectionPromise;
|
|
222
|
+
/** returns true if we are connecting to the socket */
|
|
223
|
+
isConnecting() {
|
|
224
|
+
return this.socket !== null && this.socket.readyState === 0;
|
|
363
225
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
* @param {} payload
|
|
375
|
-
* @param {} isPageVisit
|
|
376
|
-
*/
|
|
377
|
-
addEndTimeToPayload(payload, isPageVisit) {
|
|
378
|
-
const delta = Math.ceil(safePerformanceNow() - this.sessionStartTime);
|
|
379
|
-
// update session length if this is a page visit event
|
|
380
|
-
if (isPageVisit) {
|
|
381
|
-
this.sessionLength = delta;
|
|
226
|
+
/** close will close the socket opened for video frag transmission */
|
|
227
|
+
close() {
|
|
228
|
+
this.forceClosed = true;
|
|
229
|
+
// closing the video frag socket only if it was used in the past by
|
|
230
|
+
// the sessionRecorder
|
|
231
|
+
if (this.isConnected() || this.isConnecting()) {
|
|
232
|
+
if (this.socket !== null) {
|
|
233
|
+
this.socket.close(1000);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
382
236
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
237
|
+
/** Connects the web socket to metroplex and calls callback upon successfully connecting
|
|
238
|
+
*/
|
|
239
|
+
async handleConnect(forceOpen) {
|
|
240
|
+
noibuLog('metroplex handleConnect');
|
|
241
|
+
// if we are connected or about to connect, we do not reset
|
|
242
|
+
// the socket information
|
|
243
|
+
if (!forceOpen && (this.isConnected() || this.isConnecting())) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
this.currentConnectionAttempts += 1;
|
|
247
|
+
this.socket = new WebSocket(this.connectionURL, undefined, {
|
|
248
|
+
headers: {
|
|
249
|
+
'User-Agent': await getUserAgent(), // must pass useragent explicitly
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
this.socketInstanceId = uuid.v4();
|
|
253
|
+
this.socket.onerror = error => {
|
|
254
|
+
noibuLog('metroplex handleConnect, onerror', { error });
|
|
255
|
+
// use for metrics
|
|
256
|
+
// there is no useful error message/description from this event
|
|
257
|
+
};
|
|
258
|
+
// once the socket closes
|
|
259
|
+
this.socket.onclose = async (event) => {
|
|
260
|
+
noibuLog('metroplex handleConnect, onclose', { event });
|
|
261
|
+
// Reset this to prevent sending messages before the PVI is sent on reconnect
|
|
262
|
+
this.pageVisitInfoSent = false;
|
|
263
|
+
// we do not reconnect if the page is unloading
|
|
264
|
+
if (this.forceClosed) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
// track socket closure codes and times
|
|
268
|
+
this.socketCloseCodes.push(`${!isDateOverwritten() ? new Date().toISOString() : ''}:${event.code}`);
|
|
269
|
+
// we are already in the process of reconnecting
|
|
270
|
+
// we do not attempt to reconnect yet
|
|
271
|
+
if (this.isConnecting()) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
// Clear the interval that resends unconfirmed msgs so we don't try send while the socket
|
|
275
|
+
// is and starting back up, after which it will be restarted
|
|
276
|
+
if (this.retryMetroplexInterval) {
|
|
277
|
+
clearInterval(this.retryMetroplexInterval);
|
|
278
|
+
}
|
|
279
|
+
// if we tried reconnecting too many times we abandon
|
|
280
|
+
if (this.currentConnectionAttempts >=
|
|
281
|
+
GET_MAX_METROPLEX_RECONNECTION_NUMBER()) {
|
|
282
|
+
// if we tried beyond the threshold, we block ourselves a short
|
|
283
|
+
// while fix the issue
|
|
284
|
+
await ClientConfig.getInstance().lockClientUntilNextPage('Too many reconnection attempts, locking until next page');
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
// if we tried reconnecting too many times we abandon
|
|
288
|
+
if (this.connectionCount >= MAX_METROPLEX_CONNECTION_COUNT) {
|
|
289
|
+
// if we tried beyond the threshold, we block ourselves a short
|
|
290
|
+
// while while fix the issue
|
|
291
|
+
await ClientConfig.getInstance().lockClientUntilNextPage('Too many connections, locking until next page');
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
// try to reconnect on close but after a certain delay based off unsuccessful connections
|
|
295
|
+
setTimeout(() => {
|
|
296
|
+
this.handleConnect(false);
|
|
297
|
+
}, this.currentConnectionAttempts ** 2 * GET_METROPLEX_CONSECUTIVE_CONNECTION_DELAY());
|
|
298
|
+
};
|
|
299
|
+
// everytime we get a message from the socket
|
|
300
|
+
this.socket.onmessage = event => {
|
|
301
|
+
noibuLog('metroplex handleConnect, onmessage', { event });
|
|
302
|
+
this._onSocketMessage(event);
|
|
303
|
+
};
|
|
304
|
+
// the moment we open the socket
|
|
305
|
+
this.socket.onopen = () => {
|
|
306
|
+
// track socket open codes and times
|
|
307
|
+
this.socketOpens.push(`${!isDateOverwritten() ? new Date().toISOString() : ''}`);
|
|
308
|
+
this._onSocketOpen();
|
|
309
|
+
};
|
|
396
310
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
this.setupRetryMechanism();
|
|
408
|
-
// increasing the connection count everytime we connect
|
|
409
|
-
this.connectionCount += 1;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/** message handler for socket
|
|
413
|
-
* @param {} event
|
|
414
|
-
* @param {} metroAckCallback
|
|
415
|
-
*/
|
|
416
|
-
_onSocketMessage(event, metroAckCallback) {
|
|
417
|
-
switch (event.data) {
|
|
418
|
-
case STOP_STORING_VID_SOCKET_MESSAGE:
|
|
419
|
-
// stopping vids from being sent
|
|
420
|
-
this.metroplexTypeLock[VIDEO_METROPLEX_TYPE] = true;
|
|
421
|
-
StoredMetrics.getInstance().setDidCutVideo();
|
|
422
|
-
break;
|
|
423
|
-
case STOP_STORING_PV_SOCKET_MESSAGE:
|
|
424
|
-
// stopping pv from being sent
|
|
425
|
-
this.metroplexTypeLock[PV_METROPLEX_TYPE] = true;
|
|
426
|
-
StoredMetrics.getInstance().setDidCutPv();
|
|
427
|
-
break;
|
|
428
|
-
case BLOCK_SOCKET_MESSAGE:
|
|
429
|
-
// we are disabling collect for 1 day if we get the
|
|
430
|
-
// block message from metroplex. We are probably on it
|
|
431
|
-
ClientConfig.getInstance().lockClient(1440, 'Metroplex blocked script');
|
|
432
|
-
this.close();
|
|
433
|
-
break;
|
|
434
|
-
case CLOSE_CONNECTION_FORCEFULLY:
|
|
435
|
-
this.close();
|
|
436
|
-
break;
|
|
437
|
-
case OK_SOCKET_MESSAGE:
|
|
438
|
-
// we do nothing on an OK
|
|
439
|
-
break;
|
|
440
|
-
default:
|
|
441
|
-
// we now need to check if we receive text that contains
|
|
442
|
-
// the text SEQ_NUM. if that is true, then its a seq num
|
|
443
|
-
// and we need to clear out the retry queue.
|
|
444
|
-
if (event.data.includes(SEQ_NUM_ATT_NAME)) {
|
|
445
|
-
const seqNumSplit = event.data.split(`${SEQ_NUM_ATT_NAME}:`);
|
|
446
|
-
if (seqNumSplit.length < 2) {
|
|
447
|
-
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(
|
|
448
|
-
`Invalid message received from metroplex while clearing retry queue ${event.data}`,
|
|
449
|
-
false,
|
|
450
|
-
SEVERITY.error,
|
|
451
|
-
);
|
|
452
|
-
break;
|
|
453
|
-
}
|
|
454
|
-
// the second element in the string split is the sequence number
|
|
455
|
-
const seqNum = parseInt(seqNumSplit[1], 10);
|
|
456
|
-
|
|
457
|
-
// ignore sequence num if this is just page info, it will always be -1 and we don't want
|
|
458
|
-
// to disable the retry loop on this
|
|
459
|
-
if (seqNum === -1) {
|
|
460
|
-
break;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// we disable the retry loop if we start running behind
|
|
464
|
-
if (seqNum <= this.latestReceivedSeqNumber) {
|
|
465
|
-
this.isRetryLoopDisabled = true;
|
|
466
|
-
} else {
|
|
467
|
-
this.isRetryLoopDisabled = false;
|
|
468
|
-
|
|
469
|
-
// setting the latest received seqNum
|
|
470
|
-
this.latestReceivedSeqNumber = seqNum;
|
|
471
|
-
|
|
472
|
-
// clear the retry queue when for this seqNum and below
|
|
473
|
-
this._clearRetryQueue(seqNum);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// Metroplex acknowledged at least once, so resolve the promise
|
|
477
|
-
if (!this.ackedOnce && metroAckCallback) {
|
|
478
|
-
this.ackedOnce = true;
|
|
479
|
-
metroAckCallback();
|
|
480
|
-
}
|
|
311
|
+
/**
|
|
312
|
+
* connectSocket will establish a websocket connection to the metroplex
|
|
313
|
+
* service
|
|
314
|
+
*/
|
|
315
|
+
connectSocket() {
|
|
316
|
+
if (this.isConnected() || this.isConnecting()) {
|
|
317
|
+
// self resolving promise since we are already ready to send
|
|
318
|
+
// requests, if we did reach state 1, we already sent initial
|
|
319
|
+
// request, thus not sending it again.
|
|
320
|
+
return this.connectionPromise;
|
|
481
321
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
322
|
+
// we return a promisified socket resolver to be able to chain the
|
|
323
|
+
// opening of the socket
|
|
324
|
+
this.connectionPromise = this.handleConnect(false);
|
|
325
|
+
return this.connectionPromise;
|
|
326
|
+
}
|
|
327
|
+
/** Calculates and sets the end_at field of the payload
|
|
328
|
+
* @param {} payload
|
|
329
|
+
* @param {} isPageVisit
|
|
330
|
+
*/
|
|
331
|
+
addEndTimeToPayload(payload, isPageVisit) {
|
|
332
|
+
const delta = Math.ceil(safePerformanceNow() - this.sessionStartTime);
|
|
333
|
+
// update session length if this is a page visit event
|
|
334
|
+
if (isPageVisit) {
|
|
335
|
+
this.sessionLength = delta;
|
|
485
336
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
337
|
+
// Assigning this value here is much better than copying the whole object.
|
|
338
|
+
// eslint-disable-next-line no-param-reassign
|
|
339
|
+
payload[END_AT_ATT_NAME] = new Date(this.sessionTimestamp.getTime() + delta).toISOString();
|
|
489
340
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
* removes messages from the retry queue that are smaller than the
|
|
509
|
-
* latest stored message in metroplex
|
|
510
|
-
* @param {} seqNum
|
|
511
|
-
*/
|
|
512
|
-
_clearRetryQueue(seqNum) {
|
|
513
|
-
this.latestReceivedSeqNumStoredTime = new Date();
|
|
514
|
-
this.retryMessageQueue = this.retryMessageQueue.filter(
|
|
515
|
-
message =>
|
|
516
|
-
this._messagePayloadHasLargerSeqNum(
|
|
517
|
-
message,
|
|
518
|
-
PAGE_VISIT_PART_ATT_NAME,
|
|
519
|
-
seqNum,
|
|
520
|
-
) ||
|
|
521
|
-
this._messagePayloadHasLargerSeqNum(
|
|
522
|
-
message,
|
|
523
|
-
PAGE_VISIT_VID_FRAG_ATT_NAME,
|
|
524
|
-
seqNum,
|
|
525
|
-
),
|
|
526
|
-
);
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/** will resend everything that is in the retry queue */
|
|
530
|
-
_sendUnconfirmedMessages(socketWasAlreadyOpen) {
|
|
531
|
-
// we only check once. If the socket is disconnected midway, the events would
|
|
532
|
-
// still be in the retry queue.
|
|
533
|
-
if (!this.isConnected() || ClientConfig.getInstance().isClientDisabled) {
|
|
534
|
-
return;
|
|
341
|
+
/** open handler for socket */
|
|
342
|
+
async _onSocketOpen() {
|
|
343
|
+
// if we are not connected, we do not send any messages
|
|
344
|
+
if (!this.isConnected() || ClientConfig.getInstance().isClientDisabled) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
await this._sendSocketMessage(await this.getPageInformation());
|
|
348
|
+
// Set this to allow normal page visit and video frag messages to be sent
|
|
349
|
+
this.pageVisitInfoSent = true;
|
|
350
|
+
this.currentConnectionAttempts = 0;
|
|
351
|
+
// Resend the pagevisit or video message type before sending data so reset the prev type
|
|
352
|
+
this.previousMessageType = '';
|
|
353
|
+
// attempt to send the unconfirmed messages again
|
|
354
|
+
await this._sendUnconfirmedMessages(false);
|
|
355
|
+
// setting up the socket retry mechanism
|
|
356
|
+
this.setupRetryMechanism();
|
|
357
|
+
// increasing the connection count everytime we connect
|
|
358
|
+
this.connectionCount += 1;
|
|
535
359
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
360
|
+
/** message handler for socket
|
|
361
|
+
* @param {} event
|
|
362
|
+
*/
|
|
363
|
+
async _onSocketMessage(event) {
|
|
364
|
+
switch (event.data) {
|
|
365
|
+
case STOP_STORING_VID_SOCKET_MESSAGE:
|
|
366
|
+
// stopping vids from being sent
|
|
367
|
+
this.metroplexTypeLock[VIDEO_METROPLEX_TYPE] = true;
|
|
368
|
+
StoredMetrics.getInstance().setDidCutVideo();
|
|
369
|
+
break;
|
|
370
|
+
case STOP_STORING_PV_SOCKET_MESSAGE:
|
|
371
|
+
// stopping pv from being sent
|
|
372
|
+
this.metroplexTypeLock[PV_METROPLEX_TYPE] = true;
|
|
373
|
+
StoredMetrics.getInstance().setDidCutPv();
|
|
374
|
+
break;
|
|
375
|
+
case BLOCK_SOCKET_MESSAGE:
|
|
376
|
+
// we are disabling collect for 1 day if we get the
|
|
377
|
+
// block message from metroplex. We are probably on it
|
|
378
|
+
await ClientConfig.getInstance().lockClient(1440, 'Metroplex blocked script');
|
|
379
|
+
this.close();
|
|
380
|
+
break;
|
|
381
|
+
case CLOSE_CONNECTION_FORCEFULLY:
|
|
382
|
+
this.close();
|
|
383
|
+
break;
|
|
384
|
+
case OK_SOCKET_MESSAGE:
|
|
385
|
+
// we do nothing on an OK
|
|
386
|
+
break;
|
|
387
|
+
default:
|
|
388
|
+
// we now need to check if we receive text that contains
|
|
389
|
+
// the text SEQ_NUM. if that is true, then its a seq num
|
|
390
|
+
// and we need to clear out the retry queue.
|
|
391
|
+
if (event.data.includes(SEQ_NUM_ATT_NAME)) {
|
|
392
|
+
const seqNumSplit = event.data.split(`${SEQ_NUM_ATT_NAME}:`);
|
|
393
|
+
if (seqNumSplit.length < 2) {
|
|
394
|
+
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(`Invalid message received from metroplex while clearing retry queue ${event.data}`, false, SEVERITY.error);
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
// the second element in the string split is the sequence number
|
|
398
|
+
const seqNum = parseInt(seqNumSplit[1], 10);
|
|
399
|
+
// ignore sequence num if this is just page info, it will always be -1 and we don't want
|
|
400
|
+
// to disable the retry loop on this
|
|
401
|
+
if (seqNum === -1) {
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
// we disable the retry loop if we start running behind
|
|
405
|
+
if (seqNum <= this.latestReceivedSeqNumber) {
|
|
406
|
+
this.isRetryLoopDisabled = true;
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
this.isRetryLoopDisabled = false;
|
|
410
|
+
// setting the latest received seqNum
|
|
411
|
+
this.latestReceivedSeqNumber = seqNum;
|
|
412
|
+
// clear the retry queue when for this seqNum and below
|
|
413
|
+
this._clearRetryQueue(seqNum);
|
|
414
|
+
}
|
|
415
|
+
if (!this.ackedOnce) {
|
|
416
|
+
this.ackedOnce = true;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (this._tryProcessHelpCodeResponse(event.data)) {
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
break;
|
|
423
|
+
// still need to collect the id sent back from metroplex
|
|
424
|
+
}
|
|
554
425
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
)
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
const { type, payload } = this.retryMessageQueue[i];
|
|
564
|
-
|
|
565
|
-
// sending the data to metroplex
|
|
566
|
-
if (!this._sendSocketMessage(payload)) {
|
|
567
|
-
break;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
this.previousMessageType = type;
|
|
426
|
+
/**
|
|
427
|
+
* Returns true if the message's payload has the payload type given and has a sequence
|
|
428
|
+
* number higher than seqNum
|
|
429
|
+
*/
|
|
430
|
+
_messagePayloadHasLargerSeqNum(message, payloadType, seqNum) {
|
|
431
|
+
return (message.payload[payloadType] &&
|
|
432
|
+
message.payload[payloadType][SEQ_NUM_ATT_NAME] &&
|
|
433
|
+
message.payload[payloadType][SEQ_NUM_ATT_NAME] > seqNum);
|
|
571
434
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
/** sets up events that will trigger the event queue to be emptied */
|
|
582
|
-
_setupOffloadEvents() {
|
|
583
|
-
// when the page hides we try getting all the events
|
|
584
|
-
// and empty the event and retry queue.
|
|
585
|
-
addSafeEventListener(window, 'pagehide', () => {
|
|
586
|
-
this._handleUnload();
|
|
587
|
-
});
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
/**
|
|
591
|
-
* will handle the final moments of a page being active. It
|
|
592
|
-
* will try to empty both the queues with beacons.
|
|
593
|
-
*/
|
|
594
|
-
_handleUnload() {
|
|
595
|
-
// closing the socket since the page is unloading
|
|
596
|
-
this.close();
|
|
597
|
-
|
|
598
|
-
// if we disable the client within the pagevisit, we do not
|
|
599
|
-
// post anything during the unloading.
|
|
600
|
-
if (ClientConfig.getInstance().isClientDisabled) {
|
|
601
|
-
return;
|
|
435
|
+
/**
|
|
436
|
+
* removes messages from the retry queue that are smaller than the
|
|
437
|
+
* latest stored message in metroplex
|
|
438
|
+
*/
|
|
439
|
+
_clearRetryQueue(seqNum) {
|
|
440
|
+
this.latestReceivedSeqNumStoredTime = new Date();
|
|
441
|
+
this.retryMessageQueue = this.retryMessageQueue.filter(message => this._messagePayloadHasLargerSeqNum(message, PAGE_VISIT_PART_ATT_NAME, seqNum) ||
|
|
442
|
+
this._messagePayloadHasLargerSeqNum(message, PAGE_VISIT_VID_FRAG_ATT_NAME, seqNum));
|
|
602
443
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
444
|
+
/** will resend everything that is in the retry queue */
|
|
445
|
+
async _sendUnconfirmedMessages(socketWasAlreadyOpen) {
|
|
446
|
+
noibuLog('_sendUnconfirmedMessages');
|
|
447
|
+
// we only check once. If the socket is disconnected midway, the events would
|
|
448
|
+
// still be in the retry queue.
|
|
449
|
+
if (!this.isConnected() || ClientConfig.getInstance().isClientDisabled) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
// We always send if this is on socket open
|
|
453
|
+
if (socketWasAlreadyOpen) {
|
|
454
|
+
// we do not send the messages to metroplex if we are falling behind on received
|
|
455
|
+
// sequence numbers
|
|
456
|
+
const someTimeAgo = new Date();
|
|
457
|
+
someTimeAgo.setMilliseconds(someTimeAgo.getMilliseconds() - this.metroRetryFrequencyMS);
|
|
458
|
+
if (someTimeAgo < this.latestReceivedSeqNumStoredTime) {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
// we do not resend messages to metroplex if we are running behind
|
|
462
|
+
if (this.isRetryLoopDisabled) {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
// removing all the messages with a blocked type from the retry queue
|
|
467
|
+
this.retryMessageQueue = this.retryMessageQueue.filter(message => !(message.type in this.metroplexTypeLock));
|
|
468
|
+
// we dont remove any items from this queue in this function
|
|
469
|
+
for (let i = 0; i < this.retryMessageQueue.length; i += 1) {
|
|
470
|
+
const { type, payload } = this.retryMessageQueue[i];
|
|
471
|
+
// sending the data to metroplex
|
|
472
|
+
// eslint-disable-next-line no-await-in-loop
|
|
473
|
+
await this._sendSocketMessage(payload);
|
|
474
|
+
this.previousMessageType = type;
|
|
475
|
+
}
|
|
608
476
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
* will post full page visit to metroplex. It
|
|
615
|
-
* will try to empty both the queues with beacons.
|
|
616
|
-
* @param {} maxMessageSize
|
|
617
|
-
*/
|
|
618
|
-
async postFullPageVisit(maxMessageSize) {
|
|
619
|
-
if (this.retryMessageQueue.length === 0) {
|
|
620
|
-
return;
|
|
477
|
+
/** sets up the interval to empty the queue as we receive confirmation messages from metroplex */
|
|
478
|
+
setupRetryMechanism() {
|
|
479
|
+
this.retryMetroplexInterval = setInterval(() => {
|
|
480
|
+
this._sendUnconfirmedMessages(true);
|
|
481
|
+
}, METROPLEX_RETRY_FREQUENCY);
|
|
621
482
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
postInfo.push(postInfoMessage);
|
|
661
|
-
|
|
662
|
-
// resetting currentCompletePv, since we need to keep adding
|
|
663
|
-
// events to a blank object to send to metroplex.
|
|
664
|
-
// retain the video part count, so we don't overwrite anything
|
|
665
|
-
currentCompletePv = {
|
|
666
|
-
[PAGE_VISIT_INFORMATION_ATT_NAME]: pageInfo,
|
|
667
|
-
[PAGE_VISIT_PART_ATT_NAME]: [],
|
|
668
|
-
[PAGE_VISIT_VID_FRAG_ATT_NAME]: [],
|
|
669
|
-
[PAGE_VISIT_HTTP_DATA_ATT_NAME]: [],
|
|
670
|
-
[VIDEO_PART_COUNT_ATT_NAME]:
|
|
671
|
-
currentCompletePv[VIDEO_PART_COUNT_ATT_NAME],
|
|
483
|
+
/** sets up events that will trigger the event queue to be emptied */
|
|
484
|
+
_setupOffloadEvents() {
|
|
485
|
+
// when the page hides we try getting all the events
|
|
486
|
+
// and empty the event and retry queue.
|
|
487
|
+
addSafeEventListener(window, 'pagehide', () => this._handleUnload(), false);
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* will handle the final moments of a page being active. It
|
|
491
|
+
* will try to empty both the queues with beacons.
|
|
492
|
+
*/
|
|
493
|
+
async _handleUnload() {
|
|
494
|
+
// closing the socket since the page is unloading
|
|
495
|
+
this.close();
|
|
496
|
+
// if we disable the client within the pagevisit, we do not
|
|
497
|
+
// post anything during the unloading.
|
|
498
|
+
if (ClientConfig.getInstance().isClientDisabled) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
// Don't send the final complete PV if the session has become inactive
|
|
502
|
+
// we already sent the PV when went inactive
|
|
503
|
+
if (ClientConfig.getInstance().isInactive()) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
await this.postFullPageVisit(MAX_BEACON_PAYLOAD_SIZE);
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* will post full page visit to metroplex. It
|
|
510
|
+
* will try to empty both the queues with beacons.
|
|
511
|
+
*/
|
|
512
|
+
async postFullPageVisit(maxMessageSize) {
|
|
513
|
+
if (this.retryMessageQueue.length === 0) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
// debug counters
|
|
517
|
+
const postInfo = [];
|
|
518
|
+
const numDropped = {
|
|
519
|
+
[VIDEO_METROPLEX_TYPE]: 0,
|
|
520
|
+
[PV_METROPLEX_TYPE]: 0,
|
|
672
521
|
};
|
|
673
|
-
|
|
674
|
-
currentCompletePv
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
522
|
+
let currentMsgPayloadSize = 0;
|
|
523
|
+
let currentCompletePv = {
|
|
524
|
+
[PAGE_VISIT_INFORMATION_ATT_NAME]: await this.getPageInformation(),
|
|
525
|
+
[PAGE_VISIT_PART_ATT_NAME]: [],
|
|
526
|
+
[PAGE_VISIT_VID_FRAG_ATT_NAME]: [],
|
|
527
|
+
[PAGE_VISIT_HTTP_DATA_ATT_NAME]: [],
|
|
528
|
+
[VIDEO_PART_COUNT_ATT_NAME]: this.connectionCount,
|
|
529
|
+
};
|
|
530
|
+
currentCompletePv[PAGE_VISIT_INFORMATION_ATT_NAME][IS_LAST_ATT_NAME] = true;
|
|
531
|
+
const pageInfo = await this.getPageInformation();
|
|
532
|
+
this.retryMessageQueue.forEach(msg => {
|
|
533
|
+
// can't use two variables types in obj deconstruction
|
|
534
|
+
// eslint-disable-next-line prefer-const
|
|
535
|
+
let { type, payload } = msg;
|
|
536
|
+
// If the message is larger than the beacon limit then skip to the next message
|
|
537
|
+
const currentPayloadSize = new Blob([stringifyJSON(payload)]).size;
|
|
538
|
+
if (currentPayloadSize > maxMessageSize) {
|
|
539
|
+
// increment dropped count for this type
|
|
540
|
+
numDropped[type] += 1;
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
currentMsgPayloadSize += currentPayloadSize;
|
|
544
|
+
if (currentMsgPayloadSize >= maxMessageSize) {
|
|
545
|
+
this.postMessage(currentCompletePv);
|
|
546
|
+
// add to post info
|
|
547
|
+
let postInfoMessage = `Vid: ${currentCompletePv[PAGE_VISIT_VID_FRAG_ATT_NAME].length}`;
|
|
548
|
+
postInfoMessage += ` PV: ${currentCompletePv[PAGE_VISIT_PART_ATT_NAME].length}`;
|
|
549
|
+
postInfoMessage += ` HTTP: ${currentCompletePv[PAGE_VISIT_HTTP_DATA_ATT_NAME].length},`;
|
|
550
|
+
postInfo.push(postInfoMessage);
|
|
551
|
+
// resetting currentCompletePv, since we need to keep adding
|
|
552
|
+
// events to a blank object to send to metroplex.
|
|
553
|
+
// retain the video part count, so we don't overwrite anything
|
|
554
|
+
currentCompletePv = {
|
|
555
|
+
[PAGE_VISIT_INFORMATION_ATT_NAME]: pageInfo,
|
|
556
|
+
[PAGE_VISIT_PART_ATT_NAME]: [],
|
|
557
|
+
[PAGE_VISIT_VID_FRAG_ATT_NAME]: [],
|
|
558
|
+
[PAGE_VISIT_HTTP_DATA_ATT_NAME]: [],
|
|
559
|
+
[VIDEO_PART_COUNT_ATT_NAME]: currentCompletePv[VIDEO_PART_COUNT_ATT_NAME],
|
|
560
|
+
};
|
|
561
|
+
currentCompletePv[PAGE_VISIT_INFORMATION_ATT_NAME][IS_LAST_ATT_NAME] =
|
|
562
|
+
true;
|
|
563
|
+
// resetting the message size based on what is being added
|
|
564
|
+
currentMsgPayloadSize = currentPayloadSize;
|
|
565
|
+
}
|
|
566
|
+
switch (type) {
|
|
567
|
+
case VIDEO_METROPLEX_TYPE:
|
|
568
|
+
currentCompletePv[PAGE_VISIT_VID_FRAG_ATT_NAME].push(payload[PAGE_VISIT_VID_FRAG_ATT_NAME]);
|
|
569
|
+
break;
|
|
570
|
+
case PV_METROPLEX_TYPE:
|
|
571
|
+
currentCompletePv[PAGE_VISIT_PART_ATT_NAME].push(payload[PAGE_VISIT_PART_ATT_NAME]);
|
|
572
|
+
break;
|
|
573
|
+
case HTTP_DATA_METROPLEX_TYPE:
|
|
574
|
+
currentCompletePv[PAGE_VISIT_HTTP_DATA_ATT_NAME].push(payload[PAGE_VISIT_HTTP_DATA_ATT_NAME]);
|
|
575
|
+
break;
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
this.postMessage(currentCompletePv);
|
|
579
|
+
// debug log if large retry message queue
|
|
580
|
+
if (this.retryMessageQueue.length > 100) {
|
|
581
|
+
let postInfoMessage = `Vid: ${currentCompletePv[PAGE_VISIT_VID_FRAG_ATT_NAME].length}`;
|
|
582
|
+
postInfoMessage += ` PV: ${currentCompletePv[PAGE_VISIT_PART_ATT_NAME].length}`;
|
|
583
|
+
postInfoMessage += ` HTTP: ${currentCompletePv[PAGE_VISIT_HTTP_DATA_ATT_NAME].length},`;
|
|
584
|
+
postInfo.push(postInfoMessage);
|
|
585
|
+
// we completed posted the full pv, send the confirmation debug logs
|
|
586
|
+
// build the debug message
|
|
587
|
+
let message = 'POST Full PV complete';
|
|
588
|
+
// POST counts
|
|
589
|
+
message += `, POSTs count: ${postInfo.length}`;
|
|
590
|
+
message += `, POSTs info: ${stringifyJSON(postInfo)}`;
|
|
591
|
+
// Initial retry message queue size
|
|
592
|
+
message += `, Retry message queue size: ${this.retryMessageQueue.length}`;
|
|
593
|
+
// Num drops
|
|
594
|
+
if (numDropped[VIDEO_METROPLEX_TYPE] > 0) {
|
|
595
|
+
message += `, Video parts dropped: ${numDropped[VIDEO_METROPLEX_TYPE]}`;
|
|
596
|
+
}
|
|
597
|
+
if (numDropped[PV_METROPLEX_TYPE] > 0) {
|
|
598
|
+
message += `, Page visit parts dropped: ${numDropped[PV_METROPLEX_TYPE]}`;
|
|
599
|
+
}
|
|
600
|
+
if (numDropped[HTTP_DATA_METROPLEX_TYPE] > 0) {
|
|
601
|
+
message += `, HTTP data parts dropped: ${numDropped[HTTP_DATA_METROPLEX_TYPE]}`;
|
|
602
|
+
}
|
|
603
|
+
// Sequence info
|
|
604
|
+
message += `, Sequence Info: Latest ${this.messageSequenceNum}`;
|
|
605
|
+
message += ` Ack'd ${this.latestReceivedSeqNumStoredTime} ${this.latestReceivedSeqNumber}`;
|
|
606
|
+
// if client was disabled (due to inactive or otherwise) enable briefly so the
|
|
607
|
+
// debug message gets through
|
|
608
|
+
const clientDisabled = ClientConfig.getInstance().isClientDisabled;
|
|
609
|
+
ClientConfig.getInstance().isClientDisabled = false;
|
|
610
|
+
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(message, clientDisabled, SEVERITY.warn);
|
|
611
|
+
}
|
|
745
612
|
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
},
|
|
765
|
-
body: stringifyJSON(updatedMsg),
|
|
766
|
-
// keep alive outlives the current page, its the same as beacon
|
|
767
|
-
keepalive: true,
|
|
768
|
-
});
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
/**
|
|
772
|
-
* Stringifies the payload into JSON, sends it to the back end if the session
|
|
773
|
-
* is active and returns true. If inactive the session and socket are closed
|
|
774
|
-
* and this method returns false.
|
|
775
|
-
* @param {} payload
|
|
776
|
-
*/
|
|
777
|
-
_sendSocketMessage(payload) {
|
|
778
|
-
noibuLog('_sendSocketMessage');
|
|
779
|
-
const closeIfInactive = this.closeIfInactive();
|
|
780
|
-
if (closeIfInactive) {
|
|
781
|
-
noibuLog('_sendSocketMessage dropped due to', { closeIfInactive });
|
|
782
|
-
return false;
|
|
613
|
+
/**
|
|
614
|
+
* will send a message to metroplex via a post request that will outlive the current page
|
|
615
|
+
*/
|
|
616
|
+
async postMessage(msg) {
|
|
617
|
+
const updatedMsg = msg;
|
|
618
|
+
// ensure a unique video part number each call
|
|
619
|
+
updatedMsg[VIDEO_PART_COUNT_ATT_NAME] += 1;
|
|
620
|
+
// we send the remainder elements
|
|
621
|
+
unwrapNoibuWrapped(fetch)(this.postURL, {
|
|
622
|
+
method: 'POST',
|
|
623
|
+
headers: {
|
|
624
|
+
'content-type': 'application/json',
|
|
625
|
+
'User-Agent': await getUserAgent(),
|
|
626
|
+
},
|
|
627
|
+
body: stringifyJSON(updatedMsg),
|
|
628
|
+
// keep alive outlives the current page, its the same as beacon
|
|
629
|
+
keepalive: true,
|
|
630
|
+
});
|
|
783
631
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
ClientConfig.getInstance().lockClientUntilNextPage(
|
|
802
|
-
'Session is inactive, locking until next page',
|
|
803
|
-
);
|
|
804
|
-
this.close();
|
|
805
|
-
|
|
806
|
-
// post metrics now so we don't include events after going inactive
|
|
807
|
-
StoredMetrics.getInstance().postMetrics('inactive');
|
|
808
|
-
|
|
809
|
-
// post retry queue now so we don't include event after going inactive
|
|
810
|
-
this.postFullPageVisit(MAX_BEACON_PAYLOAD_SIZE);
|
|
632
|
+
/**
|
|
633
|
+
* Stringifies the payload into JSON, sends it to the back end if the session
|
|
634
|
+
* is active and returns true. If inactive the session and socket are closed
|
|
635
|
+
* and this method returns false.
|
|
636
|
+
*/
|
|
637
|
+
async _sendSocketMessage(payload) {
|
|
638
|
+
noibuLog('_sendSocketMessage');
|
|
639
|
+
const closeIfInactive = await this.closeIfInactive();
|
|
640
|
+
if (closeIfInactive) {
|
|
641
|
+
noibuLog('_sendSocketMessage dropped due to', { closeIfInactive });
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
const payloadJson = stringifyJSON(payload);
|
|
645
|
+
noibuLog(`_sendSocketMessage sending: ${payloadJson}`);
|
|
646
|
+
if (this.socket) {
|
|
647
|
+
this.socket.send(payloadJson);
|
|
648
|
+
}
|
|
811
649
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
650
|
+
/**
|
|
651
|
+
* Closes the socket connection if the session is inactive. Returns true if the
|
|
652
|
+
* session is inactive
|
|
653
|
+
*/
|
|
654
|
+
async closeIfInactive() {
|
|
655
|
+
const inactive = ClientConfig.getInstance().isInactive();
|
|
656
|
+
// if we weren't already inactive, go to inactive state
|
|
657
|
+
if (inactive && !ClientConfig.getInstance().isClientDisabled) {
|
|
658
|
+
// lock the client for 1 minute, since the lock expires
|
|
659
|
+
// only on new page loads this will lock the client until
|
|
660
|
+
// the next page is loaded.
|
|
661
|
+
await ClientConfig.getInstance().lockClientUntilNextPage('Session is inactive, locking until next page');
|
|
662
|
+
this.close();
|
|
663
|
+
Promise.all([
|
|
664
|
+
// post metrics now so we don't include events after going inactive
|
|
665
|
+
StoredMetrics.getInstance().postMetrics(),
|
|
666
|
+
// post retry queue now so we don't include event after going inactive
|
|
667
|
+
this.postFullPageVisit(MAX_BEACON_PAYLOAD_SIZE),
|
|
668
|
+
]);
|
|
669
|
+
}
|
|
670
|
+
return inactive;
|
|
671
|
+
}
|
|
672
|
+
/** will get page information, calling this will increase the connection count */
|
|
673
|
+
async getPageInformation() {
|
|
674
|
+
return {
|
|
675
|
+
[BROWSER_ID_ATT_NAME]: ClientConfig.getInstance().browserId,
|
|
676
|
+
[PV_ID_ATT_NAME]: ClientConfig.getInstance().pageVisitId,
|
|
677
|
+
[VER_ATT_NAME]: CURRENT_PV_VERSION,
|
|
678
|
+
[PV_SEQ_ATT_NAME]: await ClientConfig.getInstance().getPageVisitSeq(),
|
|
679
|
+
[ON_URL_ATT_NAME]: this._initialURL,
|
|
680
|
+
[REF_URL_ATT_NAME]: this.initialReferingURL,
|
|
681
|
+
[STARTED_AT_ATT_NAME]: this.sessionTimestamp.toISOString(),
|
|
682
|
+
[CONN_COUNT_ATT_NAME]: this.connectionCount,
|
|
683
|
+
[COLLECT_VER_ATT_NAME]: CURRENT_NOIBUJS_VERSION,
|
|
684
|
+
[IS_LAST_ATT_NAME]: false,
|
|
685
|
+
[SCRIPT_ID_ATT_NAME]: GET_SCRIPT_ID(),
|
|
686
|
+
[SCRIPT_INSTANCE_ID_ATT_NAME]: this.scriptInstanceId,
|
|
687
|
+
[METROPLEX_SOCKET_INSTANCE_ID_ATT_NAME]: this.instanceId,
|
|
688
|
+
[SOCKET_INSTANCE_ID_ATT_NAME]: this.socketInstanceId,
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Try to parse help code response and fire custom event
|
|
693
|
+
* @param {String} response
|
|
694
|
+
*/
|
|
695
|
+
_tryProcessHelpCodeResponse(response) {
|
|
696
|
+
const prefix = `${HELP_CODE_ATT_NAME}:`;
|
|
697
|
+
if (typeof response !== 'string' || !response.startsWith(prefix)) {
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
const data = response.substring(prefix.length);
|
|
701
|
+
const success = /^\d{6}$/.test(data);
|
|
702
|
+
if (this.helpCodeCb) {
|
|
703
|
+
this.helpCodeCb({ detail: { success, data } });
|
|
704
|
+
}
|
|
705
|
+
return true;
|
|
847
706
|
}
|
|
848
|
-
|
|
849
|
-
const data = response.substring(prefix.length);
|
|
850
|
-
const success = /^\d{6}$/.test(data);
|
|
851
|
-
|
|
852
|
-
this.helpCodeCb({ detail: { success, data } });
|
|
853
|
-
|
|
854
|
-
return true;
|
|
855
|
-
}
|
|
856
707
|
}
|
|
857
708
|
|
|
858
709
|
export { MetroplexSocket as default };
|