@@ -4,7 +4,97 @@ import http2 from 'node:http2';
44const RETRY_DELAY = 200 ;
55const HTTP1_ATTEMPTS = 6 ; // 3 rounds × 2 IP versions
66
7- export default function waitForLocalhost ( { port = 80 , path = '/' , useGet, statusCodes = [ 200 ] , signal} = { } ) {
7+ const tryHttp1 = ( {
8+ ipVersion,
9+ onError,
10+ method,
11+ port,
12+ path,
13+ signal,
14+ handleResponse,
15+ } ) => {
16+ const request = http . request (
17+ {
18+ method,
19+ port,
20+ path,
21+ family : ipVersion ,
22+ signal,
23+ } ,
24+ response => {
25+ handleResponse ( response . statusCode , ipVersion , onError ) ;
26+ } ,
27+ ) ;
28+
29+ request . on ( 'error' , onError ) ;
30+ request . end ( ) ;
31+ } ;
32+
33+ const noop = ( ) => { } ;
34+
35+ const tryHttp2 = ( {
36+ ipVersion,
37+ onError,
38+ method,
39+ port,
40+ path,
41+ signal,
42+ handleResponse,
43+ } ) => {
44+ const hostname = ipVersion === 6 ? '[::1]' : 'localhost' ;
45+ const client = http2 . connect ( `http://${ hostname } :${ port } ` ) ;
46+
47+ const cleanup = ( ) => {
48+ signal ?. removeEventListener ( 'abort' , cleanup , { once : true } ) ;
49+ client . off ( 'error' , handleClientError ) ;
50+ request . off ( 'response' , handleRequestResponse ) ;
51+ request . off ( 'error' , handleRequestError ) ;
52+ client . on ( 'error' , noop ) ;
53+ request . on ( 'error' , noop ) ;
54+ if ( ! client . destroyed ) {
55+ client . destroy ( ) ;
56+ }
57+ } ;
58+
59+ // Cleanup on abort
60+ signal ?. addEventListener ( 'abort' , cleanup , { once : true } ) ;
61+
62+ const handleClientError = ( ) => {
63+ cleanup ( ) ;
64+ onError ( ) ;
65+ } ;
66+
67+ client . on ( 'error' , handleClientError ) ;
68+
69+ const request = client . request ( {
70+ ':method' : method ,
71+ ':path' : path ,
72+ } ) ;
73+
74+ const handleRequestResponse = headers => {
75+ cleanup ( ) ;
76+ handleResponse ( headers [ ':status' ] , ipVersion , onError ) ;
77+ } ;
78+
79+ request . on ( 'response' , handleRequestResponse ) ;
80+
81+ const handleRequestError = ( ) => {
82+ cleanup ( ) ;
83+ onError ( ) ;
84+ } ;
85+
86+ request . on ( 'error' , handleRequestError ) ;
87+
88+ request . end ( ) ;
89+ } ;
90+
91+ export default function waitForLocalhost ( {
92+ port = 80 ,
93+ path = '/' ,
94+ useGet,
95+ statusCodes = [ 200 ] ,
96+ signal,
97+ } = { } ) {
898 return new Promise ( ( resolve , reject ) => {
999 let attemptCount = 0 ;
10100 const method = useGet ? 'GET' : 'HEAD' ;
@@ -21,10 +111,14 @@ export default function waitForLocalhost({port = 80, path = '/', useGet, statusC
21111 }
22112 } ;
23113
24- signal ?. addEventListener ( 'abort' , ( ) => {
25- cleanup ( ) ;
26- reject ( signal . reason ) ;
27- } , { once : true } ) ;
114+ signal ?. addEventListener (
115+ 'abort' ,
116+ ( ) => {
117+ cleanup ( ) ;
118+ reject ( signal . reason ) ;
119+ } ,
120+ { once : true } ,
121+ ) ;
28122
29123 const handleResponse = ( statusCode , ipVersion , onError ) => {
30124 if ( statusCodes . includes ( statusCode ) ) {
@@ -34,61 +128,18 @@ export default function waitForLocalhost({port = 80, path = '/', useGet, statusC
34128 }
35129 } ;
36130
37- const tryHttp1 = ( ipVersion , onError ) => {
38- const request = http . request ( {
131+ const tryRequest = ( ipVersion , onError ) => {
132+ const useHttp2 = attemptCount > HTTP1_ATTEMPTS ;
133+ const requestFunction = useHttp2 ? tryHttp2 : tryHttp1 ;
134+ requestFunction ( {
135+ ipVersion,
136+ onError,
39137 method,
40138 port,
41139 path,
42- family : ipVersion ,
43140 signal,
44- } , response => {
45- handleResponse ( response . statusCode , ipVersion , onError ) ;
141+ handleResponse,
46142 } ) ;
47-
48- request . on ( 'error' , onError ) ;
49- request . end ( ) ;
50- } ;
51-
52- const tryHttp2 = ( ipVersion , onError ) => {
53- const hostname = ipVersion === 6 ? '[::1]' : 'localhost' ;
54- const client = http2 . connect ( `http://${ hostname } :${ port } ` ) ;
55-
56- const cleanupClient = ( ) => {
57- if ( ! client . destroyed ) {
58- client . destroy ( ) ;
59- }
60- } ;
61-
62- // Cleanup on abort
63- signal ?. addEventListener ( 'abort' , cleanupClient , { once : true } ) ;
64-
65- client . on ( 'error' , ( ) => {
66- cleanupClient ( ) ;
67- onError ( ) ;
68- } ) ;
69-
70- const request = client . request ( {
71- ':method' : method ,
72- ':path' : path ,
73- } ) ;
74-
75- request . on ( 'response' , headers => {
76- cleanupClient ( ) ;
77- handleResponse ( headers [ ':status' ] , ipVersion , onError ) ;
78- } ) ;
79-
80- request . on ( 'error' , ( ) => {
81- cleanupClient ( ) ;
82- onError ( ) ;
83- } ) ;
84-
85- request . end ( ) ;
86- } ;
87-
88- const tryRequest = ( ipVersion , onError ) => {
89- const useHttp2 = attemptCount > HTTP1_ATTEMPTS ;
90- const requestFunction = useHttp2 ? tryHttp2 : tryHttp1 ;
91- requestFunction ( ipVersion , onError ) ;
92143 } ;
93144
94145 const retry = ( ) => {
0 commit comments