diff src/org/eclipse/jetty/server/Connector.java @ 885:150092cebf3e

remove AbstractConnector
author Franklin Schmidt <fschmidt@gmail.com>
date Tue, 04 Oct 2016 20:11:48 -0600
parents 6b210bb66c63
children df84a1741687
line wrap: on
line diff
--- a/src/org/eclipse/jetty/server/Connector.java	Tue Oct 04 16:30:02 2016 -0600
+++ b/src/org/eclipse/jetty/server/Connector.java	Tue Oct 04 20:11:48 2016 -0600
@@ -19,269 +19,1013 @@
 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.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicLong;
 
-import org.eclipse.jetty.io.Buffers;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.util.component.LifeCycle;
+import javax.servlet.ServletRequest;
 
-/** HTTP Connector.
- * Implementations of this interface provide connectors for the HTTP protocol.
- * A connector receives requests (normally from a socket) and calls the 
- * handle method of the Handler object.  These operations are performed using
- * threads from the ThreadPool set on the connector.
- * 
- * When a connector is registered with an instance of Server, then the server
- * will set itself as both the ThreadPool and the Handler.  Note that a connector
- * can be used without a Server if a thread pool and handler are directly provided.
- * 
- * 
- * 
- */
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 /**
- * @author gregw
- *
+ * 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 interface Connector extends LifeCycle
-{ 
-	/* ------------------------------------------------------------ */
-	/**
-	 * @return the name of the connector. Defaults to the HostName:port
-	 */
-	String getName();
-	
-	/* ------------------------------------------------------------ */
-	/**
-	 * Opens the connector 
-	 * @throws IOException
-	 */
-	void open() throws IOException;
+public abstract class Connector extends AggregateLifeCycle implements HttpBuffers, Dumpable
+{
+	private static final Logger LOG = LoggerFactory.getLogger(Connector.class);
+
+	private String _name;
 
-	/* ------------------------------------------------------------ */
-	void close() throws IOException;
+	private Server _server;
+	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;
 
-	/* ------------------------------------------------------------ */
-	void setServer(Server server);
-	
-	/* ------------------------------------------------------------ */
-	Server getServer();
+	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;
+
+	protected final HttpBuffersImpl _buffers = new HttpBuffersImpl();
 
 	/* ------------------------------------------------------------ */
 	/**
-	 * @return Returns the request header buffer size in bytes.
 	 */
-	int getRequestHeaderSize();
-	
+	public Connector()
+	{
+		addBean(_buffers);
+	}
+
 	/* ------------------------------------------------------------ */
-	/**
-	 * Set the size of the buffer to be used for request headers.
-	 * @param size The size in bytes.
+	/*
 	 */
-	void setRequestHeaderSize(int size);
+	public Server getServer()
+	{
+		return _server;
+	}
+
+	/* ------------------------------------------------------------ */
+	public void setServer(Server server)
+	{
+		_server = server;
+	}
+
+	/* ------------------------------------------------------------ */
+	public ThreadPoolExecutor getThreadPool()
+	{
+		return _server.threadPool;
+	}
 
 	/* ------------------------------------------------------------ */
 	/**
-	 * @return Returns the response header buffer size in bytes.
 	 */
-	int getResponseHeaderSize();
-	
+	public void setHost(String host)
+	{
+		_host = host;
+	}
+
 	/* ------------------------------------------------------------ */
-	/**
-	 * Set the size of the buffer to be used for request headers.
-	 * @param size The size in bytes.
+	/*
 	 */
-	void setResponseHeaderSize(int size);
-	
+	public String getHost()
+	{
+		return _host;
+	}
+
+	/* ------------------------------------------------------------ */
+	public void setPort(int port)
+	{
+		_port = port;
+	}
+
+	/* ------------------------------------------------------------ */
+	public int getPort()
+	{
+		return _port;
+	}
 
 	/* ------------------------------------------------------------ */
 	/**
-	 * @return factory for request buffers
+	 * @return Returns the maxIdleTime.
 	 */
-	Buffers getRequestBuffers();
+	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;
+	}
 
 	/* ------------------------------------------------------------ */
 	/**
-	 * @return factory for response buffers
+	 * @param maxIdleTime
+	 *            The maxIdleTime to set when resources are low.
 	 */
-	Buffers getResponseBuffers();
-	
-	
-	/* ------------------------------------------------------------ */
-	/**
-	 * @return Returns the requestBufferSize.
-	 */
-	int getRequestBufferSize();
-	
+	public void setLowResourcesMaxIdleTime(int maxIdleTime)
+	{
+		_lowResourceMaxIdleTime = maxIdleTime;
+	}
+
 	/* ------------------------------------------------------------ */
 	/**
-	 * Set the size of the content buffer for receiving requests. 
-	 * These buffers are only used for active connections that have
-	 * requests with bodies that will not fit within the header buffer.
-	 * @param requestBufferSize The requestBufferSize to set.
+	 * @return Returns the maxIdleTime when resources are low.
+	 * @deprecated
 	 */
-	void setRequestBufferSize(int requestBufferSize);
-	
+	@Deprecated
+	public final int getLowResourceMaxIdleTime()
+	{
+		return getLowResourcesMaxIdleTime();
+	}
+
 	/* ------------------------------------------------------------ */
 	/**
-	 * @return Returns the responseBufferSize.
+	 * @param maxIdleTime
+	 *            The maxIdleTime to set when resources are low.
+	 * @deprecated
 	 */
-	int getResponseBufferSize();
-	
-	/* ------------------------------------------------------------ */
-	/**
-	 * Set the size of the content buffer for sending responses. 
-	 * These buffers are only used for active connections that are sending 
-	 * responses with bodies that will not fit within the header buffer.
-	 * @param responseBufferSize The responseBufferSize to set.
-	 */
-	void setResponseBufferSize(int responseBufferSize);
-	
+	@Deprecated
+	public final void setLowResourceMaxIdleTime(int maxIdleTime)
+	{
+		setLowResourcesMaxIdleTime(maxIdleTime);
+	}
 
 	/* ------------------------------------------------------------ */
 	/**
-	 * @return The port to use when redirecting a request if a data constraint of integral is 
-	 * required. See {@link org.eclipse.jetty.util.security.Constraint#getDataConstraint()}
+	 * @return Returns the soLingerTime.
 	 */
-	int getIntegralPort();
+	public int getSoLingerTime()
+	{
+		return _soLingerTime;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return Returns the acceptQueueSize.
+	 */
+	public int getAcceptQueueSize()
+	{
+		return _acceptQueueSize;
+	}
 
 	/* ------------------------------------------------------------ */
 	/**
-	 * @return The schema to use when redirecting a request if a data constraint of integral is 
-	 * required. See {@link org.eclipse.jetty.util.security.Constraint#getDataConstraint()}
+	 * @param acceptQueueSize
+	 *            The acceptQueueSize to set.
+	 */
+	public void setAcceptQueueSize(int acceptQueueSize)
+	{
+		_acceptQueueSize = acceptQueueSize;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return Returns the number of acceptor threads.
 	 */
-	String getIntegralScheme();
+	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 request A request
-	 * @return true if the request is integral. This normally means the https schema has been used.
+	 * @param soLingerTime
+	 *            The soLingerTime to set or -1 to disable.
 	 */
-	boolean isIntegral(Request request);
+	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();
+
+		super.doStart();
+
+		// Start selector thread
+		synchronized (this)
+		{
+			_acceptorThreads = new Thread[getAcceptors()];
+
+			ThreadPoolExecutor _threadPool = getThreadPool();
+			for (int i = 0; i < _acceptorThreads.length; i++)
+				_threadPool.execute(new Acceptor(i));
+			if (_server.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.trace("",e);
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	public void customize(EndPoint endpoint, Request request) throws IOException
+	{
+		if (isForwarded())
+			checkForwardedHeaders(endpoint,request);
+	}
 
 	/* ------------------------------------------------------------ */
-	/**
-	 * @return The port to use when redirecting a request if a data constraint of confidential is 
-	 * required. See {@link org.eclipse.jetty.util.security.Constraint#getDataConstraint()}
+	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.trace("",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()
 	 */
-	int 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);
+	}
 
 	/* ------------------------------------------------------------ */
 	/**
-	 * @return The schema to use when redirecting a request if a data constraint of confidential is 
-	 * required. See {@link org.eclipse.jetty.util.security.Constraint#getDataConstraint()}
+	 * @param confidentialPort
+	 *            The confidentialPort to set.
 	 */
-	String getConfidentialScheme();
-	
+	public void setConfidentialPort(int confidentialPort)
+	{
+		_confidentialPort = confidentialPort;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param confidentialScheme
+	 *            The confidentialScheme to set.
+	 */
+	public void setConfidentialScheme(String confidentialScheme)
+	{
+		_confidentialScheme = confidentialScheme;
+	}
+
 	/* ------------------------------------------------------------ */
 	/**
-	 * @param request A request
-	 * @return true if the request is confidential. This normally means the https schema has been used.
+	 * @param integralPort
+	 *            The integralPort to set.
+	 */
+	public void setIntegralPort(int integralPort)
+	{
+		_integralPort = integralPort;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param integralScheme
+	 *            The integralScheme to set.
 	 */
-	boolean isConfidential(Request request);
+	public void setIntegralScheme(String integralScheme)
+	{
+		_integralScheme = integralScheme;
+	}
+
+	/* ------------------------------------------------------------ */
+	protected abstract void accept(int acceptorID) throws IOException, InterruptedException;
+
+	/* ------------------------------------------------------------ */
+	public void stopAccept(int acceptorID) throws Exception
+	{
+	}
 
 	/* ------------------------------------------------------------ */
-	/** Customize a request for an endpoint.
-	 * Called on every request to allow customization of the request for
-	 * the particular endpoint (eg security properties from a SSL connection).
-	 * @param endpoint
-	 * @param request
-	 * @throws IOException
+	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
 	 */
-	void customize(EndPoint endpoint, Request request) throws IOException;
+	public boolean isForwarded()
+	{
+		return _forwarded;
+	}
 
 	/* ------------------------------------------------------------ */
-	/** Persist an endpoint.
-	 * Called after every request if the connection is to remain open.
-	 * @param endpoint
-	 * @throws IOException
+	/**
+	 * 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)
 	 */
-	void persist(EndPoint endpoint) throws IOException;
-	
+	public void setForwarded(boolean check)
+	{
+		if (check)
+			LOG.debug("{} is forwarded",this);
+		_forwarded = check;
+	}
+
 	/* ------------------------------------------------------------ */
-	/**
-	 * @return The hostname representing the interface to which 
-	 * this connector will bind, or null for all interfaces.
-	 */
-	String getHost();
-	
-	/* ------------------------------------------------------------ */
-	/**
-	 * Set the hostname of the interface to bind to.
-	 * @param hostname The hostname representing the interface to which 
-	 * this connector will bind, or null for all interfaces.
-	 */
-	void setHost(String hostname);
+	public String getHostHeader()
+	{
+		return _hostHeader;
+	}
 
 	/* ------------------------------------------------------------ */
 	/**
-	 * @param port The port to listen of for connections or 0 if any available
-	 * port may be used.
+	 * 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.
 	 */
-	void setPort(int port);
-	
+	public void setHostHeader(String hostHeader)
+	{
+		_hostHeader = hostHeader;
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 *
+	 * @see #setForwarded(boolean)
+	 */
+	public String getForwardedHostHeader()
+	{
+		return _forwardedHostHeader;
+	}
+
 	/* ------------------------------------------------------------ */
 	/**
-	 * @return The configured port for the connector or 0 if any available
-	 * port may be used.
+	 * @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)
 	 */
-	int getPort();
-	
+	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;
+	}
+
 	/* ------------------------------------------------------------ */
 	/**
-	 * @return The actual port the connector is listening on or
-	 * -1 if it has not been opened, or -2 if it has been closed.
+	 * @see #setForwarded(boolean)
+	 */
+	public String getForwardedForHeader()
+	{
+		return _forwardedForHeader;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param forwardedRemoteAddressHeader
+	 *            The header name for forwarded for (default x-forwarded-for)
+	 * @see #setForwarded(boolean)
 	 */
-	int getLocalPort();
-	
+	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;
+	}
+
 	/* ------------------------------------------------------------ */
 	/**
-	 * @return Max Idle time for connections in milliseconds
+	 * Set the forwardedProtoHeader.
+	 *
+	 * @param forwardedProtoHeader
+	 *            the forwardedProtoHeader to set (default X-Forwarded-For)
+	 * @see #setForwarded(boolean)
 	 */
-	int getMaxIdleTime();
-	
+	public void setForwardedProtoHeader(String forwardedProtoHeader)
+	{
+		_forwardedProtoHeader = forwardedProtoHeader;
+	}
+
+	/* ------------------------------------------------------------ */
 	/**
-	 * @param ms Max Idle time for connections in milliseconds
+	 * @return The header name holding a forwarded cipher suite (default null)
 	 */
-	void setMaxIdleTime(int ms);
-	
+	public String getForwardedCipherSuiteHeader()
+	{
+		return _forwardedCipherSuiteHeader;
+	}
+
 	/* ------------------------------------------------------------ */
-	int getLowResourceMaxIdleTime();
-	void setLowResourceMaxIdleTime(int ms);
-	
+	/**
+	 * @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;
+	}
+
 	/* ------------------------------------------------------------ */
 	/**
-	 * @return the underlying socket, channel, buffer etc. for the connector.
+	 * @param forwardedSslSessionId
+	 *            The header name holding a forwarded SSL Session ID (default null)
 	 */
-	Object getConnection();
-	
-	
+	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 int getMaxBuffers()
+	{
+		return _buffers.getMaxBuffers();
+	}
+
 	/* ------------------------------------------------------------ */
-	/**
-	 * @return true if names resolution should be done.
-	 */
-	boolean getResolveNames();
-	
-	
+	@Override
+	public String toString()
+	{
+		return String.format("%s@%s:%d",
+				getClass().getSimpleName(),
+				getHost()==null?"0.0.0.0":getHost(),
+				getLocalPort()<=0?getPort():getLocalPort());
+	}
 
 	/* ------------------------------------------------------------ */
-	/** Check if low on resources.
-	 * For most connectors, low resources is measured by calling 
-	 * {@link Server#isLowOnThreads()} on the connector threadpool
-	 * or the server threadpool if there is no connector threadpool.
-	 * <p>
-	 * For blocking connectors, low resources is used to trigger
-	 * usage of {@link #getLowResourceMaxIdleTime()} for the timeout
-	 * of an idle connection.
-	 * <p>
-	 * for non-blocking connectors, the number of connections is
-	 * used instead of this method, to select the timeout of an 
-	 * idle connection.
-	 * <p>
-	 * For all connectors, low resources is used to trigger the 
-	 * usage of {@link #getLowResourceMaxIdleTime()} for read and 
-	 * write operations.
-	 * 
-	 * @return true if this connector is low on resources.
+	/* ------------------------------------------------------------ */
+	/* ------------------------------------------------------------ */
+	private class Acceptor implements Runnable
+	{
+		int _acceptor = 0;
+
+		Acceptor(int id)
+		{
+			_acceptor = id;
+		}
+
+		/* ------------------------------------------------------------ */
+		public void run()
+		{
+			Thread current = Thread.currentThread();
+			String name;
+			synchronized (Connector.this)
+			{
+				if (_acceptorThreads == null)
+					return;
+
+				_acceptorThreads[_acceptor] = current;
+				name = _acceptorThreads[_acceptor].getName();
+				current.setName(name + " Acceptor" + _acceptor + " " + Connector.this);
+			}
+			int old_priority = current.getPriority();
+
+			try
+			{
+				current.setPriority(old_priority - _acceptorPriorityOffset);
+				while (isRunning() && getConnection() != null)
+				{
+					try
+					{
+						accept(_acceptor);
+					}
+					catch (EofException e)
+					{
+						LOG.trace("",e);
+					}
+					catch (IOException e)
+					{
+						LOG.trace("",e);
+					}
+					catch (InterruptedException x)
+					{
+						// Connector has been stopped
+						LOG.trace("",x);
+					}
+					catch (Throwable e)
+					{
+						LOG.warn("",e);
+					}
+				}
+			}
+			finally
+			{
+				current.setPriority(old_priority);
+				current.setName(name);
+
+				synchronized (Connector.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;
+	}
+
+	/* ------------------------------------------------------------ */
+	protected void connectionOpened(Connection connection)
+	{
+	}
+
+	/* ------------------------------------------------------------ */
+	protected void connectionUpgraded(Connection oldConnection, Connection newConnection)
+	{
+	}
+
+	/* ------------------------------------------------------------ */
+	protected void connectionClosed(Connection connection)
+	{
+		connection.onClose();
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return the acceptorPriority
 	 */
-	public boolean isLowResources();
+	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 final boolean isLowResources()
+	{
+		return _server.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();
+		}
+	}
+
+	// from old interface
+	public abstract void open() throws IOException;
+	public abstract void close() throws IOException;
+	public abstract int getLocalPort();
+	public abstract Object getConnection();
 }