@aws-amplify/api-graphql 4.7.1 → 4.7.2-graphql-data-ka.518fb2c.0

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.
@@ -23,7 +23,7 @@ import {
23
23
  CONNECTION_INIT_TIMEOUT,
24
24
  CONNECTION_STATE_CHANGE,
25
25
  DEFAULT_KEEP_ALIVE_ALERT_TIMEOUT,
26
- DEFAULT_KEEP_ALIVE_TIMEOUT,
26
+ DEFAULT_KEEP_ALIVE_HEARTBEAT_TIMEOUT,
27
27
  MAX_DELAY_MS,
28
28
  MESSAGE_TYPES,
29
29
  NON_RETRYABLE_CODES,
@@ -83,9 +83,8 @@ export abstract class AWSWebSocketProvider {
83
83
 
84
84
  protected awsRealTimeSocket?: WebSocket;
85
85
  private socketStatus: SOCKET_STATUS = SOCKET_STATUS.CLOSED;
86
- private keepAliveTimeoutId?: ReturnType<typeof setTimeout>;
87
- private keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
88
- private keepAliveAlertTimeoutId?: ReturnType<typeof setTimeout>;
86
+ private kaTimestamp?: number;
87
+ private keepAliveHeartbeatIntervalId?: ReturnType<typeof setTimeout>;
89
88
  private promiseArray: { res(): void; rej(reason?: any): void }[] = [];
90
89
  private connectionState: ConnectionState | undefined;
91
90
  private readonly connectionStateMonitor = new ConnectionStateMonitor();
@@ -119,6 +118,7 @@ export abstract class AWSWebSocketProvider {
119
118
  return new Promise<void>((resolve, reject) => {
120
119
  if (this.awsRealTimeSocket) {
121
120
  this.awsRealTimeSocket.onclose = (_: CloseEvent) => {
121
+ this._closeSocket();
122
122
  this.subscriptionObserverMap = new Map();
123
123
  this.awsRealTimeSocket = undefined;
124
124
  resolve();
@@ -171,7 +171,7 @@ export abstract class AWSWebSocketProvider {
171
171
  this.logger.debug(
172
172
  `${CONTROL_MSG.REALTIME_SUBSCRIPTION_INIT_ERROR}: ${err}`,
173
173
  );
174
- this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
174
+ this._closeSocket();
175
175
  })
176
176
  .finally(() => {
177
177
  subscriptionStartInProgress = false;
@@ -435,7 +435,7 @@ export abstract class AWSWebSocketProvider {
435
435
  this.logger.debug({ err });
436
436
  const message = String(err.message ?? '');
437
437
  // Resolving to give the state observer time to propogate the update
438
- this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
438
+ this._closeSocket();
439
439
 
440
440
  // Capture the error only when the network didn't cause disruption
441
441
  if (
@@ -544,12 +544,7 @@ export abstract class AWSWebSocketProvider {
544
544
  setTimeout(this._closeSocketIfRequired.bind(this), 1000);
545
545
  } else {
546
546
  this.logger.debug('closing WebSocket...');
547
- if (this.keepAliveTimeoutId) {
548
- clearTimeout(this.keepAliveTimeoutId);
549
- }
550
- if (this.keepAliveAlertTimeoutId) {
551
- clearTimeout(this.keepAliveAlertTimeoutId);
552
- }
547
+
553
548
  const tempSocket = this.awsRealTimeSocket;
554
549
  // Cleaning callbacks to avoid race condition, socket still exists
555
550
  tempSocket.onclose = null;
@@ -557,7 +552,7 @@ export abstract class AWSWebSocketProvider {
557
552
  tempSocket.close(1000);
558
553
  this.awsRealTimeSocket = undefined;
559
554
  this.socketStatus = SOCKET_STATUS.CLOSED;
560
- this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
555
+ this._closeSocket();
561
556
  }
562
557
  }
563
558
 
@@ -577,13 +572,43 @@ export abstract class AWSWebSocketProvider {
577
572
  errorType: string;
578
573
  };
579
574
 
575
+ private maintainKeepAlive() {
576
+ this.kaTimestamp = Date.now();
577
+ }
578
+
579
+ private keepAliveHeartbeat(connectionTimeoutMs: number) {
580
+ const currentTime = Date.now();
581
+
582
+ // Check for missed KA message
583
+ if (
584
+ this.kaTimestamp &&
585
+ currentTime - this.kaTimestamp > DEFAULT_KEEP_ALIVE_ALERT_TIMEOUT
586
+ ) {
587
+ this.connectionStateMonitor.record(CONNECTION_CHANGE.KEEP_ALIVE_MISSED);
588
+ } else {
589
+ this.connectionStateMonitor.record(CONNECTION_CHANGE.KEEP_ALIVE);
590
+ }
591
+
592
+ // Recognize we are disconnected if we haven't seen messages in the keep alive timeout period
593
+ if (
594
+ this.kaTimestamp &&
595
+ currentTime - this.kaTimestamp > connectionTimeoutMs
596
+ ) {
597
+ this._errorDisconnect(CONTROL_MSG.TIMEOUT_DISCONNECT);
598
+ }
599
+ }
600
+
580
601
  private _handleIncomingSubscriptionMessage(message: MessageEvent) {
581
602
  if (typeof message.data !== 'string') {
582
603
  return;
583
604
  }
584
605
 
585
606
  const [isData, data] = this._handleSubscriptionData(message);
586
- if (isData) return;
607
+ if (isData) {
608
+ this.maintainKeepAlive();
609
+
610
+ return;
611
+ }
587
612
 
588
613
  const { type, id, payload } = data;
589
614
 
@@ -632,16 +657,7 @@ export abstract class AWSWebSocketProvider {
632
657
  }
633
658
 
634
659
  if (type === MESSAGE_TYPES.GQL_CONNECTION_KEEP_ALIVE) {
635
- if (this.keepAliveTimeoutId) clearTimeout(this.keepAliveTimeoutId);
636
- if (this.keepAliveAlertTimeoutId)
637
- clearTimeout(this.keepAliveAlertTimeoutId);
638
- this.keepAliveTimeoutId = setTimeout(() => {
639
- this._errorDisconnect(CONTROL_MSG.TIMEOUT_DISCONNECT);
640
- }, this.keepAliveTimeout);
641
- this.keepAliveAlertTimeoutId = setTimeout(() => {
642
- this.connectionStateMonitor.record(CONNECTION_CHANGE.KEEP_ALIVE_MISSED);
643
- }, DEFAULT_KEEP_ALIVE_ALERT_TIMEOUT);
644
- this.connectionStateMonitor.record(CONNECTION_CHANGE.KEEP_ALIVE);
660
+ this.maintainKeepAlive();
645
661
 
646
662
  return;
647
663
  }
@@ -686,13 +702,21 @@ export abstract class AWSWebSocketProvider {
686
702
  this.logger.debug(`Disconnect error: ${msg}`);
687
703
 
688
704
  if (this.awsRealTimeSocket) {
689
- this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
705
+ this._closeSocket();
690
706
  this.awsRealTimeSocket.close();
691
707
  }
692
708
 
693
709
  this.socketStatus = SOCKET_STATUS.CLOSED;
694
710
  }
695
711
 
712
+ private _closeSocket() {
713
+ if (this.keepAliveHeartbeatIntervalId) {
714
+ clearInterval(this.keepAliveHeartbeatIntervalId);
715
+ this.keepAliveHeartbeatIntervalId = undefined;
716
+ }
717
+ this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
718
+ }
719
+
696
720
  private _timeoutStartSubscriptionAck(subscriptionId: string) {
697
721
  const subscriptionObserver =
698
722
  this.subscriptionObserverMap.get(subscriptionId);
@@ -708,7 +732,7 @@ export abstract class AWSWebSocketProvider {
708
732
  subscriptionState: SUBSCRIPTION_STATUS.FAILED,
709
733
  });
710
734
 
711
- this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
735
+ this._closeSocket();
712
736
  this.logger.debug(
713
737
  'timeoutStartSubscription',
714
738
  JSON.stringify({ query, variables }),
@@ -820,6 +844,7 @@ export abstract class AWSWebSocketProvider {
820
844
  this.logger.debug(`WebSocket connection error`);
821
845
  };
822
846
  newSocket.onclose = () => {
847
+ this._closeSocket();
823
848
  reject(new Error('Connection handshake error'));
824
849
  };
825
850
  newSocket.onopen = () => {
@@ -849,6 +874,7 @@ export abstract class AWSWebSocketProvider {
849
874
 
850
875
  this.awsRealTimeSocket.onclose = event => {
851
876
  this.logger.debug(`WebSocket closed ${event.reason}`);
877
+ this._closeSocket();
852
878
  reject(new Error(JSON.stringify(event)));
853
879
  };
854
880
 
@@ -912,7 +938,11 @@ export abstract class AWSWebSocketProvider {
912
938
  return;
913
939
  }
914
940
 
915
- this.keepAliveTimeout = connectionTimeoutMs;
941
+ // Setup a keep alive heartbeat for this connection
942
+ this.keepAliveHeartbeatIntervalId = setInterval(() => {
943
+ this.keepAliveHeartbeat(connectionTimeoutMs);
944
+ }, DEFAULT_KEEP_ALIVE_HEARTBEAT_TIMEOUT);
945
+
916
946
  this.awsRealTimeSocket.onmessage =
917
947
  this._handleIncomingSubscriptionMessage.bind(this);
918
948
 
@@ -923,6 +953,7 @@ export abstract class AWSWebSocketProvider {
923
953
 
924
954
  this.awsRealTimeSocket.onclose = event => {
925
955
  this.logger.debug(`WebSocket closed ${event.reason}`);
956
+ this._closeSocket();
926
957
  this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
927
958
  };
928
959
  }
@@ -128,6 +128,11 @@ export const START_ACK_TIMEOUT = 15000;
128
128
  */
129
129
  export const DEFAULT_KEEP_ALIVE_TIMEOUT = 5 * 60 * 1000;
130
130
 
131
+ /**
132
+ * Default Time in milleseconds between monitoring checks of keep alive status
133
+ */
134
+ export const DEFAULT_KEEP_ALIVE_HEARTBEAT_TIMEOUT = 5 * 1000;
135
+
131
136
  /**
132
137
  * Default Time in milleseconds to alert for missed GQL_CONNECTION_KEEP_ALIVE message
133
138
  */