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