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 867 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |