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 |