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 }