view src/org/eclipse/jetty/io/nio/ChannelEndPoint.java @ 913:17f4fe8271de

simplify connectors
author Franklin Schmidt <fschmidt@gmail.com>
date Sat, 08 Oct 2016 23:57:11 -0600
parents 220ad4853cda
children fc521d2f098e
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.io.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SocketChannel;

import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Channel End Point.
 * <p>Holds the channel and socket for an NIO endpoint.
 *
 */
public class ChannelEndPoint implements EndPoint
{
	private static final Logger LOG = LoggerFactory.getLogger(ChannelEndPoint.class);

	protected final ByteChannel _channel;
	protected final ByteBuffer[] _gather2=new ByteBuffer[2];
	protected final Socket _socket;
	protected final InetSocketAddress _local;
	protected final InetSocketAddress _remote;
	protected volatile int _maxIdleTime;
	private volatile boolean _ishut;
	private volatile boolean _oshut;

	public ChannelEndPoint(ByteChannel channel) throws IOException
	{
		super();
		this._channel = channel;
		_socket=(channel instanceof SocketChannel)?((SocketChannel)channel).socket():null;
		if (_socket!=null)
		{
			_local=(InetSocketAddress)_socket.getLocalSocketAddress();
			_remote=(InetSocketAddress)_socket.getRemoteSocketAddress();
			_maxIdleTime=_socket.getSoTimeout();
		}
		else
		{
			_local=_remote=null;
		}
	}

	protected ChannelEndPoint(ByteChannel channel, int maxIdleTime) throws IOException
	{
		this._channel = channel;
		_maxIdleTime=maxIdleTime;
		_socket=(channel instanceof SocketChannel)?((SocketChannel)channel).socket():null;
		if (_socket!=null)
		{
			_local=(InetSocketAddress)_socket.getLocalSocketAddress();
			_remote=(InetSocketAddress)_socket.getRemoteSocketAddress();
			_socket.setSoTimeout(_maxIdleTime);
		}
		else
		{
			_local=_remote=null;
		}
	}

	public boolean isBlocking()
	{
		return  !(_channel instanceof SelectableChannel) || ((SelectableChannel)_channel).isBlocking();
	}

	public boolean blockReadable(long millisecs) throws IOException
	{
		return true;
	}

	public boolean blockWritable(long millisecs) throws IOException
	{
		return true;
	}

	/*
	 * @see org.eclipse.io.EndPoint#isOpen()
	 */
	public boolean isOpen()
	{
		return _channel.isOpen();
	}

	/** Shutdown the channel Input.
	 * Cannot be overridden. To override, see {@link #shutdownInput()}
	 * @throws IOException
	 */
	protected final void shutdownChannelInput() throws IOException
	{
		LOG.debug("ishut {}", this);
		_ishut = true;
		if (_channel.isOpen())
		{
			if (_socket != null)
			{
				try
				{
					if (!_socket.isInputShutdown())
					{
						_socket.shutdownInput();
					}
				}
				catch (SocketException e)
				{
					LOG.debug(e.toString());
					LOG.trace("",e);
				}
				finally
				{
					if (_oshut)
					{
						close();
					}
				}
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.io.EndPoint#close()
	 */
	public void shutdownInput() throws IOException
	{
		shutdownChannelInput();
	}

	protected final void shutdownChannelOutput() throws IOException
	{
		LOG.debug("oshut {}",this);
		_oshut = true;
		if (_channel.isOpen())
		{
			if (_socket != null)
			{
				try
				{
					if (!_socket.isOutputShutdown())
					{
						_socket.shutdownOutput();
					}
				}
				catch (SocketException e)
				{
					LOG.debug(e.toString());
					LOG.trace("",e);
				}
				finally
				{
					if (_ishut)
					{
						close();
					}
				}
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.io.EndPoint#close()
	 */
	public void shutdownOutput() throws IOException
	{
		shutdownChannelOutput();
	}

	public boolean isOutputShutdown()
	{
		return _oshut || !_channel.isOpen() || _socket != null && _socket.isOutputShutdown();
	}

	public boolean isInputShutdown()
	{
		return _ishut || !_channel.isOpen() || _socket != null && _socket.isInputShutdown();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.io.EndPoint#close()
	 */
	public void close() throws IOException
	{
		LOG.debug("close {}",this);
		_channel.close();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.io.EndPoint#fill(org.eclipse.io.Buffer)
	 */
	public int fill(Buffer buffer) throws IOException
	{
		if (_ishut)
			return -1;
		Buffer buf = buffer.buffer();
		int len=0;
		if (buf instanceof NIOBuffer)
		{
			final NIOBuffer nbuf = (NIOBuffer)buf;
			final ByteBuffer bbuf=nbuf.getByteBuffer();

			//noinspection SynchronizationOnLocalVariableOrMethodParameter
			try
			{
				synchronized(bbuf)
				{
					try
					{
						bbuf.position(buffer.putIndex());
						len=_channel.read(bbuf);
					}
					finally
					{
						buffer.setPutIndex(bbuf.position());
						bbuf.position(0);
					}
				}

				if (len<0 && isOpen())
				{
					if (!isInputShutdown())
						shutdownInput();
					if (isOutputShutdown())
						_channel.close();
				}
			}
			catch (IOException x)
			{
				LOG.debug("Exception while filling", x);
				try
				{
					if (_channel.isOpen())
						_channel.close();
				}
				catch (Exception xx)
				{
					LOG.trace("",xx);
				}

				if (len>0)
					throw x;
				len=-1;
			}
		}
		else
		{
			throw new IOException("Not Implemented");
		}

		return len;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer)
	 */
	public int flush(Buffer buffer) throws IOException
	{
		Buffer buf = buffer.buffer();
		int len=0;
		if (buf instanceof NIOBuffer)
		{
			final NIOBuffer nbuf = (NIOBuffer)buf;
			final ByteBuffer bbuf=nbuf.getByteBuffer().asReadOnlyBuffer();
			try
			{
				bbuf.position(buffer.getIndex());
				bbuf.limit(buffer.putIndex());
				len=_channel.write(bbuf);
			}
			finally
			{
				if (len>0)
					buffer.skip(len);
			}
		}
		else if (buf instanceof RandomAccessFileBuffer)
		{
			len = ((RandomAccessFileBuffer)buf).writeTo(_channel,buffer.getIndex(),buffer.length());
			if (len>0)
				buffer.skip(len);
		}
		else if (buffer.array()!=null)
		{
			ByteBuffer b = ByteBuffer.wrap(buffer.array(), buffer.getIndex(), buffer.length());
			len=_channel.write(b);
			if (len>0)
				buffer.skip(len);
		}
		else
		{
			throw new IOException("Not Implemented");
		}
		return len;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer, org.eclipse.io.Buffer, org.eclipse.io.Buffer)
	 */
	public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
	{
		int length=0;

		Buffer buf0 = header==null?null:header.buffer();
		Buffer buf1 = buffer==null?null:buffer.buffer();

		if (_channel instanceof GatheringByteChannel &&
			header!=null && header.length()!=0 && buf0 instanceof NIOBuffer &&
			buffer!=null && buffer.length()!=0 && buf1 instanceof NIOBuffer)
		{
			length = gatheringFlush(header,((NIOBuffer)buf0).getByteBuffer(),buffer,((NIOBuffer)buf1).getByteBuffer());
		}
		else
		{
			// flush header
			if (header!=null && header.length()>0)
				length=flush(header);

			// flush buffer
			if ((header==null || header.length()==0) &&
				 buffer!=null && buffer.length()>0)
				length+=flush(buffer);

			// flush trailer
			if ((header==null || header.length()==0) &&
				(buffer==null || buffer.length()==0) &&
				 trailer!=null && trailer.length()>0)
				length+=flush(trailer);
		}

		return length;
	}

	protected int gatheringFlush(Buffer header, ByteBuffer bbuf0, Buffer buffer, ByteBuffer bbuf1) throws IOException
	{
		int length;

		synchronized(this)
		{
			// Adjust position indexs of buf0 and buf1
			bbuf0=bbuf0.asReadOnlyBuffer();
			bbuf0.position(header.getIndex());
			bbuf0.limit(header.putIndex());
			bbuf1=bbuf1.asReadOnlyBuffer();
			bbuf1.position(buffer.getIndex());
			bbuf1.limit(buffer.putIndex());

			_gather2[0]=bbuf0;
			_gather2[1]=bbuf1;

			// do the gathering write.
			length=(int)((GatheringByteChannel)_channel).write(_gather2);

			int hl=header.length();
			if (length>hl)
			{
				header.clear();
				buffer.skip(length-hl);
			}
			else if (length>0)
			{
				header.skip(length);
			}
		}
		return length;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return Returns the channel.
	 */
	public ByteChannel getChannel()
	{
		return _channel;
	}


	/* ------------------------------------------------------------ */
	/*
	 * @see org.eclipse.io.EndPoint#getLocalAddr()
	 */
	public String getLocalAddr()
	{
		if (_socket==null)
			return null;
	   if (_local==null || _local.getAddress()==null || _local.getAddress().isAnyLocalAddress())
		   return StringUtil.ALL_INTERFACES;
		return _local.getAddress().getHostAddress();
	}

	/* ------------------------------------------------------------ */
	/*
	 * @see org.eclipse.io.EndPoint#getLocalHost()
	 */
	public String getLocalHost()
	{
		if (_socket==null)
			return null;
	   if (_local==null || _local.getAddress()==null || _local.getAddress().isAnyLocalAddress())
		   return StringUtil.ALL_INTERFACES;
		return _local.getAddress().getCanonicalHostName();
	}

	/* ------------------------------------------------------------ */
	/*
	 * @see org.eclipse.io.EndPoint#getLocalPort()
	 */
	public int getLocalPort()
	{
		if (_socket==null)
			return 0;
		if (_local==null)
			return -1;
		return _local.getPort();
	}

	/* ------------------------------------------------------------ */
	/*
	 * @see org.eclipse.io.EndPoint#getRemoteAddr()
	 */
	public String getRemoteAddr()
	{
		if (_socket==null)
			return null;
		if (_remote==null)
			return null;
		return _remote.getAddress().getHostAddress();
	}

	/* ------------------------------------------------------------ */
	/*
	 * @see org.eclipse.io.EndPoint#getRemoteHost()
	 */
	public String getRemoteHost()
	{
		if (_socket==null)
			return null;
		if (_remote==null)
			return null;
		return _remote.getAddress().getCanonicalHostName();
	}

	/* ------------------------------------------------------------ */
	/*
	 * @see org.eclipse.io.EndPoint#getRemotePort()
	 */
	public int getRemotePort()
	{
		if (_socket==null)
			return 0;
		return _remote==null?-1:_remote.getPort();
	}

	/* ------------------------------------------------------------ */
	/*
	 * @see org.eclipse.io.EndPoint#getConnection()
	 */
	public Object getTransport()
	{
		return _channel;
	}

	/* ------------------------------------------------------------ */
	public void flush()
		throws IOException
	{
	}

	/* ------------------------------------------------------------ */
	public int getMaxIdleTime()
	{
		return _maxIdleTime;
	}

	/* ------------------------------------------------------------ */
	public void setMaxIdleTime(int timeMs) throws IOException
	{
		if (_socket!=null && timeMs!=_maxIdleTime)
			_socket.setSoTimeout(timeMs>0?timeMs:0);
		_maxIdleTime=timeMs;
	}
}