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