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