View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.io.ssl;
20  
21  import java.io.IOException;
22  import java.net.SocketException;
23  import java.nio.ByteBuffer;
24  import java.nio.channels.ClosedChannelException;
25  import java.util.Arrays;
26  import java.util.concurrent.Executor;
27  
28  import javax.net.ssl.SSLEngine;
29  import javax.net.ssl.SSLEngineResult;
30  import javax.net.ssl.SSLEngineResult.HandshakeStatus;
31  import javax.net.ssl.SSLEngineResult.Status;
32  import javax.net.ssl.SSLException;
33  
34  import org.eclipse.jetty.io.AbstractConnection;
35  import org.eclipse.jetty.io.AbstractEndPoint;
36  import org.eclipse.jetty.io.ByteBufferPool;
37  import org.eclipse.jetty.io.Connection;
38  import org.eclipse.jetty.io.EndPoint;
39  import org.eclipse.jetty.io.EofException;
40  import org.eclipse.jetty.io.FillInterest;
41  import org.eclipse.jetty.io.RuntimeIOException;
42  import org.eclipse.jetty.io.SelectChannelEndPoint;
43  import org.eclipse.jetty.io.SocketBased;
44  import org.eclipse.jetty.io.WriteFlusher;
45  import org.eclipse.jetty.util.BufferUtil;
46  import org.eclipse.jetty.util.Callback;
47  import org.eclipse.jetty.util.log.Log;
48  import org.eclipse.jetty.util.log.Logger;
49  
50  /**
51   * A Connection that acts as an interceptor between an EndPoint providing SSL encrypted data
52   * and another consumer of an EndPoint (typically an {@link Connection} like HttpConnection) that
53   * wants unencrypted data.
54   * <p>
55   * The connector uses an {@link EndPoint} (typically {@link SelectChannelEndPoint}) as
56   * it's source/sink of encrypted data.   It then provides an endpoint via {@link #getDecryptedEndPoint()} to
57   * expose a source/sink of unencrypted data to another connection (eg HttpConnection).
58   * <p>
59   * The design of this class is based on a clear separation between the passive methods, which do not block nor schedule any
60   * asynchronous callbacks, and active methods that do schedule asynchronous callbacks.
61   * <p>
62   * The passive methods are {@link DecryptedEndPoint#fill(ByteBuffer)} and {@link DecryptedEndPoint#flush(ByteBuffer...)}. They make best
63   * effort attempts to progress the connection using only calls to the encrypted {@link EndPoint#fill(ByteBuffer)} and {@link EndPoint#flush(ByteBuffer...)}
64   * methods.  They will never block nor schedule any readInterest or write callbacks.   If a fill/flush cannot progress either because
65   * of network congestion or waiting for an SSL handshake message, then the fill/flush will simply return with zero bytes filled/flushed.
66   * Specifically, if a flush cannot proceed because it needs to receive a handshake message, then the flush will attempt to fill bytes from the
67   * encrypted endpoint, but if insufficient bytes are read it will NOT call {@link EndPoint#fillInterested(Callback)}.
68   * <p>
69   * It is only the active methods : {@link DecryptedEndPoint#fillInterested(Callback)} and
70   * {@link DecryptedEndPoint#write(Object, Callback, ByteBuffer...)} that may schedule callbacks by calling the encrypted
71   * {@link EndPoint#fillInterested(Callback)} and {@link EndPoint#write(Object, Callback, ByteBuffer...)}
72   * methods.  For normal data handling, the decrypted fillInterest method will result in an encrypted fillInterest and a decrypted
73   * write will result in an encrypted write. However, due to SSL handshaking requirements, it is also possible for a decrypted fill
74   * to call the encrypted write and for the decrypted flush to call the encrypted fillInterested methods.
75   * <p>
76   * MOST IMPORTANTLY, the encrypted callbacks from the active methods (#onFillable() and WriteFlusher#completeWrite()) do no filling or flushing
77   * themselves.  Instead they simple make the callbacks to the decrypted callbacks, so that the passive encrypted fill/flush will
78   * be called again and make another best effort attempt to progress the connection.
79   *
80   */
81  public class SslConnection extends AbstractConnection
82  {
83      private static final Logger LOG = Log.getLogger(SslConnection.class);
84      private static final boolean DEBUG = LOG.isDebugEnabled(); // Easy for the compiler to remove the code if DEBUG==false
85      private static final ByteBuffer __FILL_CALLED_FLUSH= BufferUtil.allocate(0);
86      private static final ByteBuffer __FLUSH_CALLED_FILL= BufferUtil.allocate(0);
87      private final ByteBufferPool _bufferPool;
88      private final SSLEngine _sslEngine;
89      private final DecryptedEndPoint _decryptedEndPoint;
90      private ByteBuffer _decryptedInput;
91      private ByteBuffer _encryptedInput;
92      private ByteBuffer _encryptedOutput;
93      private final boolean _encryptedDirectBuffers = false;
94      private final boolean _decryptedDirectBuffers = false;
95      private final Runnable _runCompletWrite = new Runnable()
96      {
97          @Override
98          public void run()
99          {
100             _decryptedEndPoint.getWriteFlusher().completeWrite();
101         }
102     };
103 
104     public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine)
105     {
106         // This connection does not execute calls to onfillable, so they will be called by the selector thread.
107         // onfillable does not block and will only wakeup another thread to do the actual reading and handling.
108         super(endPoint, executor, !EXECUTE_ONFILLABLE);
109         this._bufferPool = byteBufferPool;
110         this._sslEngine = sslEngine;
111         this._decryptedEndPoint = newDecryptedEndPoint();
112 
113         if (endPoint instanceof SocketBased)
114         {
115             try
116             {
117                 ((SocketBased) endPoint).getSocket().setSoLinger(true,30000);
118             }
119             catch (SocketException e)
120             {
121                 throw new RuntimeIOException(e);
122             }
123         }
124     }
125 
126     protected DecryptedEndPoint newDecryptedEndPoint()
127     {
128         return new DecryptedEndPoint();
129     }
130 
131     public SSLEngine getSSLEngine()
132     {
133         return _sslEngine;
134     }
135 
136     public DecryptedEndPoint getDecryptedEndPoint()
137     {
138         return _decryptedEndPoint;
139     }
140 
141     @Override
142     public void onOpen()
143     {
144         try
145         {
146             // Begin the handshake
147             _sslEngine.beginHandshake();
148             super.onOpen();
149             getDecryptedEndPoint().getConnection().onOpen();
150         }
151         catch (SSLException x)
152         {
153             getEndPoint().close();
154             throw new RuntimeIOException(x);
155         }
156     }
157 
158     @Override
159     public void onClose()
160     {
161         _decryptedEndPoint.getConnection().onClose();
162         super.onClose();
163     }
164 
165 //    @Override
166 //    public int getMessagesIn()
167 //    {
168 //        return _decryptedEndPoint.getConnection().getMessagesIn();
169 //    }
170 //
171 //    @Override
172 //    public int getMessagesOut()
173 //    {
174 //        return _decryptedEndPoint.getConnection().getMessagesOut();
175 //    }
176 
177     @Override
178     public void close()
179     {
180         getDecryptedEndPoint().getConnection().close();
181     }
182 
183     /* ------------------------------------------------------------ */
184     @Override
185     public void onFillable()
186     {
187         // onFillable means that there are encrypted bytes ready to be filled.
188         // however we do not fill them here on this callback, but instead wakeup
189         // the decrypted readInterest and/or writeFlusher so that they will attempt
190         // to do the fill and/or flush again and these calls will do the actually
191         // filling.
192 
193         if (DEBUG)
194             LOG.debug("onFillable enter {}", getEndPoint());
195 
196         // wake up whoever is doing the fill or the flush so they can
197         // do all the filling, unwrapping, wrapping and flushing
198         _decryptedEndPoint.getFillInterest().fillable();
199 
200         // If we are handshaking, then wake up any waiting write as well as it may have been blocked on the read
201         synchronized(_decryptedEndPoint)
202         {
203             if (_decryptedEndPoint._flushRequiresFillToProgress)
204             {
205                 _decryptedEndPoint._flushRequiresFillToProgress = false;
206                 getExecutor().execute(_runCompletWrite);
207             }
208         }
209 
210         if (DEBUG)
211             LOG.debug("onFillable exit {}", getEndPoint());
212     }
213 
214     /* ------------------------------------------------------------ */
215     @Override
216     public void onFillInterestedFailed(Throwable cause)
217     {
218         // this means that the fill interest in encrypted bytes has failed.
219         // However we do not handle that here on this callback, but instead wakeup
220         // the decrypted readInterest and/or writeFlusher so that they will attempt
221         // to do the fill and/or flush again and these calls will do the actually
222         // handle the cause.
223         synchronized(_decryptedEndPoint)
224         {
225             _decryptedEndPoint.getFillInterest().onFail(cause);
226 
227             if (_decryptedEndPoint._flushRequiresFillToProgress)
228             {
229                 _decryptedEndPoint._flushRequiresFillToProgress = false;
230                 _decryptedEndPoint.getWriteFlusher().onFail(cause);
231             }
232         }
233     }
234 
235     /* ------------------------------------------------------------ */
236     @Override
237     public String toString()
238     {
239         ByteBuffer b = _encryptedInput;
240         int ei=b==null?-1:b.remaining();
241         b = _encryptedOutput;
242         int eo=b==null?-1:b.remaining();
243         b = _decryptedInput;
244         int di=b==null?-1:b.remaining();
245 
246         return String.format("SslConnection@%x{%s,eio=%d/%d,di=%d} -> %s",
247                 hashCode(),
248                 _sslEngine.getHandshakeStatus(),
249                 ei,eo,di,
250                 _decryptedEndPoint.getConnection());
251     }
252 
253     /* ------------------------------------------------------------ */
254     public class DecryptedEndPoint extends AbstractEndPoint
255     {
256         private boolean _fillRequiresFlushToProgress;
257         private boolean _flushRequiresFillToProgress;
258         private boolean _cannotAcceptMoreAppDataToFlush;
259         private boolean _underFlown;
260 
261         private final Callback _writeCallback = new Callback()
262         {
263             @Override
264             public void succeeded()
265             {
266                 // This means that a write of encrypted data has completed.  Writes are done
267                 // only if there is a pending writeflusher or a read needed to write
268                 // data.  In either case the appropriate callback is passed on.
269                 synchronized (DecryptedEndPoint.this)
270                 {
271                     if (DEBUG)
272                         LOG.debug("write.complete {}", SslConnection.this.getEndPoint());
273 
274                     releaseEncryptedOutputBuffer();
275 
276                     _cannotAcceptMoreAppDataToFlush = false;
277 
278                     if (_fillRequiresFlushToProgress)
279                     {
280                         _fillRequiresFlushToProgress = false;
281                         getFillInterest().fillable();
282                     }
283 
284                     getExecutor().execute(_runCompletWrite);
285                 }
286             }
287 
288             @Override
289             public void failed(Throwable x)
290             {
291                 // This means that a write of data has failed.  Writes are done
292                 // only if there is an active writeflusher or a read needed to write
293                 // data.  In either case the appropriate callback is passed on.
294                 synchronized (DecryptedEndPoint.this)
295                 {
296                     if (DEBUG)
297                         LOG.debug("{} write.failed", SslConnection.this, x);
298                     if (_encryptedOutput != null)
299                         BufferUtil.clear(_encryptedOutput);
300                     releaseEncryptedOutputBuffer();
301 
302                     _cannotAcceptMoreAppDataToFlush = false;
303 
304                     if (_fillRequiresFlushToProgress)
305                     {
306                         _fillRequiresFlushToProgress = false;
307                         getFillInterest().onFail(x);
308                     }
309 
310                     getWriteFlusher().onFail(x);
311                 }
312             }
313         };
314 
315         public DecryptedEndPoint()
316         {
317             super(null,getEndPoint().getLocalAddress(), getEndPoint().getRemoteAddress());
318             setIdleTimeout(getEndPoint().getIdleTimeout());
319         }
320 
321         @Override
322         protected FillInterest getFillInterest()
323         {
324             return super.getFillInterest();
325         }
326 
327         @Override
328         public void setIdleTimeout(long idleTimeout)
329         {
330             super.setIdleTimeout(idleTimeout);
331             getEndPoint().setIdleTimeout(idleTimeout);
332         }
333 
334         @Override
335         protected WriteFlusher getWriteFlusher()
336         {
337             return super.getWriteFlusher();
338         }
339 
340         @Override
341         protected void onIncompleteFlush()
342         {
343             // This means that the decrypted endpoint write method was called and not
344             // all data could be wrapped. So either we need to write some encrypted data,
345             // OR if we are handshaking we need to read some encrypted data OR
346             // if neither then we should just try the flush again.
347             synchronized (DecryptedEndPoint.this)
348             {
349                 if (DEBUG)
350                     LOG.debug("onIncompleteFlush {}", getEndPoint());
351                 // If we have pending output data,
352                 if (BufferUtil.hasContent(_encryptedOutput))
353                 {
354                     // write it
355                     _cannotAcceptMoreAppDataToFlush = true;
356                     getEndPoint().write(_writeCallback, _encryptedOutput);
357                 }
358                 else if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP)
359                 {
360                     // we are actually read blocked in order to write
361                     _flushRequiresFillToProgress=true;
362                     SslConnection.this.fillInterested();
363                 }
364                 else if (isOutputShutdown())
365                 {
366                     getWriteFlusher().onClose();
367                 }
368                 else
369                 {
370                     // try the flush again
371                     getWriteFlusher().completeWrite();
372                 }
373             }
374         }
375 
376         @Override
377         protected boolean needsFill() throws IOException
378         {
379             // This means that the decrypted data consumer has called the fillInterested
380             // method on the DecryptedEndPoint, so we have to work out if there is
381             // decrypted data to be filled or what callbacks to setup to be told when there
382             // might be more encrypted data available to attempt another call to fill
383 
384             synchronized (DecryptedEndPoint.this)
385             {
386                 // Do we already have some app data, then app can fill now so return true
387                 if (BufferUtil.hasContent(_decryptedInput))
388                     return true;
389 
390                 // If we have no encrypted data to decrypt OR we have some, but it is not enough
391                 if (BufferUtil.isEmpty(_encryptedInput) || _underFlown)
392                 {
393                     // We are not ready to read data
394 
395                     // Are we actually write blocked?
396                     if (_fillRequiresFlushToProgress)
397                     {
398                         // we must be blocked trying to write before we can read
399 
400                         // Do we have data to write
401                         if (BufferUtil.hasContent(_encryptedOutput))
402                         {
403                             // write it
404                             _cannotAcceptMoreAppDataToFlush = true;
405                             getEndPoint().write(_writeCallback, _encryptedOutput);
406                         }
407                         else
408                         {
409                             // we have already written the net data
410                             // pretend we are readable so the wrap is done by next readable callback
411                             _fillRequiresFlushToProgress = false;
412                             return true;
413                         }
414                     }
415                     else
416                         // Normal readable callback
417                         // Get called back on onfillable when then is more data to fill
418                         SslConnection.this.fillInterested();
419 
420                     return false;
421                 }
422                 else
423                 {
424                     // We are ready to read data
425                     return true;
426                 }
427             }
428         }
429 
430         @Override
431         public void setConnection(Connection connection)
432         {
433             if (connection instanceof AbstractConnection)
434             {
435                 AbstractConnection a = (AbstractConnection)connection;
436                 if (a.getInputBufferSize()<_sslEngine.getSession().getApplicationBufferSize())
437                     a.setInputBufferSize(_sslEngine.getSession().getApplicationBufferSize());
438             }
439             super.setConnection(connection);
440         }
441 
442         public SslConnection getSslConnection()
443         {
444             return SslConnection.this;
445         }
446 
447         @Override
448         public synchronized int fill(ByteBuffer buffer) throws IOException
449         {
450             if (DEBUG)
451                 LOG.debug("{} fill enter", SslConnection.this);
452             try
453             {
454                 // Do we already have some decrypted data?
455                 if (BufferUtil.hasContent(_decryptedInput))
456                     return BufferUtil.flipPutFlip(_decryptedInput, buffer);
457 
458                 // We will need a network buffer
459                 if (_encryptedInput == null)
460                     _encryptedInput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
461                 else
462                     BufferUtil.compact(_encryptedInput);
463 
464                 // We also need an app buffer, but can use the passed buffer if it is big enough
465                 ByteBuffer app_in;
466                 if (BufferUtil.space(buffer) > _sslEngine.getSession().getApplicationBufferSize())
467                     app_in = buffer;
468                 else if (_decryptedInput == null)
469                     app_in = _decryptedInput = _bufferPool.acquire(_sslEngine.getSession().getApplicationBufferSize(), _decryptedDirectBuffers);
470                 else
471                     app_in = _decryptedInput;
472 
473                 // loop filling and unwrapping until we have something
474                 while (true)
475                 {
476                     // Let's try reading some encrypted data... even if we have some already.
477                     int net_filled = getEndPoint().fill(_encryptedInput);
478                     if (DEBUG)
479                         LOG.debug("{} filled {} encrypted bytes", SslConnection.this, net_filled);
480                     if (net_filled > 0)
481                         _underFlown = false;
482 
483                     // Let's try the SSL thang even if we have no net data because in that
484                     // case we want to fall through to the handshake handling
485                     int pos = BufferUtil.flipToFill(app_in);
486 
487                     SSLEngineResult unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
488 
489                     BufferUtil.flipToFlush(app_in, pos);
490                     if (DEBUG)
491                         LOG.debug("{} unwrap {}", SslConnection.this, unwrapResult);
492 
493                     // and deal with the results
494                     switch (unwrapResult.getStatus())
495                     {
496                         case BUFFER_OVERFLOW:
497                             throw new IllegalStateException();
498 
499                         case CLOSED:
500                             // Dang! we have to care about the handshake state specially for close
501                             switch (_sslEngine.getHandshakeStatus())
502                             {
503                                 case NOT_HANDSHAKING:
504                                     // We were not handshaking, so just tell the app we are closed
505                                     return -1;
506 
507                                 case NEED_TASK:
508                                     // run the task
509                                     _sslEngine.getDelegatedTask().run();
510                                     continue;
511 
512                                 case NEED_WRAP:
513                                     // we need to send some handshake data (probably to send a close handshake).
514                                     // but that will not enable any extra data to fill, so we just return -1
515                                     // The wrapping can be done by any output drivers doing flushing or shutdown output.
516                                     return -1;
517                             }
518                             throw new IllegalStateException();
519 
520                         default:
521                             if (unwrapResult.getStatus()==Status.BUFFER_UNDERFLOW)
522                                 _underFlown=true;
523 
524                             // if we produced bytes, we don't care about the handshake state for now and it can be dealt with on another call to fill or flush
525                             if (unwrapResult.bytesProduced() > 0)
526                             {
527                                 if (app_in == buffer)
528                                     return unwrapResult.bytesProduced();
529                                 return BufferUtil.flipPutFlip(_decryptedInput, buffer);
530                             }
531 
532                             // Dang! we have to care about the handshake state
533                             switch (_sslEngine.getHandshakeStatus())
534                             {
535                                 case NOT_HANDSHAKING:
536                                     // we just didn't read anything.
537                                     if (net_filled < 0)
538                                         _sslEngine.closeInbound();
539 
540                                     return 0;
541 
542                                 case NEED_TASK:
543                                     // run the task
544                                     _sslEngine.getDelegatedTask().run();
545                                     continue;
546 
547                                 case NEED_WRAP:
548                                     // we need to send some handshake data
549 
550                                     // if we are called from flush
551                                     if (buffer==__FLUSH_CALLED_FILL)
552                                         return 0; // let it do the wrapping
553 
554                                     _fillRequiresFlushToProgress = true;
555                                     flush(__FILL_CALLED_FLUSH);
556                                     if (BufferUtil.isEmpty(_encryptedOutput))
557                                     {
558                                         // the flush completed so continue
559                                         _fillRequiresFlushToProgress = false;
560                                         continue;
561                                     }
562                                     return 0;
563 
564                                 case NEED_UNWRAP:
565                                     // if we just filled some net data
566                                     if (net_filled < 0)
567                                     {
568                                         // If we call closeInbound() before having read the SSL close
569                                         // message an exception will be thrown (truncation attack).
570                                         // The TLS specification says that the sender of the SSL close
571                                         // message may just close and avoid to read the response.
572                                         // If that is the case, we avoid calling closeInbound() because
573                                         // will throw the truncation attack exception for nothing.
574                                         if (isOpen())
575                                             _sslEngine.closeInbound();
576                                     }
577                                     else if (net_filled > 0)
578                                     {
579                                         // maybe we will fill some more on a retry
580                                         continue;
581                                     }
582                                     // we need to wait for more net data
583                                     return 0;
584 
585                                 case FINISHED:
586                                     throw new IllegalStateException();
587                             }
588                     }
589                 }
590             }
591             catch (SSLException e)
592             {
593                 getEndPoint().close();
594                 throw new EofException(e);
595             }
596             catch (Exception e)
597             {
598                 getEndPoint().close();
599                 throw e;
600             }
601             finally
602             {
603                 // If we are handshaking, then wake up any waiting write as well as it may have been blocked on the read
604                 if (_flushRequiresFillToProgress)
605                 {
606                     _flushRequiresFillToProgress = false;
607                     getExecutor().execute(_runCompletWrite);
608                 }
609 
610                 if (_encryptedInput != null && !_encryptedInput.hasRemaining())
611                 {
612                     _bufferPool.release(_encryptedInput);
613                     _encryptedInput = null;
614                 }
615                 if (_decryptedInput != null && !_decryptedInput.hasRemaining())
616                 {
617                     _bufferPool.release(_decryptedInput);
618                     _decryptedInput = null;
619                 }
620                 if (DEBUG)
621                     LOG.debug("{} fill exit", SslConnection.this);
622             }
623         }
624 
625         @Override
626         public synchronized boolean flush(ByteBuffer... appOuts) throws IOException
627         {
628             // The contract for flush does not require that all appOuts bytes are written
629             // or even that any appOut bytes are written!  If the connection is write block
630             // or busy handshaking, then zero bytes may be taken from appOuts and this method
631             // will return 0 (even if some handshake bytes were flushed and filled).
632             // it is the applications responsibility to call flush again - either in a busy loop
633             // or better yet by using EndPoint#write to do the flushing.
634 
635             if (DEBUG)
636                 LOG.debug("{} flush enter {}", SslConnection.this, Arrays.toString(appOuts));
637             int consumed=0;
638             try
639             {
640                 if (_cannotAcceptMoreAppDataToFlush)
641                 {
642                     if (_sslEngine.isOutboundDone())
643                         throw new EofException(new ClosedChannelException());
644                     return false;
645                 }
646 
647                 // We will need a network buffer
648                 if (_encryptedOutput == null)
649                     _encryptedOutput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
650 
651                 while (true)
652                 {
653                     // do the funky SSL thang!
654                     // We call sslEngine.wrap to try to take bytes from appOut buffers and encrypt them into the _netOut buffer
655                     BufferUtil.compact(_encryptedOutput);
656                     int pos = BufferUtil.flipToFill(_encryptedOutput);
657                     SSLEngineResult wrapResult = _sslEngine.wrap(appOuts, _encryptedOutput);
658                     if (DEBUG)
659                         LOG.debug("{} wrap {}", SslConnection.this, wrapResult);
660                     BufferUtil.flipToFlush(_encryptedOutput, pos);
661                     if (wrapResult.bytesConsumed()>0)
662                         consumed+=wrapResult.bytesConsumed();
663 
664                     boolean all_consumed=true;
665                     // clear empty buffers to prevent position creeping up the buffer
666                     for (ByteBuffer b : appOuts)
667                     {
668                         if (BufferUtil.isEmpty(b))
669                             BufferUtil.clear(b);
670                         else
671                             all_consumed=false;
672                     }
673 
674                     // and deal with the results returned from the sslEngineWrap
675                     switch (wrapResult.getStatus())
676                     {
677                         case CLOSED:
678                             // The SSL engine has close, but there may be close handshake that needs to be written
679                             if (BufferUtil.hasContent(_encryptedOutput))
680                             {
681                                 _cannotAcceptMoreAppDataToFlush = true;
682                                 getEndPoint().flush(_encryptedOutput);
683                                 // If we failed to flush the close handshake then we will just pretend that
684                                 // the write has progressed normally and let a subsequent call to flush (or WriteFlusher#onIncompleteFlushed)
685                                 // to finish writing the close handshake.   The caller will find out about the close on a subsequent flush or fill.
686                                 if (BufferUtil.hasContent(_encryptedOutput))
687                                     return false;
688                             }
689 
690                             // otherwise we have written, and the caller will close the underlying connection
691                             return all_consumed;
692 
693                         case BUFFER_UNDERFLOW:
694                             throw new IllegalStateException();
695 
696                         default:
697                             if (DEBUG)
698                                 LOG.debug("{} {} {}", this, wrapResult.getStatus(), BufferUtil.toDetailString(_encryptedOutput));
699 
700                             // if we have net bytes, let's try to flush them
701                             if (BufferUtil.hasContent(_encryptedOutput))
702                                 getEndPoint().flush(_encryptedOutput);
703 
704                             // But we also might have more to do for the handshaking state.
705                             switch (_sslEngine.getHandshakeStatus())
706                             {
707                                 case NOT_HANDSHAKING:
708                                     // Return with the number of bytes consumed (which may be 0)
709                                     return all_consumed&&BufferUtil.isEmpty(_encryptedOutput);
710 
711                                 case NEED_TASK:
712                                     // run the task and continue
713                                     _sslEngine.getDelegatedTask().run();
714                                     continue;
715 
716                                 case NEED_WRAP:
717                                     // Hey we just wrapped! Oh well who knows what the sslEngine is thinking, so continue and we will wrap again
718                                     continue;
719 
720                                 case NEED_UNWRAP:
721                                     // Ah we need to fill some data so we can write.
722                                     // So if we were not called from fill and the app is not reading anyway
723                                     if (appOuts[0]!=__FILL_CALLED_FLUSH && !getFillInterest().isInterested())
724                                     {
725                                         // Tell the onFillable method that there might be a write to complete
726                                         _flushRequiresFillToProgress = true;
727                                         fill(__FLUSH_CALLED_FILL);
728                                         // Check if after the fill() we need to wrap again
729                                         if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_WRAP)
730                                             continue;
731                                     }
732                                     return all_consumed&&BufferUtil.isEmpty(_encryptedOutput);
733 
734                                 case FINISHED:
735                                     throw new IllegalStateException();
736 
737                             }
738                     }
739                 }
740             }
741             catch (Exception e)
742             {
743                 getEndPoint().close();
744                 throw e;
745             }
746             finally
747             {
748                 if (DEBUG)
749                     LOG.debug("{} flush exit, consumed {}", SslConnection.this, consumed);
750                 releaseEncryptedOutputBuffer();
751             }
752         }
753 
754         private void releaseEncryptedOutputBuffer()
755         {
756             if (!Thread.holdsLock(DecryptedEndPoint.this))
757                 throw new IllegalStateException();
758             if (_encryptedOutput != null && !_encryptedOutput.hasRemaining())
759             {
760                 _bufferPool.release(_encryptedOutput);
761                 _encryptedOutput = null;
762                 if (_sslEngine.isOutboundDone())
763                     getEndPoint().shutdownOutput();
764             }
765         }
766 
767         @Override
768         public void shutdownOutput()
769         {
770             _sslEngine.closeOutbound();
771             try
772             {
773                 flush(BufferUtil.EMPTY_BUFFER);
774             }
775             catch (IOException e)
776             {
777                 LOG.ignore(e);
778                 getEndPoint().close();
779             }
780         }
781 
782         @Override
783         public boolean isOutputShutdown()
784         {
785             return _sslEngine.isOutboundDone() || getEndPoint().isOutputShutdown();
786         }
787 
788         @Override
789         public void close()
790         {
791             getEndPoint().close();
792         }
793 
794         @Override
795         public boolean isOpen()
796         {
797             return getEndPoint().isOpen();
798         }
799 
800         @Override
801         public Object getTransport()
802         {
803             return getEndPoint();
804         }
805 
806         @Override
807         public boolean isInputShutdown()
808         {
809             return _sslEngine.isInboundDone();
810         }
811 
812         @Override
813         public String toString()
814         {
815             return super.toString()+"->"+getEndPoint().toString();
816         }
817     }
818 }