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

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

Issue 14493015: Add HTTP proxy tunnel support (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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | sdk/lib/io/secure_socket.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 867 matching lines...) Expand 10 before | Expand all | Expand 10 after
878 _responseCompleter.completeError(e); 878 _responseCompleter.completeError(e);
879 }); 879 });
880 } 880 }
881 881
882 void _onError(error) { 882 void _onError(error) {
883 _responseCompleter.completeError(error); 883 _responseCompleter.completeError(error);
884 } 884 }
885 885
886 void _writeHeader() { 886 void _writeHeader() {
887 var buffer = new _BufferList(); 887 var buffer = new _BufferList();
888
888 writeSP() => buffer.add(const [_CharCode.SP]); 889 writeSP() => buffer.add(const [_CharCode.SP]);
890
889 writeCRLF() => buffer.add(const [_CharCode.CR, _CharCode.LF]); 891 writeCRLF() => buffer.add(const [_CharCode.CR, _CharCode.LF]);
890 892
891 buffer.add(method.codeUnits); 893 void writePath() {
892 writeSP();
893 // Send the path for direct connections and the whole URL for
894 // proxy connections.
895 if (_proxy.isDirect) {
896 String path = uri.path; 894 String path = uri.path;
897 if (path.length == 0) path = "/"; 895 if (path.length == 0) path = "/";
898 if (uri.query != "") { 896 if (uri.query != "") {
899 if (uri.fragment != "") { 897 if (uri.fragment != "") {
900 path = "${path}?${uri.query}#${uri.fragment}"; 898 path = "${path}?${uri.query}#${uri.fragment}";
901 } else { 899 } else {
902 path = "${path}?${uri.query}"; 900 path = "${path}?${uri.query}";
903 } 901 }
904 } 902 }
905 buffer.add(path.codeUnits); 903 buffer.add(path.codeUnits);
904 }
905
906 // Write the request method.
907 buffer.add(method.codeUnits);
908 writeSP();
909 // Write the request URI.
910 if (_proxy.isDirect) {
911 writePath();
906 } else { 912 } else {
907 buffer.add(uri.toString().codeUnits); 913 if (method == "CONNECT") {
914 // For the connect method the request URI is the host:port of
915 // the requested destination of the tunnel (see RFC 2817
916 // section 5.2)
917 buffer.add(uri.domain.codeUnits);
918 buffer.add(const [_CharCode.COLON]);
919 buffer.add(uri.port.toString().codeUnits);
920 } else {
921 if (_httpClientConnection._proxyTunnel) {
922 writePath();
923 } else {
924 buffer.add(uri.toString().codeUnits);
925 }
926 }
908 } 927 }
909 writeSP(); 928 writeSP();
910 buffer.add(_Const.HTTP11); 929 buffer.add(_Const.HTTP11);
911 writeCRLF(); 930 writeCRLF();
912 931
913 // Add the cookies to the headers. 932 // Add the cookies to the headers.
914 if (!cookies.isEmpty) { 933 if (!cookies.isEmpty) {
915 StringBuffer sb = new StringBuffer(); 934 StringBuffer sb = new StringBuffer();
916 for (int i = 0; i < cookies.length; i++) { 935 for (int i = 0; i < cookies.length; i++) {
917 if (i > 0) sb.write("; "); 936 if (i > 0) sb.write("; ");
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
1017 } 1036 }
1018 1037
1019 Future close() { 1038 Future close() {
1020 _doneCompleter.complete(_consumer); 1039 _doneCompleter.complete(_consumer);
1021 return new Future.value(); 1040 return new Future.value();
1022 } 1041 }
1023 1042
1024 Future get done => _doneCompleter.future; 1043 Future get done => _doneCompleter.future;
1025 } 1044 }
1026 1045
1027
1028 class _HttpClientConnection { 1046 class _HttpClientConnection {
1029 final String key; 1047 final String key;
1030 final Socket _socket; 1048 final Socket _socket;
1049 final bool _proxyTunnel;
1031 final _HttpParser _httpParser; 1050 final _HttpParser _httpParser;
1032 StreamSubscription _subscription; 1051 StreamSubscription _subscription;
1033 final _HttpClient _httpClient; 1052 final _HttpClient _httpClient;
1053 bool _dispose = false;
1034 1054
1035 Completer<_HttpIncoming> _nextResponseCompleter; 1055 Completer<_HttpIncoming> _nextResponseCompleter;
1036 Future _streamFuture; 1056 Future _streamFuture;
1037 1057
1038 _HttpClientConnection(String this.key, 1058 _HttpClientConnection(String this.key,
1039 Socket this._socket, 1059 Socket this._socket,
1040 _HttpClient this._httpClient) 1060 _HttpClient this._httpClient,
1061 [this._proxyTunnel = false])
1041 : _httpParser = new _HttpParser.responseParser() { 1062 : _httpParser = new _HttpParser.responseParser() {
1042 _socket.pipe(_httpParser); 1063 _socket.pipe(_httpParser);
1043 1064
1044 // Set up handlers on the parser here, so we are sure to get 'onDone' from 1065 // Set up handlers on the parser here, so we are sure to get 'onDone' from
1045 // the parser. 1066 // the parser.
1046 _subscription = _httpParser.listen( 1067 _subscription = _httpParser.listen(
1047 (incoming) { 1068 (incoming) {
1048 // Only handle one incoming response at the time. Keep the 1069 // Only handle one incoming response at the time. Keep the
1049 // stream paused until the response have been processed. 1070 // stream paused until the response have been processed.
1050 _subscription.pause(); 1071 _subscription.pause();
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
1109 _httpParser.responseToMethod = method; 1130 _httpParser.responseToMethod = method;
1110 _streamFuture = outgoing.done 1131 _streamFuture = outgoing.done
1111 .then((s) { 1132 .then((s) {
1112 // Request sent, set up response completer. 1133 // Request sent, set up response completer.
1113 _nextResponseCompleter = new Completer(); 1134 _nextResponseCompleter = new Completer();
1114 1135
1115 // Listen for response. 1136 // Listen for response.
1116 _nextResponseCompleter.future 1137 _nextResponseCompleter.future
1117 .then((incoming) { 1138 .then((incoming) {
1118 incoming.dataDone.then((_) { 1139 incoming.dataDone.then((_) {
1119 if (incoming.headers.persistentConnection && 1140 if (!_dispose &&
1141 incoming.headers.persistentConnection &&
1120 request.persistentConnection) { 1142 request.persistentConnection) {
1121 // Return connection, now we are done. 1143 // Return connection, now we are done.
1122 _httpClient._returnConnection(this); 1144 _httpClient._returnConnection(this);
1123 _subscription.resume(); 1145 _subscription.resume();
1124 } else { 1146 } else {
1125 destroy(); 1147 destroy();
1126 } 1148 }
1127 }); 1149 });
1128 request._onIncoming(incoming); 1150 request._onIncoming(incoming);
1129 }) 1151 })
(...skipping 30 matching lines...) Expand all
1160 _socket.destroy(); 1182 _socket.destroy();
1161 } 1183 }
1162 1184
1163 void close() { 1185 void close() {
1164 _httpClient._connectionClosed(this); 1186 _httpClient._connectionClosed(this);
1165 _streamFuture 1187 _streamFuture
1166 // TODO(ajohnsen): Add timeout. 1188 // TODO(ajohnsen): Add timeout.
1167 .then((_) => _socket.destroy()); 1189 .then((_) => _socket.destroy());
1168 } 1190 }
1169 1191
1192 Future<_HttpClientConnection> createProxyTunnel(host, port, proxy) {
1193 _HttpClientRequest request =
1194 send(new Uri.fromComponents(domain: host, port: port),
1195 port,
1196 "CONNECT",
1197 proxy);
1198 if (proxy.isAuthenticated) {
1199 // If the proxy configuration contains user information use that
1200 // for proxy basic authorization.
1201 String auth = CryptoUtils.bytesToBase64(
1202 _encodeString("${proxy.username}:${proxy.password}"));
1203 request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth");
1204 }
1205 return request.close()
1206 .then((response) {
1207 if (response.statusCode != HttpStatus.OK) {
1208 throw "Proxy failed to establish tunnel "
1209 "(${response.statusCode} ${response.reasonPhrase})";
1210 }
1211 var socket = response._httpRequest._httpClientConnection._socket;
1212 return SecureSocket.secure(socket, host: host);
1213 })
1214 .then((secureSocket) {
1215 String key = _HttpClientConnection.makeKey(true, host, port);
1216 return new _HttpClientConnection(
1217 key, secureSocket, request._httpClient, true);
1218 });
1219 }
1220
1170 HttpConnectionInfo get connectionInfo => _HttpConnectionInfo.create(_socket); 1221 HttpConnectionInfo get connectionInfo => _HttpConnectionInfo.create(_socket);
1222
1223 static makeKey(bool isSecure, String host, int port) {
1224 return isSecure ? "ssh:$host:$port" : "$host:$port";
1225 }
1226
1171 } 1227 }
1172 1228
1173 class _ConnnectionInfo { 1229 class _ConnnectionInfo {
1174 _ConnnectionInfo(_HttpClientConnection this.connection, _Proxy this.proxy); 1230 _ConnnectionInfo(_HttpClientConnection this.connection, _Proxy this.proxy);
1175 final _HttpClientConnection connection; 1231 final _HttpClientConnection connection;
1176 final _Proxy proxy; 1232 final _Proxy proxy;
1177 } 1233 }
1178 1234
1179 1235
1180 class _HttpClient implements HttpClient { 1236 class _HttpClient implements HttpClient {
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
1268 HttpClientCredentials cr) { 1324 HttpClientCredentials cr) {
1269 _proxyCredentials.add(new _ProxyCredentials(host, port, realm, cr)); 1325 _proxyCredentials.add(new _ProxyCredentials(host, port, realm, cr));
1270 } 1326 }
1271 1327
1272 set findProxy(String f(Uri uri)) => _findProxy = f; 1328 set findProxy(String f(Uri uri)) => _findProxy = f;
1273 1329
1274 Future<HttpClientRequest> _openUrl(String method, Uri uri) { 1330 Future<HttpClientRequest> _openUrl(String method, Uri uri) {
1275 if (method == null) { 1331 if (method == null) {
1276 throw new ArgumentError(method); 1332 throw new ArgumentError(method);
1277 } 1333 }
1278 if (uri.domain.isEmpty || (uri.scheme != "http" && uri.scheme != "https")) { 1334 if (method != "CONNECT") {
1279 throw new ArgumentError("Unsupported scheme '${uri.scheme}' in $uri"); 1335 if (uri.domain.isEmpty || (uri.scheme != "http" && uri.scheme != "https")) {
Mads Ager (google) 2013/04/29 10:05:01 long line
1336 throw new ArgumentError("Unsupported scheme '${uri.scheme}' in $uri");
1337 }
1280 } 1338 }
1281 1339
1282 bool isSecure = (uri.scheme == "https"); 1340 bool isSecure = (uri.scheme == "https");
1283 int port = uri.port; 1341 int port = uri.port;
1284 if (port == 0) { 1342 if (port == 0) {
1285 port = isSecure ? 1343 port = isSecure ?
1286 HttpClient.DEFAULT_HTTPS_PORT : 1344 HttpClient.DEFAULT_HTTPS_PORT :
1287 HttpClient.DEFAULT_HTTP_PORT; 1345 HttpClient.DEFAULT_HTTP_PORT;
1288 } 1346 }
1289 // Check to see if a proxy server should be used for this connection. 1347 // Check to see if a proxy server should be used for this connection.
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
1357 int uriPort, 1415 int uriPort,
1358 _ProxyConfiguration proxyConf, 1416 _ProxyConfiguration proxyConf,
1359 bool isSecure) { 1417 bool isSecure) {
1360 Iterator<_Proxy> proxies = proxyConf.proxies.iterator; 1418 Iterator<_Proxy> proxies = proxyConf.proxies.iterator;
1361 1419
1362 Future<_ConnnectionInfo> connect(error) { 1420 Future<_ConnnectionInfo> connect(error) {
1363 if (!proxies.moveNext()) return new Future.error(error); 1421 if (!proxies.moveNext()) return new Future.error(error);
1364 _Proxy proxy = proxies.current; 1422 _Proxy proxy = proxies.current;
1365 String host = proxy.isDirect ? uriHost: proxy.host; 1423 String host = proxy.isDirect ? uriHost: proxy.host;
1366 int port = proxy.isDirect ? uriPort: proxy.port; 1424 int port = proxy.isDirect ? uriPort: proxy.port;
1367 String key = isSecure ? "ssh:$host:$port" : "$host:$port"; 1425 String key = _HttpClientConnection.makeKey(isSecure, host, port);
1368 if (_idleConnections.containsKey(key)) { 1426 if (_idleConnections.containsKey(key)) {
1369 var connection = _idleConnections[key].first; 1427 var connection = _idleConnections[key].first;
1370 _idleConnections[key].remove(connection); 1428 _idleConnections[key].remove(connection);
1371 if (_idleConnections[key].isEmpty) { 1429 if (_idleConnections[key].isEmpty) {
1372 _idleConnections.remove(key); 1430 _idleConnections.remove(key);
1373 } 1431 }
1374 _activeConnections.add(connection); 1432 _activeConnections.add(connection);
1375 return new Future.value(new _ConnnectionInfo(connection, proxy)); 1433 return new Future.value(new _ConnnectionInfo(connection, proxy));
1376 } 1434 }
1377 return (isSecure && proxy.isDirect 1435 return (isSecure && proxy.isDirect
1378 ? SecureSocket.connect(host, 1436 ? SecureSocket.connect(host,
1379 port, 1437 port,
1380 sendClientCertificate: true) 1438 sendClientCertificate: true)
1381 : Socket.connect(host, port)) 1439 : Socket.connect(host, port))
1382 .then((socket) { 1440 .then((socket) {
1383 socket.setOption(SocketOption.TCP_NODELAY, true); 1441 socket.setOption(SocketOption.TCP_NODELAY, true);
1384 var connection = new _HttpClientConnection(key, socket, this); 1442 var connection = new _HttpClientConnection(key, socket, this);
1385 _activeConnections.add(connection); 1443 if (isSecure && !proxy.isDirect) {
1386 return new _ConnnectionInfo(connection, proxy); 1444 connection._dispose = true;
1445 return connection.createProxyTunnel(uriHost, uriPort, proxy)
1446 .then((tunnel) {
1447 _activeConnections.add(tunnel);
1448 return new _ConnnectionInfo(tunnel, proxy);
1449 });
1450 } else {
1451 _activeConnections.add(connection);
1452 return new _ConnnectionInfo(connection, proxy);
1453 }
1387 }, onError: (error) { 1454 }, onError: (error) {
1388 // Continue with next proxy. 1455 // Continue with next proxy.
1389 return connect(error); 1456 return connect(error);
1390 }); 1457 });
1391 } 1458 }
1392 return connect(new HttpException("No proxies given")); 1459 return connect(new HttpException("No proxies given"));
1393 } 1460 }
1394 1461
1395 _Credentials _findCredentials(Uri url, [_AuthenticationScheme scheme]) { 1462 _Credentials _findCredentials(Uri url, [_AuthenticationScheme scheme]) {
1396 // Look for credentials. 1463 // Look for credentials.
(...skipping 593 matching lines...) Expand 10 before | Expand all | Expand 10 after
1990 2057
1991 2058
1992 class _RedirectInfo implements RedirectInfo { 2059 class _RedirectInfo implements RedirectInfo {
1993 const _RedirectInfo(int this.statusCode, 2060 const _RedirectInfo(int this.statusCode,
1994 String this.method, 2061 String this.method,
1995 Uri this.location); 2062 Uri this.location);
1996 final int statusCode; 2063 final int statusCode;
1997 final String method; 2064 final String method;
1998 final Uri location; 2065 final Uri location;
1999 } 2066 }
OLDNEW
« no previous file with comments | « no previous file | sdk/lib/io/secure_socket.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698