| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import 'dart:async'; | 5 import 'dart:async'; |
| 6 import 'dart:io'; | 6 import 'dart:io'; |
| 7 | 7 |
| 8 import 'package:async/async.dart'; |
| 9 |
| 8 import 'src/multi_headers.dart'; | 10 import 'src/multi_headers.dart'; |
| 9 import 'src/utils.dart'; | 11 import 'src/utils.dart'; |
| 10 | 12 |
| 11 /// The error code for an error caused by a port already being in use. | 13 /// The error code for an error caused by a port already being in use. |
| 12 final _addressInUseErrno = _computeAddressInUseErrno(); | 14 final _addressInUseErrno = _computeAddressInUseErrno(); |
| 13 int _computeAddressInUseErrno() { | 15 int _computeAddressInUseErrno() { |
| 14 if (Platform.isWindows) return 10048; | 16 if (Platform.isWindows) return 10048; |
| 15 if (Platform.isMacOS) return 48; | 17 if (Platform.isMacOS) return 48; |
| 16 assert(Platform.isLinux); | 18 assert(Platform.isLinux); |
| 17 return 98; | 19 return 98; |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 79 } | 81 } |
| 80 | 82 |
| 81 /// Creates an [HttpMultiServer] wrapping [servers]. | 83 /// Creates an [HttpMultiServer] wrapping [servers]. |
| 82 /// | 84 /// |
| 83 /// All [servers] should have the same configuration and none should be | 85 /// All [servers] should have the same configuration and none should be |
| 84 /// listened to when this is called. | 86 /// listened to when this is called. |
| 85 HttpMultiServer(Iterable<HttpServer> servers) | 87 HttpMultiServer(Iterable<HttpServer> servers) |
| 86 : _servers = servers.toSet(), | 88 : _servers = servers.toSet(), |
| 87 defaultResponseHeaders = new MultiHeaders( | 89 defaultResponseHeaders = new MultiHeaders( |
| 88 servers.map((server) => server.defaultResponseHeaders)), | 90 servers.map((server) => server.defaultResponseHeaders)), |
| 89 super(mergeStreams(servers)); | 91 super(StreamGroup.merge(servers)); |
| 90 | 92 |
| 91 /// Creates an [HttpServer] listening on all available loopback addresses for | 93 /// Creates an [HttpServer] listening on all available loopback addresses for |
| 92 /// this computer. | 94 /// this computer. |
| 93 /// | 95 /// |
| 94 /// See [HttpServer.bind]. | 96 /// See [HttpServer.bind]. |
| 95 static Future<HttpServer> loopback(int port, {int backlog, bool v6Only: false, | 97 static Future<HttpServer> loopback(int port, {int backlog, bool v6Only: false, |
| 96 bool shared: false}) { | 98 bool shared: false}) { |
| 97 if (backlog == null) backlog = 0; | 99 if (backlog == null) backlog = 0; |
| 98 | 100 |
| 99 return _loopback(port, (address, port) => | 101 return _loopback(port, (address, port) => |
| (...skipping 14 matching lines...) Expand all Loading... |
| 114 backlog: backlog, v6Only: v6Only, shared: shared, | 116 backlog: backlog, v6Only: v6Only, shared: shared, |
| 115 requestClientCertificate: requestClientCertificate)); | 117 requestClientCertificate: requestClientCertificate)); |
| 116 } | 118 } |
| 117 | 119 |
| 118 /// A helper method for initializing loopback servers. | 120 /// A helper method for initializing loopback servers. |
| 119 /// | 121 /// |
| 120 /// [bind] should forward to either [HttpServer.bind] or | 122 /// [bind] should forward to either [HttpServer.bind] or |
| 121 /// [HttpServer.bindSecure]. | 123 /// [HttpServer.bindSecure]. |
| 122 static Future<HttpServer> _loopback(int port, | 124 static Future<HttpServer> _loopback(int port, |
| 123 Future<HttpServer> bind(InternetAddress address, int port), | 125 Future<HttpServer> bind(InternetAddress address, int port), |
| 124 [int remainingRetries]) { | 126 [int remainingRetries]) async { |
| 125 if (remainingRetries == null) remainingRetries = 5; | 127 if (remainingRetries == null) remainingRetries = 5; |
| 126 | 128 |
| 127 return Future.wait([ | 129 var v4Server = await bind(InternetAddress.LOOPBACK_IP_V4, port); |
| 128 supportsIpV6, | 130 if (!await supportsIpV6) return v4Server; |
| 129 bind(InternetAddress.LOOPBACK_IP_V4, port) | |
| 130 ]).then((results) { | |
| 131 var supportsIpV6 = results[0]; | |
| 132 var v4Server = results[1]; | |
| 133 | 131 |
| 134 if (!supportsIpV6) return v4Server; | 132 try { |
| 135 | |
| 136 // Reuse the IPv4 server's port so that if [port] is 0, both servers use | 133 // Reuse the IPv4 server's port so that if [port] is 0, both servers use |
| 137 // the same ephemeral port. | 134 // the same ephemeral port. |
| 138 return bind(InternetAddress.LOOPBACK_IP_V6, v4Server.port) | 135 var v6Server = await bind(InternetAddress.LOOPBACK_IP_V6, v4Server.port); |
| 139 .then((v6Server) { | 136 return new HttpMultiServer([v4Server, v6Server]); |
| 140 return new HttpMultiServer([v4Server, v6Server]); | 137 } on SocketException catch (error) { |
| 141 }).catchError((error) { | 138 if (error.osError.errorCode != _addressInUseErrno) rethrow; |
| 142 if (error is! SocketException) throw error; | 139 if (port != 0) rethrow; |
| 143 if (error.osError.errorCode != _addressInUseErrno) throw error; | 140 if (remainingRetries == 0) rethrow; |
| 144 if (port != 0) throw error; | |
| 145 if (remainingRetries == 0) throw error; | |
| 146 | 141 |
| 147 // A port being available on IPv4 doesn't necessarily mean that the same | 142 // A port being available on IPv4 doesn't necessarily mean that the same |
| 148 // port is available on IPv6. If it's not (which is rare in practice), | 143 // port is available on IPv6. If it's not (which is rare in practice), |
| 149 // we try again until we find one that's available on both. | 144 // we try again until we find one that's available on both. |
| 150 v4Server.close(); | 145 v4Server.close(); |
| 151 return _loopback(port, bind, remainingRetries - 1); | 146 return await _loopback(port, bind, remainingRetries - 1); |
| 152 }); | 147 } |
| 153 }); | |
| 154 } | 148 } |
| 155 | 149 |
| 156 Future close({bool force: false}) => | 150 Future close({bool force: false}) => |
| 157 Future.wait(_servers.map((server) => server.close(force: force))); | 151 Future.wait(_servers.map((server) => server.close(force: force))); |
| 158 | 152 |
| 159 /// Returns an HttpConnectionsInfo object summarizing the total number of | 153 /// Returns an HttpConnectionsInfo object summarizing the total number of |
| 160 /// current connections handled by all the servers. | 154 /// current connections handled by all the servers. |
| 161 HttpConnectionsInfo connectionsInfo() { | 155 HttpConnectionsInfo connectionsInfo() { |
| 162 var info = new HttpConnectionsInfo(); | 156 var info = new HttpConnectionsInfo(); |
| 163 for (var server in _servers) { | 157 for (var server in _servers) { |
| 164 var subInfo = server.connectionsInfo(); | 158 var subInfo = server.connectionsInfo(); |
| 165 info.total += subInfo.total; | 159 info.total += subInfo.total; |
| 166 info.active += subInfo.active; | 160 info.active += subInfo.active; |
| 167 info.idle += subInfo.idle; | 161 info.idle += subInfo.idle; |
| 168 info.closing += subInfo.closing; | 162 info.closing += subInfo.closing; |
| 169 } | 163 } |
| 170 return info; | 164 return info; |
| 171 } | 165 } |
| 172 } | 166 } |
| OLD | NEW |