| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 class _HttpHeaders implements HttpHeaders { | 5 class _HttpHeaders implements HttpHeaders { |
| 6 _HttpHeaders() : _headers = new Map<String, List<String>>(); | 6 _HttpHeaders() : _headers = new Map<String, List<String>>(); |
| 7 | 7 |
| 8 List<String> operator[](String name) { | 8 List<String> operator[](String name) { |
| 9 name = name.toLowerCase(); | 9 name = name.toLowerCase(); |
| 10 return _headers[name]; | 10 return _headers[name]; |
| (...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 301 List<String> _noFoldingHeaders; | 301 List<String> _noFoldingHeaders; |
| 302 | 302 |
| 303 String _host; | 303 String _host; |
| 304 int _port; | 304 int _port; |
| 305 } | 305 } |
| 306 | 306 |
| 307 | 307 |
| 308 class _HeaderValue implements HeaderValue { | 308 class _HeaderValue implements HeaderValue { |
| 309 _HeaderValue([String this.value = ""]); | 309 _HeaderValue([String this.value = ""]); |
| 310 | 310 |
| 311 _HeaderValue.fromString(String value) { | 311 _HeaderValue.fromString(String value, [this.parameterSeparator = ";"]) { |
| 312 // Parse the string. | 312 // Parse the string. |
| 313 _parse(value); | 313 _parse(value); |
| 314 } | 314 } |
| 315 | 315 |
| 316 Map<String, String> get parameters { | 316 Map<String, String> get parameters { |
| 317 if (_parameters == null) _parameters = new Map<String, String>(); | 317 if (_parameters == null) _parameters = new Map<String, String>(); |
| 318 return _parameters; | 318 return _parameters; |
| 319 } | 319 } |
| 320 | 320 |
| 321 String toString() { | 321 String toString() { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 340 void skipWS() { | 340 void skipWS() { |
| 341 while (!done()) { | 341 while (!done()) { |
| 342 if (s[index] != " " && s[index] != "\t") return; | 342 if (s[index] != " " && s[index] != "\t") return; |
| 343 index++; | 343 index++; |
| 344 } | 344 } |
| 345 } | 345 } |
| 346 | 346 |
| 347 String parseValue() { | 347 String parseValue() { |
| 348 int start = index; | 348 int start = index; |
| 349 while (!done()) { | 349 while (!done()) { |
| 350 if (s[index] == " " || s[index] == "\t" || s[index] == ";") break; | 350 if (s[index] == " " || |
| 351 s[index] == "\t" || |
| 352 s[index] == parameterSeparator) break; |
| 351 index++; | 353 index++; |
| 352 } | 354 } |
| 353 return s.substring(start, index).toLowerCase(); | 355 return s.substring(start, index).toLowerCase(); |
| 354 } | 356 } |
| 355 | 357 |
| 356 void expect(String expected) { | 358 void expect(String expected) { |
| 357 if (done()) throw new HttpException("Failed to parse header value [$s]"); | 359 if (done() || s[index] != expected) { |
| 358 if (s[index] != expected) { | 360 throw new HttpException("Failed to parse header value"); |
| 359 throw new HttpException("Failed to parse header value [$s]"); | |
| 360 } | 361 } |
| 361 index++; | 362 index++; |
| 362 } | 363 } |
| 363 | 364 |
| 365 void maybeExpect(String expected) { |
| 366 if (s[index] == expected) index++; |
| 367 } |
| 368 |
| 364 void parseParameters() { | 369 void parseParameters() { |
| 365 _parameters = new Map<String, String>(); | 370 _parameters = new Map<String, String>(); |
| 366 | 371 |
| 367 String parseParameterName() { | 372 String parseParameterName() { |
| 368 int start = index; | 373 int start = index; |
| 369 while (!done()) { | 374 while (!done()) { |
| 370 if (s[index] == " " || s[index] == "\t" || s[index] == "=") break; | 375 if (s[index] == " " || s[index] == "\t" || s[index] == "=") break; |
| 371 index++; | 376 index++; |
| 372 } | 377 } |
| 373 return s.substring(start, index).toLowerCase(); | 378 return s.substring(start, index).toLowerCase(); |
| 374 } | 379 } |
| 375 | 380 |
| 376 String parseParameterValue() { | 381 String parseParameterValue() { |
| 377 if (s[index] == "\"") { | 382 if (s[index] == "\"") { |
| 378 // Parse quoted value. | 383 // Parse quoted value. |
| 379 StringBuffer sb = new StringBuffer(); | 384 StringBuffer sb = new StringBuffer(); |
| 380 index++; | 385 index++; |
| 381 while (!done()) { | 386 while (!done()) { |
| 382 if (s[index] == "\\") { | 387 if (s[index] == "\\") { |
| 383 if (index + 1 == s.length) { | 388 if (index + 1 == s.length) { |
| 384 throw new HttpException("Failed to parse header value [$s]"); | 389 throw new HttpException("Failed to parse header value"); |
| 385 } | 390 } |
| 386 index++; | 391 index++; |
| 387 } else if (s[index] == "\"") { | 392 } else if (s[index] == "\"") { |
| 388 index++; | 393 index++; |
| 389 break; | 394 break; |
| 390 } | 395 } |
| 391 sb.add(s[index]); | 396 sb.add(s[index]); |
| 392 index++; | 397 index++; |
| 393 } | 398 } |
| 394 return sb.toString(); | 399 return sb.toString(); |
| 395 } else { | 400 } else { |
| 396 // Parse non-quoted value. | 401 // Parse non-quoted value. |
| 397 return parseValue(); | 402 return parseValue(); |
| 398 } | 403 } |
| 399 } | 404 } |
| 400 | 405 |
| 401 while (!done()) { | 406 while (!done()) { |
| 402 skipWS(); | 407 skipWS(); |
| 403 if (done()) return; | 408 if (done()) return; |
| 404 String name = parseParameterName(); | 409 String name = parseParameterName(); |
| 405 skipWS(); | 410 skipWS(); |
| 406 expect("="); | 411 expect("="); |
| 407 skipWS(); | 412 skipWS(); |
| 408 String value = parseParameterValue(); | 413 String value = parseParameterValue(); |
| 409 _parameters[name] = value; | 414 _parameters[name] = value; |
| 410 skipWS(); | 415 skipWS(); |
| 411 if (done()) return; | 416 if (done()) return; |
| 412 expect(";"); | 417 expect(parameterSeparator); |
| 413 } | 418 } |
| 414 } | 419 } |
| 415 | 420 |
| 416 skipWS(); | 421 skipWS(); |
| 417 value = parseValue(); | 422 value = parseValue(); |
| 418 skipWS(); | 423 skipWS(); |
| 419 if (done()) return; | 424 if (done()) return; |
| 420 expect(";"); | 425 maybeExpect(parameterSeparator); |
| 421 parseParameters(); | 426 parseParameters(); |
| 422 } | 427 } |
| 423 | 428 |
| 424 String value; | 429 String value; |
| 430 String parameterSeparator; |
| 425 Map<String, String> _parameters; | 431 Map<String, String> _parameters; |
| 426 } | 432 } |
| 427 | 433 |
| 428 | 434 |
| 429 class _ContentType extends _HeaderValue implements ContentType { | 435 class _ContentType extends _HeaderValue implements ContentType { |
| 430 _ContentType(String primaryType, String subType) | 436 _ContentType(String primaryType, String subType) |
| 431 : _primaryType = primaryType, _subType = subType, super(""); | 437 : _primaryType = primaryType, _subType = subType, super(""); |
| 432 | 438 |
| 433 _ContentType.fromString(String value) : super.fromString(value); | 439 _ContentType.fromString(String value) : super.fromString(value); |
| 434 | 440 |
| (...skipping 1283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1718 _reasonPhrase = reasonPhrase; | 1724 _reasonPhrase = reasonPhrase; |
| 1719 } | 1725 } |
| 1720 | 1726 |
| 1721 void _onHeaderReceived(String name, String value) { | 1727 void _onHeaderReceived(String name, String value) { |
| 1722 _headers.add(name, value); | 1728 _headers.add(name, value); |
| 1723 if (name == "content-length") { | 1729 if (name == "content-length") { |
| 1724 _contentLength = parseInt(value); | 1730 _contentLength = parseInt(value); |
| 1725 } | 1731 } |
| 1726 } | 1732 } |
| 1727 | 1733 |
| 1734 void _handleUnauthorized() { |
| 1735 |
| 1736 void retryRequest(_Credentials cr) { |
| 1737 if (cr != null) { |
| 1738 if (cr.scheme == _AuthenticationScheme.DIGEST) { |
| 1739 cr.nonce = header.parameters["nonce"]; |
| 1740 cr.algorithm = header.parameters["algorithm"]; |
| 1741 cr.qop = header.parameters["qop"]; |
| 1742 } |
| 1743 // Drain body and retry. |
| 1744 // TODO(sjgesse): Support digest. |
| 1745 if (cr.scheme == _AuthenticationScheme.BASIC) { |
| 1746 inputStream.onData = inputStream.read; |
| 1747 inputStream.onClosed = _connection.retry; |
| 1748 return; |
| 1749 } |
| 1750 } |
| 1751 |
| 1752 // Fall through to here to perform normal response handling if |
| 1753 // there is no sensible authorization handling. |
| 1754 if (_connection._onResponse != null) { |
| 1755 _connection._onResponse(this); |
| 1756 } |
| 1757 } |
| 1758 |
| 1759 // Only try to authenticate if there is a challenge in the response. |
| 1760 List<String> challenge = _headers[HttpHeaders.WWW_AUTHENTICATE]; |
| 1761 if (challenge != null && challenge.length == 1) { |
| 1762 HeaderValue header = |
| 1763 new HeaderValue.fromString(challenge[0], parameterSeparator: ","); |
| 1764 _AuthenticationScheme scheme = |
| 1765 new _AuthenticationScheme.fromString(header.value); |
| 1766 String realm = header.parameters["realm"]; |
| 1767 |
| 1768 // See if any credentials are available. |
| 1769 _Credentials cr = |
| 1770 _connection._client._findCredentials( |
| 1771 _connection._request._uri, scheme); |
| 1772 |
| 1773 // Ask for more credentials if none found or the one found has |
| 1774 // already been used. If it has already been used it must now be |
| 1775 // invalid and is removed. |
| 1776 if (cr == null || cr.used) { |
| 1777 if (cr != null) { |
| 1778 _connection._client._removeCredentials(cr); |
| 1779 } |
| 1780 cr = null; |
| 1781 if (_connection._client._authenticate != null) { |
| 1782 Future authComplete = |
| 1783 _connection._client._authenticate( |
| 1784 _connection._request._uri, scheme.toString(), realm); |
| 1785 authComplete.then((credsAvailable) { |
| 1786 if (credsAvailable) { |
| 1787 cr = _connection._client._findCredentials( |
| 1788 _connection._request._uri, scheme); |
| 1789 retryRequest(cr); |
| 1790 } else { |
| 1791 if (_connection._onResponse != null) { |
| 1792 _connection._onResponse(this); |
| 1793 } |
| 1794 } |
| 1795 }); |
| 1796 return; |
| 1797 } |
| 1798 } else { |
| 1799 // If credentials found prepare for retrying the request. |
| 1800 retryRequest(cr); |
| 1801 return; |
| 1802 } |
| 1803 } |
| 1804 |
| 1805 // Fall through to here to perform normal response handling if |
| 1806 // there is no sensible authorization handling. |
| 1807 if (_connection._onResponse != null) { |
| 1808 _connection._onResponse(this); |
| 1809 } |
| 1810 } |
| 1811 |
| 1728 void _onHeadersComplete() { | 1812 void _onHeadersComplete() { |
| 1729 _headers._mutable = false; | 1813 _headers._mutable = false; |
| 1730 _buffer = new _BufferList(); | 1814 _buffer = new _BufferList(); |
| 1731 if (isRedirect && _connection.followRedirects) { | 1815 if (isRedirect && _connection.followRedirects) { |
| 1732 if (_connection._redirects == null || | 1816 if (_connection._redirects == null || |
| 1733 _connection._redirects.length < _connection.maxRedirects) { | 1817 _connection._redirects.length < _connection.maxRedirects) { |
| 1734 // Check the location header. | 1818 // Check the location header. |
| 1735 List<String> location = headers[HttpHeaders.LOCATION]; | 1819 List<String> location = headers[HttpHeaders.LOCATION]; |
| 1736 if (location == null || location.length > 1) { | 1820 if (location == null || location.length > 1) { |
| 1737 throw new RedirectException("Invalid redirect", | 1821 throw new RedirectException("Invalid redirect", |
| 1738 _connection._redirects); | 1822 _connection._redirects); |
| 1739 } | 1823 } |
| 1740 // Check for redirect loop | 1824 // Check for redirect loop |
| 1741 if (_connection._redirects != null) { | 1825 if (_connection._redirects != null) { |
| 1742 Uri redirectUrl = new Uri.fromString(location[0]); | 1826 Uri redirectUrl = new Uri.fromString(location[0]); |
| 1743 for (int i = 0; i < _connection._redirects.length; i++) { | 1827 for (int i = 0; i < _connection._redirects.length; i++) { |
| 1744 if (_connection._redirects[i].location.toString() == | 1828 if (_connection._redirects[i].location.toString() == |
| 1745 redirectUrl.toString()) { | 1829 redirectUrl.toString()) { |
| 1746 throw new RedirectLoopException(_connection._redirects); | 1830 throw new RedirectLoopException(_connection._redirects); |
| 1747 } | 1831 } |
| 1748 } | 1832 } |
| 1749 } | 1833 } |
| 1750 // Drain body and redirect. | 1834 // Drain body and redirect. |
| 1751 inputStream.onData = inputStream.read; | 1835 inputStream.onData = inputStream.read; |
| 1752 inputStream.onClosed = _connection.redirect; | 1836 inputStream.onClosed = _connection.redirect; |
| 1753 } else { | 1837 } else { |
| 1754 throw new RedirectLimitExceededException(_connection._redirects); | 1838 throw new RedirectLimitExceededException(_connection._redirects); |
| 1755 } | 1839 } |
| 1840 } else if (statusCode == HttpStatus.UNAUTHORIZED) { |
| 1841 _handleUnauthorized(); |
| 1756 } else if (_connection._onResponse != null) { | 1842 } else if (_connection._onResponse != null) { |
| 1757 _connection._onResponse(this); | 1843 _connection._onResponse(this); |
| 1758 } | 1844 } |
| 1759 } | 1845 } |
| 1760 | 1846 |
| 1761 void _onDataReceived(List<int> data) { | 1847 void _onDataReceived(List<int> data) { |
| 1762 _buffer.add(data); | 1848 _buffer.add(data); |
| 1763 if (_inputStream != null) _inputStream._dataReceived(); | 1849 if (_inputStream != null) _inputStream._dataReceived(); |
| 1764 } | 1850 } |
| 1765 | 1851 |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1900 } | 1986 } |
| 1901 | 1987 |
| 1902 void set onResponse(void handler(HttpClientResponse response)) { | 1988 void set onResponse(void handler(HttpClientResponse response)) { |
| 1903 _onResponse = handler; | 1989 _onResponse = handler; |
| 1904 } | 1990 } |
| 1905 | 1991 |
| 1906 void set onError(void callback(e)) { | 1992 void set onError(void callback(e)) { |
| 1907 _onErrorCallback = callback; | 1993 _onErrorCallback = callback; |
| 1908 } | 1994 } |
| 1909 | 1995 |
| 1996 void retry() { |
| 1997 if (_socketConn != null) { |
| 1998 throw new HttpException("Cannot retry with body data pending"); |
| 1999 } |
| 2000 // Retry the URL using the same connection instance. |
| 2001 _client._openUrl(_method, _request._uri, this); |
| 2002 } |
| 2003 |
| 1910 void redirect([String method, Uri url]) { | 2004 void redirect([String method, Uri url]) { |
| 1911 if (_socketConn != null) { | 2005 if (_socketConn != null) { |
| 1912 throw new HttpException("Cannot redirect with body data pending"); | 2006 throw new HttpException("Cannot redirect with body data pending"); |
| 1913 } | 2007 } |
| 1914 if (method == null) method = _method; | 2008 if (method == null) method = _method; |
| 1915 if (url == null) { | 2009 if (url == null) { |
| 1916 url = new Uri.fromString(_response.headers.value(HttpHeaders.LOCATION)); | 2010 url = new Uri.fromString(_response.headers.value(HttpHeaders.LOCATION)); |
| 1917 } | 2011 } |
| 1918 if (_redirects == null) { | 2012 if (_redirects == null) { |
| 1919 _redirects = new List<_RedirectInfo>(); | 2013 _redirects = new List<_RedirectInfo>(); |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2024 final String host; | 2118 final String host; |
| 2025 final int port; | 2119 final int port; |
| 2026 final bool isDirect; | 2120 final bool isDirect; |
| 2027 } | 2121 } |
| 2028 | 2122 |
| 2029 class _HttpClient implements HttpClient { | 2123 class _HttpClient implements HttpClient { |
| 2030 static const int DEFAULT_EVICTION_TIMEOUT = 60000; | 2124 static const int DEFAULT_EVICTION_TIMEOUT = 60000; |
| 2031 | 2125 |
| 2032 _HttpClient() : _openSockets = new Map(), | 2126 _HttpClient() : _openSockets = new Map(), |
| 2033 _activeSockets = new Set(), | 2127 _activeSockets = new Set(), |
| 2128 credentials = new List<_Credentials>(), |
| 2034 _shutdown = false; | 2129 _shutdown = false; |
| 2035 | 2130 |
| 2036 HttpClientConnection open( | 2131 HttpClientConnection open( |
| 2037 String method, String host, int port, String path) { | 2132 String method, String host, int port, String path) { |
| 2038 // TODO(sgjesse): The path set here can contain both query and | 2133 // TODO(sgjesse): The path set here can contain both query and |
| 2039 // fragment. They should be cracked and set correctly. | 2134 // fragment. They should be cracked and set correctly. |
| 2040 return _open(method, new Uri.fromComponents( | 2135 return _open(method, new Uri.fromComponents( |
| 2041 scheme: "http", domain: host, port: port, path: path)); | 2136 scheme: "http", domain: host, port: port, path: path)); |
| 2042 } | 2137 } |
| 2043 | 2138 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 2054 HttpClientConnection openUrl(String method, Uri url) { | 2149 HttpClientConnection openUrl(String method, Uri url) { |
| 2055 return _openUrl(method, url); | 2150 return _openUrl(method, url); |
| 2056 } | 2151 } |
| 2057 | 2152 |
| 2058 HttpClientConnection _openUrl(String method, | 2153 HttpClientConnection _openUrl(String method, |
| 2059 Uri url, | 2154 Uri url, |
| 2060 [_HttpClientConnection connection]) { | 2155 [_HttpClientConnection connection]) { |
| 2061 if (url.scheme != "http") { | 2156 if (url.scheme != "http") { |
| 2062 throw new HttpException("Unsupported URL scheme ${url.scheme}"); | 2157 throw new HttpException("Unsupported URL scheme ${url.scheme}"); |
| 2063 } | 2158 } |
| 2064 if (url.userInfo != "") { | |
| 2065 throw new HttpException("Unsupported user info ${url.userInfo}"); | |
| 2066 } | |
| 2067 return _open(method, url, connection); | 2159 return _open(method, url, connection); |
| 2068 } | 2160 } |
| 2069 | 2161 |
| 2070 HttpClientConnection get(String host, int port, String path) { | 2162 HttpClientConnection get(String host, int port, String path) { |
| 2071 return open("GET", host, port, path); | 2163 return open("GET", host, port, path); |
| 2072 } | 2164 } |
| 2073 | 2165 |
| 2074 HttpClientConnection getUrl(Uri url) => _openUrl("GET", url); | 2166 HttpClientConnection getUrl(Uri url) => _openUrl("GET", url); |
| 2075 | 2167 |
| 2076 HttpClientConnection post(String host, int port, String path) { | 2168 HttpClientConnection post(String host, int port, String path) { |
| 2077 return open("POST", host, port, path); | 2169 return open("POST", host, port, path); |
| 2078 } | 2170 } |
| 2079 | 2171 |
| 2080 HttpClientConnection postUrl(Uri url) => _openUrl("POST", url); | 2172 HttpClientConnection postUrl(Uri url) => _openUrl("POST", url); |
| 2081 | 2173 |
| 2174 set authenticate(bool f(Uri url, String scheme, String realm)) { |
| 2175 _authenticate = f; |
| 2176 } |
| 2177 |
| 2178 void addCredentials( |
| 2179 Uri url, String realm, HttpClientCredentials cr) { |
| 2180 credentials.add(new _Credentials(url, realm, cr)); |
| 2181 } |
| 2182 |
| 2082 set findProxy(String f(Uri uri)) => _findProxy = f; | 2183 set findProxy(String f(Uri uri)) => _findProxy = f; |
| 2083 | 2184 |
| 2084 void shutdown() { | 2185 void shutdown() { |
| 2085 _openSockets.forEach((String key, Queue<_SocketConnection> connections) { | 2186 _openSockets.forEach((String key, Queue<_SocketConnection> connections) { |
| 2086 while (!connections.isEmpty()) { | 2187 while (!connections.isEmpty()) { |
| 2087 _SocketConnection socketConn = connections.removeFirst(); | 2188 _SocketConnection socketConn = connections.removeFirst(); |
| 2088 socketConn._socket.close(); | 2189 socketConn._socket.close(); |
| 2089 } | 2190 } |
| 2090 }); | 2191 }); |
| 2091 _activeSockets.forEach((_SocketConnection socketConn) { | 2192 _activeSockets.forEach((_SocketConnection socketConn) { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 2115 int proxyIndex) { | 2216 int proxyIndex) { |
| 2116 | 2217 |
| 2117 void _connectionOpened(_SocketConnection socketConn, | 2218 void _connectionOpened(_SocketConnection socketConn, |
| 2118 _HttpClientConnection connection, | 2219 _HttpClientConnection connection, |
| 2119 bool usingProxy) { | 2220 bool usingProxy) { |
| 2120 connection._usingProxy = usingProxy; | 2221 connection._usingProxy = usingProxy; |
| 2121 connection._connectionEstablished(socketConn); | 2222 connection._connectionEstablished(socketConn); |
| 2122 HttpClientRequest request = connection.open(method, url); | 2223 HttpClientRequest request = connection.open(method, url); |
| 2123 request.headers.host = host; | 2224 request.headers.host = host; |
| 2124 request.headers.port = port; | 2225 request.headers.port = port; |
| 2226 if (url.userInfo != null && !url.userInfo.isEmpty()) { |
| 2227 // If the URL contains user information use that for basic |
| 2228 // authorization |
| 2229 _UTF8Encoder encoder = new _UTF8Encoder(); |
| 2230 String auth = |
| 2231 CryptoUtils.bytesToBase64(encoder.encodeString(url.userInfo)); |
| 2232 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); |
| 2233 } else { |
| 2234 // Look for credentials. |
| 2235 _Credentials cr = _findCredentials(url); |
| 2236 if (cr != null) { |
| 2237 cr.authorize(request); |
| 2238 } |
| 2239 } |
| 2125 if (connection._onRequest != null) { | 2240 if (connection._onRequest != null) { |
| 2126 connection._onRequest(request); | 2241 connection._onRequest(request); |
| 2127 } else { | 2242 } else { |
| 2128 request.outputStream.close(); | 2243 request.outputStream.close(); |
| 2129 } | 2244 } |
| 2130 } | 2245 } |
| 2131 | 2246 |
| 2132 assert(proxyIndex < proxyConfiguration.proxies.length); | 2247 assert(proxyIndex < proxyConfiguration.proxies.length); |
| 2133 | 2248 |
| 2134 // Determine the actual host to connect to. | 2249 // Determine the actual host to connect to. |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2258 if (_openSockets.isEmpty()) _cancelEvictionTimer(); | 2373 if (_openSockets.isEmpty()) _cancelEvictionTimer(); |
| 2259 } | 2374 } |
| 2260 _evictionTimer = new Timer.repeating(10000, _handleEviction); | 2375 _evictionTimer = new Timer.repeating(10000, _handleEviction); |
| 2261 } | 2376 } |
| 2262 | 2377 |
| 2263 // Return connection. | 2378 // Return connection. |
| 2264 _activeSockets.remove(socketConn); | 2379 _activeSockets.remove(socketConn); |
| 2265 sockets.addFirst(socketConn); | 2380 sockets.addFirst(socketConn); |
| 2266 } | 2381 } |
| 2267 | 2382 |
| 2383 _Credentials _findCredentials(Uri url, [_AuthenticationScheme scheme]) { |
| 2384 // Look for credentials. |
| 2385 _Credentials cr = |
| 2386 credentials.reduce(null, (_Credentials prev, _Credentials value) { |
| 2387 if (value.applies(url, scheme)) { |
| 2388 if (prev == null) return value; |
| 2389 return value.uri.path.length > prev.uri.path.length ? value : prev; |
| 2390 } else { |
| 2391 return prev; |
| 2392 } |
| 2393 }); |
| 2394 return cr; |
| 2395 } |
| 2396 |
| 2397 void _removeCredentials(_Credentials cr) { |
| 2398 int index = credentials.indexOf(cr); |
| 2399 if (index != -1) { |
| 2400 credentials.removeAt(index); |
| 2401 } |
| 2402 } |
| 2403 |
| 2268 Function _onOpen; | 2404 Function _onOpen; |
| 2269 Map<String, Queue<_SocketConnection>> _openSockets; | 2405 Map<String, Queue<_SocketConnection>> _openSockets; |
| 2270 Set<_SocketConnection> _activeSockets; | 2406 Set<_SocketConnection> _activeSockets; |
| 2407 List<_Credentials> credentials; |
| 2271 Timer _evictionTimer; | 2408 Timer _evictionTimer; |
| 2272 Function _findProxy; | 2409 Function _findProxy; |
| 2410 Function _authenticate; |
| 2273 bool _shutdown; // Has this HTTP client been shutdown? | 2411 bool _shutdown; // Has this HTTP client been shutdown? |
| 2274 } | 2412 } |
| 2275 | 2413 |
| 2276 | 2414 |
| 2277 class _HttpConnectionInfo implements HttpConnectionInfo { | 2415 class _HttpConnectionInfo implements HttpConnectionInfo { |
| 2278 String remoteHost; | 2416 String remoteHost; |
| 2279 int remotePort; | 2417 int remotePort; |
| 2280 int localPort; | 2418 int localPort; |
| 2281 } | 2419 } |
| 2282 | 2420 |
| 2283 | 2421 |
| 2284 class _DetachedSocket implements DetachedSocket { | 2422 class _DetachedSocket implements DetachedSocket { |
| 2285 _DetachedSocket(this._socket, this._unparsedData); | 2423 _DetachedSocket(this._socket, this._unparsedData); |
| 2286 Socket get socket => _socket; | 2424 Socket get socket => _socket; |
| 2287 List<int> get unparsedData => _unparsedData; | 2425 List<int> get unparsedData => _unparsedData; |
| 2288 Socket _socket; | 2426 Socket _socket; |
| 2289 List<int> _unparsedData; | 2427 List<int> _unparsedData; |
| 2290 } | 2428 } |
| 2291 | 2429 |
| 2292 | 2430 |
| 2431 class _AuthenticationScheme { |
| 2432 static const UNKNOWN = const _AuthenticationScheme(-1); |
| 2433 static const BASIC = const _AuthenticationScheme(0); |
| 2434 static const DIGEST = const _AuthenticationScheme(1); |
| 2435 |
| 2436 const _AuthenticationScheme(this._scheme); |
| 2437 |
| 2438 factory _AuthenticationScheme.fromString(String scheme) { |
| 2439 if (scheme.toLowerCase() == "basic") return BASIC; |
| 2440 if (scheme.toLowerCase() == "digest") return DIGEST; |
| 2441 return UNKNOWN; |
| 2442 } |
| 2443 |
| 2444 String toString() { |
| 2445 if (this == BASIC) return "Basic"; |
| 2446 if (this == DIGEST) return "Digest"; |
| 2447 return "Unknown"; |
| 2448 } |
| 2449 |
| 2450 final int _scheme; |
| 2451 } |
| 2452 |
| 2453 |
| 2454 class _Credentials { |
| 2455 _Credentials(this.uri, this.realm, this.credentials); |
| 2456 |
| 2457 _AuthenticationScheme get scheme => credentials.scheme; |
| 2458 |
| 2459 bool applies(Uri uri, _AuthenticationScheme scheme) { |
| 2460 if (scheme != null && credentials.scheme != scheme) return false; |
| 2461 if (uri.domain != this.uri.domain) return false; |
| 2462 int thisPort = |
| 2463 this.uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : this.uri.port; |
| 2464 int otherPort = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port; |
| 2465 if (otherPort != thisPort) return false; |
| 2466 return uri.path.startsWith(this.uri.path); |
| 2467 } |
| 2468 |
| 2469 void authorize(HttpClientRequest request) { |
| 2470 credentials.authorize(this, request); |
| 2471 used = true; |
| 2472 } |
| 2473 |
| 2474 bool used = false; |
| 2475 Uri uri; |
| 2476 String realm; |
| 2477 HttpClientCredentials credentials; |
| 2478 |
| 2479 // Digest specific fields. |
| 2480 String nonce; |
| 2481 String algorithm; |
| 2482 String qop; |
| 2483 } |
| 2484 |
| 2485 |
| 2486 class _HttpClientCredentials implements HttpClientCredentials { |
| 2487 abstract _AuthenticationScheme get scheme; |
| 2488 abstract void authorize(HttpClientRequest request); |
| 2489 } |
| 2490 |
| 2491 |
| 2492 class _HttpClientBasicCredentials implements HttpClientBasicCredentials { |
| 2493 _HttpClientBasicCredentials(this.username, |
| 2494 this.password); |
| 2495 |
| 2496 _AuthenticationScheme get scheme => _AuthenticationScheme.BASIC; |
| 2497 |
| 2498 void authorize(_Credentials _, HttpClientRequest request) { |
| 2499 // There is no mentioning of username/password encoding in RFC |
| 2500 // 2617. However there is an open draft for adding an additional |
| 2501 // accept-charset parameter to the WWW-Authenticate and |
| 2502 // Proxy-Authenticate headers, see |
| 2503 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For |
| 2504 // now always use UTF-8 encoding. |
| 2505 _UTF8Encoder encoder = new _UTF8Encoder(); |
| 2506 String auth = |
| 2507 CryptoUtils.bytesToBase64(encoder.encodeString( |
| 2508 "$username:$password")); |
| 2509 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); |
| 2510 } |
| 2511 |
| 2512 String username; |
| 2513 String password; |
| 2514 } |
| 2515 |
| 2516 |
| 2517 class _HttpClientDigestCredentials implements HttpClientDigestCredentials { |
| 2518 _HttpClientDigestCredentials(this.username, |
| 2519 this.password); |
| 2520 |
| 2521 _AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST; |
| 2522 |
| 2523 void authorize(_Credentials credentials, HttpClientRequest request) { |
| 2524 // TODO(sgjesse): Implement!!! |
| 2525 throw new UnsupportedOperationException(); |
| 2526 } |
| 2527 |
| 2528 String username; |
| 2529 String password; |
| 2530 } |
| 2531 |
| 2532 |
| 2533 |
| 2293 class _RedirectInfo implements RedirectInfo { | 2534 class _RedirectInfo implements RedirectInfo { |
| 2294 const _RedirectInfo(int this.statusCode, | 2535 const _RedirectInfo(int this.statusCode, |
| 2295 String this.method, | 2536 String this.method, |
| 2296 Uri this.location); | 2537 Uri this.location); |
| 2297 final int statusCode; | 2538 final int statusCode; |
| 2298 final String method; | 2539 final String method; |
| 2299 final Uri location; | 2540 final Uri location; |
| 2300 } | 2541 } |
| OLD | NEW |