Chromium Code Reviews| 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 |