357240 safer SSL close

This commit is contained in:
Greg Wilkins 2011-09-27 16:57:17 +10:00
parent 4b12b1b90b
commit 44923230e3
6 changed files with 351 additions and 491 deletions

View File

@ -512,6 +512,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa
try try
{ {
if (ch!=null)
ch.close(); ch.close();
} }
catch(IOException e2) catch(IOException e2)

View File

@ -41,16 +41,16 @@ import org.eclipse.jetty.util.log.Logger;
*/ */
public class SslSelectChannelEndPoint extends SelectChannelEndPoint public class SslSelectChannelEndPoint extends SelectChannelEndPoint
{ {
private static final Logger LOG = Log.getLogger(SslSelectChannelEndPoint.class); public static final Logger LOG=Log.getLogger("org.eclipse.jetty.io.nio").getLogger("ssl");
public static final Logger __log=Log.getLogger("org.eclipse.jetty.io.nio").getLogger("ssl"); private static final Buffer __EMPTY_BUFFER=new DirectNIOBuffer(0);
private static final ByteBuffer __ZERO_BUFFER=ByteBuffer.allocate(0);
private static final ByteBuffer[] __NO_BUFFERS={};
private final Buffers _buffers; private final Buffers _buffers;
private final SSLEngine _engine; private final SSLEngine _engine;
private final SSLSession _session; private final SSLSession _session;
private int _inCount;
private volatile NIOBuffer _inNIOBuffer; private volatile NIOBuffer _inNIOBuffer;
private volatile NIOBuffer _outNIOBuffer; private volatile NIOBuffer _outNIOBuffer;
@ -62,7 +62,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
private boolean _handshook=false; private boolean _handshook=false;
private boolean _allowRenegotiate=false; private boolean _allowRenegotiate=false;
private final boolean _debug = __log.isDebugEnabled(); // snapshot debug status for optimizer private final boolean _debug = LOG.isDebugEnabled(); // snapshot debug status for optimizer
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine, int maxIdleTime) public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine, int maxIdleTime)
@ -75,7 +75,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
_engine=engine; _engine=engine;
_session=engine.getSession(); _session=engine.getSession();
if (_debug) __log.debug(_session+" channel="+channel); if (_debug) LOG.debug(_session+" channel="+channel);
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@ -89,7 +89,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
_engine=engine; _engine=engine;
_session=engine.getSession(); _session=engine.getSession();
if (_debug) __log.debug(_session+" channel="+channel); if (_debug) LOG.debug(_session+" channel="+channel);
} }
int _outCount; int _outCount;
@ -119,7 +119,6 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
} }
} }
int _inCount;
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
private void needInBuffer() private void needInBuffer()
{ {
@ -196,9 +195,12 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
@Override @Override
public void shutdownOutput() throws IOException public void shutdownOutput() throws IOException
{ {
LOG.debug("{} shutdownOutput",_session);
try try
{ {
sslClose(); // All SSL closes should be graceful, as it is more secure.
// So normal SSL close can be used here.
close();
} }
finally finally
{ {
@ -206,79 +208,111 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
} }
} }
private int process() throws IOException /* ------------------------------------------------------------ */
private int process(ByteBuffer inbuf, Buffer header, Buffer buffer) throws IOException
{ {
ByteBuffer bbuf = null; // TODO ??? if (_debug)
LOG.debug("process {} {} {}",inbuf!=null,header!=null,buffer!=null);
// If there is no place to put incoming application data,
if (inbuf==null)
{
// use ZERO buffer
inbuf=__ZERO_BUFFER;
}
int received=0;
int sent=0;
int wraps=0;
HandshakeStatus initialStatus = _engine.getHandshakeStatus(); HandshakeStatus initialStatus = _engine.getHandshakeStatus();
loop: while (true) boolean progress=true;
while (progress)
{ {
// If we have encrypted data in output buffer progress=false;
if (isBufferingOutput())
// flush output data
{ {
int len=_outNIOBuffer==null?0:_outNIOBuffer.length();
// we must flush it, as the other end might be // we must flush it, as the other end might be
// waiting for that outgoing data before sending // waiting for that outgoing data before sending
// more incoming data // more incoming data
flush(); flush();
// If we were unable to flush all the data, then // If we have written some bytes, then progress has been made.
// we should break the loop and wait for the call progress|=(_outNIOBuffer==null?0:_outNIOBuffer.length())<len;
// back to handle when the SelectSet detects that
// the channel is writable again.
if (isBufferingOutput())
{
scheduleWrite();
break loop;
}
} }
// handle the current hand share status // handle the current hand share status
if (_debug) LOG.debug("status {} {}",_engine,_engine.getHandshakeStatus());
switch(_engine.getHandshakeStatus()) switch(_engine.getHandshakeStatus())
{ {
case FINISHED: case FINISHED:
throw new IllegalStateException();
case NOT_HANDSHAKING: case NOT_HANDSHAKING:
// If we are closing, then unwrap must have CLOSED result,
// so return -1 to signal upwards _handshook=true;
// If closing, don't process application data
if (_closing) if (_closing)
return -1;
// otherwise we break loop with the data we have unwrapped.
break loop;
case NEED_UNWRAP:
checkRenegotiate();
// Need more data to be unwrapped so try another call to unwrap
if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
{
// If the unwrap call did not make any progress and we are still in
// NEED_UNWRAP, then we should break the loop and wait for more data to
// arrive.
break loop;
}
// progress was made so continue the loop.
break; break;
// Try wrapping some application data
if (header!=null||buffer!=null)
{
int c=0;
if (header!=null && header.hasContent())
{
if (buffer!=null && buffer.hasContent())
c=wrap(header,buffer);
else
c=wrap(header);
progress=_result.bytesProduced()>0||_result.bytesConsumed()>0;
}
else if (buffer!=null && buffer.hasContent())
{
c=wrap(buffer);
progress=_result.bytesProduced()>0||_result.bytesConsumed()>0;
}
if (c>0)
sent+=c;
else if (c<0 && sent==0)
sent=-1;
}
// Try unwrapping some application data
if (inbuf.remaining()>0 && _inNIOBuffer!=null && _inNIOBuffer.hasContent())
{
int space=inbuf.remaining();
progress|=unwrap(inbuf);
received+=space-inbuf.remaining();
}
break;
case NEED_TASK: case NEED_TASK:
{ {
// A task needs to be run, so run it! // A task needs to be run, so run it!
Runnable task; Runnable task;
while ((task=_engine.getDelegatedTask())!=null) while ((task=_engine.getDelegatedTask())!=null)
{ {
progress=true;
task.run(); task.run();
} }
// Detect SUN JVM Bug!!! // Detect SUN JVM Bug!!!
if(initialStatus==HandshakeStatus.NOT_HANDSHAKING && if(initialStatus==HandshakeStatus.NOT_HANDSHAKING &&
_engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP && wraps==0) _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP && sent==0)
{ {
// This should be NEED_WRAP // This should be NEED_WRAP
// The fix simply detects the signature of the bug and then close the connection (fail-fast) so that ff3 will delegate to using SSL instead of TLS. // The fix simply detects the signature of the bug and then close the connection (fail-fast) so that ff3 will delegate to using SSL instead of TLS.
// This is a jvm bug on java1.6 where the SSLEngine expects more data from the initial handshake when the client(ff3-tls) already had given it. // This is a jvm bug on java1.6 where the SSLEngine expects more data from the initial handshake when the client(ff3-tls) already had given it.
// See http://jira.codehaus.org/browse/JETTY-567 for more details // See http://jira.codehaus.org/browse/JETTY-567 for more details
if (_debug) __log.warn(_session+" JETTY-567"); if (_debug) LOG.warn(_session+" JETTY-567");
return -1; return -1;
} }
break; break;
@ -287,187 +321,64 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
case NEED_WRAP: case NEED_WRAP:
{ {
checkRenegotiate(); checkRenegotiate();
// The SSL needs to send some handshake data to the other side,
// so let fill become a flush for a little bit.
wraps++;
needOutBuffer();
ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
synchronized(out_buffer)
{
try
{
// call wrap with empty application buffers, so it can
// generate required handshake messages into _outNIOBuffer
_outNIOBuffer.compact();
int put=_outNIOBuffer.putIndex();
out_buffer.position();
_result=null;
_result=_engine.wrap(__NO_BUFFERS,out_buffer);
if (_debug) __log.debug(_session+" fill wrap "+_result);
switch(_result.getStatus())
{
case BUFFER_OVERFLOW:
case BUFFER_UNDERFLOW:
LOG.warn("wrap {}",_result);
_closing=true;
break;
case CLOSED:
_closing=true;
break;
}
_outNIOBuffer.setPutIndex(put+_result.bytesProduced());
}
catch(SSLException e)
{
super.close();
throw e;
}
finally
{
out_buffer.position(0);
}
}
// flush the encrypted outNIOBuffer // The SSL needs to send some handshake data to the other side
flush(); int c=0;
freeOutBuffer(); if (header!=null && header.hasContent())
{
if (buffer!=null && buffer.hasContent())
c=wrap(header,buffer);
else
c=wrap(header);
}
else if (buffer!=null && buffer.hasContent())
c=wrap(buffer);
else
c=wrap(__EMPTY_BUFFER);
progress=_result.bytesProduced()>0||_result.bytesConsumed()>0;
if (c>0)
sent+=c;
else if (c<0 && sent==0)
sent=-1;
break; break;
} }
}
}
return -1; // TODO
}
/* ------------------------------------------------------------ */
protected void sslClose() throws IOException
{
if (_closing)
return;
_closing=true;
// TODO - this really should not be done in a loop here - but with async callbacks.
long end=System.currentTimeMillis()+getMaxIdleTime();
try
{
while (isOpen() && isBufferingOutput()&& System.currentTimeMillis()<end)
{
flush();
if (isBufferingOutput())
{
Thread.sleep(50); // TODO non blocking
flush();
}
}
_engine.closeOutbound();
loop: while (isOpen() && !(_engine.isInboundDone() && _engine.isOutboundDone()) && System.currentTimeMillis()<end)
{
while (isOpen() && isBufferingOutput() && System.currentTimeMillis()<end)
{
flush();
if (isBufferingOutput())
Thread.sleep(50);
}
if (_debug) __log.debug(_session+" closing "+_engine.getHandshakeStatus());
switch(_engine.getHandshakeStatus())
{
case FINISHED:
case NOT_HANDSHAKING:
_handshook=true;
break loop;
case NEED_UNWRAP: case NEED_UNWRAP:
Buffer buffer =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
try
{ {
ByteBuffer bbuffer = ((NIOBuffer)buffer).getByteBuffer(); checkRenegotiate();
if (!unwrap(bbuffer) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
{
break loop;
}
}
catch(SSLException e)
{
super.close();
LOG.ignore(e);
}
finally
{
_buffers.returnBuffer(buffer);
}
break;
case NEED_TASK: // Need more data to be unwrapped so try another call to unwrap
{ progress|=unwrap(inbuf);
Runnable task; if (_closing)
while ((task=_engine.getDelegatedTask())!=null) inbuf.clear();
{
task.run();
}
break;
}
case NEED_WRAP:
{
needOutBuffer();
ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
try
{
if (_outNIOBuffer.length()>0)
flush();
_outNIOBuffer.compact();
int put=_outNIOBuffer.putIndex();
out_buffer.position(put);
_result=null;
_result=_engine.wrap(__NO_BUFFERS,out_buffer);
if (_debug) __log.debug(_session+" close wrap "+_result);
_outNIOBuffer.setPutIndex(put+_result.bytesProduced());
}
catch(SSLException e)
{
super.close();
throw e;
}
finally
{
out_buffer.position(0);
freeOutBuffer();
}
break; break;
} }
} }
if (_debug) LOG.debug("{} progress {}",_session,progress);
} }
if (_debug) LOG.debug("{} received {} sent {}",_session,received,sent);
return (received<0||sent<0)?-1:(received+sent);
} }
catch (ThreadDeath x)
{
super.close();
throw x;
}
catch (Throwable x)
{
LOG.debug(x);
super.close();
}
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@Override @Override
public void close() throws IOException public void close() throws IOException
{ {
try if (_closing)
{ return;
sslClose(); LOG.debug("{} close",_session);
} _closing=true;
finally
{ // Processing will call flush(), which will handle the closing state with a closeOutbound then shutdownOutput
super.close(); process(null,null,null);
}
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@ -478,18 +389,20 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
@Override @Override
public int fill(Buffer buffer) throws IOException public int fill(Buffer buffer) throws IOException
{ {
LOG.debug("{} fill",_session);
// This end point only works on NIO buffer type (director // This end point only works on NIO buffer type (director
// or indirect), so extract the NIO buffer that is wrapped // or indirect), so extract the NIO buffer that is wrapped
// by the passed jetty Buffer. // by the passed jetty Buffer.
final ByteBuffer bbuf=extractInputBuffer(buffer); ByteBuffer bbuf=((NIOBuffer)buffer).getByteBuffer();
// remember the original size of the unencrypted buffer // remember the original size of the unencrypted buffer
int size=buffer.length(); int size=buffer.length();
HandshakeStatus initialStatus = _engine.getHandshakeStatus();
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (bbuf) synchronized (bbuf)
{ {
bbuf.position(buffer.putIndex());
try try
{ {
// Call the SSLEngine unwrap method to process data in // Call the SSLEngine unwrap method to process data in
@ -497,129 +410,9 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
// super.fill is called to read encrypted bytes. // super.fill is called to read encrypted bytes.
unwrap(bbuf); unwrap(bbuf);
// Loop through the SSL engine state machine int progress = process(bbuf,null,null);
int wraps=0; // TODO ???
loop: while (true)
{
// If we have encrypted data in output buffer
if (isBufferingOutput())
{
// we must flush it, as the other end might be
// waiting for that outgoing data before sending
// more incoming data
flush();
// If we were unable to flush all the data, then
// we should break the loop and wait for the call
// back to handle when the SelectSet detects that
// the channel is writable again.
if (isBufferingOutput())
break loop;
}
// handle the current hand share status
switch(_engine.getHandshakeStatus())
{
case FINISHED:
case NOT_HANDSHAKING:
// If we are closing, then unwrap must have CLOSED result,
// so return -1 to signal upwards
if (_closing)
return -1;
// otherwise we break loop with the data we have unwrapped.
break loop;
case NEED_UNWRAP:
checkRenegotiate();
// Need more data to be unwrapped so try another call to unwrap
if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
{
// If the unwrap call did not make any progress and we are still in
// NEED_UNWRAP, then we should break the loop and wait for more data to
// arrive.
break loop;
}
// progress was made so continue the loop.
break;
case NEED_TASK:
{
// A task needs to be run, so run it!
Runnable task;
while ((task=_engine.getDelegatedTask())!=null)
{
task.run();
}
// Detect SUN JVM Bug!!!
if(initialStatus==HandshakeStatus.NOT_HANDSHAKING &&
_engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP && wraps==0)
{
// This should be NEED_WRAP
// The fix simply detects the signature of the bug and then close the connection (fail-fast) so that ff3 will delegate to using SSL instead of TLS.
// This is a jvm bug on java1.6 where the SSLEngine expects more data from the initial handshake when the client(ff3-tls) already had given it.
// See http://jira.codehaus.org/browse/JETTY-567 for more details
if (_debug) __log.warn(_session+" JETTY-567");
return -1;
}
break;
}
case NEED_WRAP:
{
checkRenegotiate();
// The SSL needs to send some handshake data to the other side,
// so let fill become a flush for a little bit.
wraps++;
needOutBuffer();
ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
synchronized(out_buffer)
{
try
{
// call wrap with empty application buffers, so it can
// generate required handshake messages into _outNIOBuffer
_outNIOBuffer.compact();
int put=_outNIOBuffer.putIndex();
out_buffer.position();
_result=null;
_result=_engine.wrap(__NO_BUFFERS,out_buffer);
if (_debug) __log.debug(_session+" fill wrap "+_result);
switch(_result.getStatus())
{
case BUFFER_OVERFLOW:
case BUFFER_UNDERFLOW:
LOG.warn("wrap {}",_result);
_closing=true;
break;
case CLOSED:
_closing=true;
break;
}
_outNIOBuffer.setPutIndex(put+_result.bytesProduced());
}
catch(SSLException e)
{
super.close();
throw e;
}
finally
{
out_buffer.position(0);
}
}
// flush the encrypted outNIOBuffer
flush();
freeOutBuffer();
break;
}
}
}
} }
finally finally
{ {
@ -640,7 +433,8 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
@Override @Override
public int flush(Buffer buffer) throws IOException public int flush(Buffer buffer) throws IOException
{ {
return flush(buffer,null,null); LOG.debug("{} flush1",_session);
return process(null,buffer,null);
} }
@ -650,136 +444,8 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
@Override @Override
public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
{ {
int consumed=0; LOG.debug("{} flush3",_session);
int available=header==null?0:header.length(); return process(null,header,buffer);
if (buffer!=null)
available+=buffer.length();
needOutBuffer();
ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
loop: while (true)
{
if (_outNIOBuffer.length()>0)
{
flush();
if (isBufferingOutput())
break loop;
}
switch(_engine.getHandshakeStatus())
{
case FINISHED:
case NOT_HANDSHAKING:
if (_closing || available==0)
{
if (consumed==0)
consumed= -1;
break loop;
}
int c;
if (header!=null && header.length()>0)
{
if (buffer!=null && buffer.length()>0)
c=wrap(header,buffer);
else
c=wrap(header);
}
else
c=wrap(buffer);
if (c>0)
{
_handshook=true;
consumed+=c;
available-=c;
}
else if (c<0)
{
if (consumed==0)
consumed=-1;
break loop;
}
break;
case NEED_UNWRAP:
checkRenegotiate();
Buffer buf =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
try
{
ByteBuffer bbuf = ((NIOBuffer)buf).getByteBuffer();
if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
{
break loop;
}
}
finally
{
_buffers.returnBuffer(buf);
}
break;
case NEED_TASK:
{
Runnable task;
while ((task=_engine.getDelegatedTask())!=null)
{
task.run();
}
break;
}
case NEED_WRAP:
{
checkRenegotiate();
synchronized(out_buffer)
{
try
{
_outNIOBuffer.compact();
int put=_outNIOBuffer.putIndex();
out_buffer.position();
_result=null;
_result=_engine.wrap(__NO_BUFFERS,out_buffer);
if (_debug) __log.debug(_session+" flush wrap "+_result);
switch(_result.getStatus())
{
case BUFFER_OVERFLOW:
case BUFFER_UNDERFLOW:
LOG.warn("unwrap {}",_result);
_closing=true;
break;
case CLOSED:
_closing=true;
break;
}
_outNIOBuffer.setPutIndex(put+_result.bytesProduced());
}
catch(SSLException e)
{
super.close();
throw e;
}
finally
{
out_buffer.position(0);
}
}
flush();
if (isBufferingOutput())
break loop;
break;
}
}
}
freeOutBuffer();
return consumed;
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@ -790,20 +456,20 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
{ {
int flushed=super.flush(_outNIOBuffer); int flushed=super.flush(_outNIOBuffer);
if (_debug) if (_debug)
__log.debug(_session+" Flushed "+flushed+" left="+_outNIOBuffer.length()); LOG.debug(_session+" flushed "+flushed+" left="+_outNIOBuffer.length());
} }
else if (_closing) else if (_closing)
{ {
if (_engine.isOutboundDone()) if (_engine.isOutboundDone() && !super.isOutputShutdown())
{ {
if (_debug) if (_debug)
__log.debug(_session+" close"); LOG.debug(_session+" flush shutdownOutput");
super.close(); super.shutdownOutput();
} }
else else
{ {
if (_debug) if (_debug)
__log.debug(_session+" closeOutbound"); LOG.debug(_session+" flush closeOutbound");
_engine.closeOutbound(); _engine.closeOutbound();
} }
} }
@ -819,16 +485,6 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
} }
} }
/* ------------------------------------------------------------ */
private ByteBuffer extractInputBuffer(Buffer buffer)
{
assert buffer instanceof NIOBuffer;
NIOBuffer nbuf=(NIOBuffer)buffer;
ByteBuffer bbuf=nbuf.getByteBuffer();
bbuf.position(buffer.putIndex());
return bbuf;
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** /**
* @return true if progress is made * @return true if progress is made
@ -851,7 +507,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
try try
{ {
int filled=super.fill(_inNIOBuffer); int filled=super.fill(_inNIOBuffer);
if (_debug) __log.debug(_session+" unwrap filled "+filled); if (_debug) LOG.debug(_session+" filled "+filled);
if (filled < 0) if (filled < 0)
remoteClosed = true; remoteClosed = true;
// break the loop if no progress is made (we have read everything there is to read) // break the loop if no progress is made (we have read everything there is to read)
@ -878,6 +534,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
// If we have no progress and no data // If we have no progress and no data
if (total_filled==0 && _inNIOBuffer.length()==0) if (total_filled==0 && _inNIOBuffer.length()==0)
{ {
// Do we need to close?
if (isOpen() && remoteClosed) if (isOpen() && remoteClosed)
{ {
try try
@ -911,7 +568,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
// Do the unwrap // Do the unwrap
_result=_engine.unwrap(in_buffer,buffer); _result=_engine.unwrap(in_buffer,buffer);
if (_debug) __log.debug(_session+" unwrap unwrap "+_result); if (_debug) LOG.debug(_session+" unwrap "+_result);
// skip the bytes consumed // skip the bytes consumed
_inNIOBuffer.skip(_result.bytesConsumed()); _inNIOBuffer.skip(_result.bytesConsumed());
@ -935,7 +592,8 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
switch(_result.getStatus()) switch(_result.getStatus())
{ {
case BUFFER_OVERFLOW: case BUFFER_OVERFLOW:
throw new IllegalStateException(_result.toString()+" "+buffer.position()+" "+buffer.limit()); LOG.debug("{} BUFFER_OVERFLOW",_session);
return false;
case BUFFER_UNDERFLOW: case BUFFER_UNDERFLOW:
// Not enough data, // Not enough data,
@ -1005,7 +663,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
_result=null; _result=null;
_result=_engine.wrap(_gather,out_buffer); _result=_engine.wrap(_gather,out_buffer);
if (_debug) __log.debug(_session+" wrap wrap "+_result); if (_debug) LOG.debug(_session+" wrap wrap "+_result);
_outNIOBuffer.setGetIndex(0); _outNIOBuffer.setGetIndex(0);
_outNIOBuffer.setPutIndex(_result.bytesProduced()); _outNIOBuffer.setPutIndex(_result.bytesProduced());
consumed=_result.bytesConsumed(); consumed=_result.bytesConsumed();
@ -1088,7 +746,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
out_buffer.limit(out_buffer.capacity()); out_buffer.limit(out_buffer.capacity());
_result=null; _result=null;
_result=_engine.wrap(_gather[0],out_buffer); _result=_engine.wrap(_gather[0],out_buffer);
if (_debug) __log.debug(_session+" wrap wrap "+_result); if (_debug) LOG.debug(_session+" wrap "+_result);
_outNIOBuffer.setGetIndex(0); _outNIOBuffer.setGetIndex(0);
_outNIOBuffer.setPutIndex(_result.bytesProduced()); _outNIOBuffer.setPutIndex(_result.bytesProduced());
consumed=_result.bytesConsumed(); consumed=_result.bytesConsumed();
@ -1182,7 +840,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
final NIOBuffer i=_inNIOBuffer; final NIOBuffer i=_inNIOBuffer;
final NIOBuffer o=_outNIOBuffer; final NIOBuffer o=_outNIOBuffer;
return "SSL"+super.toString()+","+_engine.getHandshakeStatus()+", in/out="+ return "SSL"+super.toString()+","+_engine.getHandshakeStatus()+", in/out="+
(i==null?0:_inNIOBuffer.length())+"/"+(o==null?0:o.length())+ (i==null?0:i.length())+"/"+(o==null?0:o.length())+
" bi/o="+isBufferingInput()+"/"+isBufferingOutput()+ " bi/o="+isBufferingInput()+"/"+isBufferingOutput()+
" "+_result; " "+_result;
} }

View File

@ -26,6 +26,7 @@ public class AsyncHttpConnection extends HttpConnection
public Connection handle() throws IOException public Connection handle() throws IOException
{ {
LOG.debug("handle {}",this);
Connection connection = this; Connection connection = this;
boolean some_progress=false; boolean some_progress=false;
boolean progress=true; boolean progress=true;
@ -42,7 +43,6 @@ public class AsyncHttpConnection extends HttpConnection
progress=false; progress=false;
try try
{ {
LOG.debug("async request",_request);
// Handle resumed request // Handle resumed request
if (_request._async.isAsync() && !_request._async.isComplete()) if (_request._async.isAsync() && !_request._async.isComplete())
@ -145,6 +145,7 @@ public class AsyncHttpConnection extends HttpConnection
} }
} }
} }
LOG.debug("unhandle {}",this);
} }
return connection; return connection;
} }

View File

@ -490,17 +490,17 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
configureServer(new BigBlockHandler()); configureServer(new BigBlockHandler());
Socket client=newSocket(HOST,_connector.getLocalPort()); Socket client=newSocket(HOST,_connector.getLocalPort());
client.setSoTimeout(10000); client.setSoTimeout(20000);
try try
{ {
OutputStream os=client.getOutputStream(); OutputStream os=client.getOutputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream())); BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
os.write(( os.write((
"GET / HTTP/1.1\r\n"+ "GET /r1 HTTP/1.1\r\n"+
"host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+ "host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+
"\r\n"+ "\r\n"+
"GET / HTTP/1.1\r\n"+ "GET /r2 HTTP/1.1\r\n"+
"host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+ "host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+
"connection: close\r\n"+ "connection: close\r\n"+
"\r\n" "\r\n"
@ -583,6 +583,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
} }
} }
// Handler that sends big blocks of data in each of 10 writes, and then sends the time it took for each big block.
protected static class BigBlockHandler extends AbstractHandler protected static class BigBlockHandler extends AbstractHandler
{ {
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
@ -598,10 +599,12 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
long[] times=new long[10]; long[] times=new long[10];
for (int i=0;i<times.length;i++) for (int i=0;i<times.length;i++)
{ {
System.err.println("\nBLOCK "+request.getRequestURI()+" "+i);
long start=System.currentTimeMillis(); long start=System.currentTimeMillis();
out.write(buf); out.write(buf);
long end=System.currentTimeMillis(); long end=System.currentTimeMillis();
times[i]=end-start; times[i]=end-start;
System.err.println("Block "+request.getRequestURI()+" "+i+" "+times[i]);
} }
out.println(); out.println();
for (long t : times) for (long t : times)

View File

@ -0,0 +1,181 @@
//========================================================================
//Copyright 2004-2008 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================
// JettyTest.java --
//
// Junit test that shows the Jetty SSL bug.
//
package org.eclipse.jetty.server.ssl;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import junit.framework.TestCase;
import org.eclipse.jetty.io.nio.SslSelectChannelEndPoint;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
/**
* HttpServer Tester.
*/
public class SSLCloseTest extends TestCase
{
private static SslSelectChannelEndPoint __endp;
private static class CredulousTM implements TrustManager, X509TrustManager
{
public X509Certificate[] getAcceptedIssuers()
{
return null;
}
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException
{
return;
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException
{
return;
}
}
private static final TrustManager[] s_dummyTrustManagers=new TrustManager[] { new CredulousTM() };
// ~ Methods
// ----------------------------------------------------------------
/**
* Feed the server the entire request at once.
*
* @throws Exception
*/
public void testClose() throws Exception
{
Server server=new Server();
SslSelectChannelConnector connector=new SslSelectChannelConnector();
String keystore = System.getProperty("user.dir")+File.separator+"src"+File.separator+"test"+File.separator+"resources"+File.separator+"keystore";
connector.setPort(0);
connector.setKeystore(keystore);
connector.setPassword("storepwd");
connector.setKeyPassword("keypwd");
server.setConnectors(new Connector[]
{ connector });
server.setHandler(new WriteHandler());
server.start();
SSLContext ctx=SSLContext.getInstance("SSLv3");
ctx.init(null,s_dummyTrustManagers,new java.security.SecureRandom());
int port=connector.getLocalPort();
// System.err.println("write:"+i);
Socket socket=ctx.getSocketFactory().createSocket("localhost",port);
OutputStream os=socket.getOutputStream();
os.write("GET /test HTTP/1.1\r\nHost:test\r\nConnection:close\r\n\r\n".getBytes());
os.flush();
BufferedReader in =new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while ((line=in.readLine())!=null)
{
System.err.println(line);
if (line.trim().length()==0)
break;
}
Thread.sleep(2000);
System.err.println(__endp);
while ((line=in.readLine())!=null)
System.err.println(line);
}
private static class WriteHandler extends AbstractHandler
{
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
try
{
baseRequest.setHandled(true);
response.setStatus(200);
response.setHeader("test","value");
__endp=(SslSelectChannelEndPoint)baseRequest.getConnection().getEndPoint();
OutputStream out=response.getOutputStream();
String data = "Now is the time for all good men to come to the aid of the party.\n";
data+="How now brown cow.\n";
data+="The quick brown fox jumped over the lazy dog.\n";
// data=data+data+data+data+data+data+data+data+data+data+data+data+data;
// data=data+data+data+data+data+data+data+data+data+data+data+data+data;
data=data+data+data+data;
byte[] bytes=data.getBytes("UTF-8");
for (int i=0;i<2;i++)
{
System.err.println("Write "+i+" "+bytes.length);
out.write(bytes);
}
}
catch(RuntimeException e)
{
e.printStackTrace();
throw e;
}
catch(IOException e)
{
e.printStackTrace();
throw e;
}
catch(Error e)
{
e.printStackTrace();
throw e;
}
catch(Throwable e)
{
e.printStackTrace();
throw new ServletException(e);
}
}
}
}

View File

@ -23,6 +23,7 @@ import javax.net.ssl.TrustManagerFactory;
import org.eclipse.jetty.http.ssl.SslContextFactory; import org.eclipse.jetty.http.ssl.SslContextFactory;
import org.eclipse.jetty.server.HttpServerTestBase; import org.eclipse.jetty.server.HttpServerTestBase;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test;
/** /**
* HttpServer Tester. * HttpServer Tester.
@ -79,5 +80,20 @@ public class SslSelectChannelServerTest extends HttpServerTestBase
} }
@Test
@Override
public void testBlockingWhileWritingResponseContent() throws Exception
{
super.testBlockingWhileWritingResponseContent();
}
@Test
@Override
public void testBigBlocks() throws Exception
{
super.testBigBlocks();
}
} }