357240 safer SSL close
This commit is contained in:
parent
4b12b1b90b
commit
44923230e3
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue