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