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