Unverified Commit a0860f6e authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] retry sever socket setup (and port selection if port is unspecified) (#69351)

Fixes #69348

If the web development server fails to bind, then retry up to 5 times. If a port was not provided, select a new free port each time.
parent 44d0e52d
......@@ -136,6 +136,8 @@ class WebAssetServer implements AssetReader {
final Map<String, String> _modules;
final Map<String, String> _digests;
int get selectedPort => _httpServer.port;
void performRestart(List<String> modules) {
for (final String module in modules) {
// We skip computing the digest by using the hashCode of the underlying buffer.
......@@ -171,14 +173,27 @@ class WebAssetServer implements AssetReader {
bool testMode = false,
DwdsLauncher dwdsLauncher = Dwds.start,
}) async {
try {
InternetAddress address;
if (hostname == 'any') {
address = InternetAddress.anyIPv4;
} else {
address = (await InternetAddress.lookup(hostname)).first;
}
final HttpServer httpServer = await HttpServer.bind(address, port);
HttpServer httpServer;
dynamic lastError;
for (int i = 0; i < 5; i += 1) {
try {
httpServer = await HttpServer.bind(address, port ?? await globals.os.findFreePort());
break;
} on SocketException catch (error) {
lastError = error;
await Future<void>.delayed(const Duration(milliseconds: 100));
}
}
if (httpServer == null) {
throwToolExit('Failed to bind web development server:\n$lastError');
}
// Allow rendering in a iframe.
httpServer.defaultResponseHeaders.remove('x-frame-options', 'SAMEORIGIN');
......@@ -257,10 +272,13 @@ class WebAssetServer implements AssetReader {
useSseForDebugBackend: useSseForDebugBackend,
serveDevTools: false,
loadStrategy: FrontendServerRequireStrategyProvider(
ReloadConfiguration.none, server, _digestProvider)
.strategy,
ReloadConfiguration.none,
server,
_digestProvider,
).strategy,
expressionCompiler: expressionCompiler,
spawnDds: true);
spawnDds: true,
);
shelf.Pipeline pipeline = const shelf.Pipeline();
if (enableDwds) {
pipeline = pipeline.addMiddleware(middleware);
......@@ -273,11 +291,6 @@ class WebAssetServer implements AssetReader {
shelf.serveRequests(httpServer, cascade.handler);
server.dwds = dwds;
return server;
} on SocketException catch (err) {
throwToolExit('Failed to bind web development server:\n$err');
}
assert(false);
return null;
}
final BuildInfo _buildInfo;
......@@ -709,7 +722,7 @@ class WebDevFS implements DevFS {
/// server.
WebDevFS({
@required this.hostname,
@required this.port,
@required int port,
@required this.packagesFilePath,
@required this.urlTunneller,
@required this.useSseForDebugProxy,
......@@ -721,11 +734,10 @@ class WebDevFS implements DevFS {
@required this.chromiumLauncher,
@required this.nullAssertions,
this.testMode = false,
});
}) : _port = port;
final Uri entrypoint;
final String hostname;
final int port;
final String packagesFilePath;
final UrlTunneller urlTunneller;
final bool useSseForDebugProxy;
......@@ -736,6 +748,7 @@ class WebDevFS implements DevFS {
final ExpressionCompiler expressionCompiler;
final ChromiumLauncher chromiumLauncher;
final bool nullAssertions;
final int _port;
WebAssetServer webAssetServer;
......@@ -800,7 +813,7 @@ class WebDevFS implements DevFS {
webAssetServer = await WebAssetServer.start(
chromiumLauncher,
hostname,
port,
_port,
urlTunneller,
useSseForDebugProxy,
useSseForDebugBackend,
......@@ -810,15 +823,16 @@ class WebDevFS implements DevFS {
expressionCompiler,
testMode: testMode,
);
final int selectedPort = webAssetServer.selectedPort;
if (buildInfo.dartDefines.contains('FLUTTER_WEB_AUTO_DETECT=true')) {
webAssetServer.webRenderer = WebRendererMode.autoDetect;
} else if (buildInfo.dartDefines.contains('FLUTTER_WEB_USE_SKIA=true')) {
webAssetServer.webRenderer = WebRendererMode.canvaskit;
}
if (hostname == 'any') {
_baseUri = Uri.http('localhost:$port', '');
_baseUri = Uri.http('localhost:$selectedPort', '');
} else {
_baseUri = Uri.http('$hostname:$port', '');
_baseUri = Uri.http('$hostname:$selectedPort', '');
}
return _baseUri;
}
......
......@@ -483,11 +483,6 @@ class _ResidentWebRunner extends ResidentWebRunner {
'Launching ${globals.fsUtils.getDisplayPath(target)} '
'on ${device.device.name} in $modeName mode...',
);
final String effectiveHostname = debuggingOptions.hostname ?? 'localhost';
final int hostPort = debuggingOptions.port == null
? await globals.os.findFreePort()
: int.tryParse(debuggingOptions.port);
if (device.device is ChromiumDevice) {
_chromiumLauncher = (device.device as ChromiumDevice).chromeLauncher;
}
......@@ -498,10 +493,11 @@ class _ResidentWebRunner extends ResidentWebRunner {
debuggingOptions.webEnableExpressionEvaluation
? WebExpressionCompiler(device.generator)
: null;
device.devFS = WebDevFS(
hostname: effectiveHostname,
port: hostPort,
hostname: debuggingOptions.hostname ?? 'localhost',
port: debuggingOptions.port != null
? int.tryParse(debuggingOptions.port)
: null,
packagesFilePath: packagesFilePath,
urlTunneller: urlTunneller,
useSseForDebugProxy: debuggingOptions.webUseSseForDebugProxy,
......
......@@ -699,7 +699,7 @@ void main() {
contains('GENERATED'));
// served on localhost
expect(uri, Uri.http('localhost:0', ''));
expect(uri.host, 'localhost');
await webDevFS.destroy();
}, overrides: <Type, Generator>{
......@@ -813,7 +813,7 @@ void main() {
contains('GENERATED'));
// served on localhost
expect(uri, Uri.http('localhost:0', ''));
expect(uri.host, 'localhost');
await webDevFS.destroy();
}, overrides: <Type, Generator>{
......@@ -859,7 +859,7 @@ void main() {
final Uri uri = await webDevFS.create();
expect(uri, Uri.http('localhost:0', ''));
expect(uri.host, 'localhost');
await webDevFS.destroy();
}));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment