Swallow errors in subscriptions in http_multi_server.. BUG=19815 R=alanknight@google.com Review URL: https://codereview.chromium.org//411203002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@38532 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkgs/http_multi_server/CHANGELOG.md b/pkgs/http_multi_server/CHANGELOG.md new file mode 100644 index 0000000..ac00908 --- /dev/null +++ b/pkgs/http_multi_server/CHANGELOG.md
@@ -0,0 +1,7 @@ +## 1.0.1 + +* Ignore errors from one of the servers if others are still bound. In + particular, this works around [issue 19815][] on some Windows machines where + IPv6 failure isn't discovered until we try to connect to the socket. + +[issue 19815]: http://code.google.com/p/dart/issues/detail?id=19815
diff --git a/pkgs/http_multi_server/lib/http_multi_server.dart b/pkgs/http_multi_server/lib/http_multi_server.dart index c055367..da12888 100644 --- a/pkgs/http_multi_server/lib/http_multi_server.dart +++ b/pkgs/http_multi_server/lib/http_multi_server.dart
@@ -99,20 +99,15 @@ /// [HttpServer.bindSecure]. static Future<HttpServer> _loopback(int port, Future<HttpServer> bind(InternetAddress address, int port)) { - return Future.wait([ - supportsIpV6, - bind(InternetAddress.LOOPBACK_IP_V4, port) - ]).then((results) { - var supportsIpV6 = results[0]; - var v4Server = results[1]; - - if (!supportsIpV6) return v4Server; - + return bind(InternetAddress.LOOPBACK_IP_V4, port).then((v4Server) { // Reuse the IPv4 server's port so that if [port] is 0, both servers use // the same ephemeral port. return bind(InternetAddress.LOOPBACK_IP_V6, v4Server.port) - .then((v6Server) { - return new HttpMultiServer([v4Server, v6Server]); + .then((v6Server) => new HttpMultiServer([v4Server, v6Server])) + .catchError((error) { + // If we fail to bind to IPv6, just use IPv4. + if (error is SocketException) return v4Server; + throw error; }); }); }
diff --git a/pkgs/http_multi_server/lib/src/utils.dart b/pkgs/http_multi_server/lib/src/utils.dart index 9e95aba..d615975 100644 --- a/pkgs/http_multi_server/lib/src/utils.dart +++ b/pkgs/http_multi_server/lib/src/utils.dart
@@ -7,6 +7,8 @@ import 'dart:async'; import 'dart:io'; +// TODO(nweiz): Revert this to the version of [mergeStreams] found elsewhere in +// the repo once issue 19815 is fixed in dart:io. /// Merges all streams in [streams] into a single stream that emits all of their /// values. /// @@ -18,9 +20,17 @@ controller = new StreamController(onListen: () { for (var stream in streams) { var subscription; - subscription = stream.listen(controller.add, - onError: controller.addError, - onDone: () { + subscription = stream.listen(controller.add, onError: (error, trace) { + if (subscriptions.length == 1) { + // If the last subscription errored, pass it on. + controller.addError(error, trace); + } else { + // If only one of the subscriptions has an error (usually IPv6 failing + // late), then just remove that subscription and ignore the error. + subscriptions.remove(subscription); + subscription.cancel(); + } + }, onDone: () { subscriptions.remove(subscription); if (subscriptions.isEmpty) controller.close(); }); @@ -42,21 +52,3 @@ return controller.stream; } - -/// A cache for [supportsIpV6]. -bool _supportsIpV6; - -/// Returns whether this computer supports binding to IPv6 addresses. -Future<bool> get supportsIpV6 { - if (_supportsIpV6 != null) return new Future.value(_supportsIpV6); - - return ServerSocket.bind(InternetAddress.LOOPBACK_IP_V6, 0).then((socket) { - _supportsIpV6 = true; - socket.close(); - return true; - }).catchError((error) { - if (error is! SocketException) throw error; - _supportsIpV6 = false; - return false; - }); -}
diff --git a/pkgs/http_multi_server/pubspec.yaml b/pkgs/http_multi_server/pubspec.yaml index 4199c58..2bdb4a2 100644 --- a/pkgs/http_multi_server/pubspec.yaml +++ b/pkgs/http_multi_server/pubspec.yaml
@@ -1,5 +1,5 @@ name: http_multi_server -version: 1.0.0 +version: 1.0.1 author: "Dart Team <misc@dartlang.org>" homepage: http://www.dartlang.org description:
diff --git a/pkgs/http_multi_server/test/http_multi_server_test.dart b/pkgs/http_multi_server/test/http_multi_server_test.dart index a3d9062..0140ee7 100644 --- a/pkgs/http_multi_server/test/http_multi_server_test.dart +++ b/pkgs/http_multi_server/test/http_multi_server_test.dart
@@ -108,7 +108,7 @@ expect(http.read("http://127.0.0.1:${server.port}/"), completion(equals("got request"))); - return supportsIpV6.then((supportsIpV6) { + return _supportsIpV6.then((supportsIpV6) { if (!supportsIpV6) return; expect(http.read("http://[::1]:${server.port}/"), completion(equals("got request"))); @@ -117,6 +117,26 @@ }); } +/// A cache for [supportsIpV6]. +bool _supportsIpV6Cache; + +// TODO(nweiz): This is known to be inaccurate on Windows machines with IPv6 +// disabled (issue 19815). Tests will fail on such machines. +/// Returns whether this computer supports binding to IPv6 addresses. +Future<bool> get _supportsIpV6 { + if (_supportsIpV6Cache != null) return new Future.value(_supportsIpV6Cache); + + return ServerSocket.bind(InternetAddress.LOOPBACK_IP_V6, 0).then((socket) { + _supportsIpV6Cache = true; + socket.close(); + return true; + }).catchError((error) { + if (error is! SocketException) throw error; + _supportsIpV6Cache = false; + return false; + }); +} + /// Makes a GET request to the root of [server] and returns the response. Future<http.Response> _get(HttpServer server) => http.get(_urlFor(server));