blob: 2226242c0f772b7d5faa18e551171e2a0b821d9d [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2014 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();
}
}
}