Index: runtime/bin/socket_patch.dart |
diff --git a/runtime/bin/socket_patch.dart b/runtime/bin/socket_patch.dart |
index 7dbc01ad4bac130ac2fa5df91ba11122161170df..0d7546e5201ca92114c0e9d211dc672fd62fdbfb 100644 |
--- a/runtime/bin/socket_patch.dart |
+++ b/runtime/bin/socket_patch.dart |
@@ -273,6 +273,10 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject { |
static const int NORMAL_TOKEN_BATCH_SIZE = 8; |
static const int LISTENING_TOKEN_BATCH_SIZE = 2; |
+ static const Duration _RETRY_DURATION = const Duration(milliseconds: 250); |
+ static const Duration _RETRY_DURATION_LOOPBACK = |
+ const Duration(milliseconds: 25); |
+ |
// Use default Map so we keep order. |
static Map<int, _NativeSocket> _sockets = new Map<int, _NativeSocket>(); |
@@ -381,21 +385,25 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject { |
.then((host) { |
if (host is _InternetAddress) return [host]; |
return lookup(host) |
- .then((list) { |
- if (list.length == 0) { |
+ .then((addresses) { |
+ if (addresses.isEmpty) { |
throw createError(response, "Failed host lookup: '$host'"); |
} |
- return list; |
+ return addresses; |
}); |
}) |
.then((addresses) { |
assert(addresses is List); |
var completer = new Completer(); |
var it = addresses.iterator; |
- void run(error) { |
+ var error = null; |
+ var connecting = new HashMap(); |
+ void connectNext() { |
if (!it.moveNext()) { |
- assert(error != null); |
- completer.completeError(error); |
+ if (connecting.isEmpty) { |
+ assert(error != null); |
+ completer.completeError(error); |
+ } |
return; |
} |
var address = it.current; |
@@ -404,27 +412,45 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject { |
var result = socket.nativeCreateConnect(address._in_addr, port); |
if (result is OSError) { |
// Keep first error, if present. |
- run(error != null ? error : |
- createError(result, "Connection failed", address, port)); |
+ if (error == null) { |
+ error = createError(result, "Connection failed", address, port); |
+ } |
+ connectNext(); |
} else { |
socket.port; // Query the local port, for error messages. |
+ // Set up timer for when we should retry the next address (if any). |
Søren Gjesse
2014/08/04 09:21:37
Long line.
Anders Johnsen
2014/08/05 09:14:55
Done.
|
+ var duration = address.isLoopback ? |
+ _RETRY_DURATION_LOOPBACK : |
+ _RETRY_DURATION; |
+ var timer = new Timer(duration, connectNext); |
+ connecting[socket] = timer; |
// Setup handlers for receiving the first write event which |
// indicate that the socket is fully connected. |
socket.setHandlers( |
write: () { |
+ timer.cancel(); |
socket.setListening(read: false, write: false); |
completer.complete(socket); |
+ connecting.remove(socket); |
+ connecting.forEach((s, t) { |
+ t.cancel(); |
+ s.close(); |
Søren Gjesse
2014/08/04 09:21:37
Shouldn't s.destroy() be enough?
Anders Johnsen
2014/08/05 09:14:55
There is no destroy on _NativeSocket. We need to c
|
+ s.setHandlers(); |
+ s.setListening(read: false, write: false); |
+ }); |
}, |
error: (e) { |
+ timer.cancel(); |
socket.close(); |
// Keep first error, if present. |
- run(error != null ? error : e); |
- } |
- ); |
+ if (error == null) error = e; |
+ connecting.remove(socket); |
+ if (connecting.isEmpty) connectNext(); |
+ }); |
socket.setListening(read: false, write: true); |
} |
} |
- run(null); |
+ connectNext(); |
return completer.future; |
}); |
} |