From 5134eaca1abad38659e8e7f6c136307504a4324d Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Wed, 4 Aug 2021 16:59:53 -0700 Subject: [PATCH] Recover from used port errors when starting debug service Closes: https://github.com/dart-lang/webdev/issues/1378 --- dwds/CHANGELOG.md | 4 ++++ dwds/lib/src/services/debug_service.dart | 6 ++--- dwds/lib/src/utilities/shared.dart | 23 +++++++++++++++++++ dwds/lib/src/version.dart | 2 +- dwds/pubspec.yaml | 2 +- .../expression_compiler_service_test.dart | 7 +++--- 6 files changed, 35 insertions(+), 9 deletions(-) diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md index 69997443f..495205864 100644 --- a/dwds/CHANGELOG.md +++ b/dwds/CHANGELOG.md @@ -1,3 +1,7 @@ +## 11.2.1-dev + +- Recover from used port errors when starting debug service. + ## 11.2.0 - Throw `SentinelException` instead of `RPCError` on vm service diff --git a/dwds/lib/src/services/debug_service.dart b/dwds/lib/src/services/debug_service.dart index 066afe36c..f9c4819fe 100644 --- a/dwds/lib/src/services/debug_service.dart +++ b/dwds/lib/src/services/debug_service.dart @@ -11,7 +11,6 @@ import 'dart:math'; import 'dart:typed_data'; import 'package:dds/dds.dart'; -import 'package:http_multi_server/http_multi_server.dart'; import 'package:pedantic/pedantic.dart'; import 'package:shelf/shelf.dart' as shelf; import 'package:shelf/shelf.dart' hide Response; @@ -253,13 +252,12 @@ class DebugService { return innerHandler(request); }; } - var port = await findUnusedPort(); - var server = await HttpMultiServer.bind(hostname, port); + var server = await startHttpServer(hostname); serveRequests(server, handler); return DebugService._( chromeProxyService, server.address.host, - port, + server.port, authToken, serviceExtensionRegistry, server, diff --git a/dwds/lib/src/utilities/shared.dart b/dwds/lib/src/utilities/shared.dart index 52ab56395..a485f70d2 100644 --- a/dwds/lib/src/utilities/shared.dart +++ b/dwds/lib/src/utilities/shared.dart @@ -6,6 +6,7 @@ import 'dart:io'; +import 'package:http_multi_server/http_multi_server.dart'; import 'package:vm_service/vm_service.dart'; import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; @@ -47,6 +48,28 @@ Future findUnusedPort() async { return port; } +/// Finds unused port and binds a new http server to it. +/// +/// Retries a few times to recover from errors due to +/// another thread or process opening the same port. +Future startHttpServer(String hostname, {int port}) async { + HttpServer httpServer; + var retries = 5; + var i = 0; + while (i < retries) { + i++; + try { + httpServer = + await HttpMultiServer.bind(hostname, port ?? await findUnusedPort()); + } on SocketException { + if (i == retries) rethrow; + } + if (httpServer != null || i == retries) return httpServer; + await Future.delayed(const Duration(milliseconds: 100)); + } + return httpServer; +} + /// Throws an [ExceptionDetails] object if `exceptionDetails` is present on the /// result. void handleErrorIfPresent(WipResponse response, diff --git a/dwds/lib/src/version.dart b/dwds/lib/src/version.dart index cbd4af336..b419d06dd 100644 --- a/dwds/lib/src/version.dart +++ b/dwds/lib/src/version.dart @@ -1,2 +1,2 @@ // Generated code. Do not modify. -const packageVersion = '11.2.0'; +const packageVersion = '11.2.1-dev'; diff --git a/dwds/pubspec.yaml b/dwds/pubspec.yaml index 5665cce55..013dcd429 100644 --- a/dwds/pubspec.yaml +++ b/dwds/pubspec.yaml @@ -1,6 +1,6 @@ name: dwds # Every time this changes you need to run `pub run build_runner build`. -version: 11.2.0 +version: 11.2.1-dev homepage: https://github.com/dart-lang/webdev/tree/master/dwds description: >- A service that proxies between the Chrome debug protocol and the Dart VM diff --git a/dwds/test/expression_compiler_service_test.dart b/dwds/test/expression_compiler_service_test.dart index 2bba32f36..d4709e944 100644 --- a/dwds/test/expression_compiler_service_test.dart +++ b/dwds/test/expression_compiler_service_test.dart @@ -10,7 +10,6 @@ import 'dart:io'; import 'package:dwds/dwds.dart'; import 'package:dwds/src/utilities/shared.dart'; -import 'package:http_multi_server/http_multi_server.dart'; import 'package:path/path.dart' as p; import 'package:shelf/shelf.dart'; import 'package:shelf/shelf_io.dart' as shelf_io; @@ -54,8 +53,11 @@ void main() async { {loggerName, error, stackTrace, verbose}) => output.add('[$level] $loggerName: $message')); + // start asset server + server = await startHttpServer('localhost'); + var port = server.port; + // start expression compilation service - final port = await findUnusedPort(); final assetHandler = (request) => Response(200, body: File.fromUri(kernel).readAsBytesSync()); service = @@ -64,7 +66,6 @@ void main() async { await service.initialize(moduleFormat: 'amd'); // setup asset server - server = await HttpMultiServer.bind('localhost', port); shelf_io.serveRequests( server, Cascade().add(service.handler).add(assetHandler).handler);