| 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 class _HttpIncoming extends Stream<List<int>> { | 7 class _HttpIncoming extends Stream<List<int>> { |
| 8 final int _transferLength; | 8 final int _transferLength; |
| 9 final Completer _dataCompleter = new Completer(); | 9 final Completer _dataCompleter = new Completer(); |
| 10 Stream<List<int>> _stream; | 10 Stream<List<int>> _stream; |
| (...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 266 | 266 |
| 267 bool get _shouldAuthenticate { | 267 bool get _shouldAuthenticate { |
| 268 // Only try to authenticate if there is a challenge in the response. | 268 // Only try to authenticate if there is a challenge in the response. |
| 269 List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE]; | 269 List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE]; |
| 270 return statusCode == HttpStatus.UNAUTHORIZED && | 270 return statusCode == HttpStatus.UNAUTHORIZED && |
| 271 challenge != null && challenge.length == 1; | 271 challenge != null && challenge.length == 1; |
| 272 } | 272 } |
| 273 | 273 |
| 274 Future<HttpClientResponse> _authenticate() { | 274 Future<HttpClientResponse> _authenticate() { |
| 275 Future<HttpClientResponse> retryWithCredentials(_Credentials cr) { | 275 Future<HttpClientResponse> retryWithCredentials(_Credentials cr) { |
| 276 if (cr != null) { | 276 if (cr != null && cr.scheme != _AuthenticationScheme.UNKNOWN) { |
| 277 // TODO(sgjesse): Support digest. | 277 // Drain body and retry. |
| 278 if (cr.scheme == _AuthenticationScheme.BASIC) { | 278 return fold(null, (x, y) {}).then((_) { |
| 279 // Drain body and retry. | 279 return _httpClient._openUrlFromRequest(_httpRequest.method, |
| 280 return fold(null, (x, y) {}).then((_) { | 280 _httpRequest.uri, |
| 281 return _httpClient._openUrlFromRequest(_httpRequest.method, | 281 _httpRequest) |
| 282 _httpRequest.uri, | 282 .then((request) => request.close()); |
| 283 _httpRequest) | 283 }); |
| 284 .then((request) => request.close()); | |
| 285 }); | |
| 286 } | |
| 287 } | 284 } |
| 288 | 285 |
| 289 // Fall through to here to perform normal response handling if | 286 // Fall through to here to perform normal response handling if |
| 290 // there is no sensible authorization handling. | 287 // there is no sensible authorization handling. |
| 291 return new Future.value(this); | 288 return new Future.value(this); |
| 292 } | 289 } |
| 293 | 290 |
| 294 List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE]; | 291 List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE]; |
| 295 assert(challenge != null || challenge.length == 1); | 292 assert(challenge != null || challenge.length == 1); |
| 296 _HeaderValue header = | 293 _HeaderValue header = |
| 297 new _HeaderValue.fromString(challenge[0], parameterSeparator: ","); | 294 new _HeaderValue.fromString(challenge[0], parameterSeparator: ","); |
| 298 _AuthenticationScheme scheme = | 295 _AuthenticationScheme scheme = |
| 299 new _AuthenticationScheme.fromString(header.value); | 296 new _AuthenticationScheme.fromString(header.value); |
| 300 String realm = header.parameters["realm"]; | 297 String realm = header.parameters["realm"]; |
| 301 | 298 |
| 302 // See if any credentials are available. | 299 // See if any matching credentials are available. |
| 303 _Credentials cr = _httpClient._findCredentials(_httpRequest.uri, scheme); | 300 _Credentials cr = _httpClient._findCredentials(_httpRequest.uri, scheme); |
| 301 if (cr != null) { |
| 302 // For basic authentication don't retry already used credentials |
| 303 // as they must have already been added to the request causing |
| 304 // this authenticate response. |
| 305 if (cr.scheme == _AuthenticationScheme.BASIC && !cr.used) { |
| 306 // Credentials where found, prepare for retrying the request. |
| 307 return retryWithCredentials(cr); |
| 308 } |
| 304 | 309 |
| 305 if (cr != null && !cr.used) { | 310 // Digest authentication only supports the MD5 algorithm. |
| 306 // If credentials found prepare for retrying the request. | 311 if (cr.scheme == _AuthenticationScheme.DIGEST && |
| 307 return retryWithCredentials(cr); | 312 (header.parameters["algorithm"] == null || |
| 313 header.parameters["algorithm"].toLowerCase() == "md5")) { |
| 314 // If the nonce is not set then this is the first authenticate |
| 315 // response for these credentials. |
| 316 // TODO(sgjesse): Check for changed nonce. |
| 317 if (cr.nonce == null) { |
| 318 // Set up authentication state. |
| 319 cr.nonce = header.parameters["nonce"]; |
| 320 cr.algorithm = "MD5"; |
| 321 cr.qop = header.parameters["qop"]; |
| 322 cr.nonceCount = 0; |
| 323 } |
| 324 // Credentials where found, prepare for retrying the request. |
| 325 return retryWithCredentials(cr); |
| 326 } |
| 308 } | 327 } |
| 309 | 328 |
| 310 // Ask for more credentials if none found or the one found has | 329 // Ask for more credentials if none found or the one found has |
| 311 // already been used. If it has already been used it must now be | 330 // already been used. If it has already been used it must now be |
| 312 // invalid and is removed. | 331 // invalid and is removed. |
| 313 if (cr != null) { | 332 if (cr != null) { |
| 314 _httpClient._removeCredentials(cr); | 333 _httpClient._removeCredentials(cr); |
| 315 cr = null; | 334 cr = null; |
| 316 } | 335 } |
| 317 if (_httpClient._authenticate != null) { | 336 if (_httpClient._authenticate != null) { |
| (...skipping 1623 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1941 if (this == BASIC) return "Basic"; | 1960 if (this == BASIC) return "Basic"; |
| 1942 if (this == DIGEST) return "Digest"; | 1961 if (this == DIGEST) return "Digest"; |
| 1943 return "Unknown"; | 1962 return "Unknown"; |
| 1944 } | 1963 } |
| 1945 | 1964 |
| 1946 final int _scheme; | 1965 final int _scheme; |
| 1947 } | 1966 } |
| 1948 | 1967 |
| 1949 | 1968 |
| 1950 class _Credentials { | 1969 class _Credentials { |
| 1951 _Credentials(this.uri, this.realm, this.credentials); | 1970 _Credentials(this.uri, this.realm, this.credentials) { |
| 1971 if (credentials.scheme == _AuthenticationScheme.DIGEST) { |
| 1972 // Calculate the H(A1) value once. There is no mentioning of |
| 1973 // username/password encoding in RFC 2617. However there is an |
| 1974 // open draft for adding an additional accept-charset parameter to |
| 1975 // the WWW-Authenticate and Proxy-Authenticate headers, see |
| 1976 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For |
| 1977 // now always use UTF-8 encoding. |
| 1978 _HttpClientDigestCredentials creds = credentials; |
| 1979 var hasher = new MD5(); |
| 1980 hasher.add(_encodeString(creds.username)); |
| 1981 hasher.add([_CharCode.COLON]); |
| 1982 hasher.add(realm.codeUnits); |
| 1983 hasher.add([_CharCode.COLON]); |
| 1984 hasher.add(_encodeString(creds.password)); |
| 1985 ha1 = CryptoUtils.bytesToHex(hasher.close()); |
| 1986 } |
| 1987 } |
| 1952 | 1988 |
| 1953 _AuthenticationScheme get scheme => credentials.scheme; | 1989 _AuthenticationScheme get scheme => credentials.scheme; |
| 1954 | 1990 |
| 1955 bool applies(Uri uri, _AuthenticationScheme scheme) { | 1991 bool applies(Uri uri, _AuthenticationScheme scheme) { |
| 1956 if (scheme != null && credentials.scheme != scheme) return false; | 1992 if (scheme != null && credentials.scheme != scheme) return false; |
| 1957 if (uri.domain != this.uri.domain) return false; | 1993 if (uri.domain != this.uri.domain) return false; |
| 1958 int thisPort = | 1994 int thisPort = |
| 1959 this.uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : this.uri.port; | 1995 this.uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : this.uri.port; |
| 1960 int otherPort = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port; | 1996 int otherPort = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port; |
| 1961 if (otherPort != thisPort) return false; | 1997 if (otherPort != thisPort) return false; |
| 1962 return uri.path.startsWith(this.uri.path); | 1998 return uri.path.startsWith(this.uri.path); |
| 1963 } | 1999 } |
| 1964 | 2000 |
| 1965 void authorize(HttpClientRequest request) { | 2001 void authorize(HttpClientRequest request) { |
| 2002 // Digest credentials cannot be used without a nonce from the |
| 2003 // server. |
| 2004 if (credentials.scheme == _AuthenticationScheme.DIGEST && |
| 2005 nonce == null) { |
| 2006 return; |
| 2007 } |
| 1966 credentials.authorize(this, request); | 2008 credentials.authorize(this, request); |
| 1967 used = true; | 2009 used = true; |
| 1968 } | 2010 } |
| 1969 | 2011 |
| 1970 bool used = false; | 2012 bool used = false; |
| 1971 Uri uri; | 2013 Uri uri; |
| 1972 String realm; | 2014 String realm; |
| 1973 _HttpClientCredentials credentials; | 2015 _HttpClientCredentials credentials; |
| 1974 | 2016 |
| 1975 // Digest specific fields. | 2017 // Digest specific fields. |
| 2018 String ha1; |
| 1976 String nonce; | 2019 String nonce; |
| 1977 String algorithm; | 2020 String algorithm; |
| 1978 String qop; | 2021 String qop; |
| 2022 int nonceCount; |
| 1979 } | 2023 } |
| 1980 | 2024 |
| 1981 | 2025 |
| 1982 class _ProxyCredentials { | 2026 class _ProxyCredentials { |
| 1983 _ProxyCredentials(this.host, this.port, this.realm, this.credentials); | 2027 _ProxyCredentials(this.host, this.port, this.realm, this.credentials); |
| 1984 | 2028 |
| 1985 _AuthenticationScheme get scheme => credentials.scheme; | 2029 _AuthenticationScheme get scheme => credentials.scheme; |
| 1986 | 2030 |
| 1987 bool applies(_Proxy proxy, _AuthenticationScheme scheme) { | 2031 bool applies(_Proxy proxy, _AuthenticationScheme scheme) { |
| 1988 return proxy.host == host && proxy.port == port; | 2032 return proxy.host == host && proxy.port == port; |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2040 | 2084 |
| 2041 | 2085 |
| 2042 class _HttpClientDigestCredentials | 2086 class _HttpClientDigestCredentials |
| 2043 extends _HttpClientCredentials | 2087 extends _HttpClientCredentials |
| 2044 implements HttpClientDigestCredentials { | 2088 implements HttpClientDigestCredentials { |
| 2045 _HttpClientDigestCredentials(this.username, | 2089 _HttpClientDigestCredentials(this.username, |
| 2046 this.password); | 2090 this.password); |
| 2047 | 2091 |
| 2048 _AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST; | 2092 _AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST; |
| 2049 | 2093 |
| 2094 String authorization(_Credentials credentials, HttpClientRequest request) { |
| 2095 MD5 hasher = new MD5(); |
| 2096 hasher.add(request.method.codeUnits); |
| 2097 hasher.add([_CharCode.COLON]); |
| 2098 hasher.add(request.uri.path.codeUnits); |
| 2099 var ha2 = CryptoUtils.bytesToHex(hasher.close()); |
| 2100 |
| 2101 String qop; |
| 2102 String cnonce; |
| 2103 String nc; |
| 2104 var x; |
| 2105 hasher = new MD5(); |
| 2106 hasher.add(credentials.ha1.codeUnits); |
| 2107 hasher.add([_CharCode.COLON]); |
| 2108 if (credentials.qop == "auth") { |
| 2109 qop = credentials.qop; |
| 2110 cnonce = CryptoUtils.bytesToHex(_IOCrypto.getRandomBytes(4)); |
| 2111 ++credentials.nonceCount; |
| 2112 nc = credentials.nonceCount.toRadixString(16); |
| 2113 nc = "00000000".substring(0, 8 - nc.length + 1) + nc; |
| 2114 hasher.add(credentials.nonce.codeUnits); |
| 2115 hasher.add([_CharCode.COLON]); |
| 2116 hasher.add(nc.codeUnits); |
| 2117 hasher.add([_CharCode.COLON]); |
| 2118 hasher.add(cnonce.codeUnits); |
| 2119 hasher.add([_CharCode.COLON]); |
| 2120 hasher.add(credentials.qop.codeUnits); |
| 2121 hasher.add([_CharCode.COLON]); |
| 2122 hasher.add(ha2.codeUnits); |
| 2123 } else { |
| 2124 hasher.add(credentials.nonce.codeUnits); |
| 2125 hasher.add([_CharCode.COLON]); |
| 2126 hasher.add(ha2.codeUnits); |
| 2127 } |
| 2128 var response = CryptoUtils.bytesToHex(hasher.close()); |
| 2129 |
| 2130 StringBuffer buffer = new StringBuffer(); |
| 2131 buffer.write('Digest '); |
| 2132 buffer.write('username="$username"'); |
| 2133 buffer.write(', realm="${credentials.realm}"'); |
| 2134 buffer.write(', nonce="${credentials.nonce}"'); |
| 2135 buffer.write(', uri="${request.uri.path}"'); |
| 2136 buffer.write(', algorithm="${credentials.algorithm}"'); |
| 2137 if (qop == "auth") { |
| 2138 buffer.write(', qop="$qop"'); |
| 2139 buffer.write(', cnonce="$cnonce"'); |
| 2140 buffer.write(', nc="$nc"'); |
| 2141 } |
| 2142 buffer.write(', response="$response"'); |
| 2143 return buffer.toString(); |
| 2144 } |
| 2145 |
| 2050 void authorize(_Credentials credentials, HttpClientRequest request) { | 2146 void authorize(_Credentials credentials, HttpClientRequest request) { |
| 2051 // TODO(sgjesse): Implement!!! | 2147 request.headers.set(HttpHeaders.AUTHORIZATION, authorization(credentials, re
quest)); |
| 2052 throw new UnsupportedError("Digest authentication not yet supported"); | |
| 2053 } | 2148 } |
| 2054 | 2149 |
| 2055 void authorizeProxy(_ProxyCredentials credentials, | 2150 void authorizeProxy(_ProxyCredentials credentials, |
| 2056 HttpClientRequest request) { | 2151 HttpClientRequest request) { |
| 2057 // TODO(sgjesse): Implement!!! | 2152 // TODO(sgjesse): Implement!!! |
| 2058 throw new UnsupportedError("Digest authentication not yet supported"); | 2153 throw new UnsupportedError("Digest authentication not yet supported"); |
| 2059 } | 2154 } |
| 2060 | 2155 |
| 2061 String username; | 2156 String username; |
| 2062 String password; | 2157 String password; |
| 2063 } | 2158 } |
| 2064 | 2159 |
| 2065 | 2160 |
| 2066 class _RedirectInfo implements RedirectInfo { | 2161 class _RedirectInfo implements RedirectInfo { |
| 2067 const _RedirectInfo(int this.statusCode, | 2162 const _RedirectInfo(int this.statusCode, |
| 2068 String this.method, | 2163 String this.method, |
| 2069 Uri this.location); | 2164 Uri this.location); |
| 2070 final int statusCode; | 2165 final int statusCode; |
| 2071 final String method; | 2166 final String method; |
| 2072 final Uri location; | 2167 final Uri location; |
| 2073 } | 2168 } |
| OLD | NEW |