OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |