Chromium Code Reviews| 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 const int _OUTGOING_BUFFER_SIZE = 8 * 1024; | 7 const int _OUTGOING_BUFFER_SIZE = 8 * 1024; |
| 8 | 8 |
| 9 class _HttpIncoming extends Stream<List<int>> { | 9 class _HttpIncoming extends Stream<List<int>> { |
| 10 final int _transferLength; | 10 final int _transferLength; |
| (...skipping 1484 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1495 } | 1495 } |
| 1496 | 1496 |
| 1497 class _ConnnectionInfo { | 1497 class _ConnnectionInfo { |
| 1498 final _HttpClientConnection connection; | 1498 final _HttpClientConnection connection; |
| 1499 final _Proxy proxy; | 1499 final _Proxy proxy; |
| 1500 | 1500 |
| 1501 _ConnnectionInfo(this.connection, this.proxy); | 1501 _ConnnectionInfo(this.connection, this.proxy); |
| 1502 } | 1502 } |
| 1503 | 1503 |
| 1504 | 1504 |
| 1505 class _ConnectionTarget { | |
| 1506 // Unique key for this connection target. | |
| 1507 final String key; | |
| 1508 final Set<_HttpClientConnection> idle = new HashSet(); | |
|
Søren Gjesse
2014/05/22 08:03:23
Rename idle and active to _idle and _active to ind
Anders Johnsen
2014/05/23 07:47:55
Done.
| |
| 1509 final Set<_HttpClientConnection> active = new HashSet(); | |
| 1510 | |
| 1511 _ConnectionTarget(this.key); | |
| 1512 | |
| 1513 bool get isEmpty => idle.isEmpty && active.isEmpty; | |
| 1514 | |
| 1515 void returnConnection(_HttpClientConnection connection) { | |
| 1516 assert(active.contains(connection)); | |
| 1517 active.remove(connection); | |
| 1518 idle.add(connection); | |
| 1519 } | |
| 1520 | |
| 1521 void connectionClosed(_HttpClientConnection connection) { | |
|
Søren Gjesse
2014/05/22 08:03:23
Add the assert from above here as well.
Anders Johnsen
2014/05/23 07:47:55
Done.
| |
| 1522 active.remove(connection); | |
| 1523 idle.remove(connection); | |
| 1524 } | |
| 1525 | |
| 1526 void close(bool force) { | |
| 1527 for (var c in idle.toList()) { | |
| 1528 c.close(); | |
| 1529 } | |
| 1530 if (force) { | |
| 1531 for (var c in active.toList()) { | |
| 1532 c.destroy(); | |
| 1533 } | |
| 1534 } | |
| 1535 } | |
| 1536 } | |
| 1537 | |
| 1538 | |
| 1505 class _HttpClient implements HttpClient { | 1539 class _HttpClient implements HttpClient { |
| 1506 bool _closing = false; | 1540 bool _closing = false; |
| 1507 final Map<String, Set<_HttpClientConnection>> _idleConnections | 1541 final Map<String, _ConnectionTarget> _connectionTargets |
| 1508 = new HashMap<String, Set<_HttpClientConnection>>(); | 1542 = new HashMap<String, _ConnectionTarget>(); |
| 1509 final Map<String, Set<_HttpClientConnection>> _activeConnections | |
| 1510 = new HashMap<String, Set<_HttpClientConnection>>(); | |
| 1511 final List<_Credentials> _credentials = []; | 1543 final List<_Credentials> _credentials = []; |
| 1512 final List<_ProxyCredentials> _proxyCredentials = []; | 1544 final List<_ProxyCredentials> _proxyCredentials = []; |
| 1513 Function _authenticate; | 1545 Function _authenticate; |
| 1514 Function _authenticateProxy; | 1546 Function _authenticateProxy; |
| 1515 Function _findProxy = HttpClient.findProxyFromEnvironment; | 1547 Function _findProxy = HttpClient.findProxyFromEnvironment; |
| 1516 Duration _idleTimeout = const Duration(seconds: 15); | 1548 Duration _idleTimeout = const Duration(seconds: 15); |
| 1517 Function _badCertificateCallback; | 1549 Function _badCertificateCallback; |
| 1518 | 1550 |
| 1519 Timer _noActiveTimer; | 1551 Timer _noActiveTimer; |
| 1520 | 1552 |
| 1521 Duration get idleTimeout => _idleTimeout; | 1553 Duration get idleTimeout => _idleTimeout; |
| 1522 | 1554 |
| 1523 String userAgent = _getHttpVersion(); | 1555 String userAgent = _getHttpVersion(); |
| 1524 | 1556 |
| 1525 void set idleTimeout(Duration timeout) { | 1557 void set idleTimeout(Duration timeout) { |
| 1526 _idleTimeout = timeout; | 1558 _idleTimeout = timeout; |
| 1527 _idleConnections.values.forEach( | 1559 for (var c in _connectionTargets.values) { |
| 1528 (l) => l.forEach((c) { | 1560 for (var idle in c.idle) { |
| 1529 // Reset timer. This is fine, as it's not happening often. | 1561 // Reset timer. This is fine, as it's not happening often. |
| 1530 c.stopTimer(); | 1562 idle.stopTimer(); |
| 1531 c.startTimer(); | 1563 idle.startTimer(); |
| 1532 })); | 1564 } |
| 1565 } | |
| 1533 } | 1566 } |
| 1534 | 1567 |
| 1535 set badCertificateCallback(bool callback(X509Certificate cert, | 1568 set badCertificateCallback(bool callback(X509Certificate cert, |
| 1536 String host, | 1569 String host, |
| 1537 int port)) { | 1570 int port)) { |
| 1538 _badCertificateCallback = callback; | 1571 _badCertificateCallback = callback; |
| 1539 } | 1572 } |
| 1540 | 1573 |
| 1541 | 1574 |
| 1542 Future<HttpClientRequest> open(String method, | 1575 Future<HttpClientRequest> open(String method, |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1578 | 1611 |
| 1579 Future<HttpClientRequest> headUrl(Uri url) => _openUrl("head", url); | 1612 Future<HttpClientRequest> headUrl(Uri url) => _openUrl("head", url); |
| 1580 | 1613 |
| 1581 Future<HttpClientRequest> patch(String host, int port, String path) | 1614 Future<HttpClientRequest> patch(String host, int port, String path) |
| 1582 => open("patch", host, port, path); | 1615 => open("patch", host, port, path); |
| 1583 | 1616 |
| 1584 Future<HttpClientRequest> patchUrl(Uri url) => _openUrl("patch", url); | 1617 Future<HttpClientRequest> patchUrl(Uri url) => _openUrl("patch", url); |
| 1585 | 1618 |
| 1586 void close({bool force: false}) { | 1619 void close({bool force: false}) { |
| 1587 _closing = true; | 1620 _closing = true; |
| 1588 // Create flattened copy of _idleConnections, as 'destory' will manipulate | 1621 _connectionTargets.values.toList().forEach((c) => c.close(force)); |
| 1589 // it. | 1622 assert(!_connectionTargets.values.any((s) => s.idle.isNotEmpty)); |
| 1590 var idle = _idleConnections.values.fold( | 1623 assert(!force || _connectionTargets.isEmpty); |
| 1591 [], | |
| 1592 (l, e) { | |
| 1593 l.addAll(e); | |
| 1594 return l; | |
| 1595 }); | |
| 1596 idle.forEach((e) { | |
| 1597 e.close(); | |
| 1598 }); | |
| 1599 assert(_idleConnections.isEmpty); | |
| 1600 if (force) { | |
| 1601 for (var connection in | |
| 1602 _activeConnections.values.expand((s) => s).toList()) { | |
| 1603 connection.destroy(); | |
| 1604 } | |
| 1605 assert(_activeConnections.isEmpty); | |
| 1606 _activeConnections.clear(); | |
| 1607 } | |
| 1608 } | 1624 } |
| 1609 | 1625 |
| 1610 set authenticate(Future<bool> f(Uri url, String scheme, String realm)) { | 1626 set authenticate(Future<bool> f(Uri url, String scheme, String realm)) { |
| 1611 _authenticate = f; | 1627 _authenticate = f; |
| 1612 } | 1628 } |
| 1613 | 1629 |
| 1614 void addCredentials(Uri url, String realm, HttpClientCredentials cr) => | 1630 void addCredentials(Uri url, String realm, HttpClientCredentials cr) => |
| 1615 _credentials.add(new _SiteCredentials(url, realm, cr)); | 1631 _credentials.add(new _SiteCredentials(url, realm, cr)); |
| 1616 | 1632 |
| 1617 set authenticateProxy( | 1633 set authenticateProxy( |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1696 } | 1712 } |
| 1697 } | 1713 } |
| 1698 return request | 1714 return request |
| 1699 ..headers.chunkedTransferEncoding = false | 1715 ..headers.chunkedTransferEncoding = false |
| 1700 ..contentLength = 0; | 1716 ..contentLength = 0; |
| 1701 }); | 1717 }); |
| 1702 } | 1718 } |
| 1703 | 1719 |
| 1704 // Return a live connection to the idle pool. | 1720 // Return a live connection to the idle pool. |
| 1705 void _returnConnection(_HttpClientConnection connection) { | 1721 void _returnConnection(_HttpClientConnection connection) { |
| 1706 var key = connection.key; | 1722 _connectionTargets[connection.key].returnConnection(connection); |
| 1707 _activeConnections[key].remove(connection); | |
| 1708 if (_activeConnections[key].isEmpty) { | |
| 1709 _activeConnections.remove(key); | |
| 1710 } | |
| 1711 if (_closing) { | |
| 1712 connection.close(); | |
| 1713 return; | |
| 1714 } | |
| 1715 _idleConnections | |
| 1716 .putIfAbsent(key, () => new HashSet()) | |
| 1717 .add(connection); | |
| 1718 connection.startTimer(); | 1723 connection.startTimer(); |
| 1719 _updateTimers(); | 1724 _updateTimers(); |
| 1720 } | 1725 } |
| 1721 | 1726 |
| 1722 // Remove a closed connnection from the active set. | 1727 // Remove a closed connnection from the active set. |
| 1723 void _connectionClosed(_HttpClientConnection connection) { | 1728 void _connectionClosed(_HttpClientConnection connection) { |
| 1724 connection.stopTimer(); | 1729 connection.stopTimer(); |
| 1725 var key = connection.key; | 1730 var connectionTarget = _connectionTargets[connection.key]; |
| 1726 if (_activeConnections.containsKey(key)) { | 1731 if (connectionTarget != null) { |
| 1727 _activeConnections[key].remove(connection); | 1732 connectionTarget.connectionClosed(connection); |
| 1728 if (_activeConnections[key].isEmpty) { | 1733 if (connectionTarget.isEmpty) { |
| 1729 _activeConnections.remove(key); | 1734 _connectionTargets.remove(connection.key); |
| 1730 } | 1735 } |
| 1736 _updateTimers(); | |
| 1731 } | 1737 } |
| 1732 if (_idleConnections.containsKey(key)) { | |
| 1733 _idleConnections[key].remove(connection); | |
| 1734 if (_idleConnections[key].isEmpty) { | |
| 1735 _idleConnections.remove(key); | |
| 1736 } | |
| 1737 } | |
| 1738 _updateTimers(); | |
| 1739 } | 1738 } |
| 1740 | 1739 |
| 1741 void _updateTimers() { | 1740 void _updateTimers() { |
| 1742 if (_activeConnections.isEmpty) { | 1741 bool hasActive = _connectionTargets.values.any((t) => t.active.isNotEmpty); |
|
Søren Gjesse
2014/05/22 08:03:23
Add hasActive (or hasActiveConnections) to Connect
Anders Johnsen
2014/05/23 07:47:55
Done.
| |
| 1743 if (!_idleConnections.isEmpty && _noActiveTimer == null) { | 1742 if (!hasActive) { |
| 1743 bool hasIdle = _connectionTargets.values.any((t) => t.idle.isNotEmpty); | |
|
Søren Gjesse
2014/05/22 08:03:23
Add hasIdle (or hasIdleConnections) to ConnectionT
Anders Johnsen
2014/05/23 07:47:55
Done.
| |
| 1744 if (hasIdle && _noActiveTimer == null) { | |
| 1744 _noActiveTimer = new Timer(const Duration(milliseconds: 100), () { | 1745 _noActiveTimer = new Timer(const Duration(milliseconds: 100), () { |
| 1745 _noActiveTimer = null; | 1746 _noActiveTimer = null; |
| 1746 if (_activeConnections.isEmpty) { | 1747 bool hasActive = |
| 1748 _connectionTargets.values.any((t) => t.active.isNotEmpty); | |
|
Søren Gjesse
2014/05/22 08:03:23
Ditto.
Anders Johnsen
2014/05/23 07:47:55
Done.
| |
| 1749 if (!hasActive) { | |
| 1747 close(); | 1750 close(); |
| 1748 _closing = false; | 1751 _closing = false; |
| 1749 } | 1752 } |
| 1750 }); | 1753 }); |
| 1751 } | 1754 } |
| 1752 } else if (_noActiveTimer != null) { | 1755 } else if (_noActiveTimer != null) { |
| 1753 _noActiveTimer.cancel(); | 1756 _noActiveTimer.cancel(); |
| 1754 _noActiveTimer = null; | 1757 _noActiveTimer = null; |
| 1755 } | 1758 } |
| 1756 } | 1759 } |
| 1757 | 1760 |
| 1758 // Get a new _HttpClientConnection, either from the idle pool or created from | 1761 // Get a new _HttpClientConnection, either from the idle pool or created from |
| 1759 // a new Socket. | 1762 // a new Socket. |
| 1760 Future<_ConnnectionInfo> _getConnection(String uriHost, | 1763 Future<_ConnnectionInfo> _getConnection(String uriHost, |
| 1761 int uriPort, | 1764 int uriPort, |
| 1762 _ProxyConfiguration proxyConf, | 1765 _ProxyConfiguration proxyConf, |
| 1763 bool isSecure) { | 1766 bool isSecure) { |
| 1764 Iterator<_Proxy> proxies = proxyConf.proxies.iterator; | 1767 Iterator<_Proxy> proxies = proxyConf.proxies.iterator; |
| 1765 | 1768 |
| 1766 Future<_ConnnectionInfo> connect(error) { | 1769 Future<_ConnnectionInfo> connect(error) { |
| 1767 if (!proxies.moveNext()) return new Future.error(error); | 1770 if (!proxies.moveNext()) return new Future.error(error); |
| 1768 _Proxy proxy = proxies.current; | 1771 _Proxy proxy = proxies.current; |
| 1769 String host = proxy.isDirect ? uriHost: proxy.host; | 1772 String host = proxy.isDirect ? uriHost: proxy.host; |
| 1770 int port = proxy.isDirect ? uriPort: proxy.port; | 1773 int port = proxy.isDirect ? uriPort: proxy.port; |
| 1771 String key = _HttpClientConnection.makeKey(isSecure, host, port); | 1774 String key = _HttpClientConnection.makeKey(isSecure, host, port); |
| 1772 if (_idleConnections.containsKey(key)) { | 1775 var connectionTarget = _connectionTargets[key]; |
| 1773 var connection = _idleConnections[key].first; | 1776 if (connectionTarget != null && connectionTarget.idle.isNotEmpty) { |
|
Søren Gjesse
2014/05/22 08:03:23
Add method getConnection to ConnectionTarget to av
Anders Johnsen
2014/05/23 07:47:55
Done.
| |
| 1774 _idleConnections[key].remove(connection); | 1777 var connection = connectionTarget.idle.first; |
| 1775 if (_idleConnections[key].isEmpty) { | 1778 connectionTarget.idle.remove(connection); |
| 1776 _idleConnections.remove(key); | |
| 1777 } | |
| 1778 connection.stopTimer(); | 1779 connection.stopTimer(); |
| 1779 _activeConnections | 1780 connectionTarget.active.add(connection); |
| 1780 .putIfAbsent(key, () => new HashSet()) | |
| 1781 .add(connection); | |
| 1782 _updateTimers(); | 1781 _updateTimers(); |
| 1783 return new Future.value(new _ConnnectionInfo(connection, proxy)); | 1782 return new Future.value(new _ConnnectionInfo(connection, proxy)); |
| 1784 } | 1783 } |
| 1785 var currentBadCertificateCallback = _badCertificateCallback; | 1784 var currentBadCertificateCallback = _badCertificateCallback; |
| 1786 bool callback(X509Certificate certificate) => | 1785 bool callback(X509Certificate certificate) => |
| 1787 currentBadCertificateCallback == null ? false : | 1786 currentBadCertificateCallback == null ? false : |
| 1788 currentBadCertificateCallback(certificate, uriHost, uriPort); | 1787 currentBadCertificateCallback(certificate, uriHost, uriPort); |
| 1789 Future socketFuture = (isSecure && proxy.isDirect | 1788 Future socketFuture = (isSecure && proxy.isDirect |
| 1790 ? SecureSocket.connect(host, | 1789 ? SecureSocket.connect(host, |
| 1791 port, | 1790 port, |
| 1792 sendClientCertificate: true, | 1791 sendClientCertificate: true, |
| 1793 onBadCertificate: callback) | 1792 onBadCertificate: callback) |
| 1794 : Socket.connect(host, port)); | 1793 : Socket.connect(host, port)); |
| 1795 return socketFuture.then((socket) { | 1794 return socketFuture.then((socket) { |
| 1796 socket.setOption(SocketOption.TCP_NODELAY, true); | 1795 socket.setOption(SocketOption.TCP_NODELAY, true); |
| 1797 var connection = new _HttpClientConnection(key, socket, this); | 1796 var connection = new _HttpClientConnection(key, socket, this); |
| 1798 if (isSecure && !proxy.isDirect) { | 1797 if (isSecure && !proxy.isDirect) { |
| 1799 connection._dispose = true; | 1798 connection._dispose = true; |
| 1800 return connection.createProxyTunnel( | 1799 return connection.createProxyTunnel( |
| 1801 uriHost, uriPort, proxy, callback) | 1800 uriHost, uriPort, proxy, callback) |
| 1802 .then((tunnel) { | 1801 .then((tunnel) { |
| 1803 _activeConnections | 1802 var connectionTarget = _connectionTargets |
| 1804 .putIfAbsent(tunnel.key, () => new HashSet()) | 1803 .putIfAbsent(tunnel.key, |
| 1805 .add(tunnel); | 1804 () => new _ConnectionTarget(tunnel.key)); |
| 1805 connectionTarget.active.add(tunnel); | |
|
Søren Gjesse
2014/05/22 08:03:23
Add method addConnection to ConnectionTarget
Anders Johnsen
2014/05/23 07:47:55
Done.
| |
| 1806 return new _ConnnectionInfo(tunnel, proxy); | 1806 return new _ConnnectionInfo(tunnel, proxy); |
| 1807 }); | 1807 }); |
| 1808 } else { | 1808 } else { |
| 1809 _activeConnections | 1809 var connectionTarget = _connectionTargets |
| 1810 .putIfAbsent(key, () => new HashSet()) | 1810 .putIfAbsent(key, () => new _ConnectionTarget(key)); |
| 1811 .add(connection); | 1811 connectionTarget.active.add(connection); |
| 1812 return new _ConnnectionInfo(connection, proxy); | 1812 return new _ConnnectionInfo(connection, proxy); |
| 1813 } | 1813 } |
| 1814 }, onError: (error) { | 1814 }, onError: (error) { |
| 1815 // Continue with next proxy. | 1815 // Continue with next proxy. |
| 1816 return connect(error); | 1816 return connect(error); |
| 1817 }); | 1817 }); |
| 1818 } | 1818 } |
| 1819 return connect(new HttpException("No proxies given")); | 1819 return connect(new HttpException("No proxies given")); |
| 1820 } | 1820 } |
| 1821 | 1821 |
| (...skipping 814 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2636 const _RedirectInfo(this.statusCode, this.method, this.location); | 2636 const _RedirectInfo(this.statusCode, this.method, this.location); |
| 2637 } | 2637 } |
| 2638 | 2638 |
| 2639 String _getHttpVersion() { | 2639 String _getHttpVersion() { |
| 2640 var version = Platform.version; | 2640 var version = Platform.version; |
| 2641 // Only include major and minor version numbers. | 2641 // Only include major and minor version numbers. |
| 2642 int index = version.indexOf('.', version.indexOf('.') + 1); | 2642 int index = version.indexOf('.', version.indexOf('.') + 1); |
| 2643 version = version.substring(0, index); | 2643 version = version.substring(0, index); |
| 2644 return 'Dart/$version (dart:io)'; | 2644 return 'Dart/$version (dart:io)'; |
| 2645 } | 2645 } |
| OLD | NEW |