OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } | |
OLD | NEW |