view src/org/eclipse/jetty/server/Connector.java @ 950:a778413aefc0

add SaneSelector
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 12 Oct 2016 14:37:56 -0600
parents f5aefdc4a81a
children a021c4c9c244
line wrap: on
line source

//
//  ========================================================================
//  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.nio.channels.ServerSocketChannel;
import java.util.concurrent.ThreadPoolExecutor;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 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 Connector extends AggregateLifeCycle implements HttpBuffers, Dumpable
{
	private static final Logger LOG = LoggerFactory.getLogger(Connector.class);

	private String _name;

	public final Server server;
	private String _host;
	public final int port;
	private int _acceptors = 1;

	protected final int _maxIdleTime = 200000;
	protected int _soLingerTime = -1;

	protected final HttpBuffersImpl _buffers = new HttpBuffersImpl();

	// from child classes
	protected transient ServerSocketChannel _acceptChannel;

	public Connector(Server server,int port) {
		this.server = server;
		this.port = port;
		server.connectors.add(this);
		addBean(_buffers);
		_buffers.setRequestBufferType(Type.DIRECT);
		_buffers.setRequestHeaderType(Type.INDIRECT);
		_buffers.setResponseBufferType(Type.DIRECT);
		_buffers.setResponseHeaderType(Type.INDIRECT);
	}

	public void setHost(String host)
	{
		_host = host;
	}

	public String getHost()
	{
		return _host;
	}

	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 soLingerTime.
	 */
	public int getSoLingerTime()
	{
		return _soLingerTime;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @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
	{
		super.doStart();

		// Start selector thread
		ThreadPoolExecutor _threadPool = server.threadPool;
		for (int i = 0; i < _acceptors; i++)
			_threadPool.execute(new Acceptor());
		if (server.isLowOnThreads())
			LOG.warn("insufficient threads configured for {}",this);

		LOG.info("Started {}",this);
	}

	@Override
	protected synchronized void doStop() throws Exception
	{
		try
		{
			if (_acceptChannel != null)
				_acceptChannel.close();
			_acceptChannel=null;
		}
		catch (IOException e)
		{
			LOG.warn("",e);
		}

		super.doStop();
	}


	protected final void configure(Socket socket) throws IOException
	{
		socket.setTcpNoDelay(true);
		if (_soLingerTime >= 0)
			socket.setSoLinger(true,_soLingerTime / 1000);
		else
			socket.setSoLinger(false,0);
	}

	public void customize(EndPoint endpoint, Request request) throws IOException
	{
	}

	public void persist(EndPoint endpoint) throws IOException
	{
	}

	public boolean isConfidential(Request request)
	{
		return false;
	}

	protected abstract void accept() throws IOException, InterruptedException;

	public void stopAccept(int acceptorID) throws Exception
	{
	}

	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();
	}

	@Override
	public String toString()
	{
		return String.format("%s@%s:%d",
				getClass().getSimpleName(),
				getHost()==null?"0.0.0.0":getHost(),
				port);
	}


	private class Acceptor implements Runnable
	{

		public void run()
		{
			Thread current = Thread.currentThread();
			String name = current.getName();
			current.setName(name + " Acceptor" + " " + Connector.this);

			try
			{
				while (isRunning() && _acceptChannel != null)
				{
					try
					{
						accept();
					}
					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.setName(name);
			}
		}
	}

	public String getName()
	{
		if (_name == null)
			_name = (getHost() == null?"0.0.0.0":getHost()) + ":" + port;
		return _name;
	}

	public final boolean isLowResources()
	{
		return server.isLowOnThreads();
	}


	// from AbstractNIOConnector

	/* ------------------------------------------------------------------------------- */
	public boolean getUseDirectBuffers()
	{
		return getRequestBufferType()==Type.DIRECT;
	}

	/* ------------------------------------------------------------------------------- */
	/**
	 * @param direct If True (the default), the connector can use NIO direct buffers.
	 * Some JVMs have memory management issues (bugs) with direct buffers.
	 */
	public void setUseDirectBuffers(boolean direct)
	{
		_buffers.setRequestBufferType(direct?Type.DIRECT:Type.INDIRECT);
		_buffers.setResponseBufferType(direct?Type.DIRECT:Type.INDIRECT);
	}


	// from child classes

}