Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(118)

Side by Side Diff: sdk/lib/io/http_impl.dart

Issue 14660011: Add support for digest authentication of HTTP proxies (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 363 matching lines...) Expand 10 before | Expand all | Expand 10 after
374 _HeaderValue.parse(challenge[0], parameterSeparator: ","); 374 _HeaderValue.parse(challenge[0], parameterSeparator: ",");
375 _AuthenticationScheme scheme = 375 _AuthenticationScheme scheme =
376 new _AuthenticationScheme.fromString(header.value); 376 new _AuthenticationScheme.fromString(header.value);
377 String realm = header.parameters["realm"]; 377 String realm = header.parameters["realm"];
378 378
379 // See if any credentials are available. 379 // See if any credentials are available.
380 var proxy = _httpRequest._proxy; 380 var proxy = _httpRequest._proxy;
381 381
382 var cr = _httpClient._findProxyCredentials(proxy); 382 var cr = _httpClient._findProxyCredentials(proxy);
383 if (cr != null) { 383 if (cr != null) {
384 return retryWithProxyCredentials(cr); 384 if (cr.scheme == _AuthenticationScheme.BASIC) {
385 return retryWithProxyCredentials(cr);
386 }
387
388 // Digest authentication only supports the MD5 algorithm.
389 if (cr.scheme == _AuthenticationScheme.DIGEST &&
390 (header.parameters["algorithm"] == null ||
391 header.parameters["algorithm"].toLowerCase() == "md5")) {
392 if (cr.nonce == null || cr.nonce == header.parameters["nonce"]) {
393 // If the nonce is not set then this is the first authenticate
394 // response for these credentials. Set up authentication state.
395 if (cr.nonce == null) {
396 cr.nonce = header.parameters["nonce"];
397 cr.algorithm = "MD5";
398 cr.qop = header.parameters["qop"];
399 cr.nonceCount = 0;
400 }
401 // Credentials where found, prepare for retrying the request.
402 return retryWithProxyCredentials(cr);
403 } else if (header.parameters["stale"] != null &&
404 header.parameters["stale"].toLowerCase() == "true") {
405 // If stale is true retry with new nonce.
406 cr.nonce = header.parameters["nonce"];
407 // Credentials where found, prepare for retrying the request.
408 return retryWithProxyCredentials(cr);
409 }
410 }
385 } 411 }
386 412
387 // Ask for more credentials if none found. 413 // Ask for more credentials if none found.
388 if (_httpClient._authenticateProxy != null) { 414 if (_httpClient._authenticateProxy != null) {
389 Future authComplete = _httpClient._authenticateProxy(proxy.host, 415 Future authComplete = _httpClient._authenticateProxy(proxy.host,
390 proxy.port, 416 proxy.port,
391 "basic", 417 "basic",
392 realm); 418 realm);
393 return authComplete.then((credsAvailable) { 419 return authComplete.then((credsAvailable) {
394 if (credsAvailable) { 420 if (credsAvailable) {
(...skipping 723 matching lines...) Expand 10 before | Expand all | Expand 10 after
1118 } 1144 }
1119 }, 1145 },
1120 onDone: () { 1146 onDone: () {
1121 close(); 1147 close();
1122 }); 1148 });
1123 } 1149 }
1124 1150
1125 _HttpClientRequest send(Uri uri, int port, String method, _Proxy proxy) { 1151 _HttpClientRequest send(Uri uri, int port, String method, _Proxy proxy) {
1126 // Start with pausing the parser. 1152 // Start with pausing the parser.
1127 _subscription.pause(); 1153 _subscription.pause();
1128 _Credentials cr; // Credentials used to authorize this request. 1154 _ProxyCredentials proxyCreds; // Credentials used to authorize proxy.
1155 _SiteCredentials creds; // Credentials used to authorize this request.
1129 var outgoing = new _HttpOutgoing(_socket); 1156 var outgoing = new _HttpOutgoing(_socket);
1130 // Create new request object, wrapping the outgoing connection. 1157 // Create new request object, wrapping the outgoing connection.
1131 var request = new _HttpClientRequest(outgoing, 1158 var request = new _HttpClientRequest(outgoing,
1132 uri, 1159 uri,
1133 method, 1160 method,
1134 proxy, 1161 proxy,
1135 _httpClient, 1162 _httpClient,
1136 this); 1163 this);
1137 request.headers.host = uri.domain; 1164 request.headers.host = uri.domain;
1138 request.headers.port = port; 1165 request.headers.port = port;
1139 request.headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip"); 1166 request.headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip");
1140 if (proxy.isAuthenticated) { 1167 if (proxy.isAuthenticated) {
1141 // If the proxy configuration contains user information use that 1168 // If the proxy configuration contains user information use that
1142 // for proxy basic authorization. 1169 // for proxy basic authorization.
1143 String auth = CryptoUtils.bytesToBase64( 1170 String auth = CryptoUtils.bytesToBase64(
1144 _encodeString("${proxy.username}:${proxy.password}")); 1171 _encodeString("${proxy.username}:${proxy.password}"));
1145 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth"); 1172 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth");
1146 } else if (!proxy.isDirect && _httpClient._proxyCredentials.length > 0) { 1173 } else if (!proxy.isDirect && _httpClient._proxyCredentials.length > 0) {
1147 var cr = _httpClient._findProxyCredentials(proxy); 1174 proxyCreds = _httpClient._findProxyCredentials(proxy);
1148 if (cr != null) { 1175 if (proxyCreds != null) {
1149 cr.authorize(request); 1176 proxyCreds.authorize(request);
1150 } 1177 }
1151 } 1178 }
1152 if (uri.userInfo != null && !uri.userInfo.isEmpty) { 1179 if (uri.userInfo != null && !uri.userInfo.isEmpty) {
1153 // If the URL contains user information use that for basic 1180 // If the URL contains user information use that for basic
1154 // authorization. 1181 // authorization.
1155 String auth = 1182 String auth =
1156 CryptoUtils.bytesToBase64(_encodeString(uri.userInfo)); 1183 CryptoUtils.bytesToBase64(_encodeString(uri.userInfo));
1157 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); 1184 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth");
1158 } else { 1185 } else {
1159 // Look for credentials. 1186 // Look for credentials.
1160 cr = _httpClient._findCredentials(uri); 1187 creds = _httpClient._findCredentials(uri);
1161 if (cr != null) { 1188 if (creds != null) {
1162 cr.authorize(request); 1189 creds.authorize(request);
1163 } 1190 }
1164 } 1191 }
1165 // Start sending the request (lazy, delayed until the user provides 1192 // Start sending the request (lazy, delayed until the user provides
1166 // data). 1193 // data).
1167 _httpParser.responseToMethod = method; 1194 _httpParser.responseToMethod = method;
1168 _streamFuture = outgoing.done 1195 _streamFuture = outgoing.done
1169 .then((s) { 1196 .then((s) {
1170 // Request sent, set up response completer. 1197 // Request sent, set up response completer.
1171 _nextResponseCompleter = new Completer(); 1198 _nextResponseCompleter = new Completer();
1172 1199
1173 // Listen for response. 1200 // Listen for response.
1174 _nextResponseCompleter.future 1201 _nextResponseCompleter.future
1175 .then((incoming) { 1202 .then((incoming) {
1176 incoming.dataDone.then((_) { 1203 incoming.dataDone.then((_) {
1177 if (!_dispose && 1204 if (!_dispose &&
1178 incoming.headers.persistentConnection && 1205 incoming.headers.persistentConnection &&
1179 request.persistentConnection) { 1206 request.persistentConnection) {
1180 // Return connection, now we are done. 1207 // Return connection, now we are done.
1181 _httpClient._returnConnection(this); 1208 _httpClient._returnConnection(this);
1182 _subscription.resume(); 1209 _subscription.resume();
1183 } else { 1210 } else {
1184 destroy(); 1211 destroy();
1185 } 1212 }
1186 }); 1213 });
1187 // For digest authentication check if the server 1214 // For digest authentication if proxy check if the proxy
1188 // requests the client to start using a new nonce. 1215 // requests the client to start using a new nonce for proxy
1189 if (cr != null && cr.scheme == _AuthenticationScheme.DIGEST) { 1216 // authentication.
1217 if (proxyCreds != null &&
1218 proxyCreds.scheme == _AuthenticationScheme.DIGEST) {
1219 var authInfo = incoming.headers["proxy-authentication-info"];
1220 if (authInfo != null && authInfo.length == 1) {
1221 var header =
1222 _HeaderValue.parse(
1223 authInfo[0], parameterSeparator: ',');
1224 var nextnonce = header.parameters["nextnonce"];
1225 if (nextnonce != null) proxyCreds.nonce = nextnonce;
1226 }
1227 }
1228 // For digest authentication check if the server requests the
1229 // client to start using a new nonce.
1230 if (creds != null &&
1231 creds.scheme == _AuthenticationScheme.DIGEST) {
1190 var authInfo = incoming.headers["authentication-info"]; 1232 var authInfo = incoming.headers["authentication-info"];
1191 if (authInfo != null && authInfo.length == 1) { 1233 if (authInfo != null && authInfo.length == 1) {
1192 var header = 1234 var header =
1193 _HeaderValue.parse( 1235 _HeaderValue.parse(
1194 authInfo[0], parameterSeparator: ','); 1236 authInfo[0], parameterSeparator: ',');
1195 var nextnonce = header.parameters["nextnonce"]; 1237 var nextnonce = header.parameters["nextnonce"];
1196 if (nextnonce != null) cr.nonce = nextnonce; 1238 if (nextnonce != null) creds.nonce = nextnonce;
1197 } 1239 }
1198 } 1240 }
1199 request._onIncoming(incoming); 1241 request._onIncoming(incoming);
1200 }) 1242 })
1201 // If we see a state error, we failed to get the 'first' 1243 // If we see a state error, we failed to get the 'first'
1202 // element. 1244 // element.
1203 // Transform the error to a HttpParserException, for 1245 // Transform the error to a HttpParserException, for
1204 // consistency. 1246 // consistency.
1205 .catchError((error) { 1247 .catchError((error) {
1206 throw new HttpParserException( 1248 throw new HttpParserException(
(...skipping 811 matching lines...) Expand 10 before | Expand all | Expand 10 after
2018 String realm; 2060 String realm;
2019 bool used = false; 2061 bool used = false;
2020 2062
2021 // Digest specific fields. 2063 // Digest specific fields.
2022 String ha1; 2064 String ha1;
2023 String nonce; 2065 String nonce;
2024 String algorithm; 2066 String algorithm;
2025 String qop; 2067 String qop;
2026 int nonceCount; 2068 int nonceCount;
2027 2069
2028 _Credentials(this.realm, this.credentials) { 2070 _Credentials(this.credentials, this.realm) {
2029 if (credentials.scheme == _AuthenticationScheme.DIGEST) { 2071 if (credentials.scheme == _AuthenticationScheme.DIGEST) {
2030 // Calculate the H(A1) value once. There is no mentioning of 2072 // Calculate the H(A1) value once. There is no mentioning of
2031 // username/password encoding in RFC 2617. However there is an 2073 // username/password encoding in RFC 2617. However there is an
2032 // open draft for adding an additional accept-charset parameter to 2074 // open draft for adding an additional accept-charset parameter to
2033 // the WWW-Authenticate and Proxy-Authenticate headers, see 2075 // the WWW-Authenticate and Proxy-Authenticate headers, see
2034 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For 2076 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For
2035 // now always use UTF-8 encoding. 2077 // now always use UTF-8 encoding.
2036 _HttpClientDigestCredentials creds = credentials; 2078 _HttpClientDigestCredentials creds = credentials;
2037 var hasher = new MD5(); 2079 var hasher = new MD5();
2038 hasher.add(_encodeString(creds.username)); 2080 hasher.add(_encodeString(creds.username));
2039 hasher.add([_CharCode.COLON]); 2081 hasher.add([_CharCode.COLON]);
2040 hasher.add(realm.codeUnits); 2082 hasher.add(realm.codeUnits);
2041 hasher.add([_CharCode.COLON]); 2083 hasher.add([_CharCode.COLON]);
2042 hasher.add(_encodeString(creds.password)); 2084 hasher.add(_encodeString(creds.password));
2043 ha1 = CryptoUtils.bytesToHex(hasher.close()); 2085 ha1 = CryptoUtils.bytesToHex(hasher.close());
2044 } 2086 }
2045 } 2087 }
2046 2088
2047 _AuthenticationScheme get scheme => credentials.scheme; 2089 _AuthenticationScheme get scheme => credentials.scheme;
2048 2090
2049 void authorize(HttpClientRequest request); 2091 void authorize(HttpClientRequest request);
2050 } 2092 }
2051 2093
2052 class _SiteCredentials extends _Credentials { 2094 class _SiteCredentials extends _Credentials {
2053 Uri uri; 2095 Uri uri;
2054 2096
2055 _SiteCredentials(this.uri, realm, _HttpClientCredentials creds) 2097 _SiteCredentials(this.uri, realm, _HttpClientCredentials creds)
2056 : super(realm, creds); 2098 : super(creds, realm);
2057 2099
2058 bool applies(Uri uri, _AuthenticationScheme scheme) { 2100 bool applies(Uri uri, _AuthenticationScheme scheme) {
2059 if (scheme != null && credentials.scheme != scheme) return false; 2101 if (scheme != null && credentials.scheme != scheme) return false;
2060 if (uri.domain != this.uri.domain) return false; 2102 if (uri.domain != this.uri.domain) return false;
2061 int thisPort = 2103 int thisPort =
2062 this.uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : this.uri.port; 2104 this.uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : this.uri.port;
2063 int otherPort = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port; 2105 int otherPort = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port;
2064 if (otherPort != thisPort) return false; 2106 if (otherPort != thisPort) return false;
2065 return uri.path.startsWith(this.uri.path); 2107 return uri.path.startsWith(this.uri.path);
2066 } 2108 }
(...skipping 12 matching lines...) Expand all
2079 2121
2080 2122
2081 class _ProxyCredentials extends _Credentials { 2123 class _ProxyCredentials extends _Credentials {
2082 String host; 2124 String host;
2083 int port; 2125 int port;
2084 2126
2085 _ProxyCredentials(this.host, 2127 _ProxyCredentials(this.host,
2086 this.port, 2128 this.port,
2087 realm, 2129 realm,
2088 _HttpClientCredentials creds) 2130 _HttpClientCredentials creds)
2089 : super(realm, creds); 2131 : super(creds, realm);
2090
2091 2132
2092 bool applies(_Proxy proxy, _AuthenticationScheme scheme) { 2133 bool applies(_Proxy proxy, _AuthenticationScheme scheme) {
2093 return proxy.host == host && proxy.port == port; 2134 return proxy.host == host && proxy.port == port;
2094 } 2135 }
2095 2136
2096 void authorize(HttpClientRequest request) { 2137 void authorize(HttpClientRequest request) {
2097 // Digest credentials cannot be used without a nonce from the 2138 // Digest credentials cannot be used without a nonce from the
2098 // server. 2139 // server.
2099 if (credentials.scheme == _AuthenticationScheme.DIGEST && 2140 if (credentials.scheme == _AuthenticationScheme.DIGEST &&
2100 nonce == null) { 2141 nonce == null) {
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
2206 return buffer.toString(); 2247 return buffer.toString();
2207 } 2248 }
2208 2249
2209 void authorize(_Credentials credentials, HttpClientRequest request) { 2250 void authorize(_Credentials credentials, HttpClientRequest request) {
2210 request.headers.set(HttpHeaders.AUTHORIZATION, 2251 request.headers.set(HttpHeaders.AUTHORIZATION,
2211 authorization(credentials, request)); 2252 authorization(credentials, request));
2212 } 2253 }
2213 2254
2214 void authorizeProxy(_ProxyCredentials credentials, 2255 void authorizeProxy(_ProxyCredentials credentials,
2215 HttpClientRequest request) { 2256 HttpClientRequest request) {
2216 // TODO(sgjesse): Implement!!! 2257 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION,
2217 throw new UnsupportedError("Digest authentication not yet supported"); 2258 authorization(credentials, request));
2218 } 2259 }
2219 2260
2220 String username; 2261 String username;
2221 String password; 2262 String password;
2222 } 2263 }
2223 2264
2224 2265
2225 class _RedirectInfo implements RedirectInfo { 2266 class _RedirectInfo implements RedirectInfo {
2226 const _RedirectInfo(int this.statusCode, 2267 const _RedirectInfo(int this.statusCode,
2227 String this.method, 2268 String this.method,
2228 Uri this.location); 2269 Uri this.location);
2229 final int statusCode; 2270 final int statusCode;
2230 final String method; 2271 final String method;
2231 final Uri location; 2272 final Uri location;
2232 } 2273 }
OLDNEW
« no previous file with comments | « no previous file | tests/standalone/io/http_proxy_test.dart » ('j') | tests/standalone/io/http_proxy_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698