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

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

Issue 9589001: Handle closing of HTTP client and server (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 9 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 | « no previous file | runtime/bin/socket.dart » ('j') | tests/standalone/src/io/HttpShutdownTest.dart » ('J')
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 // Global constants. 5 // Global constants.
6 class _Const { 6 class _Const {
7 // Bytes for "HTTP/1.0". 7 // Bytes for "HTTP/1.0".
8 static final HTTP10 = const [72, 84, 84, 80, 47, 49, 46, 48]; 8 static final HTTP10 = const [72, 84, 84, 80, 47, 49, 46, 48];
9 // Bytes for "HTTP/1.1". 9 // Bytes for "HTTP/1.1".
10 static final HTTP11 = const [72, 84, 84, 80, 47, 49, 46, 49]; 10 static final HTTP11 = const [72, 84, 84, 80, 47, 49, 46, 49];
(...skipping 670 matching lines...) Expand 10 before | Expand all | Expand 10 after
681 _keepAlive = keepAlive; 681 _keepAlive = keepAlive;
682 } 682 }
683 683
684 int get statusCode() => _statusCode; 684 int get statusCode() => _statusCode;
685 void set statusCode(int statusCode) { 685 void set statusCode(int statusCode) {
686 if (_outputStream != null) return new HttpException("Header already sent"); 686 if (_outputStream != null) return new HttpException("Header already sent");
687 _statusCode = statusCode; 687 _statusCode = statusCode;
688 } 688 }
689 689
690 String get reasonPhrase() => _findReasonPhrase(_statusCode); 690 String get reasonPhrase() => _findReasonPhrase(_statusCode);
691 void set reasonPhrase(String reasonPhrase) => _reasonPhrase = reasonPhrase; 691 void set reasonPhrase(String reasonPhrase) {
692 if (_outputStream != null) return new HttpException("Header already sent");
693 _reasonPhrase = reasonPhrase;
694 }
692 695
693 // Set a header on the response. NOTE: If the same header is set 696 // Set a header on the response. NOTE: If the same header is set
694 // more than once only the last one will be part of the response. 697 // more than once only the last one will be part of the response.
695 void setHeader(String name, String value) { 698 void setHeader(String name, String value) {
696 if (_outputStream != null) return new HttpException("Header already sent"); 699 if (_outputStream != null) return new HttpException("Header already sent");
697 _setHeader(name, value); 700 _setHeader(name, value);
698 } 701 }
699 702
700 OutputStream get outputStream() { 703 OutputStream get outputStream() {
701 if (_state == DONE) throw new HttpException("Response closed"); 704 if (_state == DONE) throw new HttpException("Response closed");
(...skipping 16 matching lines...) Expand all
718 // Delegate functions for the HttpOutputStream implementation. 721 // Delegate functions for the HttpOutputStream implementation.
719 bool _streamWrite(List<int> buffer, bool copyBuffer) { 722 bool _streamWrite(List<int> buffer, bool copyBuffer) {
720 return _write(buffer, copyBuffer); 723 return _write(buffer, copyBuffer);
721 } 724 }
722 725
723 bool _streamWriteFrom(List<int> buffer, int offset, int len) { 726 bool _streamWriteFrom(List<int> buffer, int offset, int len) {
724 return _writeList(buffer, offset, len); 727 return _writeList(buffer, offset, len);
725 } 728 }
726 729
727 void _streamClose() { 730 void _streamClose() {
731 _httpConnection._phase = _HttpConnectionBase.PHASE_IDLE;
728 _state = DONE; 732 _state = DONE;
729 // Stop tracking no pending write events. 733 // Stop tracking no pending write events.
730 _httpConnection.outputStream.onNoPendingWrites = null; 734 _httpConnection.outputStream.onNoPendingWrites = null;
731 // Ensure that any trailing data is written. 735 // Ensure that any trailing data is written.
732 _writeDone(); 736 _writeDone();
733 // If the connection is closing then close the output stream to 737 // If the connection is closing then close the output stream to
734 // fully close the socket. 738 // fully close the socket.
735 if (_httpConnection._closing) { 739 if (_httpConnection._closing) {
736 _httpConnection.outputStream.close(); 740 _httpConnection.outputStream.close();
737 } 741 }
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
911 915
912 void set onError(void callback()) { 916 void set onError(void callback()) {
913 _requestOrResponse._streamSetErrorHandler(callback); 917 _requestOrResponse._streamSetErrorHandler(callback);
914 } 918 }
915 919
916 _HttpRequestResponseBase _requestOrResponse; 920 _HttpRequestResponseBase _requestOrResponse;
917 } 921 }
918 922
919 923
920 class _HttpConnectionBase { 924 class _HttpConnectionBase {
921 _HttpConnectionBase() : _sendBuffers = new Queue(), 925 static final int PHASE_IDLE = 0;
926 static final int PHASE_REQUEST = 1;
927 static final int PHASE_RESPONSE = 2;
928
929 _HttpConnectionBase() : _phase = PHASE_IDLE,
930 _sendBuffers = new Queue(),
922 _httpParser = new HttpParser(); 931 _httpParser = new HttpParser();
923 932
924 void _connectionEstablished(Socket socket) { 933 void _connectionEstablished(Socket socket) {
925 _socket = socket; 934 _socket = socket;
926 // Register handler for socket events. 935 // Register handler for socket events.
927 _socket.onData = _onData; 936 _socket.onData = _onData;
928 _socket.onClosed = _onClosed; 937 _socket.onClosed = _onClosed;
929 _socket.onError = _onError; 938 _socket.onError = _onError;
930 } 939 }
931 940
(...skipping 11 matching lines...) Expand all
943 int bytesRead = _socket.readList(buffer, 0, available); 952 int bytesRead = _socket.readList(buffer, 0, available);
944 if (bytesRead > 0) { 953 if (bytesRead > 0) {
945 int parsed = _httpParser.writeList(buffer, 0, bytesRead); 954 int parsed = _httpParser.writeList(buffer, 0, bytesRead);
946 if (parsed != bytesRead) { 955 if (parsed != bytesRead) {
947 // TODO(sgjesse): Error handling. 956 // TODO(sgjesse): Error handling.
948 _socket.close(); 957 _socket.close();
949 } 958 }
950 } 959 }
951 } 960 }
952 961
953 void _onClosed() { 962 void _onClosed() {
Anders Johnsen 2012/03/02 13:48:10 This is a lot easier to read. Thank you!
954 // Client closed socket for writing. Socket should still be open 963 if (_phase != PHASE_IDLE) {
955 // for writing the response. 964 // Client closed socket for writing. Socket should still be open
956 _closing = true; 965 // for writing the response.
966 _closing = true;
967 } else {
968 // The connection is currently not used by any request just close it.
969 _socket.close();
970 }
957 if (_onDisconnectCallback != null) _onDisconnectCallback(); 971 if (_onDisconnectCallback != null) _onDisconnectCallback();
958 } 972 }
959 973
960 void _onError() { 974 void _onError() {
961 // If an error occours, treat the socket as closed. 975 // If an error occours, treat the socket as closed.
962 _onClosed(); 976 _onClosed();
963 if (_onErrorCallback != null) { 977 if (_onErrorCallback != null) {
964 _onErrorCallback("Connection closed while sending data to client."); 978 _onErrorCallback("Connection closed while sending data to client.");
965 } 979 }
966 } 980 }
967 981
968 void set onDisconnect(void callback()) { 982 void set onDisconnect(void callback()) {
969 _onDisconnectCallback = callback; 983 _onDisconnectCallback = callback;
970 } 984 }
971 985
972 void set onError(void callback(String errorMessage)) { 986 void set onError(void callback(String errorMessage)) {
973 _onErrorCallback = callback; 987 _onErrorCallback = callback;
974 } 988 }
975 989
990 int _phase;
976 Socket _socket; 991 Socket _socket;
977 bool _closing = false; // Is the socket closed by the client? 992 bool _closing = false; // Is the socket closed by the client?
978 HttpParser _httpParser; 993 HttpParser _httpParser;
979 994
980 Queue _sendBuffers; 995 Queue _sendBuffers;
981 996
982 Function _onDisconnectCallback; 997 Function _onDisconnectCallback;
983 Function _onErrorCallback; 998 Function _onErrorCallback;
984 } 999 }
985 1000
986 1001
987 // HTTP server connection over a socket. 1002 // HTTP server connection over a socket.
988 class _HttpConnection extends _HttpConnectionBase { 1003 class _HttpConnection extends _HttpConnectionBase {
989 _HttpConnection() { 1004 _HttpConnection() {
990 // Register HTTP parser callbacks. 1005 // Register HTTP parser callbacks.
991 _httpParser.requestStart = 1006 _httpParser.requestStart =
992 (method, uri) => _onRequestStart(method, uri); 1007 (method, uri) => _onRequestStart(method, uri);
993 _httpParser.responseStart = 1008 _httpParser.responseStart =
994 (statusCode, reasonPhrase) => 1009 (statusCode, reasonPhrase) =>
995 _onResponseStart(statusCode, reasonPhrase); 1010 _onResponseStart(statusCode, reasonPhrase);
996 _httpParser.headerReceived = 1011 _httpParser.headerReceived =
997 (name, value) => _onHeaderReceived(name, value); 1012 (name, value) => _onHeaderReceived(name, value);
998 _httpParser.headersComplete = () => _onHeadersComplete(); 1013 _httpParser.headersComplete = () => _onHeadersComplete();
999 _httpParser.dataReceived = (data) => _onDataReceived(data); 1014 _httpParser.dataReceived = (data) => _onDataReceived(data);
1000 _httpParser.dataEnd = () => _onDataEnd(); 1015 _httpParser.dataEnd = () => _onDataEnd();
1001 } 1016 }
1002 1017
1003 void _onRequestStart(String method, String uri) { 1018 void _onRequestStart(String method, String uri) {
1004 // Create new request and response objects for this request. 1019 // Create new request and response objects for this request.
1020 _phase = PHASE_REQUEST;
1005 _request = new _HttpRequest(this); 1021 _request = new _HttpRequest(this);
1006 _response = new _HttpResponse(this); 1022 _response = new _HttpResponse(this);
1007 _request._onRequestStart(method, uri); 1023 _request._onRequestStart(method, uri);
1008 } 1024 }
1009 1025
1010 void _onResponseStart(int statusCode, String reasonPhrase) { 1026 void _onResponseStart(int statusCode, String reasonPhrase) {
1011 // TODO(sgjesse): Error handling. 1027 // TODO(sgjesse): Error handling.
1012 } 1028 }
1013 1029
1014 void _onHeaderReceived(String name, String value) { 1030 void _onHeaderReceived(String name, String value) {
1015 _request._onHeaderReceived(name, value); 1031 _request._onHeaderReceived(name, value);
1016 } 1032 }
1017 1033
1018 void _onHeadersComplete() { 1034 void _onHeadersComplete() {
1019 _request._onHeadersComplete(); 1035 _request._onHeadersComplete();
1020 _response.keepAlive = _httpParser.keepAlive; 1036 _response.keepAlive = _httpParser.keepAlive;
1021 if (requestReceived != null) { 1037 if (requestReceived != null) {
1022 requestReceived(_request, _response); 1038 requestReceived(_request, _response);
1023 } 1039 }
1024 } 1040 }
1025 1041
1026 void _onDataReceived(List<int> data) { 1042 void _onDataReceived(List<int> data) {
1027 _request._onDataReceived(data); 1043 _request._onDataReceived(data);
1028 } 1044 }
1029 1045
1030 void _onDataEnd() { 1046 void _onDataEnd() {
1047 // Phase might already have gone to PHASE_IDLE if the response is
1048 // sent without waiting for request body.
1049 if (_phase == PHASE_REQUEST) {
1050 _phase = PHASE_RESPONSE;
1051 }
1031 _request._onDataEnd(); 1052 _request._onDataEnd();
1032 } 1053 }
1033 1054
1034 HttpRequest _request; 1055 HttpRequest _request;
1035 HttpResponse _response; 1056 HttpResponse _response;
1036 1057
1037 // Callbacks. 1058 // Callbacks.
1038 var requestReceived; 1059 var requestReceived;
1039 } 1060 }
1040 1061
(...skipping 17 matching lines...) Expand all
1058 } 1079 }
1059 } 1080 }
1060 } 1081 }
1061 connection.onDisconnect = onDisconnect; 1082 connection.onDisconnect = onDisconnect;
1062 void onError(String errorMessage) { 1083 void onError(String errorMessage) {
1063 if (_onError != null) _onError(errorMessage); 1084 if (_onError != null) _onError(errorMessage);
1064 } 1085 }
1065 connection.onError = onError; 1086 connection.onError = onError;
1066 } 1087 }
1067 1088
1068 // TODO(ajohnsen): Use Set once Socket is Hashable. 1089 // TODO(ajohnsen): Use Set once Socket is Hashable.
Anders Johnsen 2012/03/02 13:48:10 Now that Socket is hashable, could you use a Set h
Søren Gjesse 2012/03/05 07:03:41 Done.
1069 _connections = new List<_HttpConnection>(); 1090 _connections = new List<_HttpConnection>();
1070 _server = new ServerSocket(host, port, backlog); 1091 _server = new ServerSocket(host, port, backlog);
1071 _server.onConnection = onConnection; 1092 _server.onConnection = onConnection;
1072 } 1093 }
1073 1094
1074 void close() => _server.close(); 1095 void close() => _server.close();
1075 int get port() => _server.port; 1096 int get port() => _server.port;
1076 1097
1077 void set onError(void handler(String errorMessage)) { 1098 void set onError(void handler(String errorMessage)) {
1078 _onError = handler; 1099 _onError = handler;
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after
1365 1386
1366 void _markReturned() { 1387 void _markReturned() {
1367 _socket.onData = null; 1388 _socket.onData = null;
1368 _socket.onClosed = null; 1389 _socket.onClosed = null;
1369 _socket.onError = null; 1390 _socket.onError = null;
1370 _returnTime = new Date.now(); 1391 _returnTime = new Date.now();
1371 } 1392 }
1372 1393
1373 Duration _idleTime(Date now) => now.difference(_returnTime); 1394 Duration _idleTime(Date now) => now.difference(_returnTime);
1374 1395
1396 int hashCode() => _socket.hashCode();
1397
1375 String _host; 1398 String _host;
1376 int _port; 1399 int _port;
1377 Socket _socket; 1400 Socket _socket;
1378 Date _returnTime; 1401 Date _returnTime;
1379 } 1402 }
1380 1403
1381 1404
1382 class _HttpClient implements HttpClient { 1405 class _HttpClient implements HttpClient {
1383 static final int DEFAULT_EVICTION_TIMEOUT = 60000; 1406 static final int DEFAULT_EVICTION_TIMEOUT = 60000;
1384 1407
1385 _HttpClient() : _openSockets = new Map(), _shutdown = false; 1408 _HttpClient() : _openSockets = new Map(),
1409 _activeSockets = new Set(),
1410 _shutdown = false;
1386 1411
1387 HttpClientConnection open( 1412 HttpClientConnection open(
1388 String method, String host, int port, String path) { 1413 String method, String host, int port, String path) {
1389 if (_shutdown) throw new HttpException("HttpClient shutdown"); 1414 if (_shutdown) throw new HttpException("HttpClient shutdown");
1390 return _prepareHttpClientConnection(host, port, method, path); 1415 return _prepareHttpClientConnection(host, port, method, path);
1391 } 1416 }
1392 1417
1393 HttpClientConnection get(String host, int port, String path) { 1418 HttpClientConnection get(String host, int port, String path) {
1394 return open("GET", host, port, path); 1419 return open("GET", host, port, path);
1395 } 1420 }
1396 1421
1397 HttpClientConnection post(String host, int port, String path) { 1422 HttpClientConnection post(String host, int port, String path) {
1398 return open("POST", host, port, path); 1423 return open("POST", host, port, path);
1399 } 1424 }
1400 1425
1401 void shutdown() { 1426 void shutdown() {
1402 _openSockets.forEach( 1427 _openSockets.forEach((String key, Queue<_SocketConnection> connections) {
1403 void _(String key, Queue<_SocketConnection> connections) { 1428 while (!connections.isEmpty()) {
1404 while (!connections.isEmpty()) { 1429 _SocketConnection socketConn = connections.removeFirst();
1405 var socketConn = connections.removeFirst(); 1430 socketConn._socket.close();
1406 socketConn._socket.close(); 1431 }
1407 } 1432 });
1408 }); 1433 _activeSockets.forEach((_SocketConnection socketConn) {
1434 socketConn._socket.close();
1435 });
1409 if (_evictionTimer != null) { 1436 if (_evictionTimer != null) {
1410 _evictionTimer.cancel(); 1437 _evictionTimer.cancel();
1411 } 1438 }
1412 _shutdown = true; 1439 _shutdown = true;
1413 } 1440 }
1414 1441
1415 String _connectionKey(String host, int port) { 1442 String _connectionKey(String host, int port) {
1416 return "$host:$port"; 1443 return "$host:$port";
1417 } 1444 }
1418 1445
(...skipping 15 matching lines...) Expand all
1434 1461
1435 // If there are active connections for this key get the first one 1462 // If there are active connections for this key get the first one
1436 // otherwise create a new one. 1463 // otherwise create a new one.
1437 Queue socketConnections = _openSockets[_connectionKey(host, port)]; 1464 Queue socketConnections = _openSockets[_connectionKey(host, port)];
1438 if (socketConnections == null || socketConnections.isEmpty()) { 1465 if (socketConnections == null || socketConnections.isEmpty()) {
1439 Socket socket = new Socket(host, port); 1466 Socket socket = new Socket(host, port);
1440 socket.onConnect = () { 1467 socket.onConnect = () {
1441 socket.onError = null; 1468 socket.onError = null;
1442 _SocketConnection socketConn = 1469 _SocketConnection socketConn =
1443 new _SocketConnection(host, port, socket); 1470 new _SocketConnection(host, port, socket);
1471 _activeSockets.add(socketConn);
1444 _connectionOpened(socketConn, connection); 1472 _connectionOpened(socketConn, connection);
1445 }; 1473 };
1446 socket.onError = () { 1474 socket.onError = () {
1447 if (_onError !== null) { 1475 if (_onError !== null) {
1448 _onError(HttpStatus.NETWORK_CONNECT_TIMEOUT_ERROR); 1476 _onError(HttpStatus.NETWORK_CONNECT_TIMEOUT_ERROR);
1449 } 1477 }
1450 }; 1478 };
1451 } else { 1479 } else {
1452 _SocketConnection socketConn = socketConnections.removeFirst(); 1480 _SocketConnection socketConn = socketConnections.removeFirst();
1481 _activeSockets.add(socketConn);
1453 new Timer((ignored) => _connectionOpened(socketConn, connection), 0); 1482 new Timer((ignored) => _connectionOpened(socketConn, connection), 0);
1454 1483
1455 // Get rid of eviction timer if there are no more active connections. 1484 // Get rid of eviction timer if there are no more active connections.
1456 if (socketConnections.isEmpty()) { 1485 if (socketConnections.isEmpty()) {
1457 _evictionTimer.cancel(); 1486 _evictionTimer.cancel();
1458 _evictionTimer = null; 1487 _evictionTimer = null;
1459 } 1488 }
1460 } 1489 }
1461 1490
1462 return connection; 1491 return connection;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
1494 } else { 1523 } else {
1495 break; 1524 break;
1496 } 1525 }
1497 } 1526 }
1498 }); 1527 });
1499 } 1528 }
1500 _evictionTimer = new Timer.repeating(_handleEviction, 10000); 1529 _evictionTimer = new Timer.repeating(_handleEviction, 10000);
1501 } 1530 }
1502 1531
1503 // Return connection. 1532 // Return connection.
1533 _activeSockets.remove(socketConn);
1504 sockets.addFirst(socketConn); 1534 sockets.addFirst(socketConn);
1505 socketConn._markReturned(); 1535 socketConn._markReturned();
1506 } 1536 }
1507 1537
1508 void set onError(void callback(int status)) { 1538 void set onError(void callback(int status)) {
1509 _onError = callback; 1539 _onError = callback;
1510 } 1540 }
1511 1541
1512 Function _onOpen; 1542 Function _onOpen;
1513 Function _onError; 1543 Function _onError;
1514 Map<String, Queue<_SocketConnection>> _openSockets; 1544 Map<String, Queue<_SocketConnection>> _openSockets;
1545 Set<_SocketConnection> _activeSockets;
1515 Timer _evictionTimer; 1546 Timer _evictionTimer;
1516 bool _shutdown; // Has this HTTP client been shutdown? 1547 bool _shutdown; // Has this HTTP client been shutdown?
1517 } 1548 }
1518 1549
1519 1550
1520 class HttpUtil { 1551 class HttpUtil {
1521 static String decodeUrlEncodedString(String urlEncoded) { 1552 static String decodeUrlEncodedString(String urlEncoded) {
1522 void invalidEscape() { 1553 void invalidEscape() {
1523 // TODO(sgjesse): Handle the error. 1554 // TODO(sgjesse): Handle the error.
1524 } 1555 }
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
1568 } else { 1599 } else {
1569 value = queryString.substring(currentPosition, position); 1600 value = queryString.substring(currentPosition, position);
1570 currentPosition = position + 1; 1601 currentPosition = position + 1;
1571 } 1602 }
1572 result[HttpUtil.decodeUrlEncodedString(name)] = 1603 result[HttpUtil.decodeUrlEncodedString(name)] =
1573 HttpUtil.decodeUrlEncodedString(value); 1604 HttpUtil.decodeUrlEncodedString(value);
1574 } 1605 }
1575 return result; 1606 return result;
1576 } 1607 }
1577 } 1608 }
OLDNEW
« no previous file with comments | « no previous file | runtime/bin/socket.dart » ('j') | tests/standalone/src/io/HttpShutdownTest.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698