@@ -33,25 +33,53 @@ const getLocalHost = async () => {
3333 }
3434
3535 var hostname = process . env . LOCALSTACK_HOSTNAME || DEFAULT_HOSTNAME ;
36- // attempt to connect to the given host/port
37- const socket = new net . Socket ( ) ;
38- try {
39- await socket . connect ( { host : hostname , port } ) ;
40- } catch ( e ) {
41- if ( hostname === "localhost" ) {
42- // fall back to using local IPv4 address - to fix IPv6 issue on MacOS
43- // see, https://github.com/localstack/serverless-localstack/issues/125
44- // and https://github.com/localstack/aws-cdk-local/issues/78
36+ // Fall back to using local IPv4 address if connection to localhost fails.
37+ // This workaround transparently handles systems (e.g., macOS) where
38+ // localhost resolves to IPv6 when using Nodejs >=v17. See discussion:
39+ // https://github.com/localstack/aws-cdk-local/issues/76#issuecomment-1412590519
40+ // Issue: https://github.com/localstack/aws-cdk-local/issues/78
41+ if ( hostname === "localhost" ) {
42+ try {
43+ const options = { host : hostname , port : port } ;
44+ await checkTCPConnection ( options ) ;
45+ } catch ( e ) {
4546 hostname = "127.0.0.1" ;
4647 }
47- } finally {
48- socket . destroy ( ) ;
4948 }
5049
5150 resolvedHostname = hostname ;
5251 return `${ hostname } :${ port } ` ;
5352} ;
5453
54+ /**
55+ * Checks whether a TCP connection to the given "options" can be established.
56+ * @param {object } options connection options of net.socket.connect()
57+ * https://nodejs.org/api/net.html#socketconnectoptions-connectlistener
58+ * Example: { host: "localhost", port: 4566 }
59+ * @returns {Promise } A fulfilled empty promise on successful connection and
60+ * a rejected promise on any connection error.
61+ */
62+ const checkTCPConnection = async ( options ) => {
63+ return new Promise ( ( resolve , reject ) => {
64+ const socket = new net . Socket ( ) ;
65+ const client = socket . connect ( options , ( ) => {
66+ client . end ( ) ;
67+ resolve ( ) ;
68+ } ) ;
69+
70+ client . setTimeout ( 500 ) ; // milliseconds
71+ client . on ( "timeout" , err => {
72+ client . destroy ( ) ;
73+ reject ( err ) ;
74+ } ) ;
75+
76+ client . on ( "error" , err => {
77+ client . destroy ( ) ;
78+ reject ( err ) ;
79+ } ) ;
80+ } ) ;
81+ }
82+
5583const useLocal = ( ) => {
5684 // TODO make configurable..?
5785 return true ;
0 commit comments