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 bool closed = false; | 1075 bool closed = false; |
1075 | 1076 |
1076 Completer<_HttpIncoming> _nextResponseCompleter; | 1077 Completer<_HttpIncoming> _nextResponseCompleter; |
1077 Future _streamFuture; | 1078 Future _streamFuture; |
1078 | 1079 |
1079 _HttpClientConnection(String this.key, | 1080 _HttpClientConnection(String this.key, |
1080 Socket this._socket, | 1081 Socket this._socket, |
1081 _HttpClient this._httpClient, | 1082 _HttpClient this._httpClient, |
1082 [this._proxyTunnel = false]) | 1083 [this._proxyTunnel = false]) |
1083 : _httpParser = new _HttpParser.responseParser() { | 1084 : _httpParser = new _HttpParser.responseParser() { |
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1278 key, secureSocket, request._httpClient, true); | 1279 key, secureSocket, request._httpClient, true); |
1279 }); | 1280 }); |
1280 } | 1281 } |
1281 | 1282 |
1282 HttpConnectionInfo get connectionInfo => _HttpConnectionInfo.create(_socket); | 1283 HttpConnectionInfo get connectionInfo => _HttpConnectionInfo.create(_socket); |
1283 | 1284 |
1284 static makeKey(bool isSecure, String host, int port) { | 1285 static makeKey(bool isSecure, String host, int port) { |
1285 return isSecure ? "ssh:$host:$port" : "$host:$port"; | 1286 return isSecure ? "ssh:$host:$port" : "$host:$port"; |
1286 } | 1287 } |
1287 | 1288 |
| 1289 void stopTimer() { |
| 1290 if (_idleTimer != null) { |
| 1291 _idleTimer.cancel(); |
| 1292 _idleTimer = null; |
| 1293 } |
| 1294 } |
| 1295 |
| 1296 void startTimer() { |
| 1297 assert(_idleTimer == null); |
| 1298 _idleTimer = new Timer( |
| 1299 _httpClient.idleTimeout, |
| 1300 () { |
| 1301 _idleTimer = null; |
| 1302 close(); |
| 1303 }); |
| 1304 } |
1288 } | 1305 } |
1289 | 1306 |
1290 class _ConnnectionInfo { | 1307 class _ConnnectionInfo { |
1291 _ConnnectionInfo(_HttpClientConnection this.connection, _Proxy this.proxy); | 1308 _ConnnectionInfo(_HttpClientConnection this.connection, _Proxy this.proxy); |
1292 final _HttpClientConnection connection; | 1309 final _HttpClientConnection connection; |
1293 final _Proxy proxy; | 1310 final _Proxy proxy; |
1294 } | 1311 } |
1295 | 1312 |
1296 | 1313 |
1297 class _HttpClient implements HttpClient { | 1314 class _HttpClient implements HttpClient { |
1298 // TODO(ajohnsen): Use eviction timeout. | 1315 // TODO(ajohnsen): Use eviction timeout. |
1299 static const int DEFAULT_EVICTION_TIMEOUT = 60000; | |
1300 bool _closing = false; | 1316 bool _closing = false; |
1301 | 1317 |
1302 final Map<String, Set<_HttpClientConnection>> _idleConnections | 1318 final Map<String, Set<_HttpClientConnection>> _idleConnections |
1303 = new Map<String, Set<_HttpClientConnection>>(); | 1319 = new Map<String, Set<_HttpClientConnection>>(); |
1304 final Set<_HttpClientConnection> _activeConnections | 1320 final Set<_HttpClientConnection> _activeConnections |
1305 = new Set<_HttpClientConnection>(); | 1321 = new Set<_HttpClientConnection>(); |
1306 final List<_Credentials> _credentials = []; | 1322 final List<_Credentials> _credentials = []; |
1307 final List<_ProxyCredentials> _proxyCredentials = []; | 1323 final List<_ProxyCredentials> _proxyCredentials = []; |
1308 Function _authenticate; | 1324 Function _authenticate; |
1309 Function _authenticateProxy; | 1325 Function _authenticateProxy; |
1310 Function _findProxy = HttpClient.findProxyFromEnvironment; | 1326 Function _findProxy = HttpClient.findProxyFromEnvironment; |
| 1327 Duration _idleTimeout = const Duration(seconds: 15); |
| 1328 |
| 1329 Timer _noActiveTimer; |
| 1330 |
| 1331 Duration get idleTimeout => _idleTimeout; |
| 1332 |
| 1333 void set idleTimeout(Duration timeout) { |
| 1334 _idleTimeout = timeout; |
| 1335 var idle = _idleConnections.values.forEach( |
| 1336 (l) => l.forEach((c) { |
| 1337 // Reset timer. This is fine, as it's not happening often. |
| 1338 c.stopTimer(); |
| 1339 c.startTimer(); |
| 1340 })); |
| 1341 } |
1311 | 1342 |
1312 Future<HttpClientRequest> open(String method, | 1343 Future<HttpClientRequest> open(String method, |
1313 String host, | 1344 String host, |
1314 int port, | 1345 int port, |
1315 String path) { | 1346 String path) { |
1316 // TODO(sgjesse): The path set here can contain both query and | 1347 // TODO(sgjesse): The path set here can contain both query and |
1317 // fragment. They should be cracked and set correctly. | 1348 // fragment. They should be cracked and set correctly. |
1318 return _openUrl(method, new Uri.fromComponents( | 1349 return _openUrl(method, new Uri.fromComponents( |
1319 scheme: "http", domain: host, port: port, path: path)); | 1350 scheme: "http", domain: host, port: port, path: path)); |
1320 } | 1351 } |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1478 }); | 1509 }); |
1479 } | 1510 } |
1480 | 1511 |
1481 // Return a live connection to the idle pool. | 1512 // Return a live connection to the idle pool. |
1482 void _returnConnection(_HttpClientConnection connection) { | 1513 void _returnConnection(_HttpClientConnection connection) { |
1483 _activeConnections.remove(connection); | 1514 _activeConnections.remove(connection); |
1484 if (_closing) { | 1515 if (_closing) { |
1485 connection.close(); | 1516 connection.close(); |
1486 return; | 1517 return; |
1487 } | 1518 } |
1488 // TODO(ajohnsen): Listen for socket close events. | |
1489 if (!_idleConnections.containsKey(connection.key)) { | 1519 if (!_idleConnections.containsKey(connection.key)) { |
1490 _idleConnections[connection.key] = new LinkedHashSet(); | 1520 _idleConnections[connection.key] = new LinkedHashSet(); |
1491 } | 1521 } |
1492 _idleConnections[connection.key].add(connection); | 1522 _idleConnections[connection.key].add(connection); |
| 1523 connection.startTimer(); |
| 1524 _updateTimers(); |
1493 } | 1525 } |
1494 | 1526 |
1495 // Remove a closed connnection from the active set. | 1527 // Remove a closed connnection from the active set. |
1496 void _connectionClosed(_HttpClientConnection connection) { | 1528 void _connectionClosed(_HttpClientConnection connection) { |
| 1529 connection.stopTimer(); |
1497 _activeConnections.remove(connection); | 1530 _activeConnections.remove(connection); |
1498 if (_idleConnections.containsKey(connection.key)) { | 1531 if (_idleConnections.containsKey(connection.key)) { |
1499 _idleConnections[connection.key].remove(connection); | 1532 _idleConnections[connection.key].remove(connection); |
1500 if (_idleConnections[connection.key].isEmpty) { | 1533 if (_idleConnections[connection.key].isEmpty) { |
1501 _idleConnections.remove(connection.key); | 1534 _idleConnections.remove(connection.key); |
1502 } | 1535 } |
1503 } | 1536 } |
| 1537 _updateTimers(); |
| 1538 } |
| 1539 |
| 1540 void _updateTimers() { |
| 1541 if (_activeConnections.isEmpty) { |
| 1542 if (!_idleConnections.isEmpty && _noActiveTimer == null) { |
| 1543 _noActiveTimer = new Timer(const Duration(milliseconds: 100), () { |
| 1544 _noActiveTimer = null; |
| 1545 if (_activeConnections.isEmpty) { |
| 1546 close(); |
| 1547 _closing = false; |
| 1548 } |
| 1549 }); |
| 1550 } |
| 1551 } else if (_noActiveTimer != null) { |
| 1552 _noActiveTimer.cancel(); |
| 1553 _noActiveTimer = null; |
| 1554 } |
1504 } | 1555 } |
1505 | 1556 |
1506 // Get a new _HttpClientConnection, either from the idle pool or created from | 1557 // Get a new _HttpClientConnection, either from the idle pool or created from |
1507 // a new Socket. | 1558 // a new Socket. |
1508 Future<_ConnnectionInfo> _getConnection(String uriHost, | 1559 Future<_ConnnectionInfo> _getConnection(String uriHost, |
1509 int uriPort, | 1560 int uriPort, |
1510 _ProxyConfiguration proxyConf, | 1561 _ProxyConfiguration proxyConf, |
1511 bool isSecure) { | 1562 bool isSecure) { |
1512 Iterator<_Proxy> proxies = proxyConf.proxies.iterator; | 1563 Iterator<_Proxy> proxies = proxyConf.proxies.iterator; |
1513 | 1564 |
1514 Future<_ConnnectionInfo> connect(error) { | 1565 Future<_ConnnectionInfo> connect(error) { |
1515 if (!proxies.moveNext()) return new Future.error(error); | 1566 if (!proxies.moveNext()) return new Future.error(error); |
1516 _Proxy proxy = proxies.current; | 1567 _Proxy proxy = proxies.current; |
1517 String host = proxy.isDirect ? uriHost: proxy.host; | 1568 String host = proxy.isDirect ? uriHost: proxy.host; |
1518 int port = proxy.isDirect ? uriPort: proxy.port; | 1569 int port = proxy.isDirect ? uriPort: proxy.port; |
1519 String key = _HttpClientConnection.makeKey(isSecure, host, port); | 1570 String key = _HttpClientConnection.makeKey(isSecure, host, port); |
1520 if (_idleConnections.containsKey(key)) { | 1571 if (_idleConnections.containsKey(key)) { |
1521 var connection = _idleConnections[key].first; | 1572 var connection = _idleConnections[key].first; |
1522 _idleConnections[key].remove(connection); | 1573 _idleConnections[key].remove(connection); |
1523 if (_idleConnections[key].isEmpty) { | 1574 if (_idleConnections[key].isEmpty) { |
1524 _idleConnections.remove(key); | 1575 _idleConnections.remove(key); |
1525 } | 1576 } |
| 1577 connection.stopTimer(); |
1526 _activeConnections.add(connection); | 1578 _activeConnections.add(connection); |
| 1579 _updateTimers(); |
1527 return new Future.value(new _ConnnectionInfo(connection, proxy)); | 1580 return new Future.value(new _ConnnectionInfo(connection, proxy)); |
1528 } | 1581 } |
1529 return (isSecure && proxy.isDirect | 1582 return (isSecure && proxy.isDirect |
1530 ? SecureSocket.connect(host, | 1583 ? SecureSocket.connect(host, |
1531 port, | 1584 port, |
1532 sendClientCertificate: true) | 1585 sendClientCertificate: true) |
1533 : Socket.connect(host, port)) | 1586 : Socket.connect(host, port)) |
1534 .then((socket) { | 1587 .then((socket) { |
1535 socket.setOption(SocketOption.TCP_NODELAY, true); | 1588 socket.setOption(SocketOption.TCP_NODELAY, true); |
1536 var connection = new _HttpClientConnection(key, socket, this); | 1589 var connection = new _HttpClientConnection(key, socket, this); |
(...skipping 723 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2260 | 2313 |
2261 | 2314 |
2262 class _RedirectInfo implements RedirectInfo { | 2315 class _RedirectInfo implements RedirectInfo { |
2263 const _RedirectInfo(int this.statusCode, | 2316 const _RedirectInfo(int this.statusCode, |
2264 String this.method, | 2317 String this.method, |
2265 Uri this.location); | 2318 Uri this.location); |
2266 final int statusCode; | 2319 final int statusCode; |
2267 final String method; | 2320 final String method; |
2268 final Uri location; | 2321 final Uri location; |
2269 } | 2322 } |
OLD | NEW |