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