1414
1515import { IpAddressTypes , selectIpAddress } from './ip-addresses' ;
1616import { InstanceConnectionInfo } from './instance-connection-info' ;
17- import { resolveInstanceName } from './parse-instance-connection-name' ;
17+ import {
18+ isSameInstance ,
19+ resolveInstanceName ,
20+ } from './parse-instance-connection-name' ;
1821import { InstanceMetadata } from './sqladmin-fetcher' ;
1922import { generateKeys } from './crypto' ;
2023import { RSAKeys } from './rsa-keys' ;
2124import { SslCert } from './ssl-cert' ;
2225import { getRefreshInterval , isExpirationTimeValid } from './time' ;
2326import { AuthTypes } from './auth-types' ;
27+ import { CloudSQLConnectorError } from './errors' ;
28+
29+ // Private types that describe exactly the methods
30+ // needed from tls.Socket to be able to close
31+ // sockets when the DNS Name changes.
32+ type EventFn = ( ) => void ;
33+ type ClosableSocket = {
34+ destroy : ( error ?: Error ) => void ;
35+ once : ( name : string , handler : EventFn ) => void ;
36+ } ;
2437
2538interface Fetcher {
2639 getInstanceMetadata ( {
@@ -42,6 +55,7 @@ interface CloudSQLInstanceOptions {
4255 ipType : IpAddressTypes ;
4356 limitRateInterval ?: number ;
4457 sqlAdminFetcher : Fetcher ;
58+ checkDomainInterval ?: number ;
4559}
4660
4761interface RefreshResult {
@@ -74,9 +88,13 @@ export class CloudSQLInstance {
7488 // The ongoing refresh promise is referenced by the `next` property
7589 private next ?: Promise < RefreshResult > ;
7690 private scheduledRefreshID ?: ReturnType < typeof setTimeout > | null = undefined ;
91+ private checkDomainID ?: ReturnType < typeof setInterval > | null = undefined ;
7792 /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
7893 private throttle ?: any ;
7994 private closed = false ;
95+ private checkDomainInterval : number ;
96+ private sockets = new Set < ClosableSocket > ( ) ;
97+
8098 public readonly instanceInfo : InstanceConnectionInfo ;
8199 public ephemeralCert ?: SslCert ;
82100 public host ?: string ;
@@ -98,6 +116,7 @@ export class CloudSQLInstance {
98116 this . ipType = options . ipType || IpAddressTypes . PUBLIC ;
99117 this . limitRateInterval = options . limitRateInterval || 30 * 1000 ; // 30 seconds
100118 this . sqlAdminFetcher = options . sqlAdminFetcher ;
119+ this . checkDomainInterval = options . checkDomainInterval || 30 * 1000 ;
101120 }
102121
103122 // p-throttle library has to be initialized in an async scope in order to
@@ -152,6 +171,14 @@ export class CloudSQLInstance {
152171 this . next = undefined ;
153172 return Promise . reject ( 'closed' ) ;
154173 }
174+ if ( this ?. instanceInfo ?. domainName && ! this . checkDomainID ) {
175+ this . checkDomainID = setInterval (
176+ ( ) => {
177+ this . checkDomainChanged ( ) ;
178+ } ,
179+ this . checkDomainInterval || 30 * 1000
180+ ) ;
181+ }
155182
156183 const currentRefreshId = this . scheduledRefreshID ;
157184
@@ -296,8 +323,8 @@ export class CloudSQLInstance {
296323 // If refresh has not yet started, then cancel the setTimeout
297324 if ( this . scheduledRefreshID ) {
298325 clearTimeout ( this . scheduledRefreshID ) ;
326+ this . scheduledRefreshID = null ;
299327 }
300- this . scheduledRefreshID = null ;
301328 }
302329
303330 // Mark this instance as having an active connection. This is important to
@@ -312,9 +339,48 @@ export class CloudSQLInstance {
312339 close ( ) : void {
313340 this . closed = true ;
314341 this . cancelRefresh ( ) ;
342+ if ( this . checkDomainID ) {
343+ clearInterval ( this . checkDomainID ) ;
344+ this . checkDomainID = null ;
345+ }
346+ for ( const socket of this . sockets ) {
347+ socket . destroy (
348+ new CloudSQLConnectorError ( {
349+ code : 'ERRCLOSED' ,
350+ message : 'The connector was closed.' ,
351+ } )
352+ ) ;
353+ }
315354 }
316355
317356 isClosed ( ) : boolean {
318357 return this . closed ;
319358 }
359+ async checkDomainChanged ( ) {
360+ if ( ! this . instanceInfo . domainName ) {
361+ return ;
362+ }
363+
364+ const newInfo = await resolveInstanceName (
365+ undefined ,
366+ this . instanceInfo . domainName
367+ ) ;
368+ if ( ! isSameInstance ( this . instanceInfo , newInfo ) ) {
369+ // Domain name changed. Close and remove, then create a new map entry.
370+ this . close ( ) ;
371+ }
372+ }
373+ addSocket ( socket : ClosableSocket ) {
374+ if ( ! this . instanceInfo . domainName ) {
375+ // This was not connected by domain name. Ignore all sockets.
376+ return ;
377+ }
378+
379+ // Add the socket to the list
380+ this . sockets . add ( socket ) ;
381+ // When the socket is closed, remove it.
382+ socket . once ( 'closed' , ( ) => {
383+ this . sockets . delete ( socket ) ;
384+ } ) ;
385+ }
320386}
0 commit comments