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