View Javadoc

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