1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.reactor.ssl;
29
30 import java.io.IOException;
31 import java.net.SocketAddress;
32 import java.nio.ByteBuffer;
33 import java.nio.channels.ByteChannel;
34 import java.nio.channels.CancelledKeyException;
35 import java.nio.channels.ClosedChannelException;
36 import java.nio.channels.SelectionKey;
37 import java.util.concurrent.atomic.AtomicInteger;
38 import java.util.concurrent.atomic.AtomicReference;
39 import java.util.concurrent.locks.Lock;
40
41 import javax.net.ssl.SSLContext;
42 import javax.net.ssl.SSLEngine;
43 import javax.net.ssl.SSLEngineResult;
44 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
45 import javax.net.ssl.SSLException;
46 import javax.net.ssl.SSLSession;
47
48 import org.apache.hc.core5.annotation.Contract;
49 import org.apache.hc.core5.annotation.Internal;
50 import org.apache.hc.core5.annotation.ThreadingBehavior;
51 import org.apache.hc.core5.function.Callback;
52 import org.apache.hc.core5.io.CloseMode;
53 import org.apache.hc.core5.net.NamedEndpoint;
54 import org.apache.hc.core5.reactor.Command;
55 import org.apache.hc.core5.reactor.EventMask;
56 import org.apache.hc.core5.reactor.IOEventHandler;
57 import org.apache.hc.core5.reactor.IOSession;
58 import org.apache.hc.core5.util.Args;
59 import org.apache.hc.core5.util.Asserts;
60 import org.apache.hc.core5.util.ReflectionUtils;
61 import org.apache.hc.core5.util.Timeout;
62
63
64
65
66
67
68
69
70 @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
71 @Internal
72 public class SSLIOSession implements IOSession {
73
74 enum TLSHandShakeState { READY, INITIALIZED, HANDSHAKING, COMPLETE }
75
76 private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
77
78 private final NamedEndpoint targetEndpoint;
79 private final IOSession session;
80 private final SSLEngine sslEngine;
81 private final SSLManagedBuffer inEncrypted;
82 private final SSLManagedBuffer outEncrypted;
83 private final SSLManagedBuffer inPlain;
84 private final SSLSessionInitializer initializer;
85 private final SSLSessionVerifier verifier;
86 private final Callback<SSLIOSession> sessionStartCallback;
87 private final Callback<SSLIOSession> sessionEndCallback;
88 private final Timeout connectTimeout;
89 private final SSLMode sslMode;
90 private final AtomicInteger outboundClosedCount;
91 private final AtomicReference<TLSHandShakeState> handshakeStateRef;
92 private final IOEventHandler internalEventHandler;
93
94 private int appEventMask;
95
96 private volatile boolean endOfStream;
97 private volatile Status status = Status.ACTIVE;
98 private volatile Timeout socketTimeout;
99 private volatile TlsDetails tlsDetails;
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115 public SSLIOSession(
116 final NamedEndpoint targetEndpoint,
117 final IOSession session,
118 final SSLMode sslMode,
119 final SSLContext sslContext,
120 final SSLBufferMode sslBufferMode,
121 final SSLSessionInitializer initializer,
122 final SSLSessionVerifier verifier,
123 final Callback<SSLIOSession> sessionStartCallback,
124 final Callback<SSLIOSession> sessionEndCallback,
125 final Timeout connectTimeout) {
126 super();
127 Args.notNull(session, "IO session");
128 Args.notNull(sslContext, "SSL context");
129 this.targetEndpoint = targetEndpoint;
130 this.session = session;
131 this.sslMode = sslMode;
132 this.initializer = initializer;
133 this.verifier = verifier;
134 this.sessionStartCallback = sessionStartCallback;
135 this.sessionEndCallback = sessionEndCallback;
136
137 this.appEventMask = session.getEventMask();
138 if (this.sslMode == SSLMode.CLIENT && targetEndpoint != null) {
139 this.sslEngine = sslContext.createSSLEngine(targetEndpoint.getHostName(), targetEndpoint.getPort());
140 } else {
141 this.sslEngine = sslContext.createSSLEngine();
142 }
143
144 final SSLSession sslSession = this.sslEngine.getSession();
145
146 final int netBufferSize = sslSession.getPacketBufferSize();
147 this.inEncrypted = SSLManagedBuffer.create(sslBufferMode, netBufferSize);
148 this.outEncrypted = SSLManagedBuffer.create(sslBufferMode, netBufferSize);
149
150
151 final int appBufferSize = sslSession.getApplicationBufferSize();
152 this.inPlain = SSLManagedBuffer.create(sslBufferMode, appBufferSize);
153 this.outboundClosedCount = new AtomicInteger(0);
154 this.handshakeStateRef = new AtomicReference<>(TLSHandShakeState.READY);
155 this.connectTimeout = connectTimeout;
156 this.internalEventHandler = new IOEventHandler() {
157
158 @Override
159 public void connected(final IOSession protocolSession) throws IOException {
160 if (handshakeStateRef.compareAndSet(TLSHandShakeState.READY, TLSHandShakeState.INITIALIZED)) {
161 initialize(protocolSession);
162 }
163 }
164
165 @Override
166 public void inputReady(final IOSession protocolSession, final ByteBuffer src) throws IOException {
167 if (handshakeStateRef.compareAndSet(TLSHandShakeState.READY, TLSHandShakeState.INITIALIZED)) {
168 initialize(protocolSession);
169 }
170 receiveEncryptedData();
171 doHandshake(protocolSession);
172 decryptData(protocolSession);
173 updateEventMask();
174 }
175
176 @Override
177 public void outputReady(final IOSession protocolSession) throws IOException {
178 if (handshakeStateRef.compareAndSet(TLSHandShakeState.READY, TLSHandShakeState.INITIALIZED)) {
179 initialize(protocolSession);
180 }
181 encryptData(protocolSession);
182 sendEncryptedData();
183 doHandshake(protocolSession);
184 updateEventMask();
185 }
186
187 @Override
188 public void timeout(final IOSession protocolSession, final Timeout timeout) throws IOException {
189 if (sslEngine.isInboundDone() && !sslEngine.isInboundDone()) {
190
191 close(CloseMode.IMMEDIATE);
192 }
193 ensureHandler().timeout(protocolSession, timeout);
194 }
195
196 @Override
197 public void exception(final IOSession protocolSession, final Exception cause) {
198 final IOEventHandler handler = session.getHandler();
199 if (handshakeStateRef.get() != TLSHandShakeState.COMPLETE) {
200 session.close(CloseMode.GRACEFUL);
201 close(CloseMode.IMMEDIATE);
202 }
203 if (handler != null) {
204 handler.exception(protocolSession, cause);
205 }
206 }
207
208 @Override
209 public void disconnected(final IOSession protocolSession) {
210 final IOEventHandler handler = session.getHandler();
211 if (handler != null) {
212 handler.disconnected(protocolSession);
213 }
214 }
215
216 };
217
218 }
219
220 private IOEventHandler ensureHandler() {
221 final IOEventHandler handler = session.getHandler();
222 Asserts.notNull(handler, "IO event handler");
223 return handler;
224 }
225
226 @Override
227 public IOEventHandler getHandler() {
228 return internalEventHandler;
229 }
230
231 private void initialize(final IOSession protocolSession) throws IOException {
232
233 this.socketTimeout = this.session.getSocketTimeout();
234 if (connectTimeout != null) {
235 this.session.setSocketTimeout(connectTimeout);
236 }
237
238 this.session.getLock().lock();
239 try {
240 if (this.status.compareTo(Status.CLOSING) >= 0) {
241 return;
242 }
243 switch (this.sslMode) {
244 case CLIENT:
245 this.sslEngine.setUseClientMode(true);
246 break;
247 case SERVER:
248 this.sslEngine.setUseClientMode(false);
249 break;
250 }
251 if (this.initializer != null) {
252 this.initializer.initialize(this.targetEndpoint, this.sslEngine);
253 }
254 this.handshakeStateRef.set(TLSHandShakeState.HANDSHAKING);
255 this.sslEngine.beginHandshake();
256
257 this.inEncrypted.release();
258 this.outEncrypted.release();
259 doHandshake(protocolSession);
260 } finally {
261 this.session.getLock().unlock();
262 }
263 }
264
265
266
267
268
269
270 private SSLException convert(final RuntimeException ex) {
271 Throwable cause = ex.getCause();
272 if (cause == null) {
273 cause = ex;
274 }
275 return new SSLException(cause);
276 }
277
278 private SSLEngineResult doWrap(final ByteBuffer src, final ByteBuffer dst) throws SSLException {
279 try {
280 return this.sslEngine.wrap(src, dst);
281 } catch (final RuntimeException ex) {
282 throw convert(ex);
283 }
284 }
285
286 private SSLEngineResult doUnwrap(final ByteBuffer src, final ByteBuffer dst) throws SSLException {
287 try {
288 return this.sslEngine.unwrap(src, dst);
289 } catch (final RuntimeException ex) {
290 throw convert(ex);
291 }
292 }
293
294 private void doRunTask() {
295 final Runnable r = this.sslEngine.getDelegatedTask();
296 if (r != null) {
297 r.run();
298 }
299 }
300
301 private void doHandshake(final IOSession protocolSession) throws IOException {
302 boolean handshaking = true;
303
304 SSLEngineResult result = null;
305 while (handshaking) {
306 HandshakeStatus handshakeStatus = this.sslEngine.getHandshakeStatus();
307
308
309
310
311 if (handshakeStatus == HandshakeStatus.NOT_HANDSHAKING && outboundClosedCount.get() > 0) {
312 handshakeStatus = HandshakeStatus.NEED_WRAP;
313 }
314
315 switch (handshakeStatus) {
316 case NEED_WRAP:
317
318
319 this.session.getLock().lock();
320 try {
321
322 final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire();
323
324
325 result = doWrap(EMPTY_BUFFER, outEncryptedBuf);
326
327 if (result.getStatus() != SSLEngineResult.Status.OK || result.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
328 handshaking = false;
329 }
330 break;
331 } finally {
332 this.session.getLock().unlock();
333 }
334 case NEED_UNWRAP:
335
336
337
338 final ByteBuffer inEncryptedBuf = this.inEncrypted.acquire();
339 final ByteBuffer inPlainBuf = this.inPlain.acquire();
340
341
342 inEncryptedBuf.flip();
343 try {
344 result = doUnwrap(inEncryptedBuf, inPlainBuf);
345 } finally {
346 inEncryptedBuf.compact();
347 }
348
349 try {
350 if (!inEncryptedBuf.hasRemaining() && result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) {
351 throw new SSLException("Input buffer is full");
352 }
353 } finally {
354
355 if (inEncryptedBuf.position() == 0) {
356 this.inEncrypted.release();
357 }
358 }
359
360 if (this.status.compareTo(Status.CLOSING) >= 0) {
361 this.inPlain.release();
362 }
363 if (result.getStatus() != SSLEngineResult.Status.OK) {
364 handshaking = false;
365 }
366 break;
367 case NEED_TASK:
368 doRunTask();
369 break;
370 case NOT_HANDSHAKING:
371 handshaking = false;
372 break;
373 }
374 }
375
376
377
378
379 if (result != null && result.getHandshakeStatus() == HandshakeStatus.FINISHED) {
380 this.session.setSocketTimeout(this.socketTimeout);
381 if (this.verifier != null) {
382 this.tlsDetails = this.verifier.verify(this.targetEndpoint, this.sslEngine);
383 }
384 if (this.tlsDetails == null) {
385 final SSLSession sslSession = this.sslEngine.getSession();
386 final String applicationProtocol = ReflectionUtils.callGetter(this.sslEngine, "ApplicationProtocol", String.class);
387 this.tlsDetails = new TlsDetails(sslSession, applicationProtocol);
388 }
389
390 ensureHandler().connected(protocolSession);
391
392 if (this.sessionStartCallback != null) {
393 this.sessionStartCallback.execute(this);
394 }
395 }
396 }
397
398 private void updateEventMask() {
399 this.session.getLock().lock();
400 try {
401
402 if (this.status == Status.ACTIVE
403 && (this.endOfStream || this.sslEngine.isInboundDone())) {
404 this.status = Status.CLOSING;
405 }
406 if (this.status == Status.CLOSING && !this.outEncrypted.hasData()) {
407 this.sslEngine.closeOutbound();
408 this.outboundClosedCount.incrementAndGet();
409 }
410 if (this.status == Status.CLOSING && this.sslEngine.isOutboundDone()
411 && (this.endOfStream || this.sslEngine.isInboundDone())) {
412 this.status = Status.CLOSED;
413 }
414
415 if (this.status.compareTo(Status.CLOSING) <= 0 && this.endOfStream
416 && this.sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) {
417 this.status = Status.CLOSED;
418 }
419 if (this.status == Status.CLOSED) {
420 this.session.close();
421 if (sessionEndCallback != null) {
422 sessionEndCallback.execute(this);
423 }
424 return;
425 }
426
427 if (this.sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
428 doRunTask();
429 }
430
431 final int oldMask = this.session.getEventMask();
432 int newMask = oldMask;
433 switch (this.sslEngine.getHandshakeStatus()) {
434 case NEED_WRAP:
435 newMask = EventMask.READ_WRITE;
436 break;
437 case NEED_UNWRAP:
438 newMask = EventMask.READ;
439 break;
440 case NOT_HANDSHAKING:
441 newMask = this.appEventMask;
442 break;
443 }
444
445 if (this.endOfStream && !this.inPlain.hasData()) {
446 newMask = newMask & ~EventMask.READ;
447 } else if (this.status == Status.CLOSING) {
448 newMask = newMask | EventMask.READ;
449 }
450
451
452 if (this.outEncrypted.hasData()) {
453 newMask = newMask | EventMask.WRITE;
454 } else if (this.sslEngine.isOutboundDone()) {
455 newMask = newMask & ~EventMask.WRITE;
456 }
457
458
459 if (oldMask != newMask) {
460 this.session.setEventMask(newMask);
461 }
462 } finally {
463 this.session.getLock().unlock();
464 }
465 }
466
467 private int sendEncryptedData() throws IOException {
468 this.session.getLock().lock();
469 try {
470 if (!this.outEncrypted.hasData()) {
471
472
473
474
475 return this.session.write(EMPTY_BUFFER);
476 }
477
478
479 final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire();
480
481
482
483 if (this.status == Status.CLOSED) {
484 outEncryptedBuf.clear();
485 }
486
487
488 int bytesWritten = 0;
489 if (outEncryptedBuf.position() > 0) {
490 outEncryptedBuf.flip();
491 try {
492 bytesWritten = this.session.write(outEncryptedBuf);
493 } finally {
494 outEncryptedBuf.compact();
495 }
496 }
497
498
499 if (outEncryptedBuf.position() == 0) {
500 this.outEncrypted.release();
501 }
502 return bytesWritten;
503 } finally {
504 this.session.getLock().unlock();
505 }
506 }
507
508 private int receiveEncryptedData() throws IOException {
509 if (this.endOfStream) {
510 return -1;
511 }
512
513
514 final ByteBuffer inEncryptedBuf = this.inEncrypted.acquire();
515
516
517 final int bytesRead = this.session.read(inEncryptedBuf);
518
519
520 if (inEncryptedBuf.position() == 0) {
521 this.inEncrypted.release();
522 }
523 if (bytesRead == -1) {
524 this.endOfStream = true;
525 }
526 return bytesRead;
527 }
528
529 private void decryptData(final IOSession protocolSession) throws IOException {
530 final HandshakeStatus handshakeStatus = sslEngine.getHandshakeStatus();
531 if ((handshakeStatus == HandshakeStatus.NOT_HANDSHAKING || handshakeStatus == HandshakeStatus.FINISHED)
532 && inEncrypted.hasData()) {
533 final ByteBuffer inEncryptedBuf = inEncrypted.acquire();
534 inEncryptedBuf.flip();
535 try {
536 while (inEncryptedBuf.hasRemaining()) {
537 final ByteBuffer inPlainBuf = inPlain.acquire();
538 try {
539 final SSLEngineResult result = doUnwrap(inEncryptedBuf, inPlainBuf);
540 if (!inEncryptedBuf.hasRemaining() && result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) {
541 throw new SSLException("Unable to complete SSL handshake");
542 }
543 if (sslEngine.isInboundDone()) {
544 endOfStream = true;
545 }
546 if (inPlainBuf.hasRemaining()) {
547 inPlainBuf.flip();
548 try {
549 ensureHandler().inputReady(protocolSession, inPlainBuf.hasRemaining() ? inPlainBuf : null);
550 } finally {
551 inPlainBuf.clear();
552 }
553 }
554 if (result.getStatus() != SSLEngineResult.Status.OK) {
555 if (result.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW && endOfStream) {
556 throw new SSLException("Unable to decrypt incoming data due to unexpected end of stream");
557 }
558 break;
559 }
560 } finally {
561 inPlain.release();
562 }
563 }
564 } finally {
565 inEncryptedBuf.compact();
566
567 if (inEncryptedBuf.position() == 0) {
568 inEncrypted.release();
569 }
570 }
571 }
572 if (endOfStream && !inEncrypted.hasData()) {
573 ensureHandler().inputReady(protocolSession, null);
574 }
575 }
576
577 private void encryptData(final IOSession protocolSession) throws IOException {
578 final boolean appReady;
579 this.session.getLock().lock();
580 try {
581 appReady = (this.appEventMask & SelectionKey.OP_WRITE) > 0
582 && this.status == Status.ACTIVE
583 && this.sslEngine.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING;
584 } finally {
585 this.session.getLock().unlock();
586 }
587 if (appReady) {
588 ensureHandler().outputReady(protocolSession);
589 }
590 }
591
592 @Override
593 public int write(final ByteBuffer src) throws IOException {
594 Args.notNull(src, "Byte buffer");
595 this.session.getLock().lock();
596 try {
597 if (this.status != Status.ACTIVE) {
598 throw new ClosedChannelException();
599 }
600 if (this.handshakeStateRef.get() == TLSHandShakeState.READY) {
601 return 0;
602 }
603 final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire();
604 final SSLEngineResult result = doWrap(src, outEncryptedBuf);
605 return result.bytesConsumed();
606 } finally {
607 this.session.getLock().unlock();
608 }
609 }
610
611 @Override
612 public int read(final ByteBuffer dst) {
613 return endOfStream ? -1 : 0;
614 }
615
616 @Override
617 public String getId() {
618 return session.getId();
619 }
620
621 @Override
622 public Lock getLock() {
623 return this.session.getLock();
624 }
625
626 @Override
627 public void upgrade(final IOEventHandler handler) {
628 this.session.upgrade(handler);
629 }
630
631 public TlsDetails getTlsDetails() {
632 return tlsDetails;
633 }
634
635 @Override
636 public boolean isOpen() {
637 return this.status == Status.ACTIVE && this.session.isOpen();
638 }
639
640 @Override
641 public void close() {
642 close(CloseMode.GRACEFUL);
643 }
644
645 @Override
646 public void close(final CloseMode closeMode) {
647 this.session.getLock().lock();
648 try {
649 if (closeMode == CloseMode.GRACEFUL) {
650 if (this.status.compareTo(Status.CLOSING) >= 0) {
651 return;
652 }
653 this.status = Status.CLOSING;
654 if (this.session.getSocketTimeout().isDisabled()) {
655 this.session.setSocketTimeout(Timeout.ofMilliseconds(1000));
656 }
657 try {
658
659
660
661
662 updateEventMask();
663 } catch (final CancelledKeyException ex) {
664 this.session.close(CloseMode.GRACEFUL);
665 } catch (final Exception ex) {
666 this.session.close(CloseMode.IMMEDIATE);
667 }
668 } else {
669 if (this.status == Status.CLOSED) {
670 return;
671 }
672 this.inEncrypted.release();
673 this.outEncrypted.release();
674 this.inPlain.release();
675
676 this.status = Status.CLOSED;
677 this.session.close(closeMode);
678 }
679 } finally {
680 this.session.getLock().unlock();
681 }
682 }
683
684 @Override
685 public Status getStatus() {
686 return this.status;
687 }
688
689 @Override
690 public void enqueue(final Command command, final Command.Priority priority) {
691 this.session.getLock().lock();
692 try {
693 this.session.enqueue(command, priority);
694 setEvent(SelectionKey.OP_WRITE);
695 } finally {
696 this.session.getLock().unlock();
697 }
698 }
699
700 @Override
701 public boolean hasCommands() {
702 return this.session.hasCommands();
703 }
704
705 @Override
706 public Command poll() {
707 return this.session.poll();
708 }
709
710 @Override
711 public ByteChannel channel() {
712 return this.session.channel();
713 }
714
715 @Override
716 public SocketAddress getLocalAddress() {
717 return this.session.getLocalAddress();
718 }
719
720 @Override
721 public SocketAddress getRemoteAddress() {
722 return this.session.getRemoteAddress();
723 }
724
725 @Override
726 public int getEventMask() {
727 this.session.getLock().lock();
728 try {
729 return this.appEventMask;
730 } finally {
731 this.session.getLock().unlock();
732 }
733 }
734
735 @Override
736 public void setEventMask(final int ops) {
737 this.session.getLock().lock();
738 try {
739 this.appEventMask = ops;
740 updateEventMask();
741 } finally {
742 this.session.getLock().unlock();
743 }
744 }
745
746 @Override
747 public void setEvent(final int op) {
748 this.session.getLock().lock();
749 try {
750 this.appEventMask = this.appEventMask | op;
751 updateEventMask();
752 } finally {
753 this.session.getLock().unlock();
754 }
755 }
756
757 @Override
758 public void clearEvent(final int op) {
759 this.session.getLock().lock();
760 try {
761 this.appEventMask = this.appEventMask & ~op;
762 updateEventMask();
763 } finally {
764 this.session.getLock().unlock();
765 }
766 }
767
768 @Override
769 public Timeout getSocketTimeout() {
770 return this.session.getSocketTimeout();
771 }
772
773 @Override
774 public void setSocketTimeout(final Timeout timeout) {
775 this.socketTimeout = timeout;
776 if (this.sslEngine.getHandshakeStatus() == HandshakeStatus.FINISHED) {
777 this.session.setSocketTimeout(timeout);
778 }
779 }
780
781 @Override
782 public void updateReadTime() {
783 this.session.updateReadTime();
784 }
785
786 @Override
787 public void updateWriteTime() {
788 this.session.updateWriteTime();
789 }
790
791 @Override
792 public long getLastReadTime() {
793 return this.session.getLastReadTime();
794 }
795
796 @Override
797 public long getLastWriteTime() {
798 return this.session.getLastWriteTime();
799 }
800
801 @Override
802 public long getLastEventTime() {
803 return this.session.getLastEventTime();
804 }
805
806 private static void formatOps(final StringBuilder buffer, final int ops) {
807 if ((ops & SelectionKey.OP_READ) > 0) {
808 buffer.append('r');
809 }
810 if ((ops & SelectionKey.OP_WRITE) > 0) {
811 buffer.append('w');
812 }
813 }
814
815 @Override
816 public String toString() {
817 this.session.getLock().lock();
818 try {
819 final StringBuilder buffer = new StringBuilder();
820 buffer.append(this.session);
821 buffer.append("[");
822 buffer.append(this.status);
823 buffer.append("][");
824 formatOps(buffer, this.appEventMask);
825 buffer.append("][");
826 buffer.append(this.sslEngine.getHandshakeStatus());
827 if (this.sslEngine.isInboundDone()) {
828 buffer.append("][inbound done][");
829 }
830 if (this.sslEngine.isOutboundDone()) {
831 buffer.append("][outbound done][");
832 }
833 if (this.endOfStream) {
834 buffer.append("][EOF][");
835 }
836 buffer.append("][");
837 buffer.append(!this.inEncrypted.hasData() ? 0 : inEncrypted.acquire().position());
838 buffer.append("][");
839 buffer.append(!this.inPlain.hasData() ? 0 : inPlain.acquire().position());
840 buffer.append("][");
841 buffer.append(!this.outEncrypted.hasData() ? 0 : outEncrypted.acquire().position());
842 buffer.append("]");
843 return buffer.toString();
844 } finally {
845 this.session.getLock().unlock();
846 }
847 }
848
849 }