| 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 1338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1773 _reasonPhrase = reasonPhrase; | 1779 _reasonPhrase = reasonPhrase; |
| 1774 } | 1780 } |
| 1775 | 1781 |
| 1776 void _onHeaderReceived(String name, String value) { | 1782 void _onHeaderReceived(String name, String value) { |
| 1777 _headers.add(name, value); | 1783 _headers.add(name, value); |
| 1778 if (name == "content-length") { | 1784 if (name == "content-length") { |
| 1779 _contentLength = parseInt(value); | 1785 _contentLength = parseInt(value); |
| 1780 } | 1786 } |
| 1781 } | 1787 } |
| 1782 | 1788 |
| 1789 void _handleUnauthorized() { |
| 1790 |
| 1791 void retryRequest(_Credentials cr) { |
| 1792 if (cr != null) { |
| 1793 if (cr.scheme == _AuthenticationScheme.DIGEST) { |
| 1794 cr.nonce = header.parameters["nonce"]; |
| 1795 cr.algorithm = header.parameters["algorithm"]; |
| 1796 cr.qop = header.parameters["qop"]; |
| 1797 } |
| 1798 // Drain body and retry. |
| 1799 // TODO(sjgesse): Support digest. |
| 1800 if (cr.scheme == _AuthenticationScheme.BASIC) { |
| 1801 inputStream.onData = inputStream.read; |
| 1802 inputStream.onClosed = _connection.retry; |
| 1803 return; |
| 1804 } |
| 1805 } |
| 1806 |
| 1807 // Fall through to here to perform normal response handling if |
| 1808 // there is no sensible authorization handling. |
| 1809 if (_connection._onResponse != null) { |
| 1810 _connection._onResponse(this); |
| 1811 } |
| 1812 } |
| 1813 |
| 1814 // Only try to authenticate if there is a challenge in the response. |
| 1815 List<String> challenge = _headers[HttpHeaders.WWW_AUTHENTICATE]; |
| 1816 if (challenge != null && challenge.length == 1) { |
| 1817 _HeaderValue header = |
| 1818 new _HeaderValue.fromString(challenge[0], parameterSeparator: ","); |
| 1819 _AuthenticationScheme scheme = |
| 1820 new _AuthenticationScheme.fromString(header.value); |
| 1821 String realm = header.parameters["realm"]; |
| 1822 |
| 1823 // See if any credentials are available. |
| 1824 _Credentials cr = |
| 1825 _connection._client._findCredentials( |
| 1826 _connection._request._uri, scheme); |
| 1827 |
| 1828 // Ask for more credentials if none found or the one found has |
| 1829 // already been used. If it has already been used it must now be |
| 1830 // invalid and is removed. |
| 1831 if (cr == null || cr.used) { |
| 1832 if (cr != null) { |
| 1833 _connection._client._removeCredentials(cr); |
| 1834 } |
| 1835 cr = null; |
| 1836 if (_connection._client._authenticate != null) { |
| 1837 Future authComplete = |
| 1838 _connection._client._authenticate( |
| 1839 _connection._request._uri, scheme.toString(), realm); |
| 1840 authComplete.then((credsAvailable) { |
| 1841 if (credsAvailable) { |
| 1842 cr = _connection._client._findCredentials( |
| 1843 _connection._request._uri, scheme); |
| 1844 retryRequest(cr); |
| 1845 } else { |
| 1846 if (_connection._onResponse != null) { |
| 1847 _connection._onResponse(this); |
| 1848 } |
| 1849 } |
| 1850 }); |
| 1851 return; |
| 1852 } |
| 1853 } else { |
| 1854 // If credentials found prepare for retrying the request. |
| 1855 retryRequest(cr); |
| 1856 return; |
| 1857 } |
| 1858 } |
| 1859 |
| 1860 // Fall through to here to perform normal response handling if |
| 1861 // there is no sensible authorization handling. |
| 1862 if (_connection._onResponse != null) { |
| 1863 _connection._onResponse(this); |
| 1864 } |
| 1865 } |
| 1866 |
| 1783 void _onHeadersComplete() { | 1867 void _onHeadersComplete() { |
| 1784 _headers._mutable = false; | 1868 _headers._mutable = false; |
| 1785 _buffer = new _BufferList(); | 1869 _buffer = new _BufferList(); |
| 1786 if (isRedirect && _connection.followRedirects) { | 1870 if (isRedirect && _connection.followRedirects) { |
| 1787 if (_connection._redirects == null || | 1871 if (_connection._redirects == null || |
| 1788 _connection._redirects.length < _connection.maxRedirects) { | 1872 _connection._redirects.length < _connection.maxRedirects) { |
| 1789 // Check the location header. | 1873 // Check the location header. |
| 1790 List<String> location = headers[HttpHeaders.LOCATION]; | 1874 List<String> location = headers[HttpHeaders.LOCATION]; |
| 1791 if (location == null || location.length > 1) { | 1875 if (location == null || location.length > 1) { |
| 1792 throw new RedirectException("Invalid redirect", | 1876 throw new RedirectException("Invalid redirect", |
| 1793 _connection._redirects); | 1877 _connection._redirects); |
| 1794 } | 1878 } |
| 1795 // Check for redirect loop | 1879 // Check for redirect loop |
| 1796 if (_connection._redirects != null) { | 1880 if (_connection._redirects != null) { |
| 1797 Uri redirectUrl = new Uri.fromString(location[0]); | 1881 Uri redirectUrl = new Uri.fromString(location[0]); |
| 1798 for (int i = 0; i < _connection._redirects.length; i++) { | 1882 for (int i = 0; i < _connection._redirects.length; i++) { |
| 1799 if (_connection._redirects[i].location.toString() == | 1883 if (_connection._redirects[i].location.toString() == |
| 1800 redirectUrl.toString()) { | 1884 redirectUrl.toString()) { |
| 1801 throw new RedirectLoopException(_connection._redirects); | 1885 throw new RedirectLoopException(_connection._redirects); |
| 1802 } | 1886 } |
| 1803 } | 1887 } |
| 1804 } | 1888 } |
| 1805 // Drain body and redirect. | 1889 // Drain body and redirect. |
| 1806 inputStream.onData = inputStream.read; | 1890 inputStream.onData = inputStream.read; |
| 1807 inputStream.onClosed = _connection.redirect; | 1891 inputStream.onClosed = _connection.redirect; |
| 1808 } else { | 1892 } else { |
| 1809 throw new RedirectLimitExceededException(_connection._redirects); | 1893 throw new RedirectLimitExceededException(_connection._redirects); |
| 1810 } | 1894 } |
| 1895 } else if (statusCode == HttpStatus.UNAUTHORIZED) { |
| 1896 _handleUnauthorized(); |
| 1811 } else if (_connection._onResponse != null) { | 1897 } else if (_connection._onResponse != null) { |
| 1812 _connection._onResponse(this); | 1898 _connection._onResponse(this); |
| 1813 } | 1899 } |
| 1814 } | 1900 } |
| 1815 | 1901 |
| 1816 void _onDataReceived(List<int> data) { | 1902 void _onDataReceived(List<int> data) { |
| 1817 _buffer.add(data); | 1903 _buffer.add(data); |
| 1818 if (_inputStream != null) _inputStream._dataReceived(); | 1904 if (_inputStream != null) _inputStream._dataReceived(); |
| 1819 } | 1905 } |
| 1820 | 1906 |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1955 } | 2041 } |
| 1956 | 2042 |
| 1957 void set onResponse(void handler(HttpClientResponse response)) { | 2043 void set onResponse(void handler(HttpClientResponse response)) { |
| 1958 _onResponse = handler; | 2044 _onResponse = handler; |
| 1959 } | 2045 } |
| 1960 | 2046 |
| 1961 void set onError(void callback(e)) { | 2047 void set onError(void callback(e)) { |
| 1962 _onErrorCallback = callback; | 2048 _onErrorCallback = callback; |
| 1963 } | 2049 } |
| 1964 | 2050 |
| 2051 void retry() { |
| 2052 if (_socketConn != null) { |
| 2053 throw new HttpException("Cannot retry with body data pending"); |
| 2054 } |
| 2055 // Retry the URL using the same connection instance. |
| 2056 _client._openUrl(_method, _request._uri, this); |
| 2057 } |
| 2058 |
| 1965 void redirect([String method, Uri url]) { | 2059 void redirect([String method, Uri url]) { |
| 1966 if (_socketConn != null) { | 2060 if (_socketConn != null) { |
| 1967 throw new HttpException("Cannot redirect with body data pending"); | 2061 throw new HttpException("Cannot redirect with body data pending"); |
| 1968 } | 2062 } |
| 1969 if (method == null) method = _method; | 2063 if (method == null) method = _method; |
| 1970 if (url == null) { | 2064 if (url == null) { |
| 1971 url = new Uri.fromString(_response.headers.value(HttpHeaders.LOCATION)); | 2065 url = new Uri.fromString(_response.headers.value(HttpHeaders.LOCATION)); |
| 1972 } | 2066 } |
| 1973 if (_redirects == null) { | 2067 if (_redirects == null) { |
| 1974 _redirects = new List<_RedirectInfo>(); | 2068 _redirects = new List<_RedirectInfo>(); |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2079 final String host; | 2173 final String host; |
| 2080 final int port; | 2174 final int port; |
| 2081 final bool isDirect; | 2175 final bool isDirect; |
| 2082 } | 2176 } |
| 2083 | 2177 |
| 2084 class _HttpClient implements HttpClient { | 2178 class _HttpClient implements HttpClient { |
| 2085 static const int DEFAULT_EVICTION_TIMEOUT = 60000; | 2179 static const int DEFAULT_EVICTION_TIMEOUT = 60000; |
| 2086 | 2180 |
| 2087 _HttpClient() : _openSockets = new Map(), | 2181 _HttpClient() : _openSockets = new Map(), |
| 2088 _activeSockets = new Set(), | 2182 _activeSockets = new Set(), |
| 2183 credentials = new List<_Credentials>(), |
| 2089 _shutdown = false; | 2184 _shutdown = false; |
| 2090 | 2185 |
| 2091 HttpClientConnection open( | 2186 HttpClientConnection open( |
| 2092 String method, String host, int port, String path) { | 2187 String method, String host, int port, String path) { |
| 2093 // TODO(sgjesse): The path set here can contain both query and | 2188 // TODO(sgjesse): The path set here can contain both query and |
| 2094 // fragment. They should be cracked and set correctly. | 2189 // fragment. They should be cracked and set correctly. |
| 2095 return _open(method, new Uri.fromComponents( | 2190 return _open(method, new Uri.fromComponents( |
| 2096 scheme: "http", domain: host, port: port, path: path)); | 2191 scheme: "http", domain: host, port: port, path: path)); |
| 2097 } | 2192 } |
| 2098 | 2193 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 2109 HttpClientConnection openUrl(String method, Uri url) { | 2204 HttpClientConnection openUrl(String method, Uri url) { |
| 2110 return _openUrl(method, url); | 2205 return _openUrl(method, url); |
| 2111 } | 2206 } |
| 2112 | 2207 |
| 2113 HttpClientConnection _openUrl(String method, | 2208 HttpClientConnection _openUrl(String method, |
| 2114 Uri url, | 2209 Uri url, |
| 2115 [_HttpClientConnection connection]) { | 2210 [_HttpClientConnection connection]) { |
| 2116 if (url.scheme != "http") { | 2211 if (url.scheme != "http") { |
| 2117 throw new HttpException("Unsupported URL scheme ${url.scheme}"); | 2212 throw new HttpException("Unsupported URL scheme ${url.scheme}"); |
| 2118 } | 2213 } |
| 2119 if (url.userInfo != "") { | |
| 2120 throw new HttpException("Unsupported user info ${url.userInfo}"); | |
| 2121 } | |
| 2122 return _open(method, url, connection); | 2214 return _open(method, url, connection); |
| 2123 } | 2215 } |
| 2124 | 2216 |
| 2125 HttpClientConnection get(String host, int port, String path) { | 2217 HttpClientConnection get(String host, int port, String path) { |
| 2126 return open("GET", host, port, path); | 2218 return open("GET", host, port, path); |
| 2127 } | 2219 } |
| 2128 | 2220 |
| 2129 HttpClientConnection getUrl(Uri url) => _openUrl("GET", url); | 2221 HttpClientConnection getUrl(Uri url) => _openUrl("GET", url); |
| 2130 | 2222 |
| 2131 HttpClientConnection post(String host, int port, String path) { | 2223 HttpClientConnection post(String host, int port, String path) { |
| 2132 return open("POST", host, port, path); | 2224 return open("POST", host, port, path); |
| 2133 } | 2225 } |
| 2134 | 2226 |
| 2135 HttpClientConnection postUrl(Uri url) => _openUrl("POST", url); | 2227 HttpClientConnection postUrl(Uri url) => _openUrl("POST", url); |
| 2136 | 2228 |
| 2229 set authenticate(bool f(Uri url, String scheme, String realm)) { |
| 2230 _authenticate = f; |
| 2231 } |
| 2232 |
| 2233 void addCredentials( |
| 2234 Uri url, String realm, HttpClientCredentials cr) { |
| 2235 credentials.add(new _Credentials(url, realm, cr)); |
| 2236 } |
| 2237 |
| 2137 set findProxy(String f(Uri uri)) => _findProxy = f; | 2238 set findProxy(String f(Uri uri)) => _findProxy = f; |
| 2138 | 2239 |
| 2139 void shutdown() { | 2240 void shutdown() { |
| 2140 _openSockets.forEach((String key, Queue<_SocketConnection> connections) { | 2241 _openSockets.forEach((String key, Queue<_SocketConnection> connections) { |
| 2141 while (!connections.isEmpty) { | 2242 while (!connections.isEmpty) { |
| 2142 _SocketConnection socketConn = connections.removeFirst(); | 2243 _SocketConnection socketConn = connections.removeFirst(); |
| 2143 socketConn._socket.close(); | 2244 socketConn._socket.close(); |
| 2144 } | 2245 } |
| 2145 }); | 2246 }); |
| 2146 _activeSockets.forEach((_SocketConnection socketConn) { | 2247 _activeSockets.forEach((_SocketConnection socketConn) { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 2170 int proxyIndex) { | 2271 int proxyIndex) { |
| 2171 | 2272 |
| 2172 void _connectionOpened(_SocketConnection socketConn, | 2273 void _connectionOpened(_SocketConnection socketConn, |
| 2173 _HttpClientConnection connection, | 2274 _HttpClientConnection connection, |
| 2174 bool usingProxy) { | 2275 bool usingProxy) { |
| 2175 connection._usingProxy = usingProxy; | 2276 connection._usingProxy = usingProxy; |
| 2176 connection._connectionEstablished(socketConn); | 2277 connection._connectionEstablished(socketConn); |
| 2177 HttpClientRequest request = connection.open(method, url); | 2278 HttpClientRequest request = connection.open(method, url); |
| 2178 request.headers.host = host; | 2279 request.headers.host = host; |
| 2179 request.headers.port = port; | 2280 request.headers.port = port; |
| 2281 if (url.userInfo != null && !url.userInfo.isEmpty) { |
| 2282 // If the URL contains user information use that for basic |
| 2283 // authorization |
| 2284 _UTF8Encoder encoder = new _UTF8Encoder(); |
| 2285 String auth = |
| 2286 CryptoUtils.bytesToBase64(encoder.encodeString(url.userInfo)); |
| 2287 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); |
| 2288 } else { |
| 2289 // Look for credentials. |
| 2290 _Credentials cr = _findCredentials(url); |
| 2291 if (cr != null) { |
| 2292 cr.authorize(request); |
| 2293 } |
| 2294 } |
| 2180 if (connection._onRequest != null) { | 2295 if (connection._onRequest != null) { |
| 2181 connection._onRequest(request); | 2296 connection._onRequest(request); |
| 2182 } else { | 2297 } else { |
| 2183 request.outputStream.close(); | 2298 request.outputStream.close(); |
| 2184 } | 2299 } |
| 2185 } | 2300 } |
| 2186 | 2301 |
| 2187 assert(proxyIndex < proxyConfiguration.proxies.length); | 2302 assert(proxyIndex < proxyConfiguration.proxies.length); |
| 2188 | 2303 |
| 2189 // Determine the actual host to connect to. | 2304 // Determine the actual host to connect to. |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2313 if (_openSockets.isEmpty) _cancelEvictionTimer(); | 2428 if (_openSockets.isEmpty) _cancelEvictionTimer(); |
| 2314 } | 2429 } |
| 2315 _evictionTimer = new Timer.repeating(10000, _handleEviction); | 2430 _evictionTimer = new Timer.repeating(10000, _handleEviction); |
| 2316 } | 2431 } |
| 2317 | 2432 |
| 2318 // Return connection. | 2433 // Return connection. |
| 2319 _activeSockets.remove(socketConn); | 2434 _activeSockets.remove(socketConn); |
| 2320 sockets.addFirst(socketConn); | 2435 sockets.addFirst(socketConn); |
| 2321 } | 2436 } |
| 2322 | 2437 |
| 2438 _Credentials _findCredentials(Uri url, [_AuthenticationScheme scheme]) { |
| 2439 // Look for credentials. |
| 2440 _Credentials cr = |
| 2441 credentials.reduce(null, (_Credentials prev, _Credentials value) { |
| 2442 if (value.applies(url, scheme)) { |
| 2443 if (prev == null) return value; |
| 2444 return value.uri.path.length > prev.uri.path.length ? value : prev; |
| 2445 } else { |
| 2446 return prev; |
| 2447 } |
| 2448 }); |
| 2449 return cr; |
| 2450 } |
| 2451 |
| 2452 void _removeCredentials(_Credentials cr) { |
| 2453 int index = credentials.indexOf(cr); |
| 2454 if (index != -1) { |
| 2455 credentials.removeAt(index); |
| 2456 } |
| 2457 } |
| 2458 |
| 2323 Function _onOpen; | 2459 Function _onOpen; |
| 2324 Map<String, Queue<_SocketConnection>> _openSockets; | 2460 Map<String, Queue<_SocketConnection>> _openSockets; |
| 2325 Set<_SocketConnection> _activeSockets; | 2461 Set<_SocketConnection> _activeSockets; |
| 2462 List<_Credentials> credentials; |
| 2326 Timer _evictionTimer; | 2463 Timer _evictionTimer; |
| 2327 Function _findProxy; | 2464 Function _findProxy; |
| 2465 Function _authenticate; |
| 2328 bool _shutdown; // Has this HTTP client been shutdown? | 2466 bool _shutdown; // Has this HTTP client been shutdown? |
| 2329 } | 2467 } |
| 2330 | 2468 |
| 2331 | 2469 |
| 2332 class _HttpConnectionInfo implements HttpConnectionInfo { | 2470 class _HttpConnectionInfo implements HttpConnectionInfo { |
| 2333 String remoteHost; | 2471 String remoteHost; |
| 2334 int remotePort; | 2472 int remotePort; |
| 2335 int localPort; | 2473 int localPort; |
| 2336 } | 2474 } |
| 2337 | 2475 |
| 2338 | 2476 |
| 2339 class _DetachedSocket implements DetachedSocket { | 2477 class _DetachedSocket implements DetachedSocket { |
| 2340 _DetachedSocket(this._socket, this._unparsedData); | 2478 _DetachedSocket(this._socket, this._unparsedData); |
| 2341 Socket get socket => _socket; | 2479 Socket get socket => _socket; |
| 2342 List<int> get unparsedData => _unparsedData; | 2480 List<int> get unparsedData => _unparsedData; |
| 2343 Socket _socket; | 2481 Socket _socket; |
| 2344 List<int> _unparsedData; | 2482 List<int> _unparsedData; |
| 2345 } | 2483 } |
| 2346 | 2484 |
| 2347 | 2485 |
| 2486 class _AuthenticationScheme { |
| 2487 static const UNKNOWN = const _AuthenticationScheme(-1); |
| 2488 static const BASIC = const _AuthenticationScheme(0); |
| 2489 static const DIGEST = const _AuthenticationScheme(1); |
| 2490 |
| 2491 const _AuthenticationScheme(this._scheme); |
| 2492 |
| 2493 factory _AuthenticationScheme.fromString(String scheme) { |
| 2494 if (scheme.toLowerCase() == "basic") return BASIC; |
| 2495 if (scheme.toLowerCase() == "digest") return DIGEST; |
| 2496 return UNKNOWN; |
| 2497 } |
| 2498 |
| 2499 String toString() { |
| 2500 if (this == BASIC) return "Basic"; |
| 2501 if (this == DIGEST) return "Digest"; |
| 2502 return "Unknown"; |
| 2503 } |
| 2504 |
| 2505 final int _scheme; |
| 2506 } |
| 2507 |
| 2508 |
| 2509 class _Credentials { |
| 2510 _Credentials(this.uri, this.realm, this.credentials); |
| 2511 |
| 2512 _AuthenticationScheme get scheme => credentials.scheme; |
| 2513 |
| 2514 bool applies(Uri uri, _AuthenticationScheme scheme) { |
| 2515 if (scheme != null && credentials.scheme != scheme) return false; |
| 2516 if (uri.domain != this.uri.domain) return false; |
| 2517 int thisPort = |
| 2518 this.uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : this.uri.port; |
| 2519 int otherPort = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port; |
| 2520 if (otherPort != thisPort) return false; |
| 2521 return uri.path.startsWith(this.uri.path); |
| 2522 } |
| 2523 |
| 2524 void authorize(HttpClientRequest request) { |
| 2525 credentials.authorize(this, request); |
| 2526 used = true; |
| 2527 } |
| 2528 |
| 2529 bool used = false; |
| 2530 Uri uri; |
| 2531 String realm; |
| 2532 HttpClientCredentials credentials; |
| 2533 |
| 2534 // Digest specific fields. |
| 2535 String nonce; |
| 2536 String algorithm; |
| 2537 String qop; |
| 2538 } |
| 2539 |
| 2540 |
| 2541 class _HttpClientCredentials implements HttpClientCredentials { |
| 2542 abstract _AuthenticationScheme get scheme; |
| 2543 abstract void authorize(HttpClientRequest request); |
| 2544 } |
| 2545 |
| 2546 |
| 2547 class _HttpClientBasicCredentials implements HttpClientBasicCredentials { |
| 2548 _HttpClientBasicCredentials(this.username, |
| 2549 this.password); |
| 2550 |
| 2551 _AuthenticationScheme get scheme => _AuthenticationScheme.BASIC; |
| 2552 |
| 2553 void authorize(_Credentials _, HttpClientRequest request) { |
| 2554 // There is no mentioning of username/password encoding in RFC |
| 2555 // 2617. However there is an open draft for adding an additional |
| 2556 // accept-charset parameter to the WWW-Authenticate and |
| 2557 // Proxy-Authenticate headers, see |
| 2558 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For |
| 2559 // now always use UTF-8 encoding. |
| 2560 _UTF8Encoder encoder = new _UTF8Encoder(); |
| 2561 String auth = |
| 2562 CryptoUtils.bytesToBase64(encoder.encodeString( |
| 2563 "$username:$password")); |
| 2564 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); |
| 2565 } |
| 2566 |
| 2567 String username; |
| 2568 String password; |
| 2569 } |
| 2570 |
| 2571 |
| 2572 class _HttpClientDigestCredentials implements HttpClientDigestCredentials { |
| 2573 _HttpClientDigestCredentials(this.username, |
| 2574 this.password); |
| 2575 |
| 2576 _AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST; |
| 2577 |
| 2578 void authorize(_Credentials credentials, HttpClientRequest request) { |
| 2579 // TODO(sgjesse): Implement!!! |
| 2580 throw new UnsupportedOperationException(); |
| 2581 } |
| 2582 |
| 2583 String username; |
| 2584 String password; |
| 2585 } |
| 2586 |
| 2587 |
| 2588 |
| 2348 class _RedirectInfo implements RedirectInfo { | 2589 class _RedirectInfo implements RedirectInfo { |
| 2349 const _RedirectInfo(int this.statusCode, | 2590 const _RedirectInfo(int this.statusCode, |
| 2350 String this.method, | 2591 String this.method, |
| 2351 Uri this.location); | 2592 Uri this.location); |
| 2352 final int statusCode; | 2593 final int statusCode; |
| 2353 final String method; | 2594 final String method; |
| 2354 final Uri location; | 2595 final Uri location; |
| 2355 } | 2596 } |
| OLD | NEW |