| Index: sdk/lib/io/http_impl.dart
 | 
| diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
 | 
| index ee855ec8661e76acc5bd920272ab9938bce258f8..31f2a489eb8368291fe54e74e50cfb261bfeac33 100644
 | 
| --- a/sdk/lib/io/http_impl.dart
 | 
| +++ b/sdk/lib/io/http_impl.dart
 | 
| @@ -273,17 +273,14 @@ class _HttpClientResponse
 | 
|  
 | 
|    Future<HttpClientResponse> _authenticate() {
 | 
|      Future<HttpClientResponse> retryWithCredentials(_Credentials cr) {
 | 
| -      if (cr != null) {
 | 
| -        // TODO(sgjesse): Support digest.
 | 
| -        if (cr.scheme == _AuthenticationScheme.BASIC) {
 | 
| -          // Drain body and retry.
 | 
| -          return fold(null, (x, y) {}).then((_) {
 | 
| -              return _httpClient._openUrlFromRequest(_httpRequest.method,
 | 
| -                                                     _httpRequest.uri,
 | 
| -                                                     _httpRequest)
 | 
| -                  .then((request) => request.close());
 | 
| -            });
 | 
| -        }
 | 
| +      if (cr != null && cr.scheme != _AuthenticationScheme.UNKNOWN) {
 | 
| +        // Drain body and retry.
 | 
| +        return fold(null, (x, y) {}).then((_) {
 | 
| +            return _httpClient._openUrlFromRequest(_httpRequest.method,
 | 
| +                                                   _httpRequest.uri,
 | 
| +                                                   _httpRequest)
 | 
| +                .then((request) => request.close());
 | 
| +          });
 | 
|        }
 | 
|  
 | 
|        // Fall through to here to perform normal response handling if
 | 
| @@ -299,12 +296,34 @@ class _HttpClientResponse
 | 
|          new _AuthenticationScheme.fromString(header.value);
 | 
|      String realm = header.parameters["realm"];
 | 
|  
 | 
| -    // See if any credentials are available.
 | 
| +    // See if any matching credentials are available.
 | 
|      _Credentials cr = _httpClient._findCredentials(_httpRequest.uri, scheme);
 | 
| +    if (cr != null) {
 | 
| +      // For basic authentication don't retry already used credentials
 | 
| +      // as they must have already been added to the request causing
 | 
| +      // this authenticate response.
 | 
| +      if (cr.scheme == _AuthenticationScheme.BASIC && !cr.used) {
 | 
| +        // Credentials where found, prepare for retrying the request.
 | 
| +        return retryWithCredentials(cr);
 | 
| +      }
 | 
|  
 | 
| -    if (cr != null && !cr.used) {
 | 
| -      // If credentials found prepare for retrying the request.
 | 
| -      return retryWithCredentials(cr);
 | 
| +      // Digest authentication only supports the MD5 algorithm.
 | 
| +      if (cr.scheme == _AuthenticationScheme.DIGEST &&
 | 
| +          (header.parameters["algorithm"] == null ||
 | 
| +           header.parameters["algorithm"].toLowerCase() == "md5")) {
 | 
| +        // If the nonce is not set then this is the first authenticate
 | 
| +        // response for these credentials.
 | 
| +        // TODO(sgjesse): Check for changed nonce.
 | 
| +        if (cr.nonce == null) {
 | 
| +          // Set up authentication state.
 | 
| +          cr.nonce = header.parameters["nonce"];
 | 
| +          cr.algorithm = "MD5";
 | 
| +          cr.qop = header.parameters["qop"];
 | 
| +          cr.nonceCount = 0;
 | 
| +        }
 | 
| +        // Credentials where found, prepare for retrying the request.
 | 
| +        return retryWithCredentials(cr);
 | 
| +      }
 | 
|      }
 | 
|  
 | 
|      // Ask for more credentials if none found or the one found has
 | 
| @@ -1948,7 +1967,24 @@ class _AuthenticationScheme {
 | 
|  
 | 
|  
 | 
|  class _Credentials {
 | 
| -  _Credentials(this.uri, this.realm, this.credentials);
 | 
| +  _Credentials(this.uri, this.realm, this.credentials) {
 | 
| +    if (credentials.scheme == _AuthenticationScheme.DIGEST) {
 | 
| +      // Calculate the H(A1) value once. There is no mentioning of
 | 
| +      // username/password encoding in RFC 2617. However there is an
 | 
| +      // open draft for adding an additional accept-charset parameter to
 | 
| +      // the WWW-Authenticate and Proxy-Authenticate headers, see
 | 
| +      // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For
 | 
| +      // now always use UTF-8 encoding.
 | 
| +      _HttpClientDigestCredentials creds = credentials;
 | 
| +      var hasher = new MD5();
 | 
| +      hasher.add(_encodeString(creds.username));
 | 
| +      hasher.add([_CharCode.COLON]);
 | 
| +      hasher.add(realm.codeUnits);
 | 
| +      hasher.add([_CharCode.COLON]);
 | 
| +      hasher.add(_encodeString(creds.password));
 | 
| +      ha1 = CryptoUtils.bytesToHex(hasher.close());
 | 
| +    }
 | 
| +  }
 | 
|  
 | 
|    _AuthenticationScheme get scheme => credentials.scheme;
 | 
|  
 | 
| @@ -1963,6 +1999,12 @@ class _Credentials {
 | 
|    }
 | 
|  
 | 
|    void authorize(HttpClientRequest request) {
 | 
| +    // Digest credentials cannot be used without a nonce from the
 | 
| +    // server.
 | 
| +    if (credentials.scheme == _AuthenticationScheme.DIGEST &&
 | 
| +        nonce == null) {
 | 
| +      return;
 | 
| +    }
 | 
|      credentials.authorize(this, request);
 | 
|      used = true;
 | 
|    }
 | 
| @@ -1973,9 +2015,11 @@ class _Credentials {
 | 
|    _HttpClientCredentials credentials;
 | 
|  
 | 
|    // Digest specific fields.
 | 
| +  String ha1;
 | 
|    String nonce;
 | 
|    String algorithm;
 | 
|    String qop;
 | 
| +  int nonceCount;
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -2047,9 +2091,60 @@ class _HttpClientDigestCredentials
 | 
|  
 | 
|    _AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST;
 | 
|  
 | 
| +  String authorization(_Credentials credentials, HttpClientRequest request) {
 | 
| +    MD5 hasher = new MD5();
 | 
| +    hasher.add(request.method.codeUnits);
 | 
| +    hasher.add([_CharCode.COLON]);
 | 
| +    hasher.add(request.uri.path.codeUnits);
 | 
| +    var ha2 = CryptoUtils.bytesToHex(hasher.close());
 | 
| +
 | 
| +    String qop;
 | 
| +    String cnonce;
 | 
| +    String nc;
 | 
| +    var x;
 | 
| +    hasher = new MD5();
 | 
| +    hasher.add(credentials.ha1.codeUnits);
 | 
| +    hasher.add([_CharCode.COLON]);
 | 
| +    if (credentials.qop == "auth") {
 | 
| +      qop = credentials.qop;
 | 
| +      cnonce = CryptoUtils.bytesToHex(_IOCrypto.getRandomBytes(4));
 | 
| +      ++credentials.nonceCount;
 | 
| +      nc = credentials.nonceCount.toRadixString(16);
 | 
| +      nc = "00000000".substring(0, 8 - nc.length + 1) + nc;
 | 
| +      hasher.add(credentials.nonce.codeUnits);
 | 
| +      hasher.add([_CharCode.COLON]);
 | 
| +      hasher.add(nc.codeUnits);
 | 
| +      hasher.add([_CharCode.COLON]);
 | 
| +      hasher.add(cnonce.codeUnits);
 | 
| +      hasher.add([_CharCode.COLON]);
 | 
| +      hasher.add(credentials.qop.codeUnits);
 | 
| +      hasher.add([_CharCode.COLON]);
 | 
| +      hasher.add(ha2.codeUnits);
 | 
| +    } else {
 | 
| +      hasher.add(credentials.nonce.codeUnits);
 | 
| +      hasher.add([_CharCode.COLON]);
 | 
| +      hasher.add(ha2.codeUnits);
 | 
| +    }
 | 
| +    var response = CryptoUtils.bytesToHex(hasher.close());
 | 
| +
 | 
| +    StringBuffer buffer = new StringBuffer();
 | 
| +    buffer.write('Digest ');
 | 
| +    buffer.write('username="$username"');
 | 
| +    buffer.write(', realm="${credentials.realm}"');
 | 
| +    buffer.write(', nonce="${credentials.nonce}"');
 | 
| +    buffer.write(', uri="${request.uri.path}"');
 | 
| +    buffer.write(', algorithm="${credentials.algorithm}"');
 | 
| +    if (qop == "auth") {
 | 
| +      buffer.write(', qop="$qop"');
 | 
| +      buffer.write(', cnonce="$cnonce"');
 | 
| +      buffer.write(', nc="$nc"');
 | 
| +    }
 | 
| +    buffer.write(', response="$response"');
 | 
| +    return buffer.toString();
 | 
| +  }
 | 
| +
 | 
|    void authorize(_Credentials credentials, HttpClientRequest request) {
 | 
| -    // TODO(sgjesse): Implement!!!
 | 
| -    throw new UnsupportedError("Digest authentication not yet supported");
 | 
| +    request.headers.set(HttpHeaders.AUTHORIZATION, authorization(credentials, request));
 | 
|    }
 | 
|  
 | 
|    void authorizeProxy(_ProxyCredentials credentials,
 | 
| 
 |