// ======================================================================== // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.server; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.util.concurrent.atomic.AtomicLong; import javax.servlet.ServletRequest; import org.eclipse.jetty.http.HttpBuffers; import org.eclipse.jetty.http.HttpBuffersImpl; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeaders; import org.eclipse.jetty.http.HttpSchemes; import org.eclipse.jetty.io.Buffers; import org.eclipse.jetty.io.Buffers.Type; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.util.component.AggregateLifeCycle; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.statistic.CounterStatistic; import org.eclipse.jetty.util.statistic.SampleStatistic; import org.eclipse.jetty.util.thread.ThreadPool; /** * Abstract Connector implementation. This abstract implementation of the Connector interface provides: * <ul> * <li>AbstractLifeCycle implementation</li> * <li>Implementations for connector getters and setters</li> * <li>Buffer management</li> * <li>Socket configuration</li> * <li>Base acceptor thread</li> * <li>Optional reverse proxy headers checking</li> * </ul> */ public abstract class AbstractConnector extends AggregateLifeCycle implements HttpBuffers, Connector, Dumpable { private static final Logger LOG = Log.getLogger(AbstractConnector.class); private String _name; private Server _server; private ThreadPool _threadPool; private String _host; private int _port = 0; private String _integralScheme = HttpSchemes.HTTPS; private int _integralPort = 0; private String _confidentialScheme = HttpSchemes.HTTPS; private int _confidentialPort = 0; private int _acceptQueueSize = 0; private int _acceptors = 1; private int _acceptorPriorityOffset = 0; private boolean _useDNS; private boolean _forwarded; private String _hostHeader; private String _forwardedHostHeader = HttpHeaders.X_FORWARDED_HOST; private String _forwardedServerHeader = HttpHeaders.X_FORWARDED_SERVER; private String _forwardedForHeader = HttpHeaders.X_FORWARDED_FOR; private String _forwardedProtoHeader = HttpHeaders.X_FORWARDED_PROTO; private String _forwardedCipherSuiteHeader; private String _forwardedSslSessionIdHeader; private boolean _reuseAddress = true; protected int _maxIdleTime = 200000; protected int _lowResourceMaxIdleTime = -1; protected int _soLingerTime = -1; private transient Thread[] _acceptorThreads; private final AtomicLong _statsStartedAt = new AtomicLong(-1L); /** connections to server */ private final CounterStatistic _connectionStats = new CounterStatistic(); /** requests per connection */ private final SampleStatistic _requestStats = new SampleStatistic(); /** duration of a connection */ private final SampleStatistic _connectionDurationStats = new SampleStatistic(); protected final HttpBuffersImpl _buffers = new HttpBuffersImpl(); /* ------------------------------------------------------------ */ /** */ public AbstractConnector() { addBean(_buffers); } /* ------------------------------------------------------------ */ /* */ public Server getServer() { return _server; } /* ------------------------------------------------------------ */ public void setServer(Server server) { _server = server; } /* ------------------------------------------------------------ */ public ThreadPool getThreadPool() { return _threadPool; } /* ------------------------------------------------------------ */ /** Set the ThreadPool. * The threadpool passed is added via {@link #addBean(Object)} so that * it's lifecycle may be managed as a {@link AggregateLifeCycle}. * @param pool the threadPool to set */ public void setThreadPool(ThreadPool pool) { removeBean(_threadPool); _threadPool = pool; addBean(_threadPool); } /* ------------------------------------------------------------ */ /** */ public void setHost(String host) { _host = host; } /* ------------------------------------------------------------ */ /* */ public String getHost() { return _host; } /* ------------------------------------------------------------ */ public void setPort(int port) { _port = port; } /* ------------------------------------------------------------ */ public int getPort() { return _port; } /* ------------------------------------------------------------ */ /** * @return Returns the maxIdleTime. */ public int getMaxIdleTime() { return _maxIdleTime; } /* ------------------------------------------------------------ */ /** * Set the maximum Idle time for a connection, which roughly translates to the {@link Socket#setSoTimeout(int)} call, although with NIO implementations * other mechanisms may be used to implement the timeout. The max idle time is applied: * <ul> * <li>When waiting for a new request to be received on a connection</li> * <li>When reading the headers and content of a request</li> * <li>When writing the headers and content of a response</li> * </ul> * Jetty interprets this value as the maximum time between some progress being made on the connection. So if a single byte is read or written, then the * timeout (if implemented by jetty) is reset. However, in many instances, the reading/writing is delegated to the JVM, and the semantic is more strictly * enforced as the maximum time a single read/write operation can take. Note, that as Jetty supports writes of memory mapped file buffers, then a write may * take many 10s of seconds for large content written to a slow device. * <p> * Previously, Jetty supported separate idle timeouts and IO operation timeouts, however the expense of changing the value of soTimeout was significant, so * these timeouts were merged. With the advent of NIO, it may be possible to again differentiate these values (if there is demand). * * @param maxIdleTime * The maxIdleTime to set. */ public void setMaxIdleTime(int maxIdleTime) { _maxIdleTime = maxIdleTime; } /* ------------------------------------------------------------ */ /** * @return Returns the maxIdleTime when resources are low. */ public int getLowResourcesMaxIdleTime() { return _lowResourceMaxIdleTime; } /* ------------------------------------------------------------ */ /** * @param maxIdleTime * The maxIdleTime to set when resources are low. */ public void setLowResourcesMaxIdleTime(int maxIdleTime) { _lowResourceMaxIdleTime = maxIdleTime; } /* ------------------------------------------------------------ */ /** * @return Returns the maxIdleTime when resources are low. * @deprecated */ @Deprecated public final int getLowResourceMaxIdleTime() { return getLowResourcesMaxIdleTime(); } /* ------------------------------------------------------------ */ /** * @param maxIdleTime * The maxIdleTime to set when resources are low. * @deprecated */ @Deprecated public final void setLowResourceMaxIdleTime(int maxIdleTime) { setLowResourcesMaxIdleTime(maxIdleTime); } /* ------------------------------------------------------------ */ /** * @return Returns the soLingerTime. */ public int getSoLingerTime() { return _soLingerTime; } /* ------------------------------------------------------------ */ /** * @return Returns the acceptQueueSize. */ public int getAcceptQueueSize() { return _acceptQueueSize; } /* ------------------------------------------------------------ */ /** * @param acceptQueueSize * The acceptQueueSize to set. */ public void setAcceptQueueSize(int acceptQueueSize) { _acceptQueueSize = acceptQueueSize; } /* ------------------------------------------------------------ */ /** * @return Returns the number of acceptor threads. */ public int getAcceptors() { return _acceptors; } /* ------------------------------------------------------------ */ /** * @param acceptors * The number of acceptor threads to set. */ public void setAcceptors(int acceptors) { if (acceptors > 2 * Runtime.getRuntime().availableProcessors()) LOG.warn("Acceptors should be <=2*availableProcessors: " + this); _acceptors = acceptors; } /* ------------------------------------------------------------ */ /** * @param soLingerTime * The soLingerTime to set or -1 to disable. */ public void setSoLingerTime(int soLingerTime) { _soLingerTime = soLingerTime; } /* ------------------------------------------------------------ */ @Override protected void doStart() throws Exception { if (_server == null) throw new IllegalStateException("No server"); // open listener port open(); if (_threadPool == null) { _threadPool = _server.getThreadPool(); addBean(_threadPool,false); } super.doStart(); // Start selector thread synchronized (this) { _acceptorThreads = new Thread[getAcceptors()]; for (int i = 0; i < _acceptorThreads.length; i++) if (!_threadPool.dispatch(new Acceptor(i))) throw new IllegalStateException("!accepting"); if (_threadPool.isLowOnThreads()) LOG.warn("insufficient threads configured for {}",this); } LOG.info("Started {}",this); } /* ------------------------------------------------------------ */ @Override protected void doStop() throws Exception { try { close(); } catch (IOException e) { LOG.warn(e); } super.doStop(); Thread[] acceptors; synchronized (this) { acceptors = _acceptorThreads; _acceptorThreads = null; } if (acceptors != null) { for (Thread thread : acceptors) { if (thread != null) thread.interrupt(); } } } /* ------------------------------------------------------------ */ public void join() throws InterruptedException { Thread[] threads; synchronized(this) { threads=_acceptorThreads; } if (threads != null) for (Thread thread : threads) if (thread != null) thread.join(); } /* ------------------------------------------------------------ */ protected void configure(Socket socket) throws IOException { try { socket.setTcpNoDelay(true); if (_soLingerTime >= 0) socket.setSoLinger(true,_soLingerTime / 1000); else socket.setSoLinger(false,0); } catch (Exception e) { LOG.ignore(e); } } /* ------------------------------------------------------------ */ public void customize(EndPoint endpoint, Request request) throws IOException { if (isForwarded()) checkForwardedHeaders(endpoint,request); } /* ------------------------------------------------------------ */ protected void checkForwardedHeaders(EndPoint endpoint, Request request) throws IOException { HttpFields httpFields = request.getConnection().getRequestFields(); // Do SSL first if (getForwardedCipherSuiteHeader()!=null) { String cipher_suite=httpFields.getStringField(getForwardedCipherSuiteHeader()); if (cipher_suite!=null) request.setAttribute("javax.servlet.request.cipher_suite",cipher_suite); } if (getForwardedSslSessionIdHeader()!=null) { String ssl_session_id=httpFields.getStringField(getForwardedSslSessionIdHeader()); if(ssl_session_id!=null) { request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id); request.setScheme(HttpSchemes.HTTPS); } } // Retrieving headers from the request String forwardedHost = getLeftMostFieldValue(httpFields,getForwardedHostHeader()); String forwardedServer = getLeftMostFieldValue(httpFields,getForwardedServerHeader()); String forwardedFor = getLeftMostFieldValue(httpFields,getForwardedForHeader()); String forwardedProto = getLeftMostFieldValue(httpFields,getForwardedProtoHeader()); if (_hostHeader != null) { // Update host header httpFields.put(HttpHeaders.HOST_BUFFER,_hostHeader); request.setServerName(null); request.setServerPort(-1); request.getServerName(); } else if (forwardedHost != null) { // Update host header httpFields.put(HttpHeaders.HOST_BUFFER,forwardedHost); request.setServerName(null); request.setServerPort(-1); request.getServerName(); } else if (forwardedServer != null) { // Use provided server name request.setServerName(forwardedServer); } if (forwardedFor != null) { request.setRemoteAddr(forwardedFor); InetAddress inetAddress = null; if (_useDNS) { try { inetAddress = InetAddress.getByName(forwardedFor); } catch (UnknownHostException e) { LOG.ignore(e); } } request.setRemoteHost(inetAddress == null?forwardedFor:inetAddress.getHostName()); } if (forwardedProto != null) { request.setScheme(forwardedProto); } } /* ------------------------------------------------------------ */ protected String getLeftMostFieldValue(HttpFields fields, String header) { if (header == null) return null; String headerValue = fields.getStringField(header); if (headerValue == null) return null; int commaIndex = headerValue.indexOf(','); if (commaIndex == -1) { // Single value return headerValue; } // The left-most value is the farthest downstream client return headerValue.substring(0,commaIndex); } /* ------------------------------------------------------------ */ public void persist(EndPoint endpoint) throws IOException { } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.Connector#getConfidentialPort() */ public int getConfidentialPort() { return _confidentialPort; } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.Connector#getConfidentialScheme() */ public String getConfidentialScheme() { return _confidentialScheme; } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server .Request) */ public boolean isIntegral(Request request) { return false; } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.Connector#getConfidentialPort() */ public int getIntegralPort() { return _integralPort; } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.Connector#getIntegralScheme() */ public String getIntegralScheme() { return _integralScheme; } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server.Request) */ public boolean isConfidential(Request request) { return _forwarded && request.getScheme().equalsIgnoreCase(HttpSchemes.HTTPS); } /* ------------------------------------------------------------ */ /** * @param confidentialPort * The confidentialPort to set. */ public void setConfidentialPort(int confidentialPort) { _confidentialPort = confidentialPort; } /* ------------------------------------------------------------ */ /** * @param confidentialScheme * The confidentialScheme to set. */ public void setConfidentialScheme(String confidentialScheme) { _confidentialScheme = confidentialScheme; } /* ------------------------------------------------------------ */ /** * @param integralPort * The integralPort to set. */ public void setIntegralPort(int integralPort) { _integralPort = integralPort; } /* ------------------------------------------------------------ */ /** * @param integralScheme * The integralScheme to set. */ public void setIntegralScheme(String integralScheme) { _integralScheme = integralScheme; } /* ------------------------------------------------------------ */ protected abstract void accept(int acceptorID) throws IOException, InterruptedException; /* ------------------------------------------------------------ */ public void stopAccept(int acceptorID) throws Exception { } /* ------------------------------------------------------------ */ public boolean getResolveNames() { return _useDNS; } /* ------------------------------------------------------------ */ public void setResolveNames(boolean resolve) { _useDNS = resolve; } /* ------------------------------------------------------------ */ /** * Is reverse proxy handling on? * * @return true if this connector is checking the x-forwarded-for/host/server headers */ public boolean isForwarded() { return _forwarded; } /* ------------------------------------------------------------ */ /** * Set reverse proxy handling. If set to true, then the X-Forwarded headers (or the headers set in their place) are looked for to set the request protocol, * host, server and client ip. * * @param check * true if this connector is checking the x-forwarded-for/host/server headers * @see #setForwardedForHeader(String) * @see #setForwardedHostHeader(String) * @see #setForwardedProtoHeader(String) * @see #setForwardedServerHeader(String) */ public void setForwarded(boolean check) { if (check) LOG.debug("{} is forwarded",this); _forwarded = check; } /* ------------------------------------------------------------ */ public String getHostHeader() { return _hostHeader; } /* ------------------------------------------------------------ */ /** * Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}. * This value is only used if {@link #isForwarded()} is true. * * @param hostHeader * The value of the host header to force. */ public void setHostHeader(String hostHeader) { _hostHeader = hostHeader; } /* ------------------------------------------------------------ */ /* * * @see #setForwarded(boolean) */ public String getForwardedHostHeader() { return _forwardedHostHeader; } /* ------------------------------------------------------------ */ /** * @param forwardedHostHeader * The header name for forwarded hosts (default x-forwarded-host) * @see #setForwarded(boolean) */ public void setForwardedHostHeader(String forwardedHostHeader) { _forwardedHostHeader = forwardedHostHeader; } /* ------------------------------------------------------------ */ /** * @return the header name for forwarded server. * @see #setForwarded(boolean) */ public String getForwardedServerHeader() { return _forwardedServerHeader; } /* ------------------------------------------------------------ */ /** * @param forwardedServerHeader * The header name for forwarded server (default x-forwarded-server) * @see #setForwarded(boolean) */ public void setForwardedServerHeader(String forwardedServerHeader) { _forwardedServerHeader = forwardedServerHeader; } /* ------------------------------------------------------------ */ /** * @see #setForwarded(boolean) */ public String getForwardedForHeader() { return _forwardedForHeader; } /* ------------------------------------------------------------ */ /** * @param forwardedRemoteAddressHeader * The header name for forwarded for (default x-forwarded-for) * @see #setForwarded(boolean) */ public void setForwardedForHeader(String forwardedRemoteAddressHeader) { _forwardedForHeader = forwardedRemoteAddressHeader; } /* ------------------------------------------------------------ */ /** * Get the forwardedProtoHeader. * * @return the forwardedProtoHeader (default X-Forwarded-For) * @see #setForwarded(boolean) */ public String getForwardedProtoHeader() { return _forwardedProtoHeader; } /* ------------------------------------------------------------ */ /** * Set the forwardedProtoHeader. * * @param forwardedProtoHeader * the forwardedProtoHeader to set (default X-Forwarded-For) * @see #setForwarded(boolean) */ public void setForwardedProtoHeader(String forwardedProtoHeader) { _forwardedProtoHeader = forwardedProtoHeader; } /* ------------------------------------------------------------ */ /** * @return The header name holding a forwarded cipher suite (default null) */ public String getForwardedCipherSuiteHeader() { return _forwardedCipherSuiteHeader; } /* ------------------------------------------------------------ */ /** * @param forwardedCipherSuite * The header name holding a forwarded cipher suite (default null) */ public void setForwardedCipherSuiteHeader(String forwardedCipherSuite) { _forwardedCipherSuiteHeader = forwardedCipherSuite; } /* ------------------------------------------------------------ */ /** * @return The header name holding a forwarded SSL Session ID (default null) */ public String getForwardedSslSessionIdHeader() { return _forwardedSslSessionIdHeader; } /* ------------------------------------------------------------ */ /** * @param forwardedSslSessionId * The header name holding a forwarded SSL Session ID (default null) */ public void setForwardedSslSessionIdHeader(String forwardedSslSessionId) { _forwardedSslSessionIdHeader = forwardedSslSessionId; } public int getRequestBufferSize() { return _buffers.getRequestBufferSize(); } public void setRequestBufferSize(int requestBufferSize) { _buffers.setRequestBufferSize(requestBufferSize); } public int getRequestHeaderSize() { return _buffers.getRequestHeaderSize(); } public void setRequestHeaderSize(int requestHeaderSize) { _buffers.setRequestHeaderSize(requestHeaderSize); } public int getResponseBufferSize() { return _buffers.getResponseBufferSize(); } public void setResponseBufferSize(int responseBufferSize) { _buffers.setResponseBufferSize(responseBufferSize); } public int getResponseHeaderSize() { return _buffers.getResponseHeaderSize(); } public void setResponseHeaderSize(int responseHeaderSize) { _buffers.setResponseHeaderSize(responseHeaderSize); } public Type getRequestBufferType() { return _buffers.getRequestBufferType(); } public Type getRequestHeaderType() { return _buffers.getRequestHeaderType(); } public Type getResponseBufferType() { return _buffers.getResponseBufferType(); } public Type getResponseHeaderType() { return _buffers.getResponseHeaderType(); } public void setRequestBuffers(Buffers requestBuffers) { _buffers.setRequestBuffers(requestBuffers); } public void setResponseBuffers(Buffers responseBuffers) { _buffers.setResponseBuffers(responseBuffers); } public Buffers getRequestBuffers() { return _buffers.getRequestBuffers(); } public Buffers getResponseBuffers() { return _buffers.getResponseBuffers(); } public void setMaxBuffers(int maxBuffers) { _buffers.setMaxBuffers(maxBuffers); } public int getMaxBuffers() { return _buffers.getMaxBuffers(); } /* ------------------------------------------------------------ */ @Override public String toString() { return String.format("%s@%s:%d", getClass().getSimpleName(), getHost()==null?"0.0.0.0":getHost(), getLocalPort()<=0?getPort():getLocalPort()); } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ private class Acceptor implements Runnable { int _acceptor = 0; Acceptor(int id) { _acceptor = id; } /* ------------------------------------------------------------ */ public void run() { Thread current = Thread.currentThread(); String name; synchronized (AbstractConnector.this) { if (_acceptorThreads == null) return; _acceptorThreads[_acceptor] = current; name = _acceptorThreads[_acceptor].getName(); current.setName(name + " Acceptor" + _acceptor + " " + AbstractConnector.this); } int old_priority = current.getPriority(); try { current.setPriority(old_priority - _acceptorPriorityOffset); while (isRunning() && getConnection() != null) { try { accept(_acceptor); } catch (EofException e) { LOG.ignore(e); } catch (IOException e) { LOG.ignore(e); } catch (InterruptedException x) { // Connector has been stopped LOG.ignore(x); } catch (Throwable e) { LOG.warn(e); } } } finally { current.setPriority(old_priority); current.setName(name); synchronized (AbstractConnector.this) { if (_acceptorThreads != null) _acceptorThreads[_acceptor] = null; } } } } /* ------------------------------------------------------------ */ public String getName() { if (_name == null) _name = (getHost() == null?"0.0.0.0":getHost()) + ":" + (getLocalPort() <= 0?getPort():getLocalPort()); return _name; } /* ------------------------------------------------------------ */ public void setName(String name) { _name = name; } /* ------------------------------------------------------------ */ /** * @return Get the number of requests handled by this connector since last call of statsReset(). If setStatsOn(false) then this is undefined. */ public int getRequests() { return (int)_requestStats.getTotal(); } /* ------------------------------------------------------------ */ /** * @return Returns the connectionsDurationTotal. */ public long getConnectionsDurationTotal() { return _connectionDurationStats.getTotal(); } /* ------------------------------------------------------------ */ /** * @return Number of connections accepted by the server since statsReset() called. Undefined if setStatsOn(false). */ public int getConnections() { return (int)_connectionStats.getTotal(); } /* ------------------------------------------------------------ */ /** * @return Number of connections currently open that were opened since statsReset() called. Undefined if setStatsOn(false). */ public int getConnectionsOpen() { return (int)_connectionStats.getCurrent(); } /* ------------------------------------------------------------ */ /** * @return Maximum number of connections opened simultaneously since statsReset() called. Undefined if setStatsOn(false). */ public int getConnectionsOpenMax() { return (int)_connectionStats.getMax(); } /* ------------------------------------------------------------ */ /** * @return Mean duration in milliseconds of open connections since statsReset() called. Undefined if setStatsOn(false). */ public double getConnectionsDurationMean() { return _connectionDurationStats.getMean(); } /* ------------------------------------------------------------ */ /** * @return Maximum duration in milliseconds of an open connection since statsReset() called. Undefined if setStatsOn(false). */ public long getConnectionsDurationMax() { return _connectionDurationStats.getMax(); } /* ------------------------------------------------------------ */ /** * @return Standard deviation of duration in milliseconds of open connections since statsReset() called. Undefined if setStatsOn(false). */ public double getConnectionsDurationStdDev() { return _connectionDurationStats.getStdDev(); } /* ------------------------------------------------------------ */ /** * @return Mean number of requests per connection since statsReset() called. Undefined if setStatsOn(false). */ public double getConnectionsRequestsMean() { return _requestStats.getMean(); } /* ------------------------------------------------------------ */ /** * @return Maximum number of requests per connection since statsReset() called. Undefined if setStatsOn(false). */ public int getConnectionsRequestsMax() { return (int)_requestStats.getMax(); } /* ------------------------------------------------------------ */ /** * @return Standard deviation of number of requests per connection since statsReset() called. Undefined if setStatsOn(false). */ public double getConnectionsRequestsStdDev() { return _requestStats.getStdDev(); } /* ------------------------------------------------------------ */ /** * Reset statistics. */ public void statsReset() { updateNotEqual(_statsStartedAt,-1,System.currentTimeMillis()); _requestStats.reset(); _connectionStats.reset(); _connectionDurationStats.reset(); } /* ------------------------------------------------------------ */ public void setStatsOn(boolean on) { if (on && _statsStartedAt.get() != -1) return; if (LOG.isDebugEnabled()) LOG.debug("Statistics on = " + on + " for " + this); statsReset(); _statsStartedAt.set(on?System.currentTimeMillis():-1); } /* ------------------------------------------------------------ */ /** * @return True if statistics collection is turned on. */ public boolean getStatsOn() { return _statsStartedAt.get() != -1; } /* ------------------------------------------------------------ */ /** * @return Timestamp stats were started at. */ public long getStatsOnMs() { long start = _statsStartedAt.get(); return (start != -1)?(System.currentTimeMillis() - start):0; } /* ------------------------------------------------------------ */ protected void connectionOpened(Connection connection) { if (_statsStartedAt.get() == -1) return; _connectionStats.increment(); } /* ------------------------------------------------------------ */ protected void connectionUpgraded(Connection oldConnection, Connection newConnection) { _requestStats.set((oldConnection instanceof AbstractHttpConnection)?((AbstractHttpConnection)oldConnection).getRequests():0); } /* ------------------------------------------------------------ */ protected void connectionClosed(Connection connection) { connection.onClose(); if (_statsStartedAt.get() == -1) return; long duration = System.currentTimeMillis() - connection.getTimeStamp(); int requests = (connection instanceof AbstractHttpConnection)?((AbstractHttpConnection)connection).getRequests():0; _requestStats.set(requests); _connectionStats.decrement(); _connectionDurationStats.set(duration); } /* ------------------------------------------------------------ */ /** * @return the acceptorPriority */ public int getAcceptorPriorityOffset() { return _acceptorPriorityOffset; } /* ------------------------------------------------------------ */ /** * Set the priority offset of the acceptor threads. The priority is adjusted by this amount (default 0) to either favour the acceptance of new threads and * newly active connections or to favour the handling of already dispatched connections. * * @param offset * the amount to alter the priority of the acceptor threads. */ public void setAcceptorPriorityOffset(int offset) { _acceptorPriorityOffset = offset; } /* ------------------------------------------------------------ */ /** * @return True if the the server socket will be opened in SO_REUSEADDR mode. */ public boolean getReuseAddress() { return _reuseAddress; } /* ------------------------------------------------------------ */ /** * @param reuseAddress * True if the the server socket will be opened in SO_REUSEADDR mode. */ public void setReuseAddress(boolean reuseAddress) { _reuseAddress = reuseAddress; } /* ------------------------------------------------------------ */ public boolean isLowResources() { if (_threadPool != null) return _threadPool.isLowOnThreads(); return _server.getThreadPool().isLowOnThreads(); } /* ------------------------------------------------------------ */ private void updateNotEqual(AtomicLong valueHolder, long compare, long value) { long oldValue = valueHolder.get(); while (compare != oldValue) { if (valueHolder.compareAndSet(oldValue,value)) break; oldValue = valueHolder.get(); } } }