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 updateEventMask();
261 } finally {
262 this.session.getLock().unlock();
263 }
264 }
265
266
267
268
269
270
271 private SSLException convert(final RuntimeException ex) {
272 Throwable cause = ex.getCause();
273 if (cause == null) {
274 cause = ex;
275 }
276 return new SSLException(cause);
277 }
278
279 private SSLEngineResult doWrap(final ByteBuffer src, final ByteBuffer dst) throws SSLException {
280 try {
281 return this.sslEngine.wrap(src, dst);
282 } catch (final RuntimeException ex) {
283 throw convert(ex);
284 }
285 }
286
287 private SSLEngineResult doUnwrap(final ByteBuffer src, final ByteBuffer dst) throws SSLException {
288 try {
289 return this.sslEngine.unwrap(src, dst);
290 } catch (final RuntimeException ex) {
291 throw convert(ex);
292 }
293 }
294
295 private void doRunTask() {
296 final Runnable r = this.sslEngine.getDelegatedTask();
297 if (r != null) {
298 r.run();
299 }
300 }
301
302 private void doHandshake(final IOSession protocolSession) throws IOException {
303 boolean handshaking = true;
304
305 SSLEngineResult result = null;
306 while (handshaking) {
307 HandshakeStatus handshakeStatus = this.sslEngine.getHandshakeStatus();
308
309
310
311
312 if (handshakeStatus == HandshakeStatus.NOT_HANDSHAKING && outboundClosedCount.get() > 0) {
313 handshakeStatus = HandshakeStatus.NEED_WRAP;
314 }
315
316 switch (handshakeStatus) {
317 case NEED_WRAP:
318
319
320 this.session.getLock().lock();
321 try {
322
323 final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire();
324
325
326 result = doWrap(EMPTY_BUFFER, outEncryptedBuf);
327
328 if (result.getStatus() != SSLEngineResult.Status.OK || result.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
329 handshaking = false;
330 }
331 break;
332 } finally {
333 this.session.getLock().unlock();
334 }
335 case NEED_UNWRAP:
336
337
338
339 final ByteBuffer inEncryptedBuf = this.inEncrypted.acquire();
340 final ByteBuffer inPlainBuf = this.inPlain.acquire();
341
342
343 inEncryptedBuf.flip();
344 try {
345 result = doUnwrap(inEncryptedBuf, inPlainBuf);
346 } finally {
347 inEncryptedBuf.compact();
348 }
349
350 try {
351 if (!inEncryptedBuf.hasRemaining() && result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) {
352 throw new SSLException("Input buffer is full");
353 }
354 } finally {
355
356 if (inEncryptedBuf.position() == 0) {
357 this.inEncrypted.release();
358 }
359 }
360
361 if (this.status.compareTo(Status.CLOSING) >= 0) {
362 this.inPlain.release();
363 }
364 if (result.getStatus() != SSLEngineResult.Status.OK) {
365 handshaking = false;
366 }
367 break;
368 case NEED_TASK:
369 doRunTask();
370 break;
371 case NOT_HANDSHAKING:
372 handshaking = false;
373 break;
374 }
375 }
376
377
378
379
380 if (result != null && result.getHandshakeStatus() == HandshakeStatus.FINISHED) {
381 this.handshakeStateRef.set(TLSHandShakeState.COMPLETE);
382 this.session.setSocketTimeout(this.socketTimeout);
383 if (this.verifier != null) {
384 this.tlsDetails = this.verifier.verify(this.targetEndpoint, this.sslEngine);
385 }
386 if (this.tlsDetails == null) {
387 final SSLSession sslSession = this.sslEngine.getSession();
388 final String applicationProtocol = ReflectionUtils.callGetter(this.sslEngine, "ApplicationProtocol", String.class);
389 this.tlsDetails = new TlsDetails(sslSession, applicationProtocol);
390 }
391
392 ensureHandler().connected(protocolSession);
393
394 if (this.sessionStartCallback != null) {
395 this.sessionStartCallback.execute(this);
396 }
397 }
398 }
399
400 private void updateEventMask() {
401 this.session.getLock().lock();
402 try {
403
404 if (this.status == Status.ACTIVE
405 && (this.endOfStream || this.sslEngine.isInboundDone())) {
406 this.status = Status.CLOSING;
407 }
408 if (this.status == Status.CLOSING && !this.outEncrypted.hasData()) {
409 this.sslEngine.closeOutbound();
410 this.outboundClosedCount.incrementAndGet();
411 }
412 if (this.status == Status.CLOSING && this.sslEngine.isOutboundDone()
413 && (this.endOfStream || this.sslEngine.isInboundDone())) {
414 this.status = Status.CLOSED;
415 }
416
417 if (this.status.compareTo(Status.CLOSING) <= 0 && this.endOfStream
418 && this.sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) {
419 this.status = Status.CLOSED;
420 }
421 if (this.status == Status.CLOSED) {
422 this.session.close();
423 if (sessionEndCallback != null) {
424 sessionEndCallback.execute(this);
425 }
426 return;
427 }
428
429 if (this.sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
430 doRunTask();
431 }
432
433 final int oldMask = this.session.getEventMask();
434 int newMask = oldMask;
435 switch (this.sslEngine.getHandshakeStatus()) {
436 case NEED_WRAP:
437 newMask = EventMask.READ_WRITE;
438 break;
439 case NEED_UNWRAP:
440 newMask = EventMask.READ;
441 break;
442 case NOT_HANDSHAKING:
443 newMask = this.appEventMask;
444 break;
445 }
446
447 if (this.endOfStream && !this.inPlain.hasData()) {
448 newMask = newMask & ~EventMask.READ;
449 } else if (this.status == Status.CLOSING) {
450 newMask = newMask | EventMask.READ;
451 }
452
453
454 if (this.outEncrypted.hasData()) {
455 newMask = newMask | EventMask.WRITE;
456 } else if (this.sslEngine.isOutboundDone()) {
457 newMask = newMask & ~EventMask.WRITE;
458 }
459
460
461 if (oldMask != newMask) {
462 this.session.setEventMask(newMask);
463 }
464 } finally {
465 this.session.getLock().unlock();
466 }
467 }
468
469 private int sendEncryptedData() throws IOException {
470 this.session.getLock().lock();
471 try {
472 if (!this.outEncrypted.hasData()) {
473
474
475
476
477 return this.session.write(EMPTY_BUFFER);
478 }
479
480
481 final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire();
482
483
484
485 if (this.status == Status.CLOSED) {
486 outEncryptedBuf.clear();
487 }
488
489
490 int bytesWritten = 0;
491 if (outEncryptedBuf.position() > 0) {
492 outEncryptedBuf.flip();
493 try {
494 bytesWritten = this.session.write(outEncryptedBuf);
495 } finally {
496 outEncryptedBuf.compact();
497 }
498 }
499
500
501 if (outEncryptedBuf.position() == 0) {
502 this.outEncrypted.release();
503 }
504 return bytesWritten;
505 } finally {
506 this.session.getLock().unlock();
507 }
508 }
509
510 private int receiveEncryptedData() throws IOException {
511 if (this.endOfStream) {
512 return -1;
513 }
514
515
516 final ByteBuffer inEncryptedBuf = this.inEncrypted.acquire();
517
518
519 final int bytesRead = this.session.read(inEncryptedBuf);
520
521
522 if (inEncryptedBuf.position() == 0) {
523 this.inEncrypted.release();
524 }
525 if (bytesRead == -1) {
526 this.endOfStream = true;
527 }
528 return bytesRead;
529 }
530
531 private void decryptData(final IOSession protocolSession) throws IOException {
532 final HandshakeStatus handshakeStatus = sslEngine.getHandshakeStatus();
533 if ((handshakeStatus == HandshakeStatus.NOT_HANDSHAKING || handshakeStatus == HandshakeStatus.FINISHED)
534 && inEncrypted.hasData()) {
535 final ByteBuffer inEncryptedBuf = inEncrypted.acquire();
536 inEncryptedBuf.flip();
537 try {
538 while (inEncryptedBuf.hasRemaining()) {
539 final ByteBuffer inPlainBuf = inPlain.acquire();
540 try {
541 final SSLEngineResult result = doUnwrap(inEncryptedBuf, inPlainBuf);
542 if (!inEncryptedBuf.hasRemaining() && result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) {
543 throw new SSLException("Unable to complete SSL handshake");
544 }
545 if (sslEngine.isInboundDone()) {
546 endOfStream = true;
547 }
548 if(inPlainBuf.position() > 0) {
549 inPlainBuf.flip();
550 try {
551 ensureHandler().inputReady(protocolSession, inPlainBuf.hasRemaining() ? inPlainBuf : null);
552 } finally {
553 inPlainBuf.clear();
554 }
555 }
556 if (result.getStatus() != SSLEngineResult.Status.OK) {
557 if (result.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW && endOfStream) {
558 throw new SSLException("Unable to decrypt incoming data due to unexpected end of stream");
559 }
560 break;
561 }
562 } finally {
563 inPlain.release();
564 }
565 }
566 } finally {
567 inEncryptedBuf.compact();
568
569 if (inEncryptedBuf.position() == 0) {
570 inEncrypted.release();
571 }
572 }
573 }
574 if (endOfStream && !inEncrypted.hasData()) {
575 ensureHandler().inputReady(protocolSession, null);
576 }
577 }
578
579 private void encryptData(final IOSession protocolSession) throws IOException {
580 final boolean appReady;
581 this.session.getLock().lock();
582 try {
583 appReady = (this.appEventMask & SelectionKey.OP_WRITE) > 0
584 && this.status == Status.ACTIVE
585 && this.sslEngine.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING;
586 } finally {
587 this.session.getLock().unlock();
588 }
589 if (appReady) {
590 ensureHandler().outputReady(protocolSession);
591 }
592 }
593
594 @Override
595 public int write(final ByteBuffer src) throws IOException {
596 Args.notNull(src, "Byte buffer");
597 this.session.getLock().lock();
598 try {
599 if (this.status != Status.ACTIVE) {
600 throw new ClosedChannelException();
601 }
602 if (this.handshakeStateRef.get() == TLSHandShakeState.READY) {
603 return 0;
604 }
605 final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire();
606 final SSLEngineResult result = doWrap(src, outEncryptedBuf);
607 return result.bytesConsumed();
608 } finally {
609 this.session.getLock().unlock();
610 }
611 }
612
613 @Override
614 public int read(final ByteBuffer dst) {
615 return endOfStream ? -1 : 0;
616 }
617
618 @Override
619 public String getId() {
620 return session.getId();
621 }
622
623 @Override
624 public Lock getLock() {
625 return this.session.getLock();
626 }
627
628 @Override
629 public void upgrade(final IOEventHandler handler) {
630 this.session.upgrade(handler);
631 }
632
633 public TlsDetails getTlsDetails() {
634 return tlsDetails;
635 }
636
637 @Override
638 public boolean isOpen() {
639 return this.status == Status.ACTIVE && this.session.isOpen();
640 }
641
642 @Override
643 public void close() {
644 close(CloseMode.GRACEFUL);
645 }
646
647 @Override
648 public void close(final CloseMode closeMode) {
649 this.session.getLock().lock();
650 try {
651 if (closeMode == CloseMode.GRACEFUL) {
652 if (this.status.compareTo(Status.CLOSING) >= 0) {
653 return;
654 }
655 this.status = Status.CLOSING;
656 if (this.session.getSocketTimeout().isDisabled()) {
657 this.session.setSocketTimeout(Timeout.ofMilliseconds(1000));
658 }
659 try {
660
661
662
663
664 updateEventMask();
665 } catch (final CancelledKeyException ex) {
666 this.session.close(CloseMode.GRACEFUL);
667 } catch (final Exception ex) {
668 this.session.close(CloseMode.IMMEDIATE);
669 }
670 } else {
671 if (this.status == Status.CLOSED) {
672 return;
673 }
674 this.inEncrypted.release();
675 this.outEncrypted.release();
676 this.inPlain.release();
677
678 this.status = Status.CLOSED;
679 this.session.close(closeMode);
680 }
681 } finally {
682 this.session.getLock().unlock();
683 }
684 }
685
686 @Override
687 public Status getStatus() {
688 return this.status;
689 }
690
691 @Override
692 public void enqueue(final Command command, final Command.Priority priority) {
693 this.session.getLock().lock();
694 try {
695 this.session.enqueue(command, priority);
696 setEvent(SelectionKey.OP_WRITE);
697 } finally {
698 this.session.getLock().unlock();
699 }
700 }
701
702 @Override
703 public boolean hasCommands() {
704 return this.session.hasCommands();
705 }
706
707 @Override
708 public Command poll() {
709 return this.session.poll();
710 }
711
712 @Override
713 public ByteChannel channel() {
714 return this.session.channel();
715 }
716
717 @Override
718 public SocketAddress getLocalAddress() {
719 return this.session.getLocalAddress();
720 }
721
722 @Override
723 public SocketAddress getRemoteAddress() {
724 return this.session.getRemoteAddress();
725 }
726
727 @Override
728 public int getEventMask() {
729 this.session.getLock().lock();
730 try {
731 return this.appEventMask;
732 } finally {
733 this.session.getLock().unlock();
734 }
735 }
736
737 @Override
738 public void setEventMask(final int ops) {
739 this.session.getLock().lock();
740 try {
741 this.appEventMask = ops;
742 updateEventMask();
743 } finally {
744 this.session.getLock().unlock();
745 }
746 }
747
748 @Override
749 public void setEvent(final int op) {
750 this.session.getLock().lock();
751 try {
752 this.appEventMask = this.appEventMask | op;
753 updateEventMask();
754 } finally {
755 this.session.getLock().unlock();
756 }
757 }
758
759 @Override
760 public void clearEvent(final int op) {
761 this.session.getLock().lock();
762 try {
763 this.appEventMask = this.appEventMask & ~op;
764 updateEventMask();
765 } finally {
766 this.session.getLock().unlock();
767 }
768 }
769
770 @Override
771 public Timeout getSocketTimeout() {
772 return this.session.getSocketTimeout();
773 }
774
775 @Override
776 public void setSocketTimeout(final Timeout timeout) {
777 this.socketTimeout = timeout;
778 if (this.sslEngine.getHandshakeStatus() == HandshakeStatus.FINISHED) {
779 this.session.setSocketTimeout(timeout);
780 }
781 }
782
783 @Override
784 public void updateReadTime() {
785 this.session.updateReadTime();
786 }
787
788 @Override
789 public void updateWriteTime() {
790 this.session.updateWriteTime();
791 }
792
793 @Override
794 public long getLastReadTime() {
795 return this.session.getLastReadTime();
796 }
797
798 @Override
799 public long getLastWriteTime() {
800 return this.session.getLastWriteTime();
801 }
802
803 @Override
804 public long getLastEventTime() {
805 return this.session.getLastEventTime();
806 }
807
808 private static void formatOps(final StringBuilder buffer, final int ops) {
809 if ((ops & SelectionKey.OP_READ) > 0) {
810 buffer.append('r');
811 }
812 if ((ops & SelectionKey.OP_WRITE) > 0) {
813 buffer.append('w');
814 }
815 }
816
817 @Override
818 public String toString() {
819 this.session.getLock().lock();
820 try {
821 final StringBuilder buffer = new StringBuilder();
822 buffer.append(this.session);
823 buffer.append("[");
824 buffer.append(this.status);
825 buffer.append("][");
826 formatOps(buffer, this.appEventMask);
827 buffer.append("][");
828 buffer.append(this.sslEngine.getHandshakeStatus());
829 if (this.sslEngine.isInboundDone()) {
830 buffer.append("][inbound done][");
831 }
832 if (this.sslEngine.isOutboundDone()) {
833 buffer.append("][outbound done][");
834 }
835 if (this.endOfStream) {
836 buffer.append("][EOF][");
837 }
838 buffer.append("][");
839 buffer.append(!this.inEncrypted.hasData() ? 0 : inEncrypted.acquire().position());
840 buffer.append("][");
841 buffer.append(!this.inPlain.hasData() ? 0 : inPlain.acquire().position());
842 buffer.append("][");
843 buffer.append(!this.outEncrypted.hasData() ? 0 : outEncrypted.acquire().position());
844 buffer.append("]");
845 return buffer.toString();
846 } finally {
847 this.session.getLock().unlock();
848 }
849 }
850
851 }