blob: 4b3a5d0ddf8f37c9bd2f97902c21e4ad0288449f [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 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;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Scheduler;
public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
{
private static final Logger LOG = Log.getLogger(AbstractEndPoint.class);
private final long _created=System.currentTimeMillis();
private final InetSocketAddress _local;
private final InetSocketAddress _remote;
private volatile Connection _connection;
private final FillInterest _fillInterest = new FillInterest()
{
@Override
protected void needsFillInterest() throws IOException
{
AbstractEndPoint.this.needsFillInterest();
}
};
private final WriteFlusher _writeFlusher = new WriteFlusher(this)
{
@Override
protected void onIncompleteFlush()
{
AbstractEndPoint.this.onIncompleteFlush();
}
};
protected AbstractEndPoint(Scheduler scheduler,InetSocketAddress local,InetSocketAddress remote)
{
super(scheduler);
_local=local;
_remote=remote;
}
@Override
public long getCreatedTimeStamp()
{
return _created;
}
@Override
public InetSocketAddress getLocalAddress()
{
return _local;
}
@Override
public InetSocketAddress getRemoteAddress()
{
return _remote;
}
@Override
public Connection getConnection()
{
return _connection;
}
@Override
public void setConnection(Connection connection)
{
_connection = connection;
}
@Override
public boolean isOptimizedForDirectBuffers()
{
return false;
}
@Override
public void onOpen()
{
if (LOG.isDebugEnabled())
LOG.debug("onOpen {}",this);
super.onOpen();
}
@Override
public void close()
{
onClose();
_writeFlusher.onClose();
_fillInterest.onClose();
}
protected void close(Throwable failure)
{
onClose();
_writeFlusher.onFail(failure);
_fillInterest.onFail(failure);
}
@Override
public void fillInterested(Callback callback)
{
notIdle();
_fillInterest.register(callback);
}
@Override
public boolean tryFillInterested(Callback callback)
{
notIdle();
return _fillInterest.tryRegister(callback);
}
@Override
public boolean isFillInterested()
{
return _fillInterest.isInterested();
}
@Override
public void write(Callback callback, ByteBuffer... buffers) throws IllegalStateException
{
_writeFlusher.write(callback, buffers);
}
protected abstract void onIncompleteFlush();
protected abstract void needsFillInterest() throws IOException;
public FillInterest getFillInterest()
{
return _fillInterest;
}
protected WriteFlusher getWriteFlusher()
{
return _writeFlusher;
}
@Override
protected void onIdleExpired(TimeoutException timeout)
{
Connection connection = _connection;
if (connection != null && !connection.onIdleExpired())
return;
boolean output_shutdown=isOutputShutdown();
boolean input_shutdown=isInputShutdown();
boolean fillFailed = _fillInterest.onFail(timeout);
boolean writeFailed = _writeFlusher.onFail(timeout);
// If the endpoint is half closed and there was no fill/write handling, then close here.
// This handles the situation where the connection has completed its close handling
// and the endpoint is half closed, but the other party does not complete the close.
// This perhaps should not check for half closed, however the servlet spec case allows
// for a dispatched servlet or suspended request to extend beyond the connections idle
// time. So if this test would always close an idle endpoint that is not handled, then
// we would need a mode to ignore timeouts for some HTTP states
if (isOpen() && (output_shutdown || input_shutdown) && !(fillFailed || writeFailed))
close();
else
LOG.debug("Ignored idle endpoint {}",this);
}
@Override
public void upgrade(Connection newConnection)
{
Connection old_connection = getConnection();
if (LOG.isDebugEnabled())
LOG.debug("{} upgrading from {} to {}", this, old_connection, newConnection);
ByteBuffer prefilled = (old_connection instanceof Connection.UpgradeFrom)
?((Connection.UpgradeFrom)old_connection).onUpgradeFrom():null;
old_connection.onClose();
old_connection.getEndPoint().setConnection(newConnection);
if (newConnection instanceof Connection.UpgradeTo)
((Connection.UpgradeTo)newConnection).onUpgradeTo(prefilled);
else if (BufferUtil.hasContent(prefilled))
throw new IllegalStateException();
newConnection.onOpen();
}
@Override
public String toString()
{
Class<?> c=getClass();
String name=c.getSimpleName();
while (name.length()==0 && c.getSuperclass()!=null)
{
c=c.getSuperclass();
name=c.getSimpleName();
}
Connection connection = getConnection();
return String.format("%s@%x{%s<->%d,%s,%s,%s,%s,%s,%d/%d,%s@%x}",
name,
hashCode(),
getRemoteAddress(),
getLocalAddress().getPort(),
isOpen()?"Open":"CLOSED",
isInputShutdown()?"ISHUT":"in",
isOutputShutdown()?"OSHUT":"out",
_fillInterest.toStateString(),
_writeFlusher.toStateString(),
getIdleFor(),
getIdleTimeout(),
connection == null ? null : connection.getClass().getSimpleName(),
connection == null ? 0 : connection.hashCode());
}
}