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

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: 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
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;
Anders Johnsen 2012/05/08 12:58:16 HttpStatus.MOVED_TEMPORARILY is missing here.
Søren Gjesse 2012/05/08 13:32:16 HttpStatus.MOVED_TEMPORARILY is the same status co
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 for redirect loop
1185 if (_connection._redirects != null) {
1186 Uri redirectUrl =
1187 new Uri.fromString(headers.value(HttpHeaders.LOCATION));
1188 for (int i = 0; i < _connection._redirects.length; i++) {
1189 if (_connection._redirects[i].location.toString() ==
1190 redirectUrl.toString()) {
1191 _connection._onError(new RedirectLoop(_connection._redirects));
Anders Johnsen 2012/05/08 12:58:16 Is it a problem we don't drain here?
Søren Gjesse 2012/05/08 13:32:16 No it shouldn't be. All error situations will clos
1192 return;
1193 }
1194 }
1195 }
1196 // Drain body and redirect.
1197 inputStream.onData = () => response.inputStream.read();
Anders Johnsen 2012/05/08 12:58:16 remove '() => ' and '()', and below.
Søren Gjesse 2012/05/08 13:32:16 Done.
1198 inputStream.onClosed = () => _connection.redirect();
1199 } else {
1200 _connection._onError(new RedirectLimitExceeded(_connection._redirects));
1201 }
1202 } else if (_connection._onResponse != null) {
1175 _connection._onResponse(this); 1203 _connection._onResponse(this);
1176 } 1204 }
1177 } 1205 }
1178 1206
1179 void _onDataReceived(List<int> data) { 1207 void _onDataReceived(List<int> data) {
1180 _buffer.add(data); 1208 _buffer.add(data);
1181 if (_inputStream != null) _inputStream._dataReceived(); 1209 if (_inputStream != null) _inputStream._dataReceived();
1182 } 1210 }
1183 1211
1184 void _onDataEnd() { 1212 void _onDataEnd() {
1213 _connection._responseDone();
1185 if (_inputStream != null) _inputStream._closeReceived(); 1214 if (_inputStream != null) _inputStream._closeReceived();
1186 _connection._responseDone();
1187 } 1215 }
1188 1216
1189 // Delegate functions for the HttpInputStream implementation. 1217 // Delegate functions for the HttpInputStream implementation.
1190 int _streamAvailable() { 1218 int _streamAvailable() {
1191 return _buffer.length; 1219 return _buffer.length;
1192 } 1220 }
1193 1221
1194 List<int> _streamRead(int bytesToRead) { 1222 List<int> _streamRead(int bytesToRead) {
1195 return _buffer.readBytes(bytesToRead); 1223 return _buffer.readBytes(bytesToRead);
1196 } 1224 }
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
1315 } 1343 }
1316 1344
1317 void set onResponse(void handler(HttpClientResponse response)) { 1345 void set onResponse(void handler(HttpClientResponse response)) {
1318 _onResponse = handler; 1346 _onResponse = handler;
1319 } 1347 }
1320 1348
1321 void set onError(void callback(e)) { 1349 void set onError(void callback(e)) {
1322 _onErrorCallback = callback; 1350 _onErrorCallback = callback;
1323 } 1351 }
1324 1352
1353 void redirect([String method, Uri url]) {
1354 if (_socketConn != null) {
1355 throw new HttpException("Cannot redirect with body data pending");
1356 }
1357 if (method == null) method = _method;
1358 if (url == null) {
1359 url = new Uri.fromString(_response.headers.value(HttpHeaders.LOCATION));
1360 }
1361 if (_redirects == null) {
1362 _redirects = new List<_RedirectInfo>();
1363 }
1364 _redirects.add(new _RedirectInfo(_response.statusCode, method, url));
1365 _request = null;
1366 _response = null;
1367 // Open redirect URL using the same connection instance.
1368 _client._openUrl(method, url, this);
1369 }
1370
1371 List<RedirectInfo> get redirects() => _redirects;
1372
1325 Function _onRequest; 1373 Function _onRequest;
1326 Function _onResponse; 1374 Function _onResponse;
1327 Function _onErrorCallback; 1375 Function _onErrorCallback;
1328 1376
1329 _HttpClient _client; 1377 _HttpClient _client;
1330 _SocketConnection _socketConn; 1378 _SocketConnection _socketConn;
1331 HttpClientRequest _request; 1379 HttpClientRequest _request;
1332 HttpClientResponse _response; 1380 HttpClientResponse _response;
1333 String _method; 1381 String _method;
1334 1382
1383 // Redirect handling
1384 bool followRedirects = false;
1385 int maxRedirects = 5;
1386 List<_RedirectInfo> _redirects;
1387
1335 // Callbacks. 1388 // Callbacks.
1336 var requestReceived; 1389 var requestReceived;
1337 } 1390 }
1338 1391
1339 1392
1340 // Class for holding keep-alive sockets in the cache for the HTTP 1393 // Class for holding keep-alive sockets in the cache for the HTTP
1341 // client together with the connection information. 1394 // client together with the connection information.
1342 class _SocketConnection { 1395 class _SocketConnection {
1343 _SocketConnection(String this._host, 1396 _SocketConnection(String this._host,
1344 int this._port, 1397 int this._port,
(...skipping 19 matching lines...) Expand all
1364 1417
1365 class _HttpClient implements HttpClient { 1418 class _HttpClient implements HttpClient {
1366 static final int DEFAULT_EVICTION_TIMEOUT = 60000; 1419 static final int DEFAULT_EVICTION_TIMEOUT = 60000;
1367 1420
1368 _HttpClient() : _openSockets = new Map(), 1421 _HttpClient() : _openSockets = new Map(),
1369 _activeSockets = new Set(), 1422 _activeSockets = new Set(),
1370 _shutdown = false; 1423 _shutdown = false;
1371 1424
1372 HttpClientConnection open( 1425 HttpClientConnection open(
1373 String method, String host, int port, String path) { 1426 String method, String host, int port, String path) {
1427 _open(method, host, port, path);
1428 }
1429
1430 HttpClientConnection _open(String method,
1431 String host,
1432 int port,
1433 String path,
1434 [_HttpClientConnection connection]) {
1374 if (_shutdown) throw new HttpException("HttpClient shutdown"); 1435 if (_shutdown) throw new HttpException("HttpClient shutdown");
1375 if (method == null || host == null || port == null || path == null) { 1436 if (method == null || host == null || port == null || path == null) {
1376 throw new IllegalArgumentException(null); 1437 throw new IllegalArgumentException(null);
1377 } 1438 }
1378 return _prepareHttpClientConnection(host, port, method, path); 1439 return _prepareHttpClientConnection(host, port, method, path, connection);
1379 } 1440 }
1380 1441
1381 HttpClientConnection openUrl(String method, Uri url) { 1442 HttpClientConnection openUrl(String method, Uri url) {
1443 _openUrl(method, url);
1444 }
1445
1446 HttpClientConnection _openUrl(String method,
1447 Uri url,
1448 [_HttpClientConnection connection]) {
1382 if (url.scheme != "http") { 1449 if (url.scheme != "http") {
1383 throw new HttpException("Unsupported URL scheme ${url.scheme}"); 1450 throw new HttpException("Unsupported URL scheme ${url.scheme}");
1384 } 1451 }
1385 if (url.userInfo != "") { 1452 if (url.userInfo != "") {
1386 throw new HttpException("Unsupported user info ${url.userInfo}"); 1453 throw new HttpException("Unsupported user info ${url.userInfo}");
1387 } 1454 }
1388 int port = url.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : url.port; 1455 int port = url.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : url.port;
1389 String path; 1456 String path;
1390 if (url.query != "") { 1457 if (url.query != "") {
1391 if (url.fragment != "") { 1458 if (url.fragment != "") {
1392 path = "${url.path}?${url.query}#${url.fragment}"; 1459 path = "${url.path}?${url.query}#${url.fragment}";
1393 } else { 1460 } else {
1394 path = "${url.path}?${url.query}"; 1461 path = "${url.path}?${url.query}";
1395 } 1462 }
1396 } else { 1463 } else {
1397 path = url.path; 1464 path = url.path;
1398 } 1465 }
1399 return open(method, url.domain, port, path); 1466 return _open(method, url.domain, port, path, connection);
1400 } 1467 }
1401 1468
1402 HttpClientConnection get(String host, int port, String path) { 1469 HttpClientConnection get(String host, int port, String path) {
1403 return open("GET", host, port, path); 1470 return _open("GET", host, port, path);
1404 } 1471 }
1405 1472
1406 HttpClientConnection getUrl(Uri url) => openUrl("GET", url); 1473 HttpClientConnection getUrl(Uri url) => _openUrl("GET", url);
1407 1474
1408 HttpClientConnection post(String host, int port, String path) { 1475 HttpClientConnection post(String host, int port, String path) {
1409 return open("POST", host, port, path); 1476 return _open("POST", host, port, path);
1410 } 1477 }
1411 1478
1412 HttpClientConnection postUrl(Uri url) => openUrl("POST", url); 1479 HttpClientConnection postUrl(Uri url) => _openUrl("POST", url);
1413 1480
1414 void shutdown() { 1481 void shutdown() {
1415 _openSockets.forEach((String key, Queue<_SocketConnection> connections) { 1482 _openSockets.forEach((String key, Queue<_SocketConnection> connections) {
1416 while (!connections.isEmpty()) { 1483 while (!connections.isEmpty()) {
1417 _SocketConnection socketConn = connections.removeFirst(); 1484 _SocketConnection socketConn = connections.removeFirst();
1418 socketConn._socket.close(); 1485 socketConn._socket.close();
1419 } 1486 }
1420 }); 1487 });
1421 _activeSockets.forEach((_SocketConnection socketConn) { 1488 _activeSockets.forEach((_SocketConnection socketConn) {
1422 socketConn._socket.close(); 1489 socketConn._socket.close();
1423 }); 1490 });
1424 if (_evictionTimer != null) { 1491 if (_evictionTimer != null) {
1425 _evictionTimer.cancel(); 1492 _evictionTimer.cancel();
1426 } 1493 }
1427 _shutdown = true; 1494 _shutdown = true;
1428 } 1495 }
1429 1496
1430 String _connectionKey(String host, int port) { 1497 String _connectionKey(String host, int port) {
1431 return "$host:$port"; 1498 return "$host:$port";
1432 } 1499 }
1433 1500
1434 HttpClientConnection _prepareHttpClientConnection( 1501 HttpClientConnection _prepareHttpClientConnection(
1435 String host, int port, String method, String path) { 1502 String host,
1503 int port,
1504 String method,
1505 String path,
1506 [_HttpClientConnection connection]) {
1436 1507
1437 void _connectionOpened(_SocketConnection socketConn, 1508 void _connectionOpened(_SocketConnection socketConn,
1438 _HttpClientConnection connection) { 1509 _HttpClientConnection connection) {
1439 connection._connectionEstablished(socketConn); 1510 connection._connectionEstablished(socketConn);
1440 HttpClientRequest request = connection.open(method, path); 1511 HttpClientRequest request = connection.open(method, path);
1441 request.headers.host = host; 1512 request.headers.host = host;
1442 request.headers.port = port; 1513 request.headers.port = port;
1443 if (connection._onRequest != null) { 1514 if (connection._onRequest != null) {
1444 connection._onRequest(request); 1515 connection._onRequest(request);
1445 } else { 1516 } else {
1446 request.outputStream.close(); 1517 request.outputStream.close();
1447 } 1518 }
1448 } 1519 }
1449 1520
1450 _HttpClientConnection connection = new _HttpClientConnection(this); 1521 // Create a new connection if we are not re-using an existing one.
1522 if (connection == null) {
1523 connection = new _HttpClientConnection(this);
1524 }
1451 1525
1452 // If there are active connections for this key get the first one 1526 // If there are active connections for this key get the first one
1453 // otherwise create a new one. 1527 // otherwise create a new one.
1454 Queue socketConnections = _openSockets[_connectionKey(host, port)]; 1528 Queue socketConnections = _openSockets[_connectionKey(host, port)];
1455 if (socketConnections == null || socketConnections.isEmpty()) { 1529 if (socketConnections == null || socketConnections.isEmpty()) {
1456 Socket socket = new Socket(host, port); 1530 Socket socket = new Socket(host, port);
1457 // Until the connection is established handle connection errors 1531 // Until the connection is established handle connection errors
1458 // here as the HttpClientConnection object is not yet associated 1532 // here as the HttpClientConnection object is not yet associated
1459 // with the socket. 1533 // with the socket.
1460 socket.onError = (e) { 1534 socket.onError = (e) {
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
1543 } 1617 }
1544 1618
1545 1619
1546 class _DetachedSocket implements DetachedSocket { 1620 class _DetachedSocket implements DetachedSocket {
1547 _DetachedSocket(this._socket, this._unparsedData); 1621 _DetachedSocket(this._socket, this._unparsedData);
1548 Socket get socket() => _socket; 1622 Socket get socket() => _socket;
1549 List<int> get unparsedData() => _unparsedData; 1623 List<int> get unparsedData() => _unparsedData;
1550 Socket _socket; 1624 Socket _socket;
1551 List<int> _unparsedData; 1625 List<int> _unparsedData;
1552 } 1626 }
1627
1628
1629 class _RedirectInfo implements RedirectInfo {
1630 const _RedirectInfo(int this.statusCode,
1631 String this.method,
1632 Uri this.location);
1633 final int statusCode;
1634 final String method;
1635 final Uri location;
1636 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698