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