UNPKG

46.7 kBJavaScriptView Raw
1var __extends = (this && this.__extends) || (function () {
2 var extendStatics = function (d, b) {
3 extendStatics = Object.setPrototypeOf ||
4 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
5 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
6 return extendStatics(d, b);
7 };
8 return function (d, b) {
9 extendStatics(d, b);
10 function __() { this.constructor = d; }
11 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
12 };
13})();
14var __assign = (this && this.__assign) || function () {
15 __assign = Object.assign || function(t) {
16 for (var s, i = 1, n = arguments.length; i < n; i++) {
17 s = arguments[i];
18 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
19 t[p] = s[p];
20 }
21 return t;
22 };
23 return __assign.apply(this, arguments);
24};
25var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27 return new (P || (P = Promise))(function (resolve, reject) {
28 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31 step((generator = generator.apply(thisArg, _arguments || [])).next());
32 });
33};
34var __generator = (this && this.__generator) || function (thisArg, body) {
35 var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
36 return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
37 function verb(n) { return function (v) { return step([n, v]); }; }
38 function step(op) {
39 if (f) throw new TypeError("Generator is already executing.");
40 while (_) try {
41 if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
42 if (y = 0, t) op = [op[0] & 2, t.value];
43 switch (op[0]) {
44 case 0: case 1: t = op; break;
45 case 4: _.label++; return { value: op[1], done: false };
46 case 5: _.label++; y = op[1]; op = [0]; continue;
47 case 7: op = _.ops.pop(); _.trys.pop(); continue;
48 default:
49 if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
50 if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
51 if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
52 if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
53 if (t[2]) _.ops.pop();
54 _.trys.pop(); continue;
55 }
56 op = body.call(thisArg, _);
57 } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
58 if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
59 }
60};
61var __read = (this && this.__read) || function (o, n) {
62 var m = typeof Symbol === "function" && o[Symbol.iterator];
63 if (!m) return o;
64 var i = m.call(o), r, ar = [], e;
65 try {
66 while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
67 }
68 catch (error) { e = { error: error }; }
69 finally {
70 try {
71 if (r && !r.done && (m = i["return"])) m.call(i);
72 }
73 finally { if (e) throw e.error; }
74 }
75 return ar;
76};
77/*
78 * Copyright 2017-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
79 *
80 * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
81 * the License. A copy of the License is located at
82 *
83 * http://aws.amazon.com/apache2.0/
84 *
85 * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
86 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
87 * and limitations under the License.
88 */
89import Observable from 'zen-observable-ts';
90import { GraphQLError } from 'graphql';
91import * as url from 'url';
92import { v4 as uuid } from 'uuid';
93import { Buffer } from 'buffer';
94import { Logger, Credentials, Signer, Hub, Constants, USER_AGENT_HEADER, jitteredExponentialRetry, NonRetryableError, } from '@aws-amplify/core';
95import Cache from '@aws-amplify/cache';
96import Auth from '@aws-amplify/auth';
97import { AbstractPubSubProvider } from '../PubSubProvider';
98import { CONTROL_MSG } from '../../index';
99import { AMPLIFY_SYMBOL, AWS_APPSYNC_REALTIME_HEADERS, CONNECTION_INIT_TIMEOUT, DEFAULT_KEEP_ALIVE_TIMEOUT, MAX_DELAY_MS, MESSAGE_TYPES, NON_RETRYABLE_CODES, SOCKET_STATUS, START_ACK_TIMEOUT, SUBSCRIPTION_STATUS, } from './constants';
100var logger = new Logger('AWSAppSyncRealTimeProvider');
101var dispatchApiEvent = function (event, data, message) {
102 Hub.dispatch('api', { event: event, data: data, message: message }, 'PubSub', AMPLIFY_SYMBOL);
103};
104var standardDomainPattern = /^https:\/\/\w{26}\.appsync\-api\.\w{2}(?:(?:\-\w{2,})+)\-\d\.amazonaws.com\/graphql$/i;
105var customDomainPath = '/realtime';
106var AWSAppSyncRealTimeProvider = /** @class */ (function (_super) {
107 __extends(AWSAppSyncRealTimeProvider, _super);
108 function AWSAppSyncRealTimeProvider() {
109 var _this = _super !== null && _super.apply(this, arguments) || this;
110 _this.socketStatus = SOCKET_STATUS.CLOSED;
111 _this.keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
112 _this.subscriptionObserverMap = new Map();
113 _this.promiseArray = [];
114 return _this;
115 }
116 AWSAppSyncRealTimeProvider.prototype.getNewWebSocket = function (url, protocol) {
117 return new WebSocket(url, protocol);
118 };
119 AWSAppSyncRealTimeProvider.prototype.getProviderName = function () {
120 return 'AWSAppSyncRealTimeProvider';
121 };
122 AWSAppSyncRealTimeProvider.prototype.newClient = function () {
123 throw new Error('Not used here');
124 };
125 AWSAppSyncRealTimeProvider.prototype.publish = function (_topics, _msg, _options) {
126 return __awaiter(this, void 0, void 0, function () {
127 return __generator(this, function (_a) {
128 throw new Error('Operation not supported');
129 });
130 });
131 };
132 // Check if url matches standard domain pattern
133 AWSAppSyncRealTimeProvider.prototype.isCustomDomain = function (url) {
134 return url.match(standardDomainPattern) === null;
135 };
136 AWSAppSyncRealTimeProvider.prototype.subscribe = function (_topics, options) {
137 var _this = this;
138 var appSyncGraphqlEndpoint = options === null || options === void 0 ? void 0 : options.appSyncGraphqlEndpoint;
139 return new Observable(function (observer) {
140 if (!options || !appSyncGraphqlEndpoint) {
141 observer.error({
142 errors: [
143 __assign({}, new GraphQLError("Subscribe only available for AWS AppSync endpoint")),
144 ],
145 });
146 observer.complete();
147 }
148 else {
149 var subscriptionId_1 = uuid();
150 _this._startSubscriptionWithAWSAppSyncRealTime({
151 options: options,
152 observer: observer,
153 subscriptionId: subscriptionId_1,
154 }).catch(function (err) {
155 observer.error({
156 errors: [
157 __assign({}, new GraphQLError(CONTROL_MSG.REALTIME_SUBSCRIPTION_INIT_ERROR + ": " + err)),
158 ],
159 });
160 observer.complete();
161 });
162 return function () { return __awaiter(_this, void 0, void 0, function () {
163 var subscriptionState, err_1;
164 return __generator(this, function (_a) {
165 switch (_a.label) {
166 case 0:
167 _a.trys.push([0, 2, 3, 4]);
168 // Waiting that subscription has been connected before trying to unsubscribe
169 return [4 /*yield*/, this._waitForSubscriptionToBeConnected(subscriptionId_1)];
170 case 1:
171 // Waiting that subscription has been connected before trying to unsubscribe
172 _a.sent();
173 subscriptionState = (this.subscriptionObserverMap.get(subscriptionId_1) || {}).subscriptionState;
174 if (!subscriptionState) {
175 // subscription already unsubscribed
176 return [2 /*return*/];
177 }
178 if (subscriptionState === SUBSCRIPTION_STATUS.CONNECTED) {
179 this._sendUnsubscriptionMessage(subscriptionId_1);
180 }
181 else {
182 throw new Error('Subscription never connected');
183 }
184 return [3 /*break*/, 4];
185 case 2:
186 err_1 = _a.sent();
187 logger.debug("Error while unsubscribing " + err_1);
188 return [3 /*break*/, 4];
189 case 3:
190 this._removeSubscriptionObserver(subscriptionId_1);
191 return [7 /*endfinally*/];
192 case 4: return [2 /*return*/];
193 }
194 });
195 }); };
196 }
197 });
198 };
199 Object.defineProperty(AWSAppSyncRealTimeProvider.prototype, "isSSLEnabled", {
200 get: function () {
201 return !this.options
202 .aws_appsync_dangerously_connect_to_http_endpoint_for_testing;
203 },
204 enumerable: true,
205 configurable: true
206 });
207 AWSAppSyncRealTimeProvider.prototype._startSubscriptionWithAWSAppSyncRealTime = function (_a) {
208 var options = _a.options, observer = _a.observer, subscriptionId = _a.subscriptionId;
209 var _b, _c;
210 return __awaiter(this, void 0, void 0, function () {
211 var appSyncGraphqlEndpoint, authenticationType, query, variables, apiKey, region, _d, graphql_headers, _e, additionalHeaders, subscriptionState, data, dataString, headerObj, _f, _g, subscriptionMessage, stringToAWSRealTime, err_2, message, subscriptionFailedCallback_1, _h, subscriptionFailedCallback, subscriptionReadyCallback;
212 var _j;
213 var _this = this;
214 return __generator(this, function (_k) {
215 switch (_k.label) {
216 case 0:
217 appSyncGraphqlEndpoint = options.appSyncGraphqlEndpoint, authenticationType = options.authenticationType, query = options.query, variables = options.variables, apiKey = options.apiKey, region = options.region, _d = options.graphql_headers, graphql_headers = _d === void 0 ? function () { return ({}); } : _d, _e = options.additionalHeaders, additionalHeaders = _e === void 0 ? {} : _e;
218 subscriptionState = SUBSCRIPTION_STATUS.PENDING;
219 data = {
220 query: query,
221 variables: variables,
222 };
223 // Having a subscription id map will make it simple to forward messages received
224 this.subscriptionObserverMap.set(subscriptionId, {
225 observer: observer,
226 query: query !== null && query !== void 0 ? query : '',
227 variables: variables !== null && variables !== void 0 ? variables : {},
228 subscriptionState: subscriptionState,
229 startAckTimeoutId: undefined,
230 });
231 dataString = JSON.stringify(data);
232 _f = [{}];
233 return [4 /*yield*/, this._awsRealTimeHeaderBasedAuth({
234 apiKey: apiKey,
235 appSyncGraphqlEndpoint: appSyncGraphqlEndpoint,
236 authenticationType: authenticationType,
237 payload: dataString,
238 canonicalUri: '',
239 region: region,
240 additionalHeaders: additionalHeaders,
241 })];
242 case 1:
243 _g = [__assign.apply(void 0, _f.concat([(_k.sent())]))];
244 return [4 /*yield*/, graphql_headers()];
245 case 2:
246 headerObj = __assign.apply(void 0, [__assign.apply(void 0, [__assign.apply(void 0, _g.concat([(_k.sent())])), additionalHeaders]), (_j = {}, _j[USER_AGENT_HEADER] = Constants.userAgent, _j)]);
247 subscriptionMessage = {
248 id: subscriptionId,
249 payload: {
250 data: dataString,
251 extensions: {
252 authorization: __assign({}, headerObj),
253 },
254 },
255 type: MESSAGE_TYPES.GQL_START,
256 };
257 stringToAWSRealTime = JSON.stringify(subscriptionMessage);
258 _k.label = 3;
259 case 3:
260 _k.trys.push([3, 5, , 6]);
261 return [4 /*yield*/, this._initializeWebSocketConnection({
262 apiKey: apiKey,
263 appSyncGraphqlEndpoint: appSyncGraphqlEndpoint,
264 authenticationType: authenticationType,
265 region: region,
266 additionalHeaders: additionalHeaders,
267 })];
268 case 4:
269 _k.sent();
270 return [3 /*break*/, 6];
271 case 5:
272 err_2 = _k.sent();
273 logger.debug({ err: err_2 });
274 message = (_b = err_2['message']) !== null && _b !== void 0 ? _b : '';
275 observer.error({
276 errors: [
277 __assign({}, new GraphQLError(CONTROL_MSG.CONNECTION_FAILED + ": " + message)),
278 ],
279 });
280 observer.complete();
281 subscriptionFailedCallback_1 = (this.subscriptionObserverMap.get(subscriptionId) || {}).subscriptionFailedCallback;
282 // Notify concurrent unsubscription
283 if (typeof subscriptionFailedCallback_1 === 'function') {
284 subscriptionFailedCallback_1();
285 }
286 return [2 /*return*/];
287 case 6:
288 _h = (_c = this.subscriptionObserverMap.get(subscriptionId)) !== null && _c !== void 0 ? _c : {}, subscriptionFailedCallback = _h.subscriptionFailedCallback, subscriptionReadyCallback = _h.subscriptionReadyCallback;
289 // This must be done before sending the message in order to be listening immediately
290 this.subscriptionObserverMap.set(subscriptionId, {
291 observer: observer,
292 subscriptionState: subscriptionState,
293 query: query !== null && query !== void 0 ? query : '',
294 variables: variables !== null && variables !== void 0 ? variables : {},
295 subscriptionReadyCallback: subscriptionReadyCallback,
296 subscriptionFailedCallback: subscriptionFailedCallback,
297 startAckTimeoutId: setTimeout(function () {
298 _this._timeoutStartSubscriptionAck.call(_this, subscriptionId);
299 }, START_ACK_TIMEOUT),
300 });
301 if (this.awsRealTimeSocket) {
302 this.awsRealTimeSocket.send(stringToAWSRealTime);
303 }
304 return [2 /*return*/];
305 }
306 });
307 });
308 };
309 // Waiting that subscription has been connected before trying to unsubscribe
310 AWSAppSyncRealTimeProvider.prototype._waitForSubscriptionToBeConnected = function (subscriptionId) {
311 return __awaiter(this, void 0, void 0, function () {
312 var subscriptionObserver, subscriptionState;
313 var _this = this;
314 return __generator(this, function (_a) {
315 subscriptionObserver = this.subscriptionObserverMap.get(subscriptionId);
316 if (subscriptionObserver) {
317 subscriptionState = subscriptionObserver.subscriptionState;
318 // This in case unsubscribe is invoked before sending start subscription message
319 if (subscriptionState === SUBSCRIPTION_STATUS.PENDING) {
320 return [2 /*return*/, new Promise(function (res, rej) {
321 var observer = subscriptionObserver.observer, subscriptionState = subscriptionObserver.subscriptionState, variables = subscriptionObserver.variables, query = subscriptionObserver.query;
322 _this.subscriptionObserverMap.set(subscriptionId, {
323 observer: observer,
324 subscriptionState: subscriptionState,
325 variables: variables,
326 query: query,
327 subscriptionReadyCallback: res,
328 subscriptionFailedCallback: rej,
329 });
330 })];
331 }
332 }
333 return [2 /*return*/];
334 });
335 });
336 };
337 AWSAppSyncRealTimeProvider.prototype._sendUnsubscriptionMessage = function (subscriptionId) {
338 try {
339 if (this.awsRealTimeSocket &&
340 this.awsRealTimeSocket.readyState === WebSocket.OPEN &&
341 this.socketStatus === SOCKET_STATUS.READY) {
342 // Preparing unsubscribe message to stop receiving messages for that subscription
343 var unsubscribeMessage = {
344 id: subscriptionId,
345 type: MESSAGE_TYPES.GQL_STOP,
346 };
347 var stringToAWSRealTime = JSON.stringify(unsubscribeMessage);
348 this.awsRealTimeSocket.send(stringToAWSRealTime);
349 }
350 }
351 catch (err) {
352 // If GQL_STOP is not sent because of disconnection issue, then there is nothing the client can do
353 logger.debug({ err: err });
354 }
355 };
356 AWSAppSyncRealTimeProvider.prototype._removeSubscriptionObserver = function (subscriptionId) {
357 this.subscriptionObserverMap.delete(subscriptionId);
358 // Verifying 1000ms after removing subscription in case there are new subscription unmount/mount
359 setTimeout(this._closeSocketIfRequired.bind(this), 1000);
360 };
361 AWSAppSyncRealTimeProvider.prototype._closeSocketIfRequired = function () {
362 if (this.subscriptionObserverMap.size > 0) {
363 // Active subscriptions on the WebSocket
364 return;
365 }
366 if (!this.awsRealTimeSocket) {
367 this.socketStatus = SOCKET_STATUS.CLOSED;
368 return;
369 }
370 if (this.awsRealTimeSocket.bufferedAmount > 0) {
371 // Still data on the WebSocket
372 setTimeout(this._closeSocketIfRequired.bind(this), 1000);
373 }
374 else {
375 logger.debug('closing WebSocket...');
376 if (this.keepAliveTimeoutId)
377 clearTimeout(this.keepAliveTimeoutId);
378 var tempSocket = this.awsRealTimeSocket;
379 // Cleaning callbacks to avoid race condition, socket still exists
380 tempSocket.onclose = null;
381 tempSocket.onerror = null;
382 tempSocket.close(1000);
383 this.awsRealTimeSocket = undefined;
384 this.socketStatus = SOCKET_STATUS.CLOSED;
385 }
386 };
387 AWSAppSyncRealTimeProvider.prototype._handleIncomingSubscriptionMessage = function (message) {
388 logger.debug("subscription message from AWS AppSync RealTime: " + message.data);
389 var _a = JSON.parse(message.data), _b = _a.id, id = _b === void 0 ? '' : _b, payload = _a.payload, type = _a.type;
390 var _c = this.subscriptionObserverMap.get(id) || {}, _d = _c.observer, observer = _d === void 0 ? null : _d, _e = _c.query, query = _e === void 0 ? '' : _e, _f = _c.variables, variables = _f === void 0 ? {} : _f, startAckTimeoutId = _c.startAckTimeoutId, subscriptionReadyCallback = _c.subscriptionReadyCallback, subscriptionFailedCallback = _c.subscriptionFailedCallback;
391 logger.debug({ id: id, observer: observer, query: query, variables: variables });
392 if (type === MESSAGE_TYPES.GQL_DATA && payload && payload.data) {
393 if (observer) {
394 observer.next(payload);
395 }
396 else {
397 logger.debug("observer not found for id: " + id);
398 }
399 return;
400 }
401 if (type === MESSAGE_TYPES.GQL_START_ACK) {
402 logger.debug("subscription ready for " + JSON.stringify({ query: query, variables: variables }));
403 if (typeof subscriptionReadyCallback === 'function') {
404 subscriptionReadyCallback();
405 }
406 if (startAckTimeoutId)
407 clearTimeout(startAckTimeoutId);
408 dispatchApiEvent(CONTROL_MSG.SUBSCRIPTION_ACK, { query: query, variables: variables }, 'Connection established for subscription');
409 var subscriptionState = SUBSCRIPTION_STATUS.CONNECTED;
410 if (observer) {
411 this.subscriptionObserverMap.set(id, {
412 observer: observer,
413 query: query,
414 variables: variables,
415 startAckTimeoutId: undefined,
416 subscriptionState: subscriptionState,
417 subscriptionReadyCallback: subscriptionReadyCallback,
418 subscriptionFailedCallback: subscriptionFailedCallback,
419 });
420 }
421 // TODO: emit event on hub but it requires to store the id first
422 return;
423 }
424 if (type === MESSAGE_TYPES.GQL_CONNECTION_KEEP_ALIVE) {
425 if (this.keepAliveTimeoutId)
426 clearTimeout(this.keepAliveTimeoutId);
427 this.keepAliveTimeoutId = setTimeout(this._errorDisconnect.bind(this, CONTROL_MSG.TIMEOUT_DISCONNECT), this.keepAliveTimeout);
428 return;
429 }
430 if (type === MESSAGE_TYPES.GQL_ERROR) {
431 var subscriptionState = SUBSCRIPTION_STATUS.FAILED;
432 if (observer) {
433 this.subscriptionObserverMap.set(id, {
434 observer: observer,
435 query: query,
436 variables: variables,
437 startAckTimeoutId: startAckTimeoutId,
438 subscriptionReadyCallback: subscriptionReadyCallback,
439 subscriptionFailedCallback: subscriptionFailedCallback,
440 subscriptionState: subscriptionState,
441 });
442 observer.error({
443 errors: [
444 __assign({}, new GraphQLError(CONTROL_MSG.CONNECTION_FAILED + ": " + JSON.stringify(payload))),
445 ],
446 });
447 if (startAckTimeoutId)
448 clearTimeout(startAckTimeoutId);
449 observer.complete();
450 if (typeof subscriptionFailedCallback === 'function') {
451 subscriptionFailedCallback();
452 }
453 }
454 }
455 };
456 AWSAppSyncRealTimeProvider.prototype._errorDisconnect = function (msg) {
457 logger.debug("Disconnect error: " + msg);
458 this.subscriptionObserverMap.forEach(function (_a) {
459 var observer = _a.observer;
460 if (observer && !observer.closed) {
461 observer.error({
462 errors: [__assign({}, new GraphQLError(msg))],
463 });
464 }
465 });
466 this.subscriptionObserverMap.clear();
467 if (this.awsRealTimeSocket) {
468 this.awsRealTimeSocket.close();
469 }
470 this.socketStatus = SOCKET_STATUS.CLOSED;
471 };
472 AWSAppSyncRealTimeProvider.prototype._timeoutStartSubscriptionAck = function (subscriptionId) {
473 var subscriptionObserver = this.subscriptionObserverMap.get(subscriptionId);
474 if (subscriptionObserver) {
475 var observer = subscriptionObserver.observer, query = subscriptionObserver.query, variables = subscriptionObserver.variables;
476 if (!observer) {
477 return;
478 }
479 this.subscriptionObserverMap.set(subscriptionId, {
480 observer: observer,
481 query: query,
482 variables: variables,
483 subscriptionState: SUBSCRIPTION_STATUS.FAILED,
484 });
485 if (observer && !observer.closed) {
486 observer.error({
487 errors: [
488 __assign({}, new GraphQLError("Subscription timeout " + JSON.stringify({
489 query: query,
490 variables: variables,
491 }))),
492 ],
493 });
494 // Cleanup will be automatically executed
495 observer.complete();
496 }
497 logger.debug('timeoutStartSubscription', JSON.stringify({ query: query, variables: variables }));
498 }
499 };
500 AWSAppSyncRealTimeProvider.prototype._initializeWebSocketConnection = function (_a) {
501 var _this = this;
502 var appSyncGraphqlEndpoint = _a.appSyncGraphqlEndpoint, authenticationType = _a.authenticationType, apiKey = _a.apiKey, region = _a.region, additionalHeaders = _a.additionalHeaders;
503 if (this.socketStatus === SOCKET_STATUS.READY) {
504 return;
505 }
506 return new Promise(function (res, rej) { return __awaiter(_this, void 0, void 0, function () {
507 var payloadString, headerString, _a, _b, headerQs, payloadQs, discoverableEndpoint, protocol, awsRealTimeUrl, err_3;
508 return __generator(this, function (_c) {
509 switch (_c.label) {
510 case 0:
511 this.promiseArray.push({ res: res, rej: rej });
512 if (!(this.socketStatus === SOCKET_STATUS.CLOSED)) return [3 /*break*/, 5];
513 _c.label = 1;
514 case 1:
515 _c.trys.push([1, 4, , 5]);
516 this.socketStatus = SOCKET_STATUS.CONNECTING;
517 payloadString = '{}';
518 _b = (_a = JSON).stringify;
519 return [4 /*yield*/, this._awsRealTimeHeaderBasedAuth({
520 authenticationType: authenticationType,
521 payload: payloadString,
522 canonicalUri: '/connect',
523 apiKey: apiKey,
524 appSyncGraphqlEndpoint: appSyncGraphqlEndpoint,
525 region: region,
526 additionalHeaders: additionalHeaders,
527 })];
528 case 2:
529 headerString = _b.apply(_a, [_c.sent()]);
530 headerQs = Buffer.from(headerString).toString('base64');
531 payloadQs = Buffer.from(payloadString).toString('base64');
532 discoverableEndpoint = appSyncGraphqlEndpoint !== null && appSyncGraphqlEndpoint !== void 0 ? appSyncGraphqlEndpoint : '';
533 if (this.isCustomDomain(discoverableEndpoint)) {
534 discoverableEndpoint =
535 discoverableEndpoint.concat(customDomainPath);
536 }
537 else {
538 discoverableEndpoint = discoverableEndpoint
539 .replace('appsync-api', 'appsync-realtime-api')
540 .replace('gogi-beta', 'grt-beta');
541 }
542 protocol = this.isSSLEnabled ? 'wss://' : 'ws://';
543 discoverableEndpoint = discoverableEndpoint
544 .replace('https://', protocol)
545 .replace('http://', protocol);
546 awsRealTimeUrl = discoverableEndpoint + "?header=" + headerQs + "&payload=" + payloadQs;
547 return [4 /*yield*/, this._initializeRetryableHandshake(awsRealTimeUrl)];
548 case 3:
549 _c.sent();
550 this.promiseArray.forEach(function (_a) {
551 var res = _a.res;
552 logger.debug('Notifying connection successful');
553 res();
554 });
555 this.socketStatus = SOCKET_STATUS.READY;
556 this.promiseArray = [];
557 return [3 /*break*/, 5];
558 case 4:
559 err_3 = _c.sent();
560 this.promiseArray.forEach(function (_a) {
561 var rej = _a.rej;
562 return rej(err_3);
563 });
564 this.promiseArray = [];
565 if (this.awsRealTimeSocket &&
566 this.awsRealTimeSocket.readyState === WebSocket.OPEN) {
567 this.awsRealTimeSocket.close(3001);
568 }
569 this.awsRealTimeSocket = undefined;
570 this.socketStatus = SOCKET_STATUS.CLOSED;
571 return [3 /*break*/, 5];
572 case 5: return [2 /*return*/];
573 }
574 });
575 }); });
576 };
577 AWSAppSyncRealTimeProvider.prototype._initializeRetryableHandshake = function (awsRealTimeUrl) {
578 return __awaiter(this, void 0, void 0, function () {
579 return __generator(this, function (_a) {
580 switch (_a.label) {
581 case 0:
582 logger.debug("Initializaling retryable Handshake");
583 return [4 /*yield*/, jitteredExponentialRetry(this._initializeHandshake.bind(this), [awsRealTimeUrl], MAX_DELAY_MS)];
584 case 1:
585 _a.sent();
586 return [2 /*return*/];
587 }
588 });
589 });
590 };
591 AWSAppSyncRealTimeProvider.prototype._initializeHandshake = function (awsRealTimeUrl) {
592 return __awaiter(this, void 0, void 0, function () {
593 var err_4, _a, errorType, errorCode;
594 var _this = this;
595 return __generator(this, function (_b) {
596 switch (_b.label) {
597 case 0:
598 logger.debug("Initializing handshake " + awsRealTimeUrl);
599 _b.label = 1;
600 case 1:
601 _b.trys.push([1, 4, , 5]);
602 return [4 /*yield*/, (function () {
603 return new Promise(function (res, rej) {
604 var newSocket = _this.getNewWebSocket(awsRealTimeUrl, 'graphql-ws');
605 newSocket.onerror = function () {
606 logger.debug("WebSocket connection error");
607 };
608 newSocket.onclose = function () {
609 rej(new Error('Connection handshake error'));
610 };
611 newSocket.onopen = function () {
612 _this.awsRealTimeSocket = newSocket;
613 return res();
614 };
615 });
616 })()];
617 case 2:
618 _b.sent();
619 // Step 2: wait for ack from AWS AppSyncReaTime after sending init
620 return [4 /*yield*/, (function () {
621 return new Promise(function (res, rej) {
622 if (_this.awsRealTimeSocket) {
623 var ackOk_1 = false;
624 _this.awsRealTimeSocket.onerror = function (error) {
625 logger.debug("WebSocket error " + JSON.stringify(error));
626 };
627 _this.awsRealTimeSocket.onclose = function (event) {
628 logger.debug("WebSocket closed " + event.reason);
629 rej(new Error(JSON.stringify(event)));
630 };
631 _this.awsRealTimeSocket.onmessage = function (message) {
632 logger.debug("subscription message from AWS AppSyncRealTime: " + message.data + " ");
633 var data = JSON.parse(message.data);
634 var type = data.type, _a = data.payload, _b = (_a === void 0 ? {} : _a).connectionTimeoutMs, connectionTimeoutMs = _b === void 0 ? DEFAULT_KEEP_ALIVE_TIMEOUT : _b;
635 if (type === MESSAGE_TYPES.GQL_CONNECTION_ACK) {
636 ackOk_1 = true;
637 if (_this.awsRealTimeSocket) {
638 _this.keepAliveTimeout = connectionTimeoutMs;
639 _this.awsRealTimeSocket.onmessage =
640 _this._handleIncomingSubscriptionMessage.bind(_this);
641 _this.awsRealTimeSocket.onerror = function (err) {
642 logger.debug(err);
643 _this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
644 };
645 _this.awsRealTimeSocket.onclose = function (event) {
646 logger.debug("WebSocket closed " + event.reason);
647 _this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
648 };
649 }
650 res('Cool, connected to AWS AppSyncRealTime');
651 return;
652 }
653 if (type === MESSAGE_TYPES.GQL_CONNECTION_ERROR) {
654 var _c = data.payload, _d = (_c === void 0 ? {} : _c).errors, _e = __read(_d === void 0 ? [] : _d, 1), _f = _e[0], _g = _f === void 0 ? {} : _f, _h = _g.errorType, errorType = _h === void 0 ? '' : _h, _j = _g.errorCode, errorCode = _j === void 0 ? 0 : _j;
655 rej({ errorType: errorType, errorCode: errorCode });
656 }
657 };
658 var gqlInit = {
659 type: MESSAGE_TYPES.GQL_CONNECTION_INIT,
660 };
661 _this.awsRealTimeSocket.send(JSON.stringify(gqlInit));
662 setTimeout(checkAckOk.bind(_this, ackOk_1), CONNECTION_INIT_TIMEOUT);
663 }
664 function checkAckOk(ackOk) {
665 if (!ackOk) {
666 rej(new Error("Connection timeout: ack from AWSRealTime was not received on " + CONNECTION_INIT_TIMEOUT + " ms"));
667 }
668 }
669 });
670 })()];
671 case 3:
672 // Step 2: wait for ack from AWS AppSyncReaTime after sending init
673 _b.sent();
674 return [3 /*break*/, 5];
675 case 4:
676 err_4 = _b.sent();
677 _a = err_4, errorType = _a.errorType, errorCode = _a.errorCode;
678 if (NON_RETRYABLE_CODES.includes(errorCode)) {
679 throw new NonRetryableError(errorType);
680 }
681 else if (errorType) {
682 throw new Error(errorType);
683 }
684 else {
685 throw err_4;
686 }
687 return [3 /*break*/, 5];
688 case 5: return [2 /*return*/];
689 }
690 });
691 });
692 };
693 AWSAppSyncRealTimeProvider.prototype._awsRealTimeHeaderBasedAuth = function (_a) {
694 var authenticationType = _a.authenticationType, payload = _a.payload, canonicalUri = _a.canonicalUri, appSyncGraphqlEndpoint = _a.appSyncGraphqlEndpoint, apiKey = _a.apiKey, region = _a.region, additionalHeaders = _a.additionalHeaders;
695 return __awaiter(this, void 0, void 0, function () {
696 var headerHandler, handler, host, result;
697 return __generator(this, function (_b) {
698 switch (_b.label) {
699 case 0:
700 headerHandler = {
701 API_KEY: this._awsRealTimeApiKeyHeader.bind(this),
702 AWS_IAM: this._awsRealTimeIAMHeader.bind(this),
703 OPENID_CONNECT: this._awsRealTimeOPENIDHeader.bind(this),
704 AMAZON_COGNITO_USER_POOLS: this._awsRealTimeCUPHeader.bind(this),
705 AWS_LAMBDA: this._customAuthHeader,
706 };
707 if (!(!authenticationType || !headerHandler[authenticationType])) return [3 /*break*/, 1];
708 logger.debug("Authentication type " + authenticationType + " not supported");
709 return [2 /*return*/, ''];
710 case 1:
711 handler = headerHandler[authenticationType];
712 host = url.parse(appSyncGraphqlEndpoint !== null && appSyncGraphqlEndpoint !== void 0 ? appSyncGraphqlEndpoint : '').host;
713 logger.debug("Authenticating with " + authenticationType);
714 return [4 /*yield*/, handler({
715 payload: payload,
716 canonicalUri: canonicalUri,
717 appSyncGraphqlEndpoint: appSyncGraphqlEndpoint,
718 apiKey: apiKey,
719 region: region,
720 host: host,
721 additionalHeaders: additionalHeaders,
722 })];
723 case 2:
724 result = _b.sent();
725 return [2 /*return*/, result];
726 }
727 });
728 });
729 };
730 AWSAppSyncRealTimeProvider.prototype._awsRealTimeCUPHeader = function (_a) {
731 var host = _a.host;
732 return __awaiter(this, void 0, void 0, function () {
733 var session;
734 return __generator(this, function (_b) {
735 switch (_b.label) {
736 case 0: return [4 /*yield*/, Auth.currentSession()];
737 case 1:
738 session = _b.sent();
739 return [2 /*return*/, {
740 Authorization: session.getAccessToken().getJwtToken(),
741 host: host,
742 }];
743 }
744 });
745 });
746 };
747 AWSAppSyncRealTimeProvider.prototype._awsRealTimeOPENIDHeader = function (_a) {
748 var host = _a.host;
749 return __awaiter(this, void 0, void 0, function () {
750 var token, federatedInfo, currentUser;
751 return __generator(this, function (_b) {
752 switch (_b.label) {
753 case 0: return [4 /*yield*/, Cache.getItem('federatedInfo')];
754 case 1:
755 federatedInfo = _b.sent();
756 if (!federatedInfo) return [3 /*break*/, 2];
757 token = federatedInfo.token;
758 return [3 /*break*/, 4];
759 case 2: return [4 /*yield*/, Auth.currentAuthenticatedUser()];
760 case 3:
761 currentUser = _b.sent();
762 if (currentUser) {
763 token = currentUser.token;
764 }
765 _b.label = 4;
766 case 4:
767 if (!token) {
768 throw new Error('No federated jwt');
769 }
770 return [2 /*return*/, {
771 Authorization: token,
772 host: host,
773 }];
774 }
775 });
776 });
777 };
778 AWSAppSyncRealTimeProvider.prototype._awsRealTimeApiKeyHeader = function (_a) {
779 var apiKey = _a.apiKey, host = _a.host;
780 return __awaiter(this, void 0, void 0, function () {
781 var dt, dtStr;
782 return __generator(this, function (_b) {
783 dt = new Date();
784 dtStr = dt.toISOString().replace(/[:\-]|\.\d{3}/g, '');
785 return [2 /*return*/, {
786 host: host,
787 'x-amz-date': dtStr,
788 'x-api-key': apiKey,
789 }];
790 });
791 });
792 };
793 AWSAppSyncRealTimeProvider.prototype._awsRealTimeIAMHeader = function (_a) {
794 var payload = _a.payload, canonicalUri = _a.canonicalUri, appSyncGraphqlEndpoint = _a.appSyncGraphqlEndpoint, region = _a.region;
795 return __awaiter(this, void 0, void 0, function () {
796 var endpointInfo, credentialsOK, creds, request, signed_params;
797 return __generator(this, function (_b) {
798 switch (_b.label) {
799 case 0:
800 endpointInfo = {
801 region: region,
802 service: 'appsync',
803 };
804 return [4 /*yield*/, this._ensureCredentials()];
805 case 1:
806 credentialsOK = _b.sent();
807 if (!credentialsOK) {
808 throw new Error('No credentials');
809 }
810 return [4 /*yield*/, Credentials.get().then(function (credentials) {
811 var _a = credentials, secretAccessKey = _a.secretAccessKey, accessKeyId = _a.accessKeyId, sessionToken = _a.sessionToken;
812 return {
813 secret_key: secretAccessKey,
814 access_key: accessKeyId,
815 session_token: sessionToken,
816 };
817 })];
818 case 2:
819 creds = _b.sent();
820 request = {
821 url: "" + appSyncGraphqlEndpoint + canonicalUri,
822 data: payload,
823 method: 'POST',
824 headers: __assign({}, AWS_APPSYNC_REALTIME_HEADERS),
825 };
826 signed_params = Signer.sign(request, creds, endpointInfo);
827 return [2 /*return*/, signed_params.headers];
828 }
829 });
830 });
831 };
832 AWSAppSyncRealTimeProvider.prototype._customAuthHeader = function (_a) {
833 var host = _a.host, additionalHeaders = _a.additionalHeaders;
834 if (!additionalHeaders || !additionalHeaders['Authorization']) {
835 throw new Error('No auth token specified');
836 }
837 return {
838 Authorization: additionalHeaders.Authorization,
839 host: host,
840 };
841 };
842 /**
843 * @private
844 */
845 AWSAppSyncRealTimeProvider.prototype._ensureCredentials = function () {
846 return Credentials.get()
847 .then(function (credentials) {
848 if (!credentials)
849 return false;
850 var cred = Credentials.shear(credentials);
851 logger.debug('set credentials for AWSAppSyncRealTimeProvider', cred);
852 return true;
853 })
854 .catch(function (err) {
855 logger.warn('ensure credentials error', err);
856 return false;
857 });
858 };
859 return AWSAppSyncRealTimeProvider;
860}(AbstractPubSubProvider));
861export { AWSAppSyncRealTimeProvider };
862//# sourceMappingURL=index.js.map
\No newline at end of file