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

Side by Side Diff: sdk/lib/io/http_impl.dart

Issue 176893003: Only use one timer to handle HttpServer::idleTimeout. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 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 | « sdk/lib/io/http.dart ('k') | no next file » | 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) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 part of dart.io; 5 part of dart.io;
6 6
7 const int _HEADERS_BUFFER_SIZE = 8 * 1024; 7 const int _HEADERS_BUFFER_SIZE = 8 * 1024;
8 8
9 class _HttpIncoming extends Stream<List<int>> { 9 class _HttpIncoming extends Stream<List<int>> {
10 final int _transferLength; 10 final int _transferLength;
(...skipping 1877 matching lines...) Expand 10 before | Expand all | Expand 10 after
1888 static Map<String, String> _platformEnvironmentCache = Platform.environment; 1888 static Map<String, String> _platformEnvironmentCache = Platform.environment;
1889 } 1889 }
1890 1890
1891 1891
1892 class _HttpConnection extends LinkedListEntry<_HttpConnection> { 1892 class _HttpConnection extends LinkedListEntry<_HttpConnection> {
1893 static const _ACTIVE = 0; 1893 static const _ACTIVE = 0;
1894 static const _IDLE = 1; 1894 static const _IDLE = 1;
1895 static const _CLOSING = 2; 1895 static const _CLOSING = 2;
1896 static const _DETACHED = 3; 1896 static const _DETACHED = 3;
1897 1897
1898 int _state = _IDLE;
1899
1900 final Socket _socket; 1898 final Socket _socket;
1901 final _HttpServer _httpServer; 1899 final _HttpServer _httpServer;
1902 final _HttpParser _httpParser; 1900 final _HttpParser _httpParser;
1901 int _state = _IDLE;
1903 StreamSubscription _subscription; 1902 StreamSubscription _subscription;
1904 Timer _idleTimer; 1903 Timer _idleTimer;
1905 final Uint8List _headersBuffer = new Uint8List(_HEADERS_BUFFER_SIZE); 1904 final Uint8List _headersBuffer = new Uint8List(_HEADERS_BUFFER_SIZE);
1906 1905 bool _idleMark = false;
1907 Future _streamFuture; 1906 Future _streamFuture;
1908 1907
1909 _HttpConnection(this._socket, this._httpServer) 1908 _HttpConnection(this._socket, this._httpServer)
1910 : _httpParser = new _HttpParser.requestParser() { 1909 : _httpParser = new _HttpParser.requestParser() {
1911 _startTimeout();
1912 _httpParser.listenToStream(_socket); 1910 _httpParser.listenToStream(_socket);
1913 _subscription = _httpParser.listen( 1911 _subscription = _httpParser.listen(
1914 (incoming) { 1912 (incoming) {
1915 _stopTimeout(); 1913 _httpServer._markActive(this);
1916 // If the incoming was closed, close the connection. 1914 // If the incoming was closed, close the connection.
1917 incoming.dataDone.then((closing) { 1915 incoming.dataDone.then((closing) {
1918 if (closing) destroy(); 1916 if (closing) destroy();
1919 }); 1917 });
1920 // Only handle one incoming request at the time. Keep the 1918 // Only handle one incoming request at the time. Keep the
1921 // stream paused until the request has been send. 1919 // stream paused until the request has been send.
1922 _subscription.pause(); 1920 _subscription.pause();
1923 _state = _ACTIVE; 1921 _state = _ACTIVE;
1924 var outgoing = new _HttpOutgoing(_socket); 1922 var outgoing = new _HttpOutgoing(_socket);
1925 var response = new _HttpResponse(incoming.uri, 1923 var response = new _HttpResponse(incoming.uri,
1926 incoming.headers.protocolVersion, 1924 incoming.headers.protocolVersion,
1927 outgoing, 1925 outgoing,
1928 _httpServer.serverHeader); 1926 _httpServer.serverHeader);
1929 var request = new _HttpRequest(response, incoming, _httpServer, this); 1927 var request = new _HttpRequest(response, incoming, _httpServer, this);
1930 _streamFuture = outgoing.done 1928 _streamFuture = outgoing.done
1931 .then((_) { 1929 .then((_) {
1932 response.deadline = null; 1930 response.deadline = null;
1933 if (_state == _DETACHED) return; 1931 if (_state == _DETACHED) return;
1934 if (response.persistentConnection && 1932 if (response.persistentConnection &&
1935 request.persistentConnection && 1933 request.persistentConnection &&
1936 incoming.fullBodyRead && 1934 incoming.fullBodyRead &&
1937 !_httpParser.upgrade && 1935 !_httpParser.upgrade &&
1938 !_httpServer.closed) { 1936 !_httpServer.closed) {
1939 _state = _IDLE; 1937 _state = _IDLE;
1940 _startTimeout(); 1938 _idleMark = false;
1939 _httpServer._markIdle(this);
1941 // Resume the subscription for incoming requests as the 1940 // Resume the subscription for incoming requests as the
1942 // request is now processed. 1941 // request is now processed.
1943 _subscription.resume(); 1942 _subscription.resume();
1944 } else { 1943 } else {
1945 // Close socket, keep-alive not used or body sent before 1944 // Close socket, keep-alive not used or body sent before
1946 // received data was handled. 1945 // received data was handled.
1947 destroy(); 1946 destroy();
1948 } 1947 }
1949 }, onError: (_) { 1948 }, onError: (_) {
1950 destroy(); 1949 destroy();
1951 }); 1950 });
1952 response._ignoreBody = request.method == "HEAD"; 1951 response._ignoreBody = request.method == "HEAD";
1953 response._httpRequest = request; 1952 response._httpRequest = request;
1954 _httpServer._handleRequest(request); 1953 _httpServer._handleRequest(request);
1955 }, 1954 },
1956 onDone: () { 1955 onDone: () {
1957 destroy(); 1956 destroy();
1958 }, 1957 },
1959 onError: (error) { 1958 onError: (error) {
1960 // Ignore failed requests that was closed before headers was received. 1959 // Ignore failed requests that was closed before headers was received.
1961 destroy(); 1960 destroy();
1962 }); 1961 });
1963 } 1962 }
1964 1963
1965 void _startTimeout() { 1964 void markIdle() {
1966 assert(_state == _IDLE); 1965 _idleMark = true;
1967 _stopTimeout();
1968 if (_httpServer.idleTimeout == null) return;
1969 _idleTimer = new Timer(_httpServer.idleTimeout, () {
1970 destroy();
1971 });
1972 } 1966 }
1973 1967
1974 void _stopTimeout() { 1968 bool get isMarkedIdle => _idleMark;
1975 if (_idleTimer != null) _idleTimer.cancel();
1976 }
1977 1969
1978 void destroy() { 1970 void destroy() {
1979 _stopTimeout();
1980 if (_state == _CLOSING || _state == _DETACHED) return; 1971 if (_state == _CLOSING || _state == _DETACHED) return;
1981 _state = _CLOSING; 1972 _state = _CLOSING;
1982 _socket.destroy(); 1973 _socket.destroy();
1983 _httpServer._connectionClosed(this); 1974 _httpServer._connectionClosed(this);
1984 } 1975 }
1985 1976
1986 Future<Socket> detachSocket() { 1977 Future<Socket> detachSocket() {
1987 _stopTimeout();
1988 _state = _DETACHED; 1978 _state = _DETACHED;
1989 // Remove connection from server. 1979 // Remove connection from server.
1990 _httpServer._connectionClosed(this); 1980 _httpServer._connectionClosed(this);
1991 1981
1992 _HttpDetachedIncoming detachedIncoming = _httpParser.detachIncoming(); 1982 _HttpDetachedIncoming detachedIncoming = _httpParser.detachIncoming();
1993 1983
1994 return _streamFuture.then((_) { 1984 return _streamFuture.then((_) {
1995 return new _DetachedSocket(_socket, detachedIncoming); 1985 return new _DetachedSocket(_socket, detachedIncoming);
1996 }); 1986 });
1997 } 1987 }
1998 1988
1999 HttpConnectionInfo get connectionInfo => _HttpConnectionInfo.create(_socket); 1989 HttpConnectionInfo get connectionInfo => _HttpConnectionInfo.create(_socket);
2000 1990
2001 bool get _isActive => _state == _ACTIVE; 1991 bool get _isActive => _state == _ACTIVE;
2002 bool get _isIdle => _state == _IDLE; 1992 bool get _isIdle => _state == _IDLE;
2003 bool get _isClosing => _state == _CLOSING; 1993 bool get _isClosing => _state == _CLOSING;
2004 bool get _isDetached => _state == _DETACHED; 1994 bool get _isDetached => _state == _DETACHED;
2005 } 1995 }
2006 1996
2007 1997
2008 // HTTP server waiting for socket connections. 1998 // HTTP server waiting for socket connections.
2009 class _HttpServer extends Stream<HttpRequest> implements HttpServer { 1999 class _HttpServer extends Stream<HttpRequest> implements HttpServer {
2010 String serverHeader; 2000 String serverHeader;
2011 2001
2012 Duration idleTimeout = const Duration(seconds: 120); 2002 Duration _idleTimeout;
2003 Timer _idleTimer;
2013 2004
2014 static Future<HttpServer> bind(address, int port, int backlog) { 2005 static Future<HttpServer> bind(address, int port, int backlog) {
2015 return ServerSocket.bind(address, port, backlog: backlog).then((socket) { 2006 return ServerSocket.bind(address, port, backlog: backlog).then((socket) {
2016 return new _HttpServer._(socket, true); 2007 return new _HttpServer._(socket, true);
2017 }); 2008 });
2018 } 2009 }
2019 2010
2020 static Future<HttpServer> bindSecure(address, 2011 static Future<HttpServer> bindSecure(address,
2021 int port, 2012 int port,
2022 int backlog, 2013 int backlog,
2023 String certificate_name, 2014 String certificate_name,
2024 bool requestClientCertificate) { 2015 bool requestClientCertificate) {
2025 return SecureServerSocket.bind( 2016 return SecureServerSocket.bind(
2026 address, 2017 address,
2027 port, 2018 port,
2028 certificate_name, 2019 certificate_name,
2029 backlog: backlog, 2020 backlog: backlog,
2030 requestClientCertificate: requestClientCertificate) 2021 requestClientCertificate: requestClientCertificate)
2031 .then((socket) { 2022 .then((socket) {
2032 return new _HttpServer._(socket, true); 2023 return new _HttpServer._(socket, true);
2033 }); 2024 });
2034 } 2025 }
2035 2026
2036 _HttpServer._(this._serverSocket, this._closeServer) { 2027 _HttpServer._(this._serverSocket, this._closeServer) {
2037 _controller = new StreamController<HttpRequest>(sync: true, 2028 _controller = new StreamController<HttpRequest>(sync: true,
2038 onCancel: close); 2029 onCancel: close);
2030 idleTimeout = const Duration(seconds: 120);
2039 } 2031 }
2040 2032
2041 _HttpServer.listenOn(this._serverSocket) : _closeServer = false { 2033 _HttpServer.listenOn(this._serverSocket) : _closeServer = false {
2042 _controller = new StreamController<HttpRequest>(sync: true, 2034 _controller = new StreamController<HttpRequest>(sync: true,
2043 onCancel: close); 2035 onCancel: close);
2036 idleTimeout = const Duration(seconds: 120);
2037 }
2038
2039 Duration get idleTimeout => _idleTimeout;
2040
2041 void set idleTimeout(Duration duration) {
2042 if (_idleTimer != null) {
2043 _idleTimer.cancel();
2044 _idleTimer = null;
2045 }
2046 _idleTimeout = duration;
2047 if (_idleTimeout != null) {
2048 _idleTimer = new Timer.periodic(_idleTimeout, (_) {
2049 for (var idle in _idleConnections.toList()) {
2050 if (idle.isMarkedIdle) {
2051 idle.destroy();
2052 } else {
2053 idle.markIdle();
2054 }
2055 }
2056 });
2057 }
2044 } 2058 }
2045 2059
2046 StreamSubscription<HttpRequest> listen(void onData(HttpRequest event), 2060 StreamSubscription<HttpRequest> listen(void onData(HttpRequest event),
2047 {Function onError, 2061 {Function onError,
2048 void onDone(), 2062 void onDone(),
2049 bool cancelOnError}) { 2063 bool cancelOnError}) {
2050 _serverSocket.listen( 2064 _serverSocket.listen(
2051 (Socket socket) { 2065 (Socket socket) {
2052 socket.setOption(SocketOption.TCP_NODELAY, true); 2066 socket.setOption(SocketOption.TCP_NODELAY, true);
2053 // Accept the client connection. 2067 // Accept the client connection.
2054 _HttpConnection connection = new _HttpConnection(socket, this); 2068 _HttpConnection connection = new _HttpConnection(socket, this);
2055 _connections.add(connection); 2069 _idleConnections.add(connection);
2056 }, 2070 },
2057 onError: (error) { 2071 onError: (error) {
2058 // Ignore HandshakeExceptions as they are bound to a single request, 2072 // Ignore HandshakeExceptions as they are bound to a single request,
2059 // and are not fatal for the server. 2073 // and are not fatal for the server.
2060 if (error is! HandshakeException) { 2074 if (error is! HandshakeException) {
2061 _controller.addError(error); 2075 _controller.addError(error);
2062 } 2076 }
2063 }, 2077 },
2064 onDone: _controller.close); 2078 onDone: _controller.close);
2065 return _controller.stream.listen(onData, 2079 return _controller.stream.listen(onData,
2066 onError: onError, 2080 onError: onError,
2067 onDone: onDone, 2081 onDone: onDone,
2068 cancelOnError: cancelOnError); 2082 cancelOnError: cancelOnError);
2069 } 2083 }
2070 2084
2071 Future close({bool force: false}) { 2085 Future close({bool force: false}) {
2072 closed = true; 2086 closed = true;
2073 Future result; 2087 Future result;
2074 if (_serverSocket != null && _closeServer) { 2088 if (_serverSocket != null && _closeServer) {
2075 result = _serverSocket.close(); 2089 result = _serverSocket.close();
2076 } else { 2090 } else {
2077 result = new Future.value(); 2091 result = new Future.value();
2078 } 2092 }
2093 idleTimeout = null;
2079 if (force) { 2094 if (force) {
2080 for (var c in _connections.toList()) { 2095 for (var c in _activeConnections.toList()) {
2081 c.destroy(); 2096 c.destroy();
2082 } 2097 }
2083 assert(_connections.isEmpty); 2098 assert(_activeConnections.isEmpty);
2084 } else { 2099 }
2085 for (var c in _connections.where((c) => c._isIdle).toList()) { 2100 for (var c in _idleConnections.toList()) {
2086 c.destroy(); 2101 c.destroy();
2087 }
2088 } 2102 }
2089 _maybeCloseSessionManager(); 2103 _maybeCloseSessionManager();
2090 return result; 2104 return result;
2091 } 2105 }
2092 2106
2093 void _maybeCloseSessionManager() { 2107 void _maybeCloseSessionManager() {
2094 if (closed && 2108 if (closed &&
2095 _connections.isEmpty && 2109 _idleConnections.isEmpty &&
2110 _activeConnections.isEmpty &&
2096 _sessionManagerInstance != null) { 2111 _sessionManagerInstance != null) {
2097 _sessionManagerInstance.close(); 2112 _sessionManagerInstance.close();
2098 _sessionManagerInstance = null; 2113 _sessionManagerInstance = null;
2099 } 2114 }
2100 } 2115 }
2101 2116
2102 int get port { 2117 int get port {
2103 if (closed) throw new HttpException("HttpServer is not bound to a socket"); 2118 if (closed) throw new HttpException("HttpServer is not bound to a socket");
2104 return _serverSocket.port; 2119 return _serverSocket.port;
2105 } 2120 }
2106 2121
2107 InternetAddress get address { 2122 InternetAddress get address {
2108 if (closed) throw new HttpException("HttpServer is not bound to a socket"); 2123 if (closed) throw new HttpException("HttpServer is not bound to a socket");
2109 return _serverSocket.address; 2124 return _serverSocket.address;
2110 } 2125 }
2111 2126
2112 set sessionTimeout(int timeout) { 2127 set sessionTimeout(int timeout) {
2113 _sessionManager.sessionTimeout = timeout; 2128 _sessionManager.sessionTimeout = timeout;
2114 } 2129 }
2115 2130
2116 void _handleRequest(HttpRequest request) => _controller.add(request); 2131 void _handleRequest(HttpRequest request) => _controller.add(request);
2117 2132
2118 void _handleError(error) { 2133 void _handleError(error) {
2119 if (!closed) _controller.addError(error); 2134 if (!closed) _controller.addError(error);
2120 } 2135 }
2121 2136
2122 void _connectionClosed(_HttpConnection connection) { 2137 void _connectionClosed(_HttpConnection connection) {
2123 _connections.remove(connection); 2138 // Remove itself from either idle or active connections.
2139 connection.unlink();
2124 _maybeCloseSessionManager(); 2140 _maybeCloseSessionManager();
2125 } 2141 }
2126 2142
2143 void _markIdle(_HttpConnection connection) {
2144 _activeConnections.remove(connection);
2145 _idleConnections.add(connection);
2146 }
2147
2148 void _markActive(_HttpConnection connection) {
2149 _idleConnections.remove(connection);
2150 _activeConnections.add(connection);
2151 }
2152
2127 _HttpSessionManager get _sessionManager { 2153 _HttpSessionManager get _sessionManager {
2128 // Lazy init. 2154 // Lazy init.
2129 if (_sessionManagerInstance == null) { 2155 if (_sessionManagerInstance == null) {
2130 _sessionManagerInstance = new _HttpSessionManager(); 2156 _sessionManagerInstance = new _HttpSessionManager();
2131 } 2157 }
2132 return _sessionManagerInstance; 2158 return _sessionManagerInstance;
2133 } 2159 }
2134 2160
2135 HttpConnectionsInfo connectionsInfo() { 2161 HttpConnectionsInfo connectionsInfo() {
2136 HttpConnectionsInfo result = new HttpConnectionsInfo(); 2162 HttpConnectionsInfo result = new HttpConnectionsInfo();
2137 result.total = _connections.length; 2163 result.total = _activeConnections.length + _idleConnections.length;
2138 _connections.forEach((_HttpConnection conn) { 2164 _activeConnections.forEach((_HttpConnection conn) {
2139 if (conn._isActive) { 2165 if (conn._isActive) {
2140 result.active++; 2166 result.active++;
2141 } else if (conn._isIdle) {
2142 result.idle++;
2143 } else { 2167 } else {
2144 assert(conn._isClosing); 2168 assert(conn._isClosing);
2145 result.closing++; 2169 result.closing++;
2146 } 2170 }
2147 }); 2171 });
2172 _idleConnections.forEach((_HttpConnection conn) {
2173 result.idle++;
2174 assert(conn._isIdle);
2175 });
2148 return result; 2176 return result;
2149 } 2177 }
2150 2178
2151 _HttpSessionManager _sessionManagerInstance; 2179 _HttpSessionManager _sessionManagerInstance;
2152 2180
2153 // Indicated if the http server has been closed. 2181 // Indicated if the http server has been closed.
2154 bool closed = false; 2182 bool closed = false;
2155 2183
2156 // The server listen socket. Untyped as it can be both ServerSocket and 2184 // The server listen socket. Untyped as it can be both ServerSocket and
2157 // SecureServerSocket. 2185 // SecureServerSocket.
2158 final _serverSocket; 2186 final _serverSocket;
2159 final bool _closeServer; 2187 final bool _closeServer;
2160 2188
2161 // Set of currently connected clients. 2189 // Set of currently connected clients.
2162 final LinkedList<_HttpConnection> _connections 2190 final LinkedList<_HttpConnection> _activeConnections
2191 = new LinkedList<_HttpConnection>();
2192 final LinkedList<_HttpConnection> _idleConnections
2163 = new LinkedList<_HttpConnection>(); 2193 = new LinkedList<_HttpConnection>();
2164 StreamController<HttpRequest> _controller; 2194 StreamController<HttpRequest> _controller;
2165 // TODO(ajohnsen): Use close queue?
2166 } 2195 }
2167 2196
2168 2197
2169 class _ProxyConfiguration { 2198 class _ProxyConfiguration {
2170 static const String PROXY_PREFIX = "PROXY "; 2199 static const String PROXY_PREFIX = "PROXY ";
2171 static const String DIRECT_PREFIX = "DIRECT"; 2200 static const String DIRECT_PREFIX = "DIRECT";
2172 2201
2173 _ProxyConfiguration(String configuration) : proxies = new List<_Proxy>() { 2202 _ProxyConfiguration(String configuration) : proxies = new List<_Proxy>() {
2174 if (configuration == null) { 2203 if (configuration == null) {
2175 throw new HttpException("Invalid proxy configuration $configuration"); 2204 throw new HttpException("Invalid proxy configuration $configuration");
(...skipping 392 matching lines...) Expand 10 before | Expand all | Expand 10 after
2568 const _RedirectInfo(this.statusCode, this.method, this.location); 2597 const _RedirectInfo(this.statusCode, this.method, this.location);
2569 } 2598 }
2570 2599
2571 String _getHttpVersion() { 2600 String _getHttpVersion() {
2572 var version = Platform.version; 2601 var version = Platform.version;
2573 // Only include major and minor version numbers. 2602 // Only include major and minor version numbers.
2574 int index = version.indexOf('.', version.indexOf('.') + 1); 2603 int index = version.indexOf('.', version.indexOf('.') + 1);
2575 version = version.substring(0, index); 2604 version = version.substring(0, index);
2576 return 'Dart/$version (dart:io)'; 2605 return 'Dart/$version (dart:io)';
2577 } 2606 }
OLDNEW
« no previous file with comments | « sdk/lib/io/http.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698