Chromium Code Reviews| Index: sdk/lib/io/http_impl.dart |
| diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart |
| index e9b610e00fedc9aa32c6c885e65b1935b9dc5e3f..d0d730e865305775f8c0332375e41636da71de9b 100644 |
| --- a/sdk/lib/io/http_impl.dart |
| +++ b/sdk/lib/io/http_impl.dart |
| @@ -1502,12 +1502,62 @@ class _ConnnectionInfo { |
| } |
| +class _ConnectionTarget { |
| + // Unique key for this connection target. |
| + final String key; |
| + final Set<_HttpClientConnection> _idle = new HashSet(); |
| + final Set<_HttpClientConnection> _active = new HashSet(); |
| + |
| + _ConnectionTarget(this.key); |
| + |
| + bool get isEmpty => _idle.isEmpty && _active.isEmpty; |
| + |
| + bool get hasIdle => _idle.isNotEmpty; |
| + |
| + bool get hasActive => _active.isNotEmpty; |
| + |
| + _HttpClientConnection takeIdle() { |
| + assert(hasIdle); |
| + _HttpClientConnection connection = _idle.first; |
| + _idle.remove(connection); |
| + connection.stopTimer(); |
| + _active.add(connection); |
| + return connection; |
| + } |
| + |
| + void addNewActive(_HttpClientConnection connection) { |
| + _active.add(connection); |
| + } |
| + |
| + void returnConnection(_HttpClientConnection connection) { |
| + assert(_active.contains(connection)); |
| + _active.remove(connection); |
| + _idle.add(connection); |
| + } |
| + |
| + void connectionClosed(_HttpClientConnection connection) { |
| + assert(!_active.contains(connection) || !_idle.contains(connection)); |
| + _active.remove(connection); |
| + _idle.remove(connection); |
| + } |
| + |
| + void close(bool force) { |
| + for (var c in _idle.toList()) { |
| + c.close(); |
| + } |
| + if (force) { |
| + for (var c in _active.toList()) { |
| + c.destroy(); |
| + } |
| + } |
| + } |
| +} |
| + |
| + |
| class _HttpClient implements HttpClient { |
| bool _closing = false; |
| - final Map<String, Set<_HttpClientConnection>> _idleConnections |
| - = new HashMap<String, Set<_HttpClientConnection>>(); |
| - final Map<String, Set<_HttpClientConnection>> _activeConnections |
| - = new HashMap<String, Set<_HttpClientConnection>>(); |
| + final Map<String, _ConnectionTarget> _connectionTargets |
| + = new HashMap<String, _ConnectionTarget>(); |
| final List<_Credentials> _credentials = []; |
| final List<_ProxyCredentials> _proxyCredentials = []; |
| Function _authenticate; |
| @@ -1524,12 +1574,13 @@ class _HttpClient implements HttpClient { |
| void set idleTimeout(Duration timeout) { |
| _idleTimeout = timeout; |
| - _idleConnections.values.forEach( |
| - (l) => l.forEach((c) { |
| - // Reset timer. This is fine, as it's not happening often. |
| - c.stopTimer(); |
| - c.startTimer(); |
| - })); |
| + for (var c in _connectionTargets.values) { |
| + for (var idle in c.idle) { |
| + // Reset timer. This is fine, as it's not happening often. |
| + idle.stopTimer(); |
| + idle.startTimer(); |
| + } |
| + } |
| } |
| set badCertificateCallback(bool callback(X509Certificate cert, |
| @@ -1585,26 +1636,9 @@ class _HttpClient implements HttpClient { |
| void close({bool force: false}) { |
| _closing = true; |
| - // Create flattened copy of _idleConnections, as 'destory' will manipulate |
| - // it. |
| - var idle = _idleConnections.values.fold( |
| - [], |
| - (l, e) { |
| - l.addAll(e); |
| - return l; |
| - }); |
| - idle.forEach((e) { |
| - e.close(); |
| - }); |
| - assert(_idleConnections.isEmpty); |
| - if (force) { |
| - for (var connection in |
| - _activeConnections.values.expand((s) => s).toList()) { |
| - connection.destroy(); |
| - } |
| - assert(_activeConnections.isEmpty); |
| - _activeConnections.clear(); |
| - } |
| + _connectionTargets.values.toList().forEach((c) => c.close(force)); |
| + assert(!_connectionTargets.values.any((s) => s.hasIdle)); |
| + assert(!force || _connectionTargets.isEmpty); |
| } |
| set authenticate(Future<bool> f(Uri url, String scheme, String realm)) { |
| @@ -1703,18 +1737,7 @@ class _HttpClient implements HttpClient { |
| // Return a live connection to the idle pool. |
| void _returnConnection(_HttpClientConnection connection) { |
| - var key = connection.key; |
| - _activeConnections[key].remove(connection); |
| - if (_activeConnections[key].isEmpty) { |
| - _activeConnections.remove(key); |
| - } |
| - if (_closing) { |
| - connection.close(); |
| - return; |
| - } |
| - _idleConnections |
| - .putIfAbsent(key, () => new HashSet()) |
| - .add(connection); |
| + _connectionTargets[connection.key].returnConnection(connection); |
| connection.startTimer(); |
| _updateTimers(); |
| } |
| @@ -1722,28 +1745,26 @@ class _HttpClient implements HttpClient { |
| // Remove a closed connnection from the active set. |
| void _connectionClosed(_HttpClientConnection connection) { |
| connection.stopTimer(); |
| - var key = connection.key; |
| - if (_activeConnections.containsKey(key)) { |
| - _activeConnections[key].remove(connection); |
| - if (_activeConnections[key].isEmpty) { |
| - _activeConnections.remove(key); |
| + var connectionTarget = _connectionTargets[connection.key]; |
| + if (connectionTarget != null) { |
| + connectionTarget.connectionClosed(connection); |
| + if (connectionTarget.isEmpty) { |
| + _connectionTargets.remove(connection.key); |
| } |
| + _updateTimers(); |
| } |
| - if (_idleConnections.containsKey(key)) { |
| - _idleConnections[key].remove(connection); |
| - if (_idleConnections[key].isEmpty) { |
| - _idleConnections.remove(key); |
| - } |
| - } |
| - _updateTimers(); |
| } |
| void _updateTimers() { |
| - if (_activeConnections.isEmpty) { |
| - if (!_idleConnections.isEmpty && _noActiveTimer == null) { |
| + bool hasActive = _connectionTargets.values.any((t) => t.hasActive); |
|
Søren Gjesse
2014/05/23 09:49:11
We could consider abstracting the _connectionTarge
|
| + if (!hasActive) { |
| + bool hasIdle = _connectionTargets.values.any((t) => t.hasIdle); |
| + if (hasIdle && _noActiveTimer == null) { |
| _noActiveTimer = new Timer(const Duration(milliseconds: 100), () { |
| _noActiveTimer = null; |
| - if (_activeConnections.isEmpty) { |
| + bool hasActive = |
| + _connectionTargets.values.any((t) => t.hasActive); |
| + if (!hasActive) { |
| close(); |
| _closing = false; |
| } |
| @@ -1769,16 +1790,9 @@ class _HttpClient implements HttpClient { |
| String host = proxy.isDirect ? uriHost: proxy.host; |
| int port = proxy.isDirect ? uriPort: proxy.port; |
| String key = _HttpClientConnection.makeKey(isSecure, host, port); |
| - if (_idleConnections.containsKey(key)) { |
| - var connection = _idleConnections[key].first; |
| - _idleConnections[key].remove(connection); |
| - if (_idleConnections[key].isEmpty) { |
| - _idleConnections.remove(key); |
| - } |
| - connection.stopTimer(); |
| - _activeConnections |
| - .putIfAbsent(key, () => new HashSet()) |
| - .add(connection); |
| + var connectionTarget = _connectionTargets[key]; |
| + if (connectionTarget != null && connectionTarget.hasIdle) { |
| + var connection = connectionTarget.takeIdle(); |
| _updateTimers(); |
| return new Future.value(new _ConnnectionInfo(connection, proxy)); |
| } |
| @@ -1800,15 +1814,16 @@ class _HttpClient implements HttpClient { |
| return connection.createProxyTunnel( |
| uriHost, uriPort, proxy, callback) |
| .then((tunnel) { |
| - _activeConnections |
| - .putIfAbsent(tunnel.key, () => new HashSet()) |
| - .add(tunnel); |
| + var connectionTarget = _connectionTargets |
| + .putIfAbsent(tunnel.key, |
| + () => new _ConnectionTarget(tunnel.key)); |
| + connectionTarget.addNewActive(tunnel); |
| return new _ConnnectionInfo(tunnel, proxy); |
| }); |
| } else { |
| - _activeConnections |
| - .putIfAbsent(key, () => new HashSet()) |
| - .add(connection); |
| + var connectionTarget = _connectionTargets |
| + .putIfAbsent(key, () => new _ConnectionTarget(key)); |
| + connectionTarget.addNewActive(connection); |
| return new _ConnnectionInfo(connection, proxy); |
| } |
| }, onError: (error) { |