Chromium Code Reviews| 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 // Only try to authenticate if there is a challenge in the response. | |
| 1736 List<String> challenge = _headers[HttpHeaders.WWW_AUTHENTICATE]; | |
| 1737 if (challenge != null && challenge.length == 1) { | |
| 1738 HeaderValue header = | |
| 1739 new HeaderValue.fromString(challenge[0], parameterSeparator: ","); | |
| 1740 _AuthenticationScheme scheme = | |
| 1741 new _AuthenticationScheme.fromString(header.value); | |
| 1742 String realm = header.parameters["realm"]; | |
| 1743 | |
| 1744 // See if any credentials are available. | |
| 1745 _Credentials cr = | |
| 1746 _connection._client._findCredentials( | |
| 1747 _connection._request._uri, scheme); | |
| 1748 | |
| 1749 // Ask for more credentials if none found or the one found has | |
| 1750 // already been used. If is has already been used it must now be | |
|
Mads Ager (google)
2012/10/26 11:07:02
is -> it
Søren Gjesse
2012/10/26 12:59:02
Done.
| |
| 1751 // invalid and is removed. | |
| 1752 if (cr == null || cr.used) { | |
| 1753 if (cr != null) { | |
| 1754 _connection._client._removeCredentials(cr); | |
| 1755 } | |
| 1756 cr = null; | |
| 1757 if (_connection._client._authenticate != null && | |
| 1758 _connection._client._authenticate( | |
| 1759 _connection._request._uri, scheme.toString(), realm)) { | |
| 1760 cr = _connection._client._findCredentials(_connection._request._uri, | |
| 1761 scheme); | |
| 1762 } | |
| 1763 } | |
| 1764 | |
| 1765 // If credentials found prepare for retrying the request. | |
| 1766 if (cr != null) { | |
| 1767 if (cr.scheme == _AuthenticationScheme.DIGEST) { | |
| 1768 cr.nonce = header.parameters["nonce"]; | |
| 1769 cr.algorithm = header.parameters["algorithm"]; | |
| 1770 cr.qop = header.parameters["qop"]; | |
| 1771 } | |
| 1772 // Drain body and retry. | |
| 1773 // TODO(sgesse): Support digest. | |
|
Mads Ager (google)
2012/10/26 11:07:02
Should we throw an Unsupported exception for now o
Søren Gjesse
2012/10/26 12:59:02
As it is it will just fall through and pass the st
| |
| 1774 if (cr.scheme == _AuthenticationScheme.BASIC) { | |
| 1775 inputStream.onData = inputStream.read; | |
| 1776 inputStream.onClosed = _connection.retry; | |
| 1777 return; | |
| 1778 } | |
| 1779 } | |
| 1780 } | |
| 1781 | |
| 1782 // Fall through to here to perform normal response handling if | |
| 1783 // there is no sensible authorization handling. | |
| 1784 if (_connection._onResponse != null) { | |
| 1785 _connection._onResponse(this); | |
| 1786 } | |
| 1787 } | |
| 1788 | |
| 1728 void _onHeadersComplete() { | 1789 void _onHeadersComplete() { |
| 1729 _headers._mutable = false; | 1790 _headers._mutable = false; |
| 1730 _buffer = new _BufferList(); | 1791 _buffer = new _BufferList(); |
| 1731 if (isRedirect && _connection.followRedirects) { | 1792 if (isRedirect && _connection.followRedirects) { |
| 1732 if (_connection._redirects == null || | 1793 if (_connection._redirects == null || |
| 1733 _connection._redirects.length < _connection.maxRedirects) { | 1794 _connection._redirects.length < _connection.maxRedirects) { |
| 1734 // Check the location header. | 1795 // Check the location header. |
| 1735 List<String> location = headers[HttpHeaders.LOCATION]; | 1796 List<String> location = headers[HttpHeaders.LOCATION]; |
| 1736 if (location == null || location.length > 1) { | 1797 if (location == null || location.length > 1) { |
| 1737 throw new RedirectException("Invalid redirect", | 1798 throw new RedirectException("Invalid redirect", |
| 1738 _connection._redirects); | 1799 _connection._redirects); |
| 1739 } | 1800 } |
| 1740 // Check for redirect loop | 1801 // Check for redirect loop |
| 1741 if (_connection._redirects != null) { | 1802 if (_connection._redirects != null) { |
| 1742 Uri redirectUrl = new Uri.fromString(location[0]); | 1803 Uri redirectUrl = new Uri.fromString(location[0]); |
| 1743 for (int i = 0; i < _connection._redirects.length; i++) { | 1804 for (int i = 0; i < _connection._redirects.length; i++) { |
| 1744 if (_connection._redirects[i].location.toString() == | 1805 if (_connection._redirects[i].location.toString() == |
| 1745 redirectUrl.toString()) { | 1806 redirectUrl.toString()) { |
| 1746 throw new RedirectLoopException(_connection._redirects); | 1807 throw new RedirectLoopException(_connection._redirects); |
| 1747 } | 1808 } |
| 1748 } | 1809 } |
| 1749 } | 1810 } |
| 1750 // Drain body and redirect. | 1811 // Drain body and redirect. |
| 1751 inputStream.onData = inputStream.read; | 1812 inputStream.onData = inputStream.read; |
| 1752 inputStream.onClosed = _connection.redirect; | 1813 inputStream.onClosed = _connection.redirect; |
| 1753 } else { | 1814 } else { |
| 1754 throw new RedirectLimitExceededException(_connection._redirects); | 1815 throw new RedirectLimitExceededException(_connection._redirects); |
| 1755 } | 1816 } |
| 1817 } else if (statusCode == HttpStatus.UNAUTHORIZED) { | |
| 1818 _handleUnauthorized(); | |
| 1756 } else if (_connection._onResponse != null) { | 1819 } else if (_connection._onResponse != null) { |
| 1757 _connection._onResponse(this); | 1820 _connection._onResponse(this); |
| 1758 } | 1821 } |
| 1759 } | 1822 } |
| 1760 | 1823 |
| 1761 void _onDataReceived(List<int> data) { | 1824 void _onDataReceived(List<int> data) { |
| 1762 _buffer.add(data); | 1825 _buffer.add(data); |
| 1763 if (_inputStream != null) _inputStream._dataReceived(); | 1826 if (_inputStream != null) _inputStream._dataReceived(); |
| 1764 } | 1827 } |
| 1765 | 1828 |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1900 } | 1963 } |
| 1901 | 1964 |
| 1902 void set onResponse(void handler(HttpClientResponse response)) { | 1965 void set onResponse(void handler(HttpClientResponse response)) { |
| 1903 _onResponse = handler; | 1966 _onResponse = handler; |
| 1904 } | 1967 } |
| 1905 | 1968 |
| 1906 void set onError(void callback(e)) { | 1969 void set onError(void callback(e)) { |
| 1907 _onErrorCallback = callback; | 1970 _onErrorCallback = callback; |
| 1908 } | 1971 } |
| 1909 | 1972 |
| 1973 void retry() { | |
| 1974 if (_socketConn != null) { | |
| 1975 throw new HttpException("Cannot retry with body data pending"); | |
| 1976 } | |
| 1977 // Retry the URL using the same connection instance. | |
| 1978 _client._openUrl(_method, _request._uri, this); | |
| 1979 } | |
| 1980 | |
| 1910 void redirect([String method, Uri url]) { | 1981 void redirect([String method, Uri url]) { |
| 1911 if (_socketConn != null) { | 1982 if (_socketConn != null) { |
| 1912 throw new HttpException("Cannot redirect with body data pending"); | 1983 throw new HttpException("Cannot redirect with body data pending"); |
| 1913 } | 1984 } |
| 1914 if (method == null) method = _method; | 1985 if (method == null) method = _method; |
| 1915 if (url == null) { | 1986 if (url == null) { |
| 1916 url = new Uri.fromString(_response.headers.value(HttpHeaders.LOCATION)); | 1987 url = new Uri.fromString(_response.headers.value(HttpHeaders.LOCATION)); |
| 1917 } | 1988 } |
| 1918 if (_redirects == null) { | 1989 if (_redirects == null) { |
| 1919 _redirects = new List<_RedirectInfo>(); | 1990 _redirects = new List<_RedirectInfo>(); |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2024 final String host; | 2095 final String host; |
| 2025 final int port; | 2096 final int port; |
| 2026 final bool isDirect; | 2097 final bool isDirect; |
| 2027 } | 2098 } |
| 2028 | 2099 |
| 2029 class _HttpClient implements HttpClient { | 2100 class _HttpClient implements HttpClient { |
| 2030 static const int DEFAULT_EVICTION_TIMEOUT = 60000; | 2101 static const int DEFAULT_EVICTION_TIMEOUT = 60000; |
| 2031 | 2102 |
| 2032 _HttpClient() : _openSockets = new Map(), | 2103 _HttpClient() : _openSockets = new Map(), |
| 2033 _activeSockets = new Set(), | 2104 _activeSockets = new Set(), |
| 2105 credentials = new List<_Credentials>(), | |
| 2034 _shutdown = false; | 2106 _shutdown = false; |
| 2035 | 2107 |
| 2036 HttpClientConnection open( | 2108 HttpClientConnection open( |
| 2037 String method, String host, int port, String path) { | 2109 String method, String host, int port, String path) { |
| 2038 // TODO(sgjesse): The path set here can contain both query and | 2110 // TODO(sgjesse): The path set here can contain both query and |
| 2039 // fragment. They should be cracked and set correctly. | 2111 // fragment. They should be cracked and set correctly. |
| 2040 return _open(method, new Uri.fromComponents( | 2112 return _open(method, new Uri.fromComponents( |
| 2041 scheme: "http", domain: host, port: port, path: path)); | 2113 scheme: "http", domain: host, port: port, path: path)); |
| 2042 } | 2114 } |
| 2043 | 2115 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 2054 HttpClientConnection openUrl(String method, Uri url) { | 2126 HttpClientConnection openUrl(String method, Uri url) { |
| 2055 return _openUrl(method, url); | 2127 return _openUrl(method, url); |
| 2056 } | 2128 } |
| 2057 | 2129 |
| 2058 HttpClientConnection _openUrl(String method, | 2130 HttpClientConnection _openUrl(String method, |
| 2059 Uri url, | 2131 Uri url, |
| 2060 [_HttpClientConnection connection]) { | 2132 [_HttpClientConnection connection]) { |
| 2061 if (url.scheme != "http") { | 2133 if (url.scheme != "http") { |
| 2062 throw new HttpException("Unsupported URL scheme ${url.scheme}"); | 2134 throw new HttpException("Unsupported URL scheme ${url.scheme}"); |
| 2063 } | 2135 } |
| 2064 if (url.userInfo != "") { | |
| 2065 throw new HttpException("Unsupported user info ${url.userInfo}"); | |
| 2066 } | |
| 2067 return _open(method, url, connection); | 2136 return _open(method, url, connection); |
| 2068 } | 2137 } |
| 2069 | 2138 |
| 2070 HttpClientConnection get(String host, int port, String path) { | 2139 HttpClientConnection get(String host, int port, String path) { |
| 2071 return open("GET", host, port, path); | 2140 return open("GET", host, port, path); |
| 2072 } | 2141 } |
| 2073 | 2142 |
| 2074 HttpClientConnection getUrl(Uri url) => _openUrl("GET", url); | 2143 HttpClientConnection getUrl(Uri url) => _openUrl("GET", url); |
| 2075 | 2144 |
| 2076 HttpClientConnection post(String host, int port, String path) { | 2145 HttpClientConnection post(String host, int port, String path) { |
| 2077 return open("POST", host, port, path); | 2146 return open("POST", host, port, path); |
| 2078 } | 2147 } |
| 2079 | 2148 |
| 2080 HttpClientConnection postUrl(Uri url) => _openUrl("POST", url); | 2149 HttpClientConnection postUrl(Uri url) => _openUrl("POST", url); |
| 2081 | 2150 |
| 2151 set authenticate(bool f(Uri url, String scheme, String realm)) { | |
| 2152 _authenticate = f; | |
| 2153 } | |
| 2154 | |
| 2155 void addCredentials( | |
| 2156 Uri url, String realm, HttpClientCredentials cr) { | |
| 2157 credentials.add(new _Credentials(url, realm, cr)); | |
| 2158 } | |
| 2159 | |
| 2082 set findProxy(String f(Uri uri)) => _findProxy = f; | 2160 set findProxy(String f(Uri uri)) => _findProxy = f; |
| 2083 | 2161 |
| 2084 void shutdown() { | 2162 void shutdown() { |
| 2085 _openSockets.forEach((String key, Queue<_SocketConnection> connections) { | 2163 _openSockets.forEach((String key, Queue<_SocketConnection> connections) { |
| 2086 while (!connections.isEmpty()) { | 2164 while (!connections.isEmpty()) { |
| 2087 _SocketConnection socketConn = connections.removeFirst(); | 2165 _SocketConnection socketConn = connections.removeFirst(); |
| 2088 socketConn._socket.close(); | 2166 socketConn._socket.close(); |
| 2089 } | 2167 } |
| 2090 }); | 2168 }); |
| 2091 _activeSockets.forEach((_SocketConnection socketConn) { | 2169 _activeSockets.forEach((_SocketConnection socketConn) { |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 2115 int proxyIndex) { | 2193 int proxyIndex) { |
| 2116 | 2194 |
| 2117 void _connectionOpened(_SocketConnection socketConn, | 2195 void _connectionOpened(_SocketConnection socketConn, |
| 2118 _HttpClientConnection connection, | 2196 _HttpClientConnection connection, |
| 2119 bool usingProxy) { | 2197 bool usingProxy) { |
| 2120 connection._usingProxy = usingProxy; | 2198 connection._usingProxy = usingProxy; |
| 2121 connection._connectionEstablished(socketConn); | 2199 connection._connectionEstablished(socketConn); |
| 2122 HttpClientRequest request = connection.open(method, url); | 2200 HttpClientRequest request = connection.open(method, url); |
| 2123 request.headers.host = host; | 2201 request.headers.host = host; |
| 2124 request.headers.port = port; | 2202 request.headers.port = port; |
| 2203 if (url.userInfo != null && !url.userInfo.isEmpty()) { | |
| 2204 // If the URL contains user information use that for basic | |
| 2205 // authorization | |
| 2206 _UTF8Encoder encoder = new _UTF8Encoder(); | |
| 2207 String auth = | |
| 2208 CryptoUtils.bytesToBase64(encoder.encodeString(url.userInfo)); | |
| 2209 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); | |
| 2210 } else { | |
| 2211 // Look for credentials. | |
| 2212 _Credentials cr = _findCredentials(url); | |
| 2213 if (cr != null) { | |
| 2214 cr.authorize(request); | |
| 2215 } | |
| 2216 } | |
| 2125 if (connection._onRequest != null) { | 2217 if (connection._onRequest != null) { |
| 2126 connection._onRequest(request); | 2218 connection._onRequest(request); |
| 2127 } else { | 2219 } else { |
| 2128 request.outputStream.close(); | 2220 request.outputStream.close(); |
| 2129 } | 2221 } |
| 2130 } | 2222 } |
| 2131 | 2223 |
| 2132 assert(proxyIndex < proxyConfiguration.proxies.length); | 2224 assert(proxyIndex < proxyConfiguration.proxies.length); |
| 2133 | 2225 |
| 2134 // Determine the actual host to connect to. | 2226 // 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(); | 2350 if (_openSockets.isEmpty()) _cancelEvictionTimer(); |
| 2259 } | 2351 } |
| 2260 _evictionTimer = new Timer.repeating(10000, _handleEviction); | 2352 _evictionTimer = new Timer.repeating(10000, _handleEviction); |
| 2261 } | 2353 } |
| 2262 | 2354 |
| 2263 // Return connection. | 2355 // Return connection. |
| 2264 _activeSockets.remove(socketConn); | 2356 _activeSockets.remove(socketConn); |
| 2265 sockets.addFirst(socketConn); | 2357 sockets.addFirst(socketConn); |
| 2266 } | 2358 } |
| 2267 | 2359 |
| 2360 _Credentials _findCredentials(Uri url, [_AuthenticationScheme scheme]) { | |
| 2361 // Look for credentials. | |
| 2362 _Credentials cr = | |
| 2363 credentials.reduce(null, (_Credentials prev, _Credentials value) { | |
| 2364 if (value.applies(url, scheme)) { | |
| 2365 if (prev == null) return value; | |
| 2366 return value.uri.path.length > prev.uri.path.length ? value : prev; | |
| 2367 } else { | |
| 2368 return prev; | |
| 2369 } | |
| 2370 }); | |
| 2371 return cr; | |
| 2372 } | |
| 2373 | |
| 2374 void _removeCredentials(_Credentials cr) { | |
| 2375 int index = credentials.indexOf(cr); | |
| 2376 if (index != -1) { | |
| 2377 credentials.removeAt(index); | |
| 2378 } | |
| 2379 } | |
| 2380 | |
| 2268 Function _onOpen; | 2381 Function _onOpen; |
| 2269 Map<String, Queue<_SocketConnection>> _openSockets; | 2382 Map<String, Queue<_SocketConnection>> _openSockets; |
| 2270 Set<_SocketConnection> _activeSockets; | 2383 Set<_SocketConnection> _activeSockets; |
| 2384 List<_Credentials> credentials; | |
| 2271 Timer _evictionTimer; | 2385 Timer _evictionTimer; |
| 2272 Function _findProxy; | 2386 Function _findProxy; |
| 2387 Function _authenticate; | |
| 2273 bool _shutdown; // Has this HTTP client been shutdown? | 2388 bool _shutdown; // Has this HTTP client been shutdown? |
| 2274 } | 2389 } |
| 2275 | 2390 |
| 2276 | 2391 |
| 2277 class _HttpConnectionInfo implements HttpConnectionInfo { | 2392 class _HttpConnectionInfo implements HttpConnectionInfo { |
| 2278 String remoteHost; | 2393 String remoteHost; |
| 2279 int remotePort; | 2394 int remotePort; |
| 2280 int localPort; | 2395 int localPort; |
| 2281 } | 2396 } |
| 2282 | 2397 |
| 2283 | 2398 |
| 2284 class _DetachedSocket implements DetachedSocket { | 2399 class _DetachedSocket implements DetachedSocket { |
| 2285 _DetachedSocket(this._socket, this._unparsedData); | 2400 _DetachedSocket(this._socket, this._unparsedData); |
| 2286 Socket get socket => _socket; | 2401 Socket get socket => _socket; |
| 2287 List<int> get unparsedData => _unparsedData; | 2402 List<int> get unparsedData => _unparsedData; |
| 2288 Socket _socket; | 2403 Socket _socket; |
| 2289 List<int> _unparsedData; | 2404 List<int> _unparsedData; |
| 2290 } | 2405 } |
| 2291 | 2406 |
| 2292 | 2407 |
| 2408 class _AuthenticationScheme { | |
| 2409 static const UNKNOWN = const _AuthenticationScheme(-1); | |
| 2410 static const BASIC = const _AuthenticationScheme(0); | |
| 2411 static const DIGEST = const _AuthenticationScheme(1); | |
| 2412 | |
| 2413 const _AuthenticationScheme(this._scheme); | |
| 2414 | |
| 2415 factory _AuthenticationScheme.fromString(String scheme) { | |
| 2416 if (scheme.toLowerCase() == "basic") return BASIC; | |
| 2417 if (scheme.toLowerCase() == "digest") return DIGEST; | |
| 2418 return UNKNOWN; | |
| 2419 } | |
| 2420 | |
| 2421 String toString() { | |
| 2422 if (this == BASIC) return "Basic"; | |
| 2423 if (this == DIGEST) return "Digest"; | |
| 2424 return "Unknown"; | |
| 2425 } | |
| 2426 | |
| 2427 final int _scheme; | |
| 2428 } | |
| 2429 | |
| 2430 | |
| 2431 class _Credentials { | |
| 2432 _Credentials(this.uri, this.realm, this.credentials); | |
| 2433 | |
| 2434 _AuthenticationScheme get scheme => credentials.scheme; | |
| 2435 | |
| 2436 bool applies(Uri uri, _AuthenticationScheme scheme) { | |
| 2437 if (scheme != null && credentials.scheme != scheme) return false; | |
| 2438 if (uri.domain != this.uri.domain) return false; | |
| 2439 int thisPort = | |
| 2440 this.uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : this.uri.port; | |
| 2441 int otherPort = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port; | |
| 2442 if (otherPort != thisPort) return false; | |
| 2443 return uri.path.startsWith(this.uri.path); | |
| 2444 } | |
| 2445 | |
| 2446 void authorize(HttpClientRequest request) { | |
| 2447 credentials.authorize(this, request); | |
| 2448 used = true; | |
| 2449 } | |
| 2450 | |
| 2451 bool used = false; | |
| 2452 Uri uri; | |
| 2453 String realm; | |
| 2454 HttpClientCredentials credentials; | |
| 2455 | |
| 2456 // Digest specific fields. | |
| 2457 String nonce; | |
| 2458 String algorithm; | |
| 2459 String qop; | |
| 2460 } | |
| 2461 | |
| 2462 | |
| 2463 class _HttpClientCredentials implements HttpClientCredentials { | |
| 2464 abstract _AuthenticationScheme get scheme; | |
| 2465 abstract void authorize(HttpClientRequest request); | |
| 2466 } | |
| 2467 | |
| 2468 | |
| 2469 class _HttpClientBasicCredentials implements HttpClientBasicCredentials { | |
| 2470 _HttpClientBasicCredentials(this.username, | |
| 2471 this.password); | |
| 2472 | |
| 2473 _AuthenticationScheme get scheme => _AuthenticationScheme.BASIC; | |
| 2474 | |
| 2475 void authorize(_Credentials _, HttpClientRequest request) { | |
| 2476 // There is no mentioning of username/password encoding in RFC | |
| 2477 // 2617. However there is an open draft for adding an additional | |
| 2478 // accept-charset parameter to the WWW-Authenticate and | |
| 2479 // Proxy-Authenticate headers, see | |
| 2480 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For | |
| 2481 // now always use UTF-8 encoding. | |
| 2482 _UTF8Encoder encoder = new _UTF8Encoder(); | |
| 2483 String auth = | |
| 2484 CryptoUtils.bytesToBase64(encoder.encodeString( | |
| 2485 "$username:$password")); | |
| 2486 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); | |
| 2487 } | |
| 2488 | |
| 2489 String username; | |
| 2490 String password; | |
| 2491 } | |
| 2492 | |
| 2493 | |
| 2494 class _HttpClientDigestCredentials implements HttpClientDigestCredentials { | |
| 2495 _HttpClientDigestCredentials(this.username, | |
| 2496 this.password); | |
| 2497 | |
| 2498 _AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST; | |
| 2499 | |
| 2500 void authorize(_Credentials credentials, HttpClientRequest request) { | |
| 2501 // TODO(sgjesse): Implement!!! | |
|
Mads Ager (google)
2012/10/26 11:07:02
Throw unsupported exception?
Søren Gjesse
2012/10/26 12:59:02
Done.
| |
| 2502 } | |
| 2503 | |
| 2504 String username; | |
| 2505 String password; | |
| 2506 } | |
| 2507 | |
| 2508 | |
| 2509 | |
| 2293 class _RedirectInfo implements RedirectInfo { | 2510 class _RedirectInfo implements RedirectInfo { |
| 2294 const _RedirectInfo(int this.statusCode, | 2511 const _RedirectInfo(int this.statusCode, |
| 2295 String this.method, | 2512 String this.method, |
| 2296 Uri this.location); | 2513 Uri this.location); |
| 2297 final int statusCode; | 2514 final int statusCode; |
| 2298 final String method; | 2515 final String method; |
| 2299 final Uri location; | 2516 final Uri location; |
| 2300 } | 2517 } |
| OLD | NEW |