@@ -16,6 +16,7 @@ import 'dart:async';
1616import 'dart:io' ;
1717
1818import 'package:args/command_runner.dart' ;
19+ import 'package:http/http.dart' as http;
1920import 'package:io/ansi.dart' ;
2021import 'package:io/io.dart' ;
2122
@@ -149,6 +150,8 @@ class ServeCommand extends Command<int> {
149150 for (final dir in portsToServeByDir.keys) dir: await findUnusedPort ()
150151 };
151152
153+ final startupTimer = Stopwatch ()..start ();
154+
152155 // Start the underlying `webdev serve` process.
153156 webdevServer = await WebdevServer .start ([
154157 if (hostname != 'localhost' ) '--hostname=$hostname ' ,
@@ -186,6 +189,69 @@ class ServeCommand extends Command<int> {
186189 }
187190 }
188191
192+ if (! proxiesFailed) {
193+ final hostToProbe = hostname == 'any' ? 'localhost' : hostname;
194+ final readyByDir = {
195+ for (final dir in portsToServeByDir.keys) dir: Completer <void >()
196+ };
197+
198+ for (final dir in portsToServeByDir.keys) {
199+ unawaited (() async {
200+ try {
201+ await _waitForProxyToServe (
202+ host: hostToProbe,
203+ port: portsToServeByDir[dir]! ,
204+ );
205+ if (! readyByDir[dir]! .isCompleted) {
206+ readyByDir[dir]! .complete ();
207+ }
208+ } catch (e, stackTrace) {
209+ if (! readyByDir[dir]! .isCompleted) {
210+ readyByDir[dir]! .completeError (e, stackTrace);
211+ }
212+ }
213+ }());
214+ }
215+ // to reduce exception noise, wait a few seconds
216+ await Future .delayed (const Duration (seconds: 3 ));
217+
218+ await Future .any ([
219+ Future .wait (readyByDir.values.map ((completer) => completer.future)),
220+ exitCodeCompleter.future,
221+ ]);
222+
223+ if (! exitCodeCompleter.isCompleted) {
224+ final elapsedSeconds =
225+ (startupTimer.elapsedMilliseconds / 1000 ).toStringAsFixed (1 );
226+ stdout.writeln ('[INFO] Succeeded after $elapsedSeconds seconds' );
227+ startupTimer.stop ();
228+ }
229+ }
230+
189231 return exitCodeCompleter.future;
190232 }
233+
234+ static Future <void > _waitForProxyToServe ({
235+ required String host,
236+ required int port,
237+ }) async {
238+ const pollInterval = Duration (milliseconds: 1000 );
239+
240+ while (true ) {
241+ final uri = Uri .parse ('http://$host :$port ' );
242+ try {
243+ stdout.writeln ('[INFO] Pinging server $uri to detect responsiveness...an exception from the server means it is not ready.' );
244+ final response = await http.get (uri);
245+ if (response.statusCode == 200 ) {
246+ stdout.writeln ('[INFO] $uri responded with 200, server is ready!' );
247+ return ;
248+ }
249+ stdout.writeln ('[INFO] $uri responded with ${response .statusCode }, server is not ready yet.' );
250+ } catch (_) {
251+ // Keep polling until webdev starts listening and serving successfully.
252+ stdout.writeln ('[INFO] $uri responded with an error, server is not ready yet.' );
253+ }
254+ await Future .delayed (pollInterval);
255+ }
256+ }
191257}
0 commit comments