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 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
244 unsubscribeOnError: unsubscribeOnError); | 244 unsubscribeOnError: unsubscribeOnError); |
245 } | 245 } |
246 | 246 |
247 Future<Socket> detachSocket() { | 247 Future<Socket> detachSocket() { |
248 _httpClient._connectionClosed(_httpRequest._httpClientConnection); | 248 _httpClient._connectionClosed(_httpRequest._httpClientConnection); |
249 return _httpRequest._httpClientConnection.detachSocket(); | 249 return _httpRequest._httpClientConnection.detachSocket(); |
250 } | 250 } |
251 | 251 |
252 HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo; | 252 HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo; |
253 | 253 |
254 bool get _shouldAuthenticateProxy { | |
255 // Only try to authenticate if there is a challenge in the response. | |
256 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.
| |
257 return statusCode == HttpStatus.PROXY_AUTHENTICATION_REQUIRED && | |
258 challenge != null && challenge.length == 1; | |
259 } | |
260 | |
254 bool get _shouldAuthenticate { | 261 bool get _shouldAuthenticate { |
255 // Only try to authenticate if there is a challenge in the response. | 262 // Only try to authenticate if there is a challenge in the response. |
256 List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE]; | 263 List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE]; |
257 return statusCode == HttpStatus.UNAUTHORIZED && | 264 return statusCode == HttpStatus.UNAUTHORIZED && |
258 challenge != null && challenge.length == 1; | 265 challenge != null && challenge.length == 1; |
259 } | 266 } |
260 | 267 |
261 Future<HttpClientResponse> _authenticate() { | 268 Future<HttpClientResponse> _authenticate() { |
262 Future<HttpClientResponse> retryWithCredentials(_Credentials cr) { | 269 Future<HttpClientResponse> retryWithCredentials(_Credentials cr) { |
263 if (cr != null) { | 270 if (cr != null) { |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
308 return authComplete.then((credsAvailable) { | 315 return authComplete.then((credsAvailable) { |
309 if (credsAvailable) { | 316 if (credsAvailable) { |
310 cr = _httpClient._findCredentials(_httpRequest.uri, scheme); | 317 cr = _httpClient._findCredentials(_httpRequest.uri, scheme); |
311 return retryWithCredentials(cr); | 318 return retryWithCredentials(cr); |
312 } else { | 319 } else { |
313 // No credentials available, complete with original response. | 320 // No credentials available, complete with original response. |
314 return this; | 321 return this; |
315 } | 322 } |
316 }); | 323 }); |
317 } | 324 } |
325 | |
318 // No credentials were found and the callback was not set. | 326 // No credentials were found and the callback was not set. |
319 return new Future.immediate(this); | 327 return new Future.immediate(this); |
320 } | 328 } |
329 | |
330 Future<HttpClientResponse> _authenticateProxy() { | |
331 Future<HttpClientResponse> retryWithProxyCredentials(_ProxyCredentials cr) { | |
332 _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.
| |
333 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.
| |
334 return fold(null, (x, y) {}).then((_) { | |
335 return _httpClient._openUrlFromRequest(_httpRequest.method, | |
336 _httpRequest.uri, | |
337 _httpRequest) | |
338 .then((request) => request.close()); | |
339 }); | |
340 } | |
341 | |
342 List<String> challenge = headers[HttpHeaders.PROXY_AUTHENTICATE]; | |
343 assert(challenge != null || challenge.length == 1); | |
344 _HeaderValue header = | |
345 new _HeaderValue.fromString(challenge[0], parameterSeparator: ","); | |
346 _AuthenticationScheme scheme = | |
347 new _AuthenticationScheme.fromString(header.value); | |
348 String realm = header.parameters["realm"]; | |
349 | |
350 // See if any credentials are available. | |
351 var proxy = _httpRequest._proxy; | |
352 | |
353 var cr = _httpClient._findProxyCredentials(proxy); | |
354 if (cr != null) { | |
355 return retryWithProxyCredentials(cr); | |
356 } | |
357 | |
358 // Ask for more credentials if none found. | |
359 if (_httpClient._authenticateProxy != null) { | |
360 Future authComplete = _httpClient._authenticateProxy(proxy.host, | |
361 proxy.port, | |
362 "basic", | |
363 realm); | |
364 return authComplete.then((credsAvailable) { | |
365 if (credsAvailable) { | |
366 var cr = _httpClient._findProxyCredentials(proxy); | |
367 return retryWithProxyCredentials(cr); | |
368 } else { | |
369 // No credentials available, complete with original response. | |
370 return this; | |
371 } | |
372 }); | |
373 } | |
374 | |
375 // No credentials were found and the callback was not set. | |
376 return new Future.immediate(this); | |
377 } | |
321 } | 378 } |
322 | 379 |
323 | 380 |
324 abstract class _HttpOutboundMessage<T> implements IOSink { | 381 abstract class _HttpOutboundMessage<T> implements IOSink { |
325 // Used to mark when the body should be written. This is used for HEAD | 382 // Used to mark when the body should be written. This is used for HEAD |
326 // requests and in error handling. | 383 // requests and in error handling. |
327 bool _ignoreBody = false; | 384 bool _ignoreBody = false; |
328 bool _headersWritten = false; | 385 bool _headersWritten = false; |
329 | 386 |
330 IOSink _ioSink; | 387 IOSink _ioSink; |
(...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
698 final Uri uri; | 755 final Uri uri; |
699 final List<Cookie> cookies = new List<Cookie>(); | 756 final List<Cookie> cookies = new List<Cookie>(); |
700 | 757 |
701 // The HttpClient this request belongs to. | 758 // The HttpClient this request belongs to. |
702 final _HttpClient _httpClient; | 759 final _HttpClient _httpClient; |
703 final _HttpClientConnection _httpClientConnection; | 760 final _HttpClientConnection _httpClientConnection; |
704 | 761 |
705 final Completer<HttpClientResponse> _responseCompleter | 762 final Completer<HttpClientResponse> _responseCompleter |
706 = new Completer<HttpClientResponse>(); | 763 = new Completer<HttpClientResponse>(); |
707 | 764 |
708 final bool _usingProxy; | 765 final _Proxy _proxy; |
709 | 766 |
710 Future<HttpClientResponse> _response; | 767 Future<HttpClientResponse> _response; |
711 | 768 |
712 // TODO(ajohnsen): Get default value from client? | 769 // TODO(ajohnsen): Get default value from client? |
713 bool _followRedirects = true; | 770 bool _followRedirects = true; |
714 | 771 |
715 int _maxRedirects = 5; | 772 int _maxRedirects = 5; |
716 | 773 |
717 List<RedirectInfo> _responseRedirects = []; | 774 List<RedirectInfo> _responseRedirects = []; |
718 | 775 |
719 _HttpClientRequest(_HttpOutgoing outgoing, | 776 _HttpClientRequest(_HttpOutgoing outgoing, |
720 Uri this.uri, | 777 Uri this.uri, |
721 String this.method, | 778 String this.method, |
722 bool this._usingProxy, | 779 _Proxy this._proxy, |
723 _HttpClient this._httpClient, | 780 _HttpClient this._httpClient, |
724 _HttpClientConnection this._httpClientConnection) | 781 _HttpClientConnection this._httpClientConnection) |
725 : super("1.1", outgoing) { | 782 : super("1.1", outgoing) { |
726 // GET and HEAD have 'content-length: 0' by default. | 783 // GET and HEAD have 'content-length: 0' by default. |
727 if (method == "GET" || method == "HEAD") { | 784 if (method == "GET" || method == "HEAD") { |
728 contentLength = 0; | 785 contentLength = 0; |
729 } | 786 } |
730 } | 787 } |
731 | 788 |
732 Future<HttpClientResponse> get done { | 789 Future<HttpClientResponse> get done { |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
770 if (response.redirects.length < maxRedirects) { | 827 if (response.redirects.length < maxRedirects) { |
771 // Redirect and drain response. | 828 // Redirect and drain response. |
772 future = response.fold(null, (x, y) {}) | 829 future = response.fold(null, (x, y) {}) |
773 .then((_) => response.redirect()); | 830 .then((_) => response.redirect()); |
774 } else { | 831 } else { |
775 // End with exception, too many redirects. | 832 // End with exception, too many redirects. |
776 future = response.fold(null, (x, y) {}) | 833 future = response.fold(null, (x, y) {}) |
777 .then((_) => new Future.immediateError( | 834 .then((_) => new Future.immediateError( |
778 new RedirectLimitExceededException(response.redirects))); | 835 new RedirectLimitExceededException(response.redirects))); |
779 } | 836 } |
837 } else if (response._shouldAuthenticateProxy) { | |
838 future = response._authenticateProxy(); | |
780 } else if (response._shouldAuthenticate) { | 839 } else if (response._shouldAuthenticate) { |
781 future = response._authenticate(); | 840 future = response._authenticate(); |
782 } else { | 841 } else { |
783 future = new Future<HttpClientResponse>.immediate(response); | 842 future = new Future<HttpClientResponse>.immediate(response); |
784 } | 843 } |
785 future.then( | 844 future.then( |
786 (v) => _responseCompleter.complete(v), | 845 (v) => _responseCompleter.complete(v), |
787 onError: (e) { | 846 onError: (e) { |
788 _responseCompleter.completeError(e); | 847 _responseCompleter.completeError(e); |
789 }); | 848 }); |
790 } | 849 } |
791 | 850 |
792 void _onError(AsyncError error) { | 851 void _onError(AsyncError error) { |
793 _responseCompleter.completeError(error); | 852 _responseCompleter.completeError(error); |
794 } | 853 } |
795 | 854 |
796 void _writeHeader() { | 855 void _writeHeader() { |
797 var buffer = new _BufferList(); | 856 var buffer = new _BufferList(); |
798 writeSP() => buffer.add(const [_CharCode.SP]); | 857 writeSP() => buffer.add(const [_CharCode.SP]); |
799 writeCRLF() => buffer.add(const [_CharCode.CR, _CharCode.LF]); | 858 writeCRLF() => buffer.add(const [_CharCode.CR, _CharCode.LF]); |
800 | 859 |
801 buffer.add(method.codeUnits); | 860 buffer.add(method.codeUnits); |
802 writeSP(); | 861 writeSP(); |
803 // Send the path for direct connections and the whole URL for | 862 // Send the path for direct connections and the whole URL for |
804 // proxy connections. | 863 // proxy connections. |
805 if (!_usingProxy) { | 864 if (_proxy.isDirect) { |
806 String path = uri.path; | 865 String path = uri.path; |
807 if (path.length == 0) path = "/"; | 866 if (path.length == 0) path = "/"; |
808 if (uri.query != "") { | 867 if (uri.query != "") { |
809 if (uri.fragment != "") { | 868 if (uri.fragment != "") { |
810 path = "${path}?${uri.query}#${uri.fragment}"; | 869 path = "${path}?${uri.query}#${uri.fragment}"; |
811 } else { | 870 } else { |
812 path = "${path}?${uri.query}"; | 871 path = "${path}?${uri.query}"; |
813 } | 872 } |
814 } | 873 } |
815 buffer.add(path.codeUnits); | 874 buffer.add(path.codeUnits); |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
973 if (_nextResponseCompleter != null) { | 1032 if (_nextResponseCompleter != null) { |
974 _nextResponseCompleter.completeError(error); | 1033 _nextResponseCompleter.completeError(error); |
975 _nextResponseCompleter = null; | 1034 _nextResponseCompleter = null; |
976 } | 1035 } |
977 }, | 1036 }, |
978 onDone: () { | 1037 onDone: () { |
979 close(); | 1038 close(); |
980 }); | 1039 }); |
981 } | 1040 } |
982 | 1041 |
983 _HttpClientRequest send(Uri uri, int port, String method, bool isDirect) { | 1042 _HttpClientRequest send(Uri uri, int port, String method, _Proxy proxy) { |
984 // Start with pausing the parser. | 1043 // Start with pausing the parser. |
985 _subscription.pause(); | 1044 _subscription.pause(); |
986 var outgoing = new _HttpOutgoing(); | 1045 var outgoing = new _HttpOutgoing(); |
987 // Create new request object, wrapping the outgoing connection. | 1046 // Create new request object, wrapping the outgoing connection. |
988 var request = new _HttpClientRequest(outgoing, | 1047 var request = new _HttpClientRequest(outgoing, |
989 uri, | 1048 uri, |
990 method, | 1049 method, |
991 !isDirect, | 1050 proxy, |
992 _httpClient, | 1051 _httpClient, |
993 this); | 1052 this); |
994 request.headers.host = uri.domain; | 1053 request.headers.host = uri.domain; |
995 request.headers.port = port; | 1054 request.headers.port = port; |
996 request.headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip"); | 1055 request.headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip"); |
1056 if (proxy.isAuthenticated) { | |
1057 // If the proxy configuration contains user information use that | |
1058 // for proxy basic authorization. | |
1059 String auth = CryptoUtils.bytesToBase64( | |
1060 _encodeString("${proxy.username}:${proxy.password}")); | |
1061 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth"); | |
1062 } else if (!proxy.isDirect && _httpClient._proxyCredentials.length > 0) { | |
1063 var cr = _httpClient._findProxyCredentials(proxy); | |
1064 if (cr != null) { | |
1065 cr.authorize(request); | |
1066 } | |
1067 } | |
997 if (uri.userInfo != null && !uri.userInfo.isEmpty) { | 1068 if (uri.userInfo != null && !uri.userInfo.isEmpty) { |
998 // If the URL contains user information use that for basic | 1069 // If the URL contains user information use that for basic |
999 // authorization | 1070 // authorization. |
1000 String auth = | 1071 String auth = |
1001 CryptoUtils.bytesToBase64(_encodeString(uri.userInfo)); | 1072 CryptoUtils.bytesToBase64(_encodeString(uri.userInfo)); |
1002 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); | 1073 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); |
1003 } else { | 1074 } else { |
1004 // Look for credentials. | 1075 // Look for credentials. |
1005 _Credentials cr = _httpClient._findCredentials(uri); | 1076 _Credentials cr = _httpClient._findCredentials(uri); |
1006 if (cr != null) { | 1077 if (cr != null) { |
1007 cr.authorize(request); | 1078 cr.authorize(request); |
1008 } | 1079 } |
1009 } | 1080 } |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1088 class _HttpClient implements HttpClient { | 1159 class _HttpClient implements HttpClient { |
1089 // TODO(ajohnsen): Use eviction timeout. | 1160 // TODO(ajohnsen): Use eviction timeout. |
1090 static const int DEFAULT_EVICTION_TIMEOUT = 60000; | 1161 static const int DEFAULT_EVICTION_TIMEOUT = 60000; |
1091 bool _closing = false; | 1162 bool _closing = false; |
1092 | 1163 |
1093 final Map<String, Queue<_HttpClientConnection>> _idleConnections | 1164 final Map<String, Queue<_HttpClientConnection>> _idleConnections |
1094 = new Map<String, Queue<_HttpClientConnection>>(); | 1165 = new Map<String, Queue<_HttpClientConnection>>(); |
1095 final Set<_HttpClientConnection> _activeConnections | 1166 final Set<_HttpClientConnection> _activeConnections |
1096 = new Set<_HttpClientConnection>(); | 1167 = new Set<_HttpClientConnection>(); |
1097 final List<_Credentials> _credentials = []; | 1168 final List<_Credentials> _credentials = []; |
1169 final List<_ProxyCredentials> _proxyCredentials = []; | |
1098 Function _authenticate; | 1170 Function _authenticate; |
1171 Function _authenticateProxy; | |
1099 Function _findProxy = HttpClient.findProxyFromEnvironment; | 1172 Function _findProxy = HttpClient.findProxyFromEnvironment; |
1100 | 1173 |
1101 Future<HttpClientRequest> open(String method, | 1174 Future<HttpClientRequest> open(String method, |
1102 String host, | 1175 String host, |
1103 int port, | 1176 int port, |
1104 String path) { | 1177 String path) { |
1105 // TODO(sgjesse): The path set here can contain both query and | 1178 // TODO(sgjesse): The path set here can contain both query and |
1106 // fragment. They should be cracked and set correctly. | 1179 // fragment. They should be cracked and set correctly. |
1107 return _openUrl(method, new Uri.fromComponents( | 1180 return _openUrl(method, new Uri.fromComponents( |
1108 scheme: "http", domain: host, port: port, path: path)); | 1181 scheme: "http", domain: host, port: port, path: path)); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1156 } | 1229 } |
1157 | 1230 |
1158 set authenticate(Future<bool> f(Uri url, String scheme, String realm)) { | 1231 set authenticate(Future<bool> f(Uri url, String scheme, String realm)) { |
1159 _authenticate = f; | 1232 _authenticate = f; |
1160 } | 1233 } |
1161 | 1234 |
1162 void addCredentials(Uri url, String realm, HttpClientCredentials cr) { | 1235 void addCredentials(Uri url, String realm, HttpClientCredentials cr) { |
1163 _credentials.add(new _Credentials(url, realm, cr)); | 1236 _credentials.add(new _Credentials(url, realm, cr)); |
1164 } | 1237 } |
1165 | 1238 |
1239 set authenticateProxy( | |
1240 Future<bool> f(String host, int port, String scheme, String realm)) { | |
1241 _authenticateProxy = f; | |
1242 } | |
1243 | |
1244 void addProxyCredentials(String host, | |
1245 int port, | |
1246 String realm, | |
1247 HttpClientCredentials cr) { | |
1248 _proxyCredentials.add(new _ProxyCredentials(host, port, realm, cr)); | |
1249 } | |
1250 | |
1166 set findProxy(String f(Uri uri)) => _findProxy = f; | 1251 set findProxy(String f(Uri uri)) => _findProxy = f; |
1167 | 1252 |
1168 Future<HttpClientRequest> _openUrl(String method, Uri uri) { | 1253 Future<HttpClientRequest> _openUrl(String method, Uri uri) { |
1169 if (method == null) { | 1254 if (method == null) { |
1170 throw new ArgumentError(method); | 1255 throw new ArgumentError(method); |
1171 } | 1256 } |
1172 if (uri.domain.isEmpty || (uri.scheme != "http" && uri.scheme != "https")) { | 1257 if (uri.domain.isEmpty || (uri.scheme != "http" && uri.scheme != "https")) { |
1173 throw new ArgumentError("Unsupported scheme '${uri.scheme}' in $uri"); | 1258 throw new ArgumentError("Unsupported scheme '${uri.scheme}' in $uri"); |
1174 } | 1259 } |
1175 | 1260 |
(...skipping 13 matching lines...) Expand all Loading... | |
1189 proxyConf = new _ProxyConfiguration(_findProxy(uri)); | 1274 proxyConf = new _ProxyConfiguration(_findProxy(uri)); |
1190 } catch (error, stackTrace) { | 1275 } catch (error, stackTrace) { |
1191 return new Future.immediateError(error, stackTrace); | 1276 return new Future.immediateError(error, stackTrace); |
1192 } | 1277 } |
1193 } | 1278 } |
1194 return _getConnection(uri.domain, port, proxyConf, isSecure) | 1279 return _getConnection(uri.domain, port, proxyConf, isSecure) |
1195 .then((info) { | 1280 .then((info) { |
1196 return info.connection.send(uri, | 1281 return info.connection.send(uri, |
1197 port, | 1282 port, |
1198 method.toUpperCase(), | 1283 method.toUpperCase(), |
1199 info.proxy.isDirect); | 1284 info.proxy); |
1200 }); | 1285 }); |
1201 } | 1286 } |
1202 | 1287 |
1203 Future<HttpClientRequest> _openUrlFromRequest(String method, | 1288 Future<HttpClientRequest> _openUrlFromRequest(String method, |
1204 Uri uri, | 1289 Uri uri, |
1205 _HttpClientRequest previous) { | 1290 _HttpClientRequest previous) { |
1206 return openUrl(method, uri).then((_HttpClientRequest request) { | 1291 return openUrl(method, uri).then((_HttpClientRequest request) { |
1207 // Only follow redirects if initial request did. | 1292 // Only follow redirects if initial request did. |
1208 request.followRedirects = previous.followRedirects; | 1293 request.followRedirects = previous.followRedirects; |
1209 // Allow same number of redirects. | 1294 // Allow same number of redirects. |
1210 request.maxRedirects = previous.maxRedirects; | 1295 request.maxRedirects = previous.maxRedirects; |
1211 // Copy headers | 1296 // Copy headers. |
1212 for (var header in previous.headers._headers.keys) { | 1297 for (var header in previous.headers._headers.keys) { |
1213 if (request.headers[header] == null) { | 1298 if (request.headers[header] == null) { |
1214 request.headers.set(header, previous.headers[header]); | 1299 request.headers.set(header, previous.headers[header]); |
1215 } | 1300 } |
1216 } | 1301 } |
1217 request.headers.chunkedTransferEncoding = false; | 1302 request.headers.chunkedTransferEncoding = false; |
1218 request.contentLength = 0; | 1303 request.contentLength = 0; |
1219 return request; | 1304 return request; |
1220 }); | 1305 }); |
1221 } | 1306 } |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1292 if (value.applies(url, scheme)) { | 1377 if (value.applies(url, scheme)) { |
1293 if (prev == null) return value; | 1378 if (prev == null) return value; |
1294 return value.uri.path.length > prev.uri.path.length ? value : prev; | 1379 return value.uri.path.length > prev.uri.path.length ? value : prev; |
1295 } else { | 1380 } else { |
1296 return prev; | 1381 return prev; |
1297 } | 1382 } |
1298 }); | 1383 }); |
1299 return cr; | 1384 return cr; |
1300 } | 1385 } |
1301 | 1386 |
1387 _ProxyCredentials _findProxyCredentials(_Proxy proxy) { | |
1388 // Look for credentials. | |
1389 var it = _proxyCredentials.iterator; | |
1390 while (it.moveNext()) { | |
1391 if (it.current.applies(proxy, _AuthenticationScheme.BASIC)) { | |
1392 return it.current; | |
1393 } | |
1394 } | |
1395 } | |
1396 | |
1302 void _removeCredentials(_Credentials cr) { | 1397 void _removeCredentials(_Credentials cr) { |
1303 int index = _credentials.indexOf(cr); | 1398 int index = _credentials.indexOf(cr); |
1304 if (index != -1) { | 1399 if (index != -1) { |
1305 _credentials.removeAt(index); | 1400 _credentials.removeAt(index); |
1306 } | 1401 } |
1307 } | 1402 } |
1308 | 1403 |
1309 static String _findProxyFromEnvironment(Uri url, | 1404 static String _findProxyFromEnvironment(Uri url, |
1310 Map<String, String> environment) { | 1405 Map<String, String> environment) { |
1311 checkNoProxy(String option) { | 1406 checkNoProxy(String option) { |
(...skipping 271 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1583 | 1678 |
1584 _ProxyConfiguration(String configuration) : proxies = new List<_Proxy>() { | 1679 _ProxyConfiguration(String configuration) : proxies = new List<_Proxy>() { |
1585 if (configuration == null) { | 1680 if (configuration == null) { |
1586 throw new HttpException("Invalid proxy configuration $configuration"); | 1681 throw new HttpException("Invalid proxy configuration $configuration"); |
1587 } | 1682 } |
1588 List<String> list = configuration.split(";"); | 1683 List<String> list = configuration.split(";"); |
1589 list.forEach((String proxy) { | 1684 list.forEach((String proxy) { |
1590 proxy = proxy.trim(); | 1685 proxy = proxy.trim(); |
1591 if (!proxy.isEmpty) { | 1686 if (!proxy.isEmpty) { |
1592 if (proxy.startsWith(PROXY_PREFIX)) { | 1687 if (proxy.startsWith(PROXY_PREFIX)) { |
1688 String username; | |
1689 String password; | |
1690 // Skip the "PROXY " prefix. | |
1691 proxy = proxy.substring(PROXY_PREFIX.length).trim(); | |
1692 // Look for proxy authentication. | |
1693 int at = proxy.indexOf("@"); | |
1694 if (at != -1) { | |
1695 String userinfo = proxy.substring(0, at).trim(); | |
1696 proxy = proxy.substring(at + 1).trim(); | |
1697 int colon = userinfo.indexOf(":"); | |
1698 if (colon == -1 || colon == 0 || colon == proxy.length - 1) { | |
1699 throw new HttpException( | |
1700 "Invalid proxy configuration $configuration"); | |
1701 } | |
1702 username = userinfo.substring(0, colon).trim(); | |
1703 password = userinfo.substring(colon + 1).trim(); | |
1704 } | |
1705 // Look for proxy host and port. | |
1593 int colon = proxy.indexOf(":"); | 1706 int colon = proxy.indexOf(":"); |
1594 if (colon == -1 || colon == 0 || colon == proxy.length - 1) { | 1707 if (colon == -1 || colon == 0 || colon == proxy.length - 1) { |
1595 throw new HttpException( | 1708 throw new HttpException( |
1596 "Invalid proxy configuration $configuration"); | 1709 "Invalid proxy configuration $configuration"); |
1597 } | 1710 } |
1598 // Skip the "PROXY " prefix. | 1711 String host = proxy.substring(0, colon).trim(); |
1599 String host = proxy.substring(PROXY_PREFIX.length, colon).trim(); | |
1600 String portString = proxy.substring(colon + 1).trim(); | 1712 String portString = proxy.substring(colon + 1).trim(); |
1601 int port; | 1713 int port; |
1602 try { | 1714 try { |
1603 port = int.parse(portString); | 1715 port = int.parse(portString); |
1604 } on FormatException catch (e) { | 1716 } on FormatException catch (e) { |
1605 throw new HttpException( | 1717 throw new HttpException( |
1606 "Invalid proxy configuration $configuration, " | 1718 "Invalid proxy configuration $configuration, " |
1607 "invalid port '$portString'"); | 1719 "invalid port '$portString'"); |
1608 } | 1720 } |
1609 proxies.add(new _Proxy(host, port)); | 1721 proxies.add(new _Proxy(host, port, username, password)); |
1610 } else if (proxy.trim() == DIRECT_PREFIX) { | 1722 } else if (proxy.trim() == DIRECT_PREFIX) { |
1611 proxies.add(new _Proxy.direct()); | 1723 proxies.add(new _Proxy.direct()); |
1612 } else { | 1724 } else { |
1613 throw new HttpException("Invalid proxy configuration $configuration"); | 1725 throw new HttpException("Invalid proxy configuration $configuration"); |
1614 } | 1726 } |
1615 } | 1727 } |
1616 }); | 1728 }); |
1617 } | 1729 } |
1618 | 1730 |
1619 const _ProxyConfiguration.direct() | 1731 const _ProxyConfiguration.direct() |
1620 : proxies = const [const _Proxy.direct()]; | 1732 : proxies = const [const _Proxy.direct()]; |
1621 | 1733 |
1622 final List<_Proxy> proxies; | 1734 final List<_Proxy> proxies; |
1623 } | 1735 } |
1624 | 1736 |
1625 | 1737 |
1626 class _Proxy { | 1738 class _Proxy { |
1627 const _Proxy(this.host, this.port) : isDirect = false; | 1739 const _Proxy( |
1628 const _Proxy.direct() : host = null, port = null, isDirect = true; | 1740 this.host, this.port, this.username, this.password) : isDirect = false; |
1741 const _Proxy.direct() : host = null, port = null, | |
1742 username = null, password = null, isDirect = true; | |
1743 | |
1744 bool get isAuthenticated => username != null; | |
1629 | 1745 |
1630 final String host; | 1746 final String host; |
1631 final int port; | 1747 final int port; |
1748 final String username; | |
1749 final String password; | |
1632 final bool isDirect; | 1750 final bool isDirect; |
1633 } | 1751 } |
1634 | 1752 |
1635 | 1753 |
1636 class _HttpConnectionInfo implements HttpConnectionInfo { | 1754 class _HttpConnectionInfo implements HttpConnectionInfo { |
1637 static _HttpConnectionInfo create(Socket socket) { | 1755 static _HttpConnectionInfo create(Socket socket) { |
1638 if (socket == null) return null; | 1756 if (socket == null) return null; |
1639 try { | 1757 try { |
1640 _HttpConnectionInfo info = new _HttpConnectionInfo._(); | 1758 _HttpConnectionInfo info = new _HttpConnectionInfo._(); |
1641 info.remoteHost = socket.remoteHost; | 1759 info.remoteHost = socket.remoteHost; |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1768 String realm; | 1886 String realm; |
1769 _HttpClientCredentials credentials; | 1887 _HttpClientCredentials credentials; |
1770 | 1888 |
1771 // Digest specific fields. | 1889 // Digest specific fields. |
1772 String nonce; | 1890 String nonce; |
1773 String algorithm; | 1891 String algorithm; |
1774 String qop; | 1892 String qop; |
1775 } | 1893 } |
1776 | 1894 |
1777 | 1895 |
1896 class _ProxyCredentials { | |
1897 _ProxyCredentials(this.host, this.port, this.realm, this.credentials); | |
1898 | |
1899 _AuthenticationScheme get scheme => credentials.scheme; | |
1900 | |
1901 bool applies(_Proxy proxy, _AuthenticationScheme scheme) { | |
1902 return proxy.host == host && proxy.port == port; | |
1903 } | |
1904 | |
1905 void authorize(HttpClientRequest request) { | |
1906 credentials.authorizeProxy(this, request); | |
1907 } | |
1908 | |
1909 String host; | |
1910 int port; | |
1911 String realm; | |
1912 _HttpClientCredentials credentials; | |
1913 } | |
1914 | |
1915 | |
1778 abstract class _HttpClientCredentials implements HttpClientCredentials { | 1916 abstract class _HttpClientCredentials implements HttpClientCredentials { |
1779 _AuthenticationScheme get scheme; | 1917 _AuthenticationScheme get scheme; |
1780 void authorize(_Credentials credentials, HttpClientRequest request); | 1918 void authorize(_Credentials credentials, HttpClientRequest request); |
1781 } | 1919 } |
1782 | 1920 |
1783 | 1921 |
1784 class _HttpClientBasicCredentials | 1922 class _HttpClientBasicCredentials |
1785 extends _HttpClientCredentials | 1923 extends _HttpClientCredentials |
1786 implements HttpClientBasicCredentials { | 1924 implements HttpClientBasicCredentials { |
1787 _HttpClientBasicCredentials(this.username, | 1925 _HttpClientBasicCredentials(this.username, |
1788 this.password); | 1926 this.password); |
1789 | 1927 |
1790 _AuthenticationScheme get scheme => _AuthenticationScheme.BASIC; | 1928 _AuthenticationScheme get scheme => _AuthenticationScheme.BASIC; |
1791 | 1929 |
1792 void authorize(_Credentials _, HttpClientRequest request) { | 1930 String authorization() { |
1793 // There is no mentioning of username/password encoding in RFC | 1931 // There is no mentioning of username/password encoding in RFC |
1794 // 2617. However there is an open draft for adding an additional | 1932 // 2617. However there is an open draft for adding an additional |
1795 // accept-charset parameter to the WWW-Authenticate and | 1933 // accept-charset parameter to the WWW-Authenticate and |
1796 // Proxy-Authenticate headers, see | 1934 // Proxy-Authenticate headers, see |
1797 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For | 1935 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For |
1798 // now always use UTF-8 encoding. | 1936 // now always use UTF-8 encoding. |
1799 String auth = | 1937 String auth = |
1800 CryptoUtils.bytesToBase64(_encodeString("$username:$password")); | 1938 CryptoUtils.bytesToBase64(_encodeString("$username:$password")); |
1801 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); | 1939 return "Basic $auth"; |
1940 } | |
1941 | |
1942 void authorize(_Credentials _, HttpClientRequest request) { | |
1943 request.headers.set(HttpHeaders.AUTHORIZATION, authorization()); | |
1944 } | |
1945 | |
1946 void authorizeProxy(_ProxyCredentials _, HttpClientRequest request) { | |
1947 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, authorization()); | |
1802 } | 1948 } |
1803 | 1949 |
1804 String username; | 1950 String username; |
1805 String password; | 1951 String password; |
1806 } | 1952 } |
1807 | 1953 |
1808 | 1954 |
1809 class _HttpClientDigestCredentials | 1955 class _HttpClientDigestCredentials |
1810 extends _HttpClientCredentials | 1956 extends _HttpClientCredentials |
1811 implements HttpClientDigestCredentials { | 1957 implements HttpClientDigestCredentials { |
1812 _HttpClientDigestCredentials(this.username, | 1958 _HttpClientDigestCredentials(this.username, |
1813 this.password); | 1959 this.password); |
1814 | 1960 |
1815 _AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST; | 1961 _AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST; |
1816 | 1962 |
1817 void authorize(_Credentials credentials, HttpClientRequest request) { | 1963 void authorize(_Credentials credentials, HttpClientRequest request) { |
1818 // TODO(sgjesse): Implement!!! | 1964 // TODO(sgjesse): Implement!!! |
1819 throw new UnsupportedError("Digest authentication not yet supported"); | 1965 throw new UnsupportedError("Digest authentication not yet supported"); |
1820 } | 1966 } |
1821 | 1967 |
1968 void authorizeProxy(_Credentials credentials, HttpClientRequest request) { | |
1969 // TODO(sgjesse): Implement!!! | |
1970 throw new UnsupportedError("Digest authentication not yet supported"); | |
1971 } | |
1972 | |
1822 String username; | 1973 String username; |
1823 String password; | 1974 String password; |
1824 } | 1975 } |
1825 | 1976 |
1826 | 1977 |
1827 class _RedirectInfo implements RedirectInfo { | 1978 class _RedirectInfo implements RedirectInfo { |
1828 const _RedirectInfo(int this.statusCode, | 1979 const _RedirectInfo(int this.statusCode, |
1829 String this.method, | 1980 String this.method, |
1830 Uri this.location); | 1981 Uri this.location); |
1831 final int statusCode; | 1982 final int statusCode; |
1832 final String method; | 1983 final String method; |
1833 final Uri location; | 1984 final Uri location; |
1834 } | 1985 } |
OLD | NEW |