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

Unified Diff: sdk/lib/io/http_impl.dart

Issue 13915007: Add support for proxies requiing authentication (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 8 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 side-by-side diff with in-line comments
Download patch
Index: sdk/lib/io/http_impl.dart
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
index 952b2703a82a7952e6e0c4681851253336938353..965a6fd009d9e6a7ddccce10e9c95b18655e8857 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -251,6 +251,13 @@ class _HttpClientResponse
HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo;
+ bool get _shouldAuthenticateProxy {
+ // Only try to authenticate if there is a challenge in the response.
+ List<String> challenge = headers[HttpHeaders.PROXY_AUTHENTICATE];
Anders Johnsen 2013/04/15 07:07:19 Use headers.value? It should do the length == 1 ch
Søren Gjesse 2013/04/15 07:42:00 Value throws, and I don't want that here.
+ return statusCode == HttpStatus.PROXY_AUTHENTICATION_REQUIRED &&
+ challenge != null && challenge.length == 1;
+ }
+
bool get _shouldAuthenticate {
// Only try to authenticate if there is a challenge in the response.
List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE];
@@ -315,6 +322,56 @@ class _HttpClientResponse
}
});
}
+
+ // No credentials were found and the callback was not set.
+ return new Future.immediate(this);
+ }
+
+ Future<HttpClientResponse> _authenticateProxy() {
+ Future<HttpClientResponse> retryWithProxyCredentials(_ProxyCredentials cr) {
+ _httpRequest.headers._mutable = true;
Anders Johnsen 2013/04/15 07:07:19 Are you sure about this? Don't you want to modify
Søren Gjesse 2013/04/15 07:42:00 Not needed - removed.
+ cr.authorize(_httpRequest);
Anders Johnsen 2013/04/15 07:07:19 This should be done automatically in _HttpClientRe
Søren Gjesse 2013/04/15 07:42:00 Good point - removed.
+ return fold(null, (x, y) {}).then((_) {
+ return _httpClient._openUrlFromRequest(_httpRequest.method,
+ _httpRequest.uri,
+ _httpRequest)
+ .then((request) => request.close());
+ });
+ }
+
+ List<String> challenge = headers[HttpHeaders.PROXY_AUTHENTICATE];
+ assert(challenge != null || challenge.length == 1);
+ _HeaderValue header =
+ new _HeaderValue.fromString(challenge[0], parameterSeparator: ",");
+ _AuthenticationScheme scheme =
+ new _AuthenticationScheme.fromString(header.value);
+ String realm = header.parameters["realm"];
+
+ // See if any credentials are available.
+ var proxy = _httpRequest._proxy;
+
+ var cr = _httpClient._findProxyCredentials(proxy);
+ if (cr != null) {
+ return retryWithProxyCredentials(cr);
+ }
+
+ // Ask for more credentials if none found.
+ if (_httpClient._authenticateProxy != null) {
+ Future authComplete = _httpClient._authenticateProxy(proxy.host,
+ proxy.port,
+ "basic",
+ realm);
+ return authComplete.then((credsAvailable) {
+ if (credsAvailable) {
+ var cr = _httpClient._findProxyCredentials(proxy);
+ return retryWithProxyCredentials(cr);
+ } else {
+ // No credentials available, complete with original response.
+ return this;
+ }
+ });
+ }
+
// No credentials were found and the callback was not set.
return new Future.immediate(this);
}
@@ -705,7 +762,7 @@ class _HttpClientRequest extends _HttpOutboundMessage<HttpClientResponse>
final Completer<HttpClientResponse> _responseCompleter
= new Completer<HttpClientResponse>();
- final bool _usingProxy;
+ final _Proxy _proxy;
Future<HttpClientResponse> _response;
@@ -719,7 +776,7 @@ class _HttpClientRequest extends _HttpOutboundMessage<HttpClientResponse>
_HttpClientRequest(_HttpOutgoing outgoing,
Uri this.uri,
String this.method,
- bool this._usingProxy,
+ _Proxy this._proxy,
_HttpClient this._httpClient,
_HttpClientConnection this._httpClientConnection)
: super("1.1", outgoing) {
@@ -777,6 +834,8 @@ class _HttpClientRequest extends _HttpOutboundMessage<HttpClientResponse>
.then((_) => new Future.immediateError(
new RedirectLimitExceededException(response.redirects)));
}
+ } else if (response._shouldAuthenticateProxy) {
+ future = response._authenticateProxy();
} else if (response._shouldAuthenticate) {
future = response._authenticate();
} else {
@@ -802,7 +861,7 @@ class _HttpClientRequest extends _HttpOutboundMessage<HttpClientResponse>
writeSP();
// Send the path for direct connections and the whole URL for
// proxy connections.
- if (!_usingProxy) {
+ if (_proxy.isDirect) {
String path = uri.path;
if (path.length == 0) path = "/";
if (uri.query != "") {
@@ -980,7 +1039,7 @@ class _HttpClientConnection {
});
}
- _HttpClientRequest send(Uri uri, int port, String method, bool isDirect) {
+ _HttpClientRequest send(Uri uri, int port, String method, _Proxy proxy) {
// Start with pausing the parser.
_subscription.pause();
var outgoing = new _HttpOutgoing();
@@ -988,15 +1047,27 @@ class _HttpClientConnection {
var request = new _HttpClientRequest(outgoing,
uri,
method,
- !isDirect,
+ proxy,
_httpClient,
this);
request.headers.host = uri.domain;
request.headers.port = port;
request.headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip");
+ if (proxy.isAuthenticated) {
+ // If the proxy configuration contains user information use that
+ // for proxy basic authorization.
+ String auth = CryptoUtils.bytesToBase64(
+ _encodeString("${proxy.username}:${proxy.password}"));
+ request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth");
+ } else if (!proxy.isDirect && _httpClient._proxyCredentials.length > 0) {
+ var cr = _httpClient._findProxyCredentials(proxy);
+ if (cr != null) {
+ cr.authorize(request);
+ }
+ }
if (uri.userInfo != null && !uri.userInfo.isEmpty) {
// If the URL contains user information use that for basic
- // authorization
+ // authorization.
String auth =
CryptoUtils.bytesToBase64(_encodeString(uri.userInfo));
request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth");
@@ -1095,7 +1166,9 @@ class _HttpClient implements HttpClient {
final Set<_HttpClientConnection> _activeConnections
= new Set<_HttpClientConnection>();
final List<_Credentials> _credentials = [];
+ final List<_ProxyCredentials> _proxyCredentials = [];
Function _authenticate;
+ Function _authenticateProxy;
Function _findProxy = HttpClient.findProxyFromEnvironment;
Future<HttpClientRequest> open(String method,
@@ -1163,6 +1236,18 @@ class _HttpClient implements HttpClient {
_credentials.add(new _Credentials(url, realm, cr));
}
+ set authenticateProxy(
+ Future<bool> f(String host, int port, String scheme, String realm)) {
+ _authenticateProxy = f;
+ }
+
+ void addProxyCredentials(String host,
+ int port,
+ String realm,
+ HttpClientCredentials cr) {
+ _proxyCredentials.add(new _ProxyCredentials(host, port, realm, cr));
+ }
+
set findProxy(String f(Uri uri)) => _findProxy = f;
Future<HttpClientRequest> _openUrl(String method, Uri uri) {
@@ -1196,7 +1281,7 @@ class _HttpClient implements HttpClient {
return info.connection.send(uri,
port,
method.toUpperCase(),
- info.proxy.isDirect);
+ info.proxy);
});
}
@@ -1208,7 +1293,7 @@ class _HttpClient implements HttpClient {
request.followRedirects = previous.followRedirects;
// Allow same number of redirects.
request.maxRedirects = previous.maxRedirects;
- // Copy headers
+ // Copy headers.
for (var header in previous.headers._headers.keys) {
if (request.headers[header] == null) {
request.headers.set(header, previous.headers[header]);
@@ -1299,6 +1384,16 @@ class _HttpClient implements HttpClient {
return cr;
}
+ _ProxyCredentials _findProxyCredentials(_Proxy proxy) {
+ // Look for credentials.
+ var it = _proxyCredentials.iterator;
+ while (it.moveNext()) {
+ if (it.current.applies(proxy, _AuthenticationScheme.BASIC)) {
+ return it.current;
+ }
+ }
+ }
+
void _removeCredentials(_Credentials cr) {
int index = _credentials.indexOf(cr);
if (index != -1) {
@@ -1590,13 +1685,30 @@ class _ProxyConfiguration {
proxy = proxy.trim();
if (!proxy.isEmpty) {
if (proxy.startsWith(PROXY_PREFIX)) {
+ String username;
+ String password;
+ // Skip the "PROXY " prefix.
+ proxy = proxy.substring(PROXY_PREFIX.length).trim();
+ // Look for proxy authentication.
+ int at = proxy.indexOf("@");
+ if (at != -1) {
+ String userinfo = proxy.substring(0, at).trim();
+ proxy = proxy.substring(at + 1).trim();
+ int colon = userinfo.indexOf(":");
+ if (colon == -1 || colon == 0 || colon == proxy.length - 1) {
+ throw new HttpException(
+ "Invalid proxy configuration $configuration");
+ }
+ username = userinfo.substring(0, colon).trim();
+ password = userinfo.substring(colon + 1).trim();
+ }
+ // Look for proxy host and port.
int colon = proxy.indexOf(":");
if (colon == -1 || colon == 0 || colon == proxy.length - 1) {
throw new HttpException(
"Invalid proxy configuration $configuration");
}
- // Skip the "PROXY " prefix.
- String host = proxy.substring(PROXY_PREFIX.length, colon).trim();
+ String host = proxy.substring(0, colon).trim();
String portString = proxy.substring(colon + 1).trim();
int port;
try {
@@ -1606,7 +1718,7 @@ class _ProxyConfiguration {
"Invalid proxy configuration $configuration, "
"invalid port '$portString'");
}
- proxies.add(new _Proxy(host, port));
+ proxies.add(new _Proxy(host, port, username, password));
} else if (proxy.trim() == DIRECT_PREFIX) {
proxies.add(new _Proxy.direct());
} else {
@@ -1624,11 +1736,17 @@ class _ProxyConfiguration {
class _Proxy {
- const _Proxy(this.host, this.port) : isDirect = false;
- const _Proxy.direct() : host = null, port = null, isDirect = true;
+ const _Proxy(
+ this.host, this.port, this.username, this.password) : isDirect = false;
+ const _Proxy.direct() : host = null, port = null,
+ username = null, password = null, isDirect = true;
+
+ bool get isAuthenticated => username != null;
final String host;
final int port;
+ final String username;
+ final String password;
final bool isDirect;
}
@@ -1775,6 +1893,26 @@ class _Credentials {
}
+class _ProxyCredentials {
+ _ProxyCredentials(this.host, this.port, this.realm, this.credentials);
+
+ _AuthenticationScheme get scheme => credentials.scheme;
+
+ bool applies(_Proxy proxy, _AuthenticationScheme scheme) {
+ return proxy.host == host && proxy.port == port;
+ }
+
+ void authorize(HttpClientRequest request) {
+ credentials.authorizeProxy(this, request);
+ }
+
+ String host;
+ int port;
+ String realm;
+ _HttpClientCredentials credentials;
+}
+
+
abstract class _HttpClientCredentials implements HttpClientCredentials {
_AuthenticationScheme get scheme;
void authorize(_Credentials credentials, HttpClientRequest request);
@@ -1789,7 +1927,7 @@ class _HttpClientBasicCredentials
_AuthenticationScheme get scheme => _AuthenticationScheme.BASIC;
- void authorize(_Credentials _, HttpClientRequest request) {
+ String authorization() {
// 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
@@ -1798,7 +1936,15 @@ class _HttpClientBasicCredentials
// now always use UTF-8 encoding.
String auth =
CryptoUtils.bytesToBase64(_encodeString("$username:$password"));
- request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth");
+ return "Basic $auth";
+ }
+
+ void authorize(_Credentials _, HttpClientRequest request) {
+ request.headers.set(HttpHeaders.AUTHORIZATION, authorization());
+ }
+
+ void authorizeProxy(_ProxyCredentials _, HttpClientRequest request) {
+ request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, authorization());
}
String username;
@@ -1819,6 +1965,11 @@ class _HttpClientDigestCredentials
throw new UnsupportedError("Digest authentication not yet supported");
}
+ void authorizeProxy(_Credentials credentials, HttpClientRequest request) {
+ // TODO(sgjesse): Implement!!!
+ throw new UnsupportedError("Digest authentication not yet supported");
+ }
+
String username;
String password;
}

Powered by Google App Engine
This is Rietveld 408576698