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