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

Side by Side Diff: runtime/bin/http_impl.dart

Issue 10386024: Add redirect support to the HTTP client (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addressed review comments Created 8 years, 7 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 | « runtime/bin/http.dart ('k') | tests/standalone/io/http_redirect_test.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) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 class _HttpHeaders implements HttpHeaders { 5 class _HttpHeaders implements HttpHeaders {
6 _HttpHeaders() : _headers = new Map<String, List<String>>(); 6 _HttpHeaders() : _headers = new Map<String, List<String>>();
7 7
8 List<String> operator[](String name) { 8 List<String> operator[](String name) {
9 name = name.toLowerCase(); 9 name = name.toLowerCase();
10 return _headers[name]; 10 return _headers[name];
(...skipping 1130 matching lines...) Expand 10 before | Expand all | Expand 10 after
1141 class _HttpClientResponse 1141 class _HttpClientResponse
1142 extends _HttpRequestResponseBase implements HttpClientResponse { 1142 extends _HttpRequestResponseBase implements HttpClientResponse {
1143 _HttpClientResponse(_HttpClientConnection connection) 1143 _HttpClientResponse(_HttpClientConnection connection)
1144 : super(connection) { 1144 : super(connection) {
1145 _connection = connection; 1145 _connection = connection;
1146 } 1146 }
1147 1147
1148 int get statusCode() => _statusCode; 1148 int get statusCode() => _statusCode;
1149 String get reasonPhrase() => _reasonPhrase; 1149 String get reasonPhrase() => _reasonPhrase;
1150 1150
1151 bool get isRedirect() {
1152 return statusCode == HttpStatus.MOVED_PERMANENTLY ||
1153 statusCode == HttpStatus.FOUND ||
1154 statusCode == HttpStatus.SEE_OTHER ||
1155 statusCode == HttpStatus.TEMPORARY_REDIRECT;
1156 }
1157
1151 InputStream get inputStream() { 1158 InputStream get inputStream() {
1152 if (_inputStream == null) { 1159 if (_inputStream == null) {
1153 _inputStream = new _HttpInputStream(this); 1160 _inputStream = new _HttpInputStream(this);
1154 } 1161 }
1155 return _inputStream; 1162 return _inputStream;
1156 } 1163 }
1157 1164
1158 void _onRequestStart(String method, String uri, String version) { 1165 void _onRequestStart(String method, String uri, String version) {
1159 // TODO(sgjesse): Error handling 1166 // TODO(sgjesse): Error handling
1160 } 1167 }
1161 1168
1162 void _onResponseStart(int statusCode, String reasonPhrase, String version) { 1169 void _onResponseStart(int statusCode, String reasonPhrase, String version) {
1163 _statusCode = statusCode; 1170 _statusCode = statusCode;
1164 _reasonPhrase = reasonPhrase; 1171 _reasonPhrase = reasonPhrase;
1165 } 1172 }
1166 1173
1167 void _onHeaderReceived(String name, String value) { 1174 void _onHeaderReceived(String name, String value) {
1168 _headers.add(name, value); 1175 _headers.add(name, value);
1169 } 1176 }
1170 1177
1171 void _onHeadersComplete() { 1178 void _onHeadersComplete() {
1172 _headers._mutable = false; 1179 _headers._mutable = false;
1173 _buffer = new _BufferList(); 1180 _buffer = new _BufferList();
1174 if (_connection._onResponse != null) { 1181 if (isRedirect && _connection.followRedirects) {
1182 if (_connection._redirects == null ||
1183 _connection._redirects.length < _connection.maxRedirects) {
1184 // Check the location header.
1185 List<String> location = headers[HttpHeaders.LOCATION];
1186 if (location == null || location.length > 1) {
1187 throw new RedirectException("Invalid redirect",
1188 _connection._redirects);
1189 }
1190 // Check for redirect loop
1191 if (_connection._redirects != null) {
1192 Uri redirectUrl = new Uri.fromString(location[0]);
1193 for (int i = 0; i < _connection._redirects.length; i++) {
1194 if (_connection._redirects[i].location.toString() ==
1195 redirectUrl.toString()) {
1196 throw new RedirectLoop(_connection._redirects);
1197 }
1198 }
1199 }
1200 // Drain body and redirect.
1201 inputStream.onData = inputStream.read;
1202 inputStream.onClosed = _connection.redirect;
1203 } else {
1204 throw new RedirectLimitExceeded(_connection._redirects);
1205 }
1206 } else if (_connection._onResponse != null) {
1175 _connection._onResponse(this); 1207 _connection._onResponse(this);
1176 } 1208 }
1177 } 1209 }
1178 1210
1179 void _onDataReceived(List<int> data) { 1211 void _onDataReceived(List<int> data) {
1180 _buffer.add(data); 1212 _buffer.add(data);
1181 if (_inputStream != null) _inputStream._dataReceived(); 1213 if (_inputStream != null) _inputStream._dataReceived();
1182 } 1214 }
1183 1215
1184 void _onDataEnd() { 1216 void _onDataEnd() {
1217 _connection._responseDone();
1185 if (_inputStream != null) _inputStream._closeReceived(); 1218 if (_inputStream != null) _inputStream._closeReceived();
1186 _connection._responseDone();
1187 } 1219 }
1188 1220
1189 // Delegate functions for the HttpInputStream implementation. 1221 // Delegate functions for the HttpInputStream implementation.
1190 int _streamAvailable() { 1222 int _streamAvailable() {
1191 return _buffer.length; 1223 return _buffer.length;
1192 } 1224 }
1193 1225
1194 List<int> _streamRead(int bytesToRead) { 1226 List<int> _streamRead(int bytesToRead) {
1195 return _buffer.readBytes(bytesToRead); 1227 return _buffer.readBytes(bytesToRead);
1196 } 1228 }
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
1315 } 1347 }
1316 1348
1317 void set onResponse(void handler(HttpClientResponse response)) { 1349 void set onResponse(void handler(HttpClientResponse response)) {
1318 _onResponse = handler; 1350 _onResponse = handler;
1319 } 1351 }
1320 1352
1321 void set onError(void callback(e)) { 1353 void set onError(void callback(e)) {
1322 _onErrorCallback = callback; 1354 _onErrorCallback = callback;
1323 } 1355 }
1324 1356
1357 void redirect([String method, Uri url]) {
1358 if (_socketConn != null) {
1359 throw new HttpException("Cannot redirect with body data pending");
1360 }
1361 if (method == null) method = _method;
1362 if (url == null) {
1363 url = new Uri.fromString(_response.headers.value(HttpHeaders.LOCATION));
1364 }
1365 if (_redirects == null) {
1366 _redirects = new List<_RedirectInfo>();
1367 }
1368 _redirects.add(new _RedirectInfo(_response.statusCode, method, url));
1369 _request = null;
1370 _response = null;
1371 // Open redirect URL using the same connection instance.
1372 _client._openUrl(method, url, this);
1373 }
1374
1375 List<RedirectInfo> get redirects() => _redirects;
1376
1325 Function _onRequest; 1377 Function _onRequest;
1326 Function _onResponse; 1378 Function _onResponse;
1327 Function _onErrorCallback; 1379 Function _onErrorCallback;
1328 1380
1329 _HttpClient _client; 1381 _HttpClient _client;
1330 _SocketConnection _socketConn; 1382 _SocketConnection _socketConn;
1331 HttpClientRequest _request; 1383 HttpClientRequest _request;
1332 HttpClientResponse _response; 1384 HttpClientResponse _response;
1333 String _method; 1385 String _method;
1334 1386
1387 // Redirect handling
1388 bool followRedirects = true;
1389 int maxRedirects = 5;
1390 List<_RedirectInfo> _redirects;
1391
1335 // Callbacks. 1392 // Callbacks.
1336 var requestReceived; 1393 var requestReceived;
1337 } 1394 }
1338 1395
1339 1396
1340 // Class for holding keep-alive sockets in the cache for the HTTP 1397 // Class for holding keep-alive sockets in the cache for the HTTP
1341 // client together with the connection information. 1398 // client together with the connection information.
1342 class _SocketConnection { 1399 class _SocketConnection {
1343 _SocketConnection(String this._host, 1400 _SocketConnection(String this._host,
1344 int this._port, 1401 int this._port,
(...skipping 19 matching lines...) Expand all
1364 1421
1365 class _HttpClient implements HttpClient { 1422 class _HttpClient implements HttpClient {
1366 static final int DEFAULT_EVICTION_TIMEOUT = 60000; 1423 static final int DEFAULT_EVICTION_TIMEOUT = 60000;
1367 1424
1368 _HttpClient() : _openSockets = new Map(), 1425 _HttpClient() : _openSockets = new Map(),
1369 _activeSockets = new Set(), 1426 _activeSockets = new Set(),
1370 _shutdown = false; 1427 _shutdown = false;
1371 1428
1372 HttpClientConnection open( 1429 HttpClientConnection open(
1373 String method, String host, int port, String path) { 1430 String method, String host, int port, String path) {
1431 _open(method, host, port, path);
1432 }
1433
1434 HttpClientConnection _open(String method,
1435 String host,
1436 int port,
1437 String path,
1438 [_HttpClientConnection connection]) {
1374 if (_shutdown) throw new HttpException("HttpClient shutdown"); 1439 if (_shutdown) throw new HttpException("HttpClient shutdown");
1375 if (method == null || host == null || port == null || path == null) { 1440 if (method == null || host == null || port == null || path == null) {
1376 throw new IllegalArgumentException(null); 1441 throw new IllegalArgumentException(null);
1377 } 1442 }
1378 return _prepareHttpClientConnection(host, port, method, path); 1443 return _prepareHttpClientConnection(host, port, method, path, connection);
1379 } 1444 }
1380 1445
1381 HttpClientConnection openUrl(String method, Uri url) { 1446 HttpClientConnection openUrl(String method, Uri url) {
1447 _openUrl(method, url);
1448 }
1449
1450 HttpClientConnection _openUrl(String method,
1451 Uri url,
1452 [_HttpClientConnection connection]) {
1382 if (url.scheme != "http") { 1453 if (url.scheme != "http") {
1383 throw new HttpException("Unsupported URL scheme ${url.scheme}"); 1454 throw new HttpException("Unsupported URL scheme ${url.scheme}");
1384 } 1455 }
1385 if (url.userInfo != "") { 1456 if (url.userInfo != "") {
1386 throw new HttpException("Unsupported user info ${url.userInfo}"); 1457 throw new HttpException("Unsupported user info ${url.userInfo}");
1387 } 1458 }
1388 int port = url.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : url.port; 1459 int port = url.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : url.port;
1389 String path; 1460 String path;
1390 if (url.query != "") { 1461 if (url.query != "") {
1391 if (url.fragment != "") { 1462 if (url.fragment != "") {
1392 path = "${url.path}?${url.query}#${url.fragment}"; 1463 path = "${url.path}?${url.query}#${url.fragment}";
1393 } else { 1464 } else {
1394 path = "${url.path}?${url.query}"; 1465 path = "${url.path}?${url.query}";
1395 } 1466 }
1396 } else { 1467 } else {
1397 path = url.path; 1468 path = url.path;
1398 } 1469 }
1399 return open(method, url.domain, port, path); 1470 return _open(method, url.domain, port, path, connection);
1400 } 1471 }
1401 1472
1402 HttpClientConnection get(String host, int port, String path) { 1473 HttpClientConnection get(String host, int port, String path) {
1403 return open("GET", host, port, path); 1474 return _open("GET", host, port, path);
1404 } 1475 }
1405 1476
1406 HttpClientConnection getUrl(Uri url) => openUrl("GET", url); 1477 HttpClientConnection getUrl(Uri url) => _openUrl("GET", url);
1407 1478
1408 HttpClientConnection post(String host, int port, String path) { 1479 HttpClientConnection post(String host, int port, String path) {
1409 return open("POST", host, port, path); 1480 return _open("POST", host, port, path);
1410 } 1481 }
1411 1482
1412 HttpClientConnection postUrl(Uri url) => openUrl("POST", url); 1483 HttpClientConnection postUrl(Uri url) => _openUrl("POST", url);
1413 1484
1414 void shutdown() { 1485 void shutdown() {
1415 _openSockets.forEach((String key, Queue<_SocketConnection> connections) { 1486 _openSockets.forEach((String key, Queue<_SocketConnection> connections) {
1416 while (!connections.isEmpty()) { 1487 while (!connections.isEmpty()) {
1417 _SocketConnection socketConn = connections.removeFirst(); 1488 _SocketConnection socketConn = connections.removeFirst();
1418 socketConn._socket.close(); 1489 socketConn._socket.close();
1419 } 1490 }
1420 }); 1491 });
1421 _activeSockets.forEach((_SocketConnection socketConn) { 1492 _activeSockets.forEach((_SocketConnection socketConn) {
1422 socketConn._socket.close(); 1493 socketConn._socket.close();
1423 }); 1494 });
1424 if (_evictionTimer != null) { 1495 if (_evictionTimer != null) {
1425 _evictionTimer.cancel(); 1496 _evictionTimer.cancel();
1426 } 1497 }
1427 _shutdown = true; 1498 _shutdown = true;
1428 } 1499 }
1429 1500
1430 String _connectionKey(String host, int port) { 1501 String _connectionKey(String host, int port) {
1431 return "$host:$port"; 1502 return "$host:$port";
1432 } 1503 }
1433 1504
1434 HttpClientConnection _prepareHttpClientConnection( 1505 HttpClientConnection _prepareHttpClientConnection(
1435 String host, int port, String method, String path) { 1506 String host,
1507 int port,
1508 String method,
1509 String path,
1510 [_HttpClientConnection connection]) {
1436 1511
1437 void _connectionOpened(_SocketConnection socketConn, 1512 void _connectionOpened(_SocketConnection socketConn,
1438 _HttpClientConnection connection) { 1513 _HttpClientConnection connection) {
1439 connection._connectionEstablished(socketConn); 1514 connection._connectionEstablished(socketConn);
1440 HttpClientRequest request = connection.open(method, path); 1515 HttpClientRequest request = connection.open(method, path);
1441 request.headers.host = host; 1516 request.headers.host = host;
1442 request.headers.port = port; 1517 request.headers.port = port;
1443 if (connection._onRequest != null) { 1518 if (connection._onRequest != null) {
1444 connection._onRequest(request); 1519 connection._onRequest(request);
1445 } else { 1520 } else {
1446 request.outputStream.close(); 1521 request.outputStream.close();
1447 } 1522 }
1448 } 1523 }
1449 1524
1450 _HttpClientConnection connection = new _HttpClientConnection(this); 1525 // Create a new connection if we are not re-using an existing one.
1526 if (connection == null) {
1527 connection = new _HttpClientConnection(this);
1528 }
1451 1529
1452 // If there are active connections for this key get the first one 1530 // If there are active connections for this key get the first one
1453 // otherwise create a new one. 1531 // otherwise create a new one.
1454 Queue socketConnections = _openSockets[_connectionKey(host, port)]; 1532 Queue socketConnections = _openSockets[_connectionKey(host, port)];
1455 if (socketConnections == null || socketConnections.isEmpty()) { 1533 if (socketConnections == null || socketConnections.isEmpty()) {
1456 Socket socket = new Socket(host, port); 1534 Socket socket = new Socket(host, port);
1457 // Until the connection is established handle connection errors 1535 // Until the connection is established handle connection errors
1458 // here as the HttpClientConnection object is not yet associated 1536 // here as the HttpClientConnection object is not yet associated
1459 // with the socket. 1537 // with the socket.
1460 socket.onError = (e) { 1538 socket.onError = (e) {
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
1543 } 1621 }
1544 1622
1545 1623
1546 class _DetachedSocket implements DetachedSocket { 1624 class _DetachedSocket implements DetachedSocket {
1547 _DetachedSocket(this._socket, this._unparsedData); 1625 _DetachedSocket(this._socket, this._unparsedData);
1548 Socket get socket() => _socket; 1626 Socket get socket() => _socket;
1549 List<int> get unparsedData() => _unparsedData; 1627 List<int> get unparsedData() => _unparsedData;
1550 Socket _socket; 1628 Socket _socket;
1551 List<int> _unparsedData; 1629 List<int> _unparsedData;
1552 } 1630 }
1631
1632
1633 class _RedirectInfo implements RedirectInfo {
1634 const _RedirectInfo(int this.statusCode,
1635 String this.method,
1636 Uri this.location);
1637 final int statusCode;
1638 final String method;
1639 final Uri location;
1640 }
OLDNEW
« no previous file with comments | « runtime/bin/http.dart ('k') | tests/standalone/io/http_redirect_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698