Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(99)

Side by Side Diff: sdk/lib/io/http_impl.dart

Issue 15268004: Add timeouts to HttpClient. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 part of dart.io; 5 part of dart.io;
6 6
7 class _HttpIncoming extends Stream<List<int>> { 7 class _HttpIncoming extends Stream<List<int>> {
8 final int _transferLength; 8 final int _transferLength;
9 final Completer _dataCompleter = new Completer(); 9 final Completer _dataCompleter = new Completer();
10 Stream<List<int>> _stream; 10 Stream<List<int>> _stream;
(...skipping 1053 matching lines...) Expand 10 before | Expand all | Expand 10 after
1064 } 1064 }
1065 1065
1066 class _HttpClientConnection { 1066 class _HttpClientConnection {
1067 final String key; 1067 final String key;
1068 final Socket _socket; 1068 final Socket _socket;
1069 final bool _proxyTunnel; 1069 final bool _proxyTunnel;
1070 final _HttpParser _httpParser; 1070 final _HttpParser _httpParser;
1071 StreamSubscription _subscription; 1071 StreamSubscription _subscription;
1072 final _HttpClient _httpClient; 1072 final _HttpClient _httpClient;
1073 bool _dispose = false; 1073 bool _dispose = false;
1074 Timer _idleTimer;
1074 1075
1075 Completer<_HttpIncoming> _nextResponseCompleter; 1076 Completer<_HttpIncoming> _nextResponseCompleter;
1076 Future _streamFuture; 1077 Future _streamFuture;
1077 1078
1078 _HttpClientConnection(String this.key, 1079 _HttpClientConnection(String this.key,
1079 Socket this._socket, 1080 Socket this._socket,
1080 _HttpClient this._httpClient, 1081 _HttpClient this._httpClient,
1081 [this._proxyTunnel = false]) 1082 [this._proxyTunnel = false])
1082 : _httpParser = new _HttpParser.responseParser() { 1083 : _httpParser = new _HttpParser.responseParser() {
1083 _socket.pipe(_httpParser); 1084 _socket.pipe(_httpParser);
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
1266 key, secureSocket, request._httpClient, true); 1267 key, secureSocket, request._httpClient, true);
1267 }); 1268 });
1268 } 1269 }
1269 1270
1270 HttpConnectionInfo get connectionInfo => _HttpConnectionInfo.create(_socket); 1271 HttpConnectionInfo get connectionInfo => _HttpConnectionInfo.create(_socket);
1271 1272
1272 static makeKey(bool isSecure, String host, int port) { 1273 static makeKey(bool isSecure, String host, int port) {
1273 return isSecure ? "ssh:$host:$port" : "$host:$port"; 1274 return isSecure ? "ssh:$host:$port" : "$host:$port";
1274 } 1275 }
1275 1276
1277 void stopTimer() {
1278 if (_idleTimer != null) {
1279 _idleTimer.cancel();
1280 _idleTimer = null;
1281 }
1282 }
1283
1284 void startTimer() {
1285 assert(_idleTimer == null);
1286 _idleTimer = new Timer(
1287 _httpClient.idleTimeout,
1288 () {
1289 _idleTimer = null;
1290 close();
1291 });
1292 }
1276 } 1293 }
1277 1294
1278 class _ConnnectionInfo { 1295 class _ConnnectionInfo {
1279 _ConnnectionInfo(_HttpClientConnection this.connection, _Proxy this.proxy); 1296 _ConnnectionInfo(_HttpClientConnection this.connection, _Proxy this.proxy);
1280 final _HttpClientConnection connection; 1297 final _HttpClientConnection connection;
1281 final _Proxy proxy; 1298 final _Proxy proxy;
1282 } 1299 }
1283 1300
1284 1301
1285 class _HttpClient implements HttpClient { 1302 class _HttpClient implements HttpClient {
1286 // TODO(ajohnsen): Use eviction timeout. 1303 // TODO(ajohnsen): Use eviction timeout.
1287 static const int DEFAULT_EVICTION_TIMEOUT = 60000;
1288 bool _closing = false; 1304 bool _closing = false;
1289 1305
1290 final Map<String, Set<_HttpClientConnection>> _idleConnections 1306 final Map<String, Set<_HttpClientConnection>> _idleConnections
1291 = new Map<String, Set<_HttpClientConnection>>(); 1307 = new Map<String, Set<_HttpClientConnection>>();
1292 final Set<_HttpClientConnection> _activeConnections 1308 final Set<_HttpClientConnection> _activeConnections
1293 = new Set<_HttpClientConnection>(); 1309 = new Set<_HttpClientConnection>();
1294 final List<_Credentials> _credentials = []; 1310 final List<_Credentials> _credentials = [];
1295 final List<_ProxyCredentials> _proxyCredentials = []; 1311 final List<_ProxyCredentials> _proxyCredentials = [];
1296 Function _authenticate; 1312 Function _authenticate;
1297 Function _authenticateProxy; 1313 Function _authenticateProxy;
1298 Function _findProxy = HttpClient.findProxyFromEnvironment; 1314 Function _findProxy = HttpClient.findProxyFromEnvironment;
1315 Duration _idleTimeout = const Duration(seconds: 15);
1316
1317 Timer _noActiveTimer;
1318
1319 Duration get idleTimeout => _idleTimeout;
1320
1321 void set idleTimeout(Duration timeout) {
1322 _idleTimeout = timeout;
1323 var idle = _idleConnections.values.forEach(
1324 (l) => l.forEach((c) {
1325 // Reset timer. This is fine, as it's not happening often.
1326 c.stopTimer();
1327 c.startTimer();
1328 }));
1329 }
1299 1330
1300 Future<HttpClientRequest> open(String method, 1331 Future<HttpClientRequest> open(String method,
1301 String host, 1332 String host,
1302 int port, 1333 int port,
1303 String path) { 1334 String path) {
1304 // TODO(sgjesse): The path set here can contain both query and 1335 // TODO(sgjesse): The path set here can contain both query and
1305 // fragment. They should be cracked and set correctly. 1336 // fragment. They should be cracked and set correctly.
1306 return _openUrl(method, new Uri.fromComponents( 1337 return _openUrl(method, new Uri.fromComponents(
1307 scheme: "http", domain: host, port: port, path: path)); 1338 scheme: "http", domain: host, port: port, path: path));
1308 } 1339 }
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
1457 }); 1488 });
1458 } 1489 }
1459 1490
1460 // Return a live connection to the idle pool. 1491 // Return a live connection to the idle pool.
1461 void _returnConnection(_HttpClientConnection connection) { 1492 void _returnConnection(_HttpClientConnection connection) {
1462 _activeConnections.remove(connection); 1493 _activeConnections.remove(connection);
1463 if (_closing) { 1494 if (_closing) {
1464 connection.close(); 1495 connection.close();
1465 return; 1496 return;
1466 } 1497 }
1467 // TODO(ajohnsen): Listen for socket close events.
1468 if (!_idleConnections.containsKey(connection.key)) { 1498 if (!_idleConnections.containsKey(connection.key)) {
1469 _idleConnections[connection.key] = new LinkedHashSet(); 1499 _idleConnections[connection.key] = new LinkedHashSet();
1470 } 1500 }
1471 _idleConnections[connection.key].add(connection); 1501 _idleConnections[connection.key].add(connection);
1502 connection.startTimer();
1503 _updateTimers();
1472 } 1504 }
1473 1505
1474 // Remove a closed connnection from the active set. 1506 // Remove a closed connnection from the active set.
1475 void _connectionClosed(_HttpClientConnection connection) { 1507 void _connectionClosed(_HttpClientConnection connection) {
1508 connection.stopTimer();
1476 _activeConnections.remove(connection); 1509 _activeConnections.remove(connection);
1477 if (_idleConnections.containsKey(connection.key)) { 1510 if (_idleConnections.containsKey(connection.key)) {
1478 _idleConnections[connection.key].remove(connection); 1511 _idleConnections[connection.key].remove(connection);
1479 if (_idleConnections[connection.key].isEmpty) { 1512 if (_idleConnections[connection.key].isEmpty) {
1480 _idleConnections.remove(connection.key); 1513 _idleConnections.remove(connection.key);
1481 } 1514 }
1482 } 1515 }
1516 _updateTimers();
1517 }
1518
1519 void _updateTimers() {
1520 if (_activeConnections.isEmpty) {
1521 if (!_idleConnections.isEmpty && _noActiveTimer == null) {
1522 _noActiveTimer = new Timer(const Duration(milliseconds: 100), () {
1523 _noActiveTimer = null;
1524 if (_activeConnections.isEmpty) {
1525 close();
1526 _closing = false;
1527 }
1528 });
1529 }
1530 } else if (_noActiveTimer != null) {
1531 _noActiveTimer.cancel();
1532 _noActiveTimer = null;
1533 }
1483 } 1534 }
1484 1535
1485 // Get a new _HttpClientConnection, either from the idle pool or created from 1536 // Get a new _HttpClientConnection, either from the idle pool or created from
1486 // a new Socket. 1537 // a new Socket.
1487 Future<_ConnnectionInfo> _getConnection(String uriHost, 1538 Future<_ConnnectionInfo> _getConnection(String uriHost,
1488 int uriPort, 1539 int uriPort,
1489 _ProxyConfiguration proxyConf, 1540 _ProxyConfiguration proxyConf,
1490 bool isSecure) { 1541 bool isSecure) {
1491 Iterator<_Proxy> proxies = proxyConf.proxies.iterator; 1542 Iterator<_Proxy> proxies = proxyConf.proxies.iterator;
1492 1543
1493 Future<_ConnnectionInfo> connect(error) { 1544 Future<_ConnnectionInfo> connect(error) {
1494 if (!proxies.moveNext()) return new Future.error(error); 1545 if (!proxies.moveNext()) return new Future.error(error);
1495 _Proxy proxy = proxies.current; 1546 _Proxy proxy = proxies.current;
1496 String host = proxy.isDirect ? uriHost: proxy.host; 1547 String host = proxy.isDirect ? uriHost: proxy.host;
1497 int port = proxy.isDirect ? uriPort: proxy.port; 1548 int port = proxy.isDirect ? uriPort: proxy.port;
1498 String key = _HttpClientConnection.makeKey(isSecure, host, port); 1549 String key = _HttpClientConnection.makeKey(isSecure, host, port);
1499 if (_idleConnections.containsKey(key)) { 1550 if (_idleConnections.containsKey(key)) {
1500 var connection = _idleConnections[key].first; 1551 var connection = _idleConnections[key].first;
1501 _idleConnections[key].remove(connection); 1552 _idleConnections[key].remove(connection);
1502 if (_idleConnections[key].isEmpty) { 1553 if (_idleConnections[key].isEmpty) {
1503 _idleConnections.remove(key); 1554 _idleConnections.remove(key);
1504 } 1555 }
1556 connection.stopTimer();
1505 _activeConnections.add(connection); 1557 _activeConnections.add(connection);
1558 _updateTimers();
1506 return new Future.value(new _ConnnectionInfo(connection, proxy)); 1559 return new Future.value(new _ConnnectionInfo(connection, proxy));
1507 } 1560 }
1508 return (isSecure && proxy.isDirect 1561 return (isSecure && proxy.isDirect
1509 ? SecureSocket.connect(host, 1562 ? SecureSocket.connect(host,
1510 port, 1563 port,
1511 sendClientCertificate: true) 1564 sendClientCertificate: true)
1512 : Socket.connect(host, port)) 1565 : Socket.connect(host, port))
1513 .then((socket) { 1566 .then((socket) {
1514 socket.setOption(SocketOption.TCP_NODELAY, true); 1567 socket.setOption(SocketOption.TCP_NODELAY, true);
1515 var connection = new _HttpClientConnection(key, socket, this); 1568 var connection = new _HttpClientConnection(key, socket, this);
(...skipping 719 matching lines...) Expand 10 before | Expand all | Expand 10 after
2235 2288
2236 2289
2237 class _RedirectInfo implements RedirectInfo { 2290 class _RedirectInfo implements RedirectInfo {
2238 const _RedirectInfo(int this.statusCode, 2291 const _RedirectInfo(int this.statusCode,
2239 String this.method, 2292 String this.method,
2240 Uri this.location); 2293 Uri this.location);
2241 final int statusCode; 2294 final int statusCode;
2242 final String method; 2295 final String method;
2243 final Uri location; 2296 final Uri location;
2244 } 2297 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698