Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'jetty-http/src/main/java/org/eclipse/jetty/http/ssl/SslSelectChannelEndPoint.java')
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/ssl/SslSelectChannelEndPoint.java789
1 files changed, 789 insertions, 0 deletions
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/ssl/SslSelectChannelEndPoint.java b/jetty-http/src/main/java/org/eclipse/jetty/http/ssl/SslSelectChannelEndPoint.java
new file mode 100644
index 0000000000..0357f57c32
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/ssl/SslSelectChannelEndPoint.java
@@ -0,0 +1,789 @@
+// ========================================================================
+// Copyright (c) 2004-2009 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.http.ssl;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.Buffers;
+import org.eclipse.jetty.io.nio.NIOBuffer;
+import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
+import org.eclipse.jetty.io.nio.SelectorManager;
+import org.eclipse.jetty.util.log.Log;
+
+/* ------------------------------------------------------------ */
+/**
+ * SslHttpChannelEndPoint.
+ *
+ *
+ *
+ */
+public class SslSelectChannelEndPoint extends SelectChannelEndPoint
+{
+ private static final ByteBuffer[] __NO_BUFFERS={};
+
+ private Buffers _buffers;
+
+ private SSLEngine _engine;
+ private ByteBuffer _inBuffer;
+ private NIOBuffer _inNIOBuffer;
+ private ByteBuffer _outBuffer;
+ private NIOBuffer _outNIOBuffer;
+
+ private NIOBuffer[] _reuseBuffer=new NIOBuffer[2];
+ private ByteBuffer[] _gather=new ByteBuffer[2];
+
+ private boolean _closing=false;
+ private SSLEngineResult _result;
+ private String _last;
+
+ // ssl
+ protected SSLSession _session;
+
+ // TODO get rid of this
+ // StringBuilder h = new StringBuilder(500);
+ /*
+ class H
+ {
+ H append(Object o)
+ {
+ System.err.print(o);
+ return this;
+ }
+ };
+ H h = new H();
+ */
+
+ /* ------------------------------------------------------------ */
+ public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine)
+ throws SSLException, IOException
+ {
+ super(channel,selectSet,key);
+ _buffers=buffers;
+
+ // ssl
+ _engine=engine;
+ _session=engine.getSession();
+
+ // TODO pool buffers and use only when needed.
+ _outNIOBuffer=(NIOBuffer)buffers.getBuffer(_session.getPacketBufferSize());
+ _outBuffer=_outNIOBuffer.getByteBuffer();
+ _inNIOBuffer=(NIOBuffer)buffers.getBuffer(_session.getPacketBufferSize());
+ _inBuffer=_inNIOBuffer.getByteBuffer();
+
+ // h.append("CONSTRUCTED\n");
+ }
+
+ // TODO get rid of these dumps
+ public void dump()
+ {
+ System.err.println(_result);
+ // System.err.println(h.toString());
+ // System.err.println("--");
+ }
+
+ /* ------------------------------------------------------------ */
+ /* (non-Javadoc)
+ * @see org.eclipse.io.nio.SelectChannelEndPoint#idleExpired()
+ */
+ protected void idleExpired()
+ {
+ try
+ {
+ _selectSet.getManager().dispatch(new Runnable()
+ {
+ public void run()
+ {
+ doIdleExpired();
+ }
+ });
+ }
+ catch(Exception e)
+ {
+ Log.ignore(e);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ protected void doIdleExpired()
+ {
+ // h.append("IDLE EXPIRED\n");
+ super.idleExpired();
+ }
+
+ /* ------------------------------------------------------------ */
+ public void close() throws IOException
+ {
+ // TODO - this really should not be done in a loop here - but with async callbacks.
+
+ // h.append("CLOSE\n");
+ _closing=true;
+ try
+ {
+ int tries=0;
+
+ while (_outNIOBuffer.length()>0)
+ {
+ // TODO REMOVE loop check
+ if (tries++>100)
+ throw new IllegalStateException();
+ flush();
+ Thread.sleep(100); // TODO yuck
+ }
+
+ _engine.closeOutbound();
+
+ loop: while (isOpen() && !(_engine.isInboundDone() && _engine.isOutboundDone()))
+ {
+ // TODO REMOVE loop check
+ if (tries++>100)
+ throw new IllegalStateException();
+
+ if (_outNIOBuffer.length()>0)
+ {
+ flush();
+ Thread.sleep(100); // TODO yuck
+ }
+
+ switch(_engine.getHandshakeStatus())
+ {
+ case FINISHED:
+ case NOT_HANDSHAKING:
+ break loop;
+
+ case NEED_UNWRAP:
+ Buffer buffer =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
+ try
+ {
+ ByteBuffer bbuffer = ((NIOBuffer)buffer).getByteBuffer();
+ if (!unwrap(bbuffer) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
+ {
+ // h.append("break loop\n");
+ break loop;
+ }
+ }
+ catch(SSLException e)
+ {
+ Log.ignore(e);
+ }
+ finally
+ {
+ _buffers.returnBuffer(buffer);
+ }
+ break;
+
+ case NEED_TASK:
+ {
+ Runnable task;
+ while ((task=_engine.getDelegatedTask())!=null)
+ {
+ task.run();
+ }
+ break;
+ }
+
+ case NEED_WRAP:
+ {
+ if (_outNIOBuffer.length()>0)
+ flush();
+
+ try
+ {
+ _outNIOBuffer.compact();
+ int put=_outNIOBuffer.putIndex();
+ _outBuffer.position(put);
+ _result=null;
+ _last="close wrap";
+ _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
+ _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
+ }
+ finally
+ {
+ _outBuffer.position(0);
+ }
+
+ flush();
+
+ break;
+ }
+ }
+ }
+ }
+ catch(IOException e)
+ {
+ Log.ignore(e);
+ }
+ catch (InterruptedException e)
+ {
+ Log.ignore(e);
+ }
+ finally
+ {
+ super.close();
+
+ if (_inNIOBuffer!=null)
+ _buffers.returnBuffer(_inNIOBuffer);
+ if (_outNIOBuffer!=null)
+ _buffers.returnBuffer(_outNIOBuffer);
+ if (_reuseBuffer[0]!=null)
+ _buffers.returnBuffer(_reuseBuffer[0]);
+ if (_reuseBuffer[1]!=null)
+ _buffers.returnBuffer(_reuseBuffer[1]);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ */
+ public int fill(Buffer buffer) throws IOException
+ {
+ ByteBuffer bbuf=extractInputBuffer(buffer);
+ int size=buffer.length();
+ HandshakeStatus initialStatus = _engine.getHandshakeStatus();
+ synchronized (bbuf)
+ {
+ try
+ {
+ unwrap(bbuf);
+
+ int tries=0, wraps=0;
+ loop: while (true)
+ {
+ // TODO REMOVE loop check
+ if (tries++>100)
+ throw new IllegalStateException();
+
+ // h.append("Fill(Buffer)\n");
+
+ if (_outNIOBuffer.length()>0)
+ flush();
+
+ // h.append("status=").append(_engine.getHandshakeStatus()).append('\n');
+ switch(_engine.getHandshakeStatus())
+ {
+ case FINISHED:
+ case NOT_HANDSHAKING:
+ if (_closing)
+ return -1;
+ break loop;
+
+ case NEED_UNWRAP:
+ if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
+ {
+ // h.append("break loop\n");
+ break loop;
+ }
+ break;
+
+ case NEED_TASK:
+ {
+ Runnable task;
+ while ((task=_engine.getDelegatedTask())!=null)
+ {
+ // h.append("run task\n");
+ task.run();
+ }
+ if(initialStatus==HandshakeStatus.NOT_HANDSHAKING &&
+ HandshakeStatus.NEED_UNWRAP==_engine.getHandshakeStatus() && 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
+ return -1;
+ }
+ break;
+ }
+
+ case NEED_WRAP:
+ {
+ wraps++;
+ synchronized(_outBuffer)
+ {
+ try
+ {
+ _outNIOBuffer.compact();
+ int put=_outNIOBuffer.putIndex();
+ _outBuffer.position();
+ _result=null;
+ _last="fill wrap";
+ _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
+ switch(_result.getStatus())
+ {
+ case BUFFER_OVERFLOW:
+ case BUFFER_UNDERFLOW:
+ Log.warn("wrap {}",_result);
+ case CLOSED:
+ _closing=true;
+ }
+
+ // h.append("wrap ").append(_result).append('\n');
+ _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
+ }
+ finally
+ {
+ _outBuffer.position(0);
+ }
+ }
+
+ flush();
+
+ break;
+ }
+ }
+ }
+ }
+ catch(SSLException e)
+ {
+ Log.warn(e.toString());
+ Log.debug(e);
+ throw e;
+ }
+ finally
+ {
+ buffer.setPutIndex(bbuf.position());
+ bbuf.position(0);
+ }
+ }
+ return buffer.length()-size;
+
+ }
+
+ /* ------------------------------------------------------------ */
+ public int flush(Buffer buffer) throws IOException
+ {
+ return flush(buffer,null,null);
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /*
+ */
+ public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
+ {
+ int consumed=0;
+ int available=header.length();
+ if (buffer!=null)
+ available+=buffer.length();
+
+ int tries=0;
+ loop: while (true)
+ {
+ // TODO REMOVE loop check
+ if (tries++>100)
+ throw new IllegalStateException();
+
+ // h.append("Flush ").append(tries).append(' ').append(_outNIOBuffer.length()).append('\n');
+
+ if (_outNIOBuffer.length()>0)
+ flush();
+
+ // h.append(_engine.getHandshakeStatus()).append('\n');
+
+ switch(_engine.getHandshakeStatus())
+ {
+ case FINISHED:
+ case NOT_HANDSHAKING:
+
+ if (_closing || available==0)
+ {
+ if (consumed==0)
+ consumed= -1;
+ break loop;
+ }
+
+ int c=(header!=null && buffer!=null)?wrap(header,buffer):wrap(header);
+ if (c>0)
+ {
+ consumed+=c;
+ available-=c;
+ }
+ else if (c<0)
+ {
+ if (consumed==0)
+ consumed=-1;
+ break loop;
+ }
+
+ break;
+
+ case NEED_UNWRAP:
+ Buffer buf =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
+ try
+ {
+ ByteBuffer bbuf = ((NIOBuffer)buf).getByteBuffer();
+ if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
+ {
+ // h.append("break").append('\n');
+ break loop;
+ }
+ }
+ finally
+ {
+ _buffers.returnBuffer(buf);
+ }
+
+ break;
+
+ case NEED_TASK:
+ {
+ Runnable task;
+ while ((task=_engine.getDelegatedTask())!=null)
+ {
+ // h.append("run task\n");
+ task.run();
+ }
+ break;
+ }
+
+ case NEED_WRAP:
+ {
+ synchronized(_outBuffer)
+ {
+ try
+ {
+ _outNIOBuffer.compact();
+ int put=_outNIOBuffer.putIndex();
+ _outBuffer.position();
+ _result=null;
+ _last="flush wrap";
+ _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
+ switch(_result.getStatus())
+ {
+ case BUFFER_OVERFLOW:
+ case BUFFER_UNDERFLOW:
+ Log.warn("unwrap {}",_result);
+ case CLOSED:
+ _closing=true;
+ }
+ // h.append("wrap=").append(_result).append('\n');
+ _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
+ }
+ finally
+ {
+ _outBuffer.position(0);
+ }
+ }
+
+ flush();
+
+ break;
+ }
+ }
+ }
+
+ return consumed;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public void flush() throws IOException
+ {
+ while (_outNIOBuffer.length()>0)
+ {
+ int flushed=super.flush(_outNIOBuffer);
+
+ // h.append("flushed=").append(flushed).append(" of ").append(_outNIOBuffer.length()).append('\n');
+ if (flushed==0)
+ {
+ Thread.yield();
+ flushed=super.flush(_outNIOBuffer);
+ // h.append("flushed2=").append(flushed).append(" of ").append(_outNIOBuffer.length()).append('\n');
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ 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
+ */
+ private boolean unwrap(ByteBuffer buffer) throws IOException
+ {
+ if (_inNIOBuffer.hasContent())
+ _inNIOBuffer.compact();
+ else
+ _inNIOBuffer.clear();
+
+ int total_filled=0;
+ while (_inNIOBuffer.space()>0 && super.isOpen())
+ {
+ try
+ {
+ int filled=super.fill(_inNIOBuffer);
+ // h.append("fill=").append(filled).append('\n');
+ if (filled<=0)
+ break;
+ total_filled+=filled;
+ }
+ catch(IOException e)
+ {
+ if (_inNIOBuffer.length()==0)
+ throw e;
+ break;
+ }
+ }
+
+ // h.append("inNIOBuffer=").append(_inNIOBuffer.length()).append('\n');
+
+ if (_inNIOBuffer.length()==0)
+ {
+ if(!isOpen())
+ throw new org.eclipse.jetty.io.EofException();
+ return false;
+ }
+
+ try
+ {
+ _inBuffer.position(_inNIOBuffer.getIndex());
+ _inBuffer.limit(_inNIOBuffer.putIndex());
+ _result=null;
+ _last="unwrap";
+ _result=_engine.unwrap(_inBuffer,buffer);
+ // h.append("unwrap=").append(_result).append('\n');
+ _inNIOBuffer.skip(_result.bytesConsumed());
+ }
+ finally
+ {
+ _inBuffer.position(0);
+ _inBuffer.limit(_inBuffer.capacity());
+ }
+
+
+ switch(_result.getStatus())
+ {
+ case BUFFER_OVERFLOW:
+ case BUFFER_UNDERFLOW:
+ if (Log.isDebugEnabled()) Log.debug("unwrap {}",_result);
+ return (total_filled > 0);
+
+ case CLOSED:
+ _closing=true;
+ case OK:
+ boolean progress=total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
+ // h.append("progress=").append(progress).append('\n');
+ return progress;
+ default:
+ Log.warn("unwrap "+_result);
+ throw new IOException(_result.toString());
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ private ByteBuffer extractOutputBuffer(Buffer buffer,int n)
+ {
+ NIOBuffer nBuf=null;
+
+ if (buffer.buffer() instanceof NIOBuffer)
+ {
+ nBuf=(NIOBuffer)buffer.buffer();
+ return nBuf.getByteBuffer();
+ }
+ else
+ {
+ if (_reuseBuffer[n]==null)
+ _reuseBuffer[n] = (NIOBuffer)_buffers.getBuffer(_session.getApplicationBufferSize());
+ NIOBuffer buf = _reuseBuffer[n];
+ buf.clear();
+ buf.put(buffer);
+ return buf.getByteBuffer();
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ private int wrap(Buffer header, Buffer buffer) throws IOException
+ {
+ _gather[0]=extractOutputBuffer(header,0);
+ synchronized(_gather[0])
+ {
+ _gather[0].position(header.getIndex());
+ _gather[0].limit(header.putIndex());
+
+ _gather[1]=extractOutputBuffer(buffer,1);
+
+ synchronized(_gather[1])
+ {
+ _gather[1].position(buffer.getIndex());
+ _gather[1].limit(buffer.putIndex());
+
+ synchronized(_outBuffer)
+ {
+ int consumed=0;
+ try
+ {
+ _outNIOBuffer.clear();
+ _outBuffer.position(0);
+ _outBuffer.limit(_outBuffer.capacity());
+
+ _result=null;
+ _last="wrap wrap";
+ _result=_engine.wrap(_gather,_outBuffer);
+ // h.append("wrap2=").append(_result).append('\n');
+ _outNIOBuffer.setGetIndex(0);
+ _outNIOBuffer.setPutIndex(_result.bytesProduced());
+ consumed=_result.bytesConsumed();
+ }
+ finally
+ {
+ _outBuffer.position(0);
+
+ if (consumed>0 && header!=null)
+ {
+ int len=consumed<header.length()?consumed:header.length();
+ header.skip(len);
+ consumed-=len;
+ _gather[0].position(0);
+ _gather[0].limit(_gather[0].capacity());
+ }
+ if (consumed>0 && buffer!=null)
+ {
+ int len=consumed<buffer.length()?consumed:buffer.length();
+ buffer.skip(len);
+ consumed-=len;
+ _gather[1].position(0);
+ _gather[1].limit(_gather[1].capacity());
+ }
+ assert consumed==0;
+ }
+ }
+ }
+ }
+
+
+ switch(_result.getStatus())
+ {
+ case BUFFER_OVERFLOW:
+ case BUFFER_UNDERFLOW:
+ Log.warn("unwrap {}",_result);
+
+ case OK:
+ return _result.bytesConsumed();
+ case CLOSED:
+ _closing=true;
+ return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
+
+ default:
+ Log.warn("wrap "+_result);
+ throw new IOException(_result.toString());
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ private int wrap(Buffer header) throws IOException
+ {
+ _gather[0]=extractOutputBuffer(header,0);
+ synchronized(_gather[0])
+ {
+ _gather[0].position(header.getIndex());
+ _gather[0].limit(header.putIndex());
+
+ int consumed=0;
+ synchronized(_outBuffer)
+ {
+ try
+ {
+ _outNIOBuffer.clear();
+ _outBuffer.position(0);
+ _outBuffer.limit(_outBuffer.capacity());
+ _result=null;
+ _last="wrap wrap";
+ _result=_engine.wrap(_gather[0],_outBuffer);
+ // h.append("wrap1=").append(_result).append('\n');
+ _outNIOBuffer.setGetIndex(0);
+ _outNIOBuffer.setPutIndex(_result.bytesProduced());
+ consumed=_result.bytesConsumed();
+ }
+ finally
+ {
+ _outBuffer.position(0);
+
+ if (consumed>0 && header!=null)
+ {
+ int len=consumed<header.length()?consumed:header.length();
+ header.skip(len);
+ consumed-=len;
+ _gather[0].position(0);
+ _gather[0].limit(_gather[0].capacity());
+ }
+ assert consumed==0;
+ }
+ }
+ }
+ switch(_result.getStatus())
+ {
+ case BUFFER_OVERFLOW:
+ case BUFFER_UNDERFLOW:
+ Log.warn("unwrap {}",_result);
+
+ case OK:
+ return _result.bytesConsumed();
+ case CLOSED:
+ _closing=true;
+ return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
+
+ default:
+ Log.warn("wrap "+_result);
+ throw new IOException(_result.toString());
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ public boolean isBufferingInput()
+ {
+ return _inNIOBuffer.hasContent();
+ }
+
+ /* ------------------------------------------------------------ */
+ public boolean isBufferingOutput()
+ {
+ return _outNIOBuffer.hasContent();
+ }
+
+ /* ------------------------------------------------------------ */
+ public boolean isBufferred()
+ {
+ return true;
+ }
+
+ /* ------------------------------------------------------------ */
+ public SSLEngine getSSLEngine()
+ {
+ return _engine;
+ }
+
+ /* ------------------------------------------------------------ */
+ public String toString()
+ {
+ return super.toString()+","+_engine.getHandshakeStatus()+", in/out="+_inNIOBuffer.length()+"/"+_outNIOBuffer.length()+" last "+_last+" "+_result;
+ }
+}

Back to the top