001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.nio.reactor.ssl; 029 030import java.io.IOException; 031import java.net.Socket; 032import java.net.SocketAddress; 033import java.nio.ByteBuffer; 034import java.nio.channels.ByteChannel; 035import java.nio.channels.CancelledKeyException; 036import java.nio.channels.ClosedChannelException; 037import java.nio.channels.SelectionKey; 038 039import javax.net.ssl.SSLContext; 040import javax.net.ssl.SSLEngine; 041import javax.net.ssl.SSLEngineResult; 042import javax.net.ssl.SSLEngineResult.HandshakeStatus; 043import javax.net.ssl.SSLEngineResult.Status; 044import javax.net.ssl.SSLException; 045import javax.net.ssl.SSLSession; 046 047import org.apache.http.HttpHost; 048import org.apache.http.annotation.Contract; 049import org.apache.http.annotation.ThreadingBehavior; 050import org.apache.http.nio.reactor.EventMask; 051import org.apache.http.nio.reactor.IOSession; 052import org.apache.http.nio.reactor.SessionBufferStatus; 053import org.apache.http.nio.reactor.SocketAccessor; 054import org.apache.http.util.Args; 055import org.apache.http.util.Asserts; 056 057/** 058 * {@code SSLIOSession} is a decorator class intended to transparently extend 059 * an {@link IOSession} with transport layer security capabilities based on 060 * the SSL/TLS protocol. 061 * <p> 062 * The resultant instance of {@code SSLIOSession} must be added to the original 063 * I/O session as an attribute with the {@link #SESSION_KEY} key. 064 * <pre> 065 * SSLContext sslContext = SSLContext.getInstance("SSL"); 066 * sslContext.init(null, null, null); 067 * SSLIOSession sslsession = new SSLIOSession( 068 * iosession, SSLMode.CLIENT, sslContext, null); 069 * iosession.setAttribute(SSLIOSession.SESSION_KEY, sslsession); 070 * </pre> 071 * 072 * @since 4.2 073 */ 074@Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) 075public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAccessor { 076 077 /** 078 * Name of the context attribute key, which can be used to obtain the 079 * SSL session. 080 */ 081 public static final String SESSION_KEY = "http.session.ssl"; 082 083 private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); 084 085 private final IOSession session; 086 private final SSLEngine sslEngine; 087 private final SSLBuffer inEncrypted; 088 private final SSLBuffer outEncrypted; 089 private final SSLBuffer inPlain; 090 private final InternalByteChannel channel; 091 private final SSLSetupHandler handler; 092 093 private int appEventMask; 094 private SessionBufferStatus appBufferStatus; 095 096 private boolean endOfStream; 097 private volatile SSLMode sslMode; 098 private volatile int status; 099 private volatile boolean initialized; 100 101 /** 102 * Creates new instance of {@code SSLIOSession} class. The instances created uses a 103 * {@link PermanentSSLBufferManagementStrategy} to manage its buffers. 104 * 105 * @param session I/O session to be decorated with the TLS/SSL capabilities. 106 * @param sslMode SSL mode (client or server) 107 * @param host original host (applicable in client mode only) 108 * @param sslContext SSL context to use for this I/O session. 109 * @param handler optional SSL setup handler. May be {@code null}. 110 * 111 * @since 4.4 112 */ 113 public SSLIOSession( 114 final IOSession session, 115 final SSLMode sslMode, 116 final HttpHost host, 117 final SSLContext sslContext, 118 final SSLSetupHandler handler) { 119 this(session, sslMode, host, sslContext, handler, new PermanentSSLBufferManagementStrategy()); 120 } 121 122 /** 123 * Creates new instance of {@code SSLIOSession} class. 124 * 125 * @param session I/O session to be decorated with the TLS/SSL capabilities. 126 * @param sslMode SSL mode (client or server) 127 * @param host original host (applicable in client mode only) 128 * @param sslContext SSL context to use for this I/O session. 129 * @param handler optional SSL setup handler. May be {@code null}. 130 * @param bufferManagementStrategy buffer management strategy 131 */ 132 public SSLIOSession( 133 final IOSession session, 134 final SSLMode sslMode, 135 final HttpHost host, 136 final SSLContext sslContext, 137 final SSLSetupHandler handler, 138 final SSLBufferManagementStrategy bufferManagementStrategy) { 139 super(); 140 Args.notNull(session, "IO session"); 141 Args.notNull(sslContext, "SSL context"); 142 Args.notNull(bufferManagementStrategy, "Buffer management strategy"); 143 this.session = session; 144 this.sslMode = sslMode; 145 this.appEventMask = session.getEventMask(); 146 this.channel = new InternalByteChannel(); 147 this.handler = handler; 148 149 // Override the status buffer interface 150 this.session.setBufferStatus(this); 151 152 if (this.sslMode == SSLMode.CLIENT && host != null) { 153 this.sslEngine = sslContext.createSSLEngine(host.getHostName(), host.getPort()); 154 } else { 155 this.sslEngine = sslContext.createSSLEngine(); 156 } 157 158 // Allocate buffers for network (encrypted) data 159 final int netBuffersize = this.sslEngine.getSession().getPacketBufferSize(); 160 this.inEncrypted = bufferManagementStrategy.constructBuffer(netBuffersize); 161 this.outEncrypted = bufferManagementStrategy.constructBuffer(netBuffersize); 162 163 // Allocate buffers for application (unencrypted) data 164 final int appBuffersize = this.sslEngine.getSession().getApplicationBufferSize(); 165 this.inPlain = bufferManagementStrategy.constructBuffer(appBuffersize); 166 } 167 168 /** 169 * Creates new instance of {@code SSLIOSession} class. 170 * 171 * @param session I/O session to be decorated with the TLS/SSL capabilities. 172 * @param sslMode SSL mode (client or server) 173 * @param sslContext SSL context to use for this I/O session. 174 * @param handler optional SSL setup handler. May be {@code null}. 175 */ 176 public SSLIOSession( 177 final IOSession session, 178 final SSLMode sslMode, 179 final SSLContext sslContext, 180 final SSLSetupHandler handler) { 181 this(session, sslMode, null, sslContext, handler); 182 } 183 184 protected SSLSetupHandler getSSLSetupHandler() { 185 return this.handler; 186 } 187 188 /** 189 * Returns {@code true} is the session has been fully initialized, 190 * {@code false} otherwise. 191 */ 192 public boolean isInitialized() { 193 return this.initialized; 194 } 195 196 /** 197 * Initializes the session in the given {@link SSLMode}. This method 198 * invokes the {@link SSLSetupHandler#initalize(SSLEngine)} callback 199 * if an instance of {@link SSLSetupHandler} was specified at 200 * the construction time. 201 * 202 * @deprecated (4.3) SSL mode must be set at construction time. 203 */ 204 @Deprecated 205 public synchronized void initialize(final SSLMode sslMode) throws SSLException { 206 this.sslMode = sslMode; 207 initialize(); 208 } 209 210 /** 211 * Initializes the session. This method invokes the {@link 212 * SSLSetupHandler#initalize(SSLEngine)} callback if an instance of 213 * {@link SSLSetupHandler} was specified at the construction time. 214 * 215 * @throws SSLException in case of a SSL protocol exception. 216 * @throws IllegalStateException if the session has already been initialized. 217 */ 218 public synchronized void initialize() throws SSLException { 219 Asserts.check(!this.initialized, "SSL I/O session already initialized"); 220 if (this.status >= IOSession.CLOSING) { 221 return; 222 } 223 switch (this.sslMode) { 224 case CLIENT: 225 this.sslEngine.setUseClientMode(true); 226 break; 227 case SERVER: 228 this.sslEngine.setUseClientMode(false); 229 break; 230 } 231 if (this.handler != null) { 232 this.handler.initalize(this.sslEngine); 233 } 234 this.initialized = true; 235 this.sslEngine.beginHandshake(); 236 237 this.inEncrypted.release(); 238 this.outEncrypted.release(); 239 this.inPlain.release(); 240 241 doHandshake(); 242 } 243 244 public synchronized SSLSession getSSLSession() { 245 return this.sslEngine.getSession(); 246 } 247 248 // A works-around for exception handling craziness in Sun/Oracle's SSLEngine 249 // implementation. 250 // 251 // sun.security.pkcs11.wrapper.PKCS11Exception is re-thrown as 252 // plain RuntimeException in sun.security.ssl.Handshaker#checkThrown 253 private SSLException convert(final RuntimeException ex) { 254 Throwable cause = ex.getCause(); 255 if (cause == null) { 256 cause = ex; 257 } 258 return new SSLException(cause); 259 } 260 261 private SSLEngineResult doWrap(final ByteBuffer src, final ByteBuffer dst) throws SSLException { 262 try { 263 return this.sslEngine.wrap(src, dst); 264 } catch (final RuntimeException ex) { 265 throw convert(ex); 266 } 267 } 268 269 private SSLEngineResult doUnwrap(final ByteBuffer src, final ByteBuffer dst) throws SSLException { 270 try { 271 return this.sslEngine.unwrap(src, dst); 272 } catch (final RuntimeException ex) { 273 throw convert(ex); 274 } 275 } 276 277 private void doRunTask() throws SSLException { 278 try { 279 final Runnable r = this.sslEngine.getDelegatedTask(); 280 if (r != null) { 281 r.run(); 282 } 283 } catch (final RuntimeException ex) { 284 throw convert(ex); 285 } 286 } 287 288 private void doHandshake() throws SSLException { 289 boolean handshaking = true; 290 291 SSLEngineResult result = null; 292 while (handshaking) { 293 switch (this.sslEngine.getHandshakeStatus()) { 294 case NEED_WRAP: 295 // Generate outgoing handshake data 296 297 // Acquire buffer 298 final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire(); 299 300 // Just wrap an empty buffer because there is no data to write. 301 result = doWrap(ByteBuffer.allocate(0), outEncryptedBuf); 302 303 if (result.getStatus() != Status.OK) { 304 handshaking = false; 305 } 306 break; 307 case NEED_UNWRAP: 308 // Process incoming handshake data 309 310 // Acquire buffers 311 final ByteBuffer inEncryptedBuf = this.inEncrypted.acquire(); 312 final ByteBuffer inPlainBuf = this.inPlain.acquire(); 313 314 // Perform operations 315 inEncryptedBuf.flip(); 316 result = doUnwrap(inEncryptedBuf, inPlainBuf); 317 inEncryptedBuf.compact(); 318 319 320 try { 321 if (!inEncryptedBuf.hasRemaining() && result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) { 322 throw new SSLException("Input buffer is full"); 323 } 324 } finally { 325 // Release inEncrypted if empty 326 if (inEncryptedBuf.position() == 0) { 327 this.inEncrypted.release(); 328 } 329 } 330 331 if (this.status >= IOSession.CLOSING) { 332 this.inPlain.release(); 333 } 334 if (result.getStatus() != Status.OK) { 335 handshaking = false; 336 } 337 break; 338 case NEED_TASK: 339 doRunTask(); 340 break; 341 case NOT_HANDSHAKING: 342 handshaking = false; 343 break; 344 case FINISHED: 345 break; 346 } 347 } 348 349 // The SSLEngine has just finished handshaking. This value is only generated by a call 350 // to SSLEngine.wrap()/unwrap() when that call finishes a handshake. 351 // It is never generated by SSLEngine.getHandshakeStatus(). 352 if (result != null && result.getHandshakeStatus() == HandshakeStatus.FINISHED) { 353 if (this.handler != null) { 354 this.handler.verify(this.session, this.sslEngine.getSession()); 355 } 356 } 357 } 358 359 private void updateEventMask() { 360 // Graceful session termination 361 if (this.status == CLOSING && !this.outEncrypted.hasData()) { 362 this.sslEngine.closeOutbound(); 363 } 364 if (this.status == CLOSING && this.sslEngine.isOutboundDone() 365 && (this.endOfStream || this.sslEngine.isInboundDone())) { 366 this.status = CLOSED; 367 } 368 // Abnormal session termination 369 if (this.status == ACTIVE && this.endOfStream 370 && this.sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) { 371 this.status = CLOSED; 372 } 373 if (this.status == CLOSED) { 374 this.session.close(); 375 return; 376 } 377 // Need to toggle the event mask for this channel? 378 final int oldMask = this.session.getEventMask(); 379 int newMask = oldMask; 380 switch (this.sslEngine.getHandshakeStatus()) { 381 case NEED_WRAP: 382 newMask = EventMask.READ_WRITE; 383 break; 384 case NEED_UNWRAP: 385 newMask = EventMask.READ; 386 break; 387 case NOT_HANDSHAKING: 388 newMask = this.appEventMask; 389 break; 390 case NEED_TASK: 391 break; 392 case FINISHED: 393 break; 394 } 395 396 // Do we have encrypted data ready to be sent? 397 if (this.outEncrypted.hasData()) { 398 newMask = newMask | EventMask.WRITE; 399 } 400 401 // Update the mask if necessary 402 if (oldMask != newMask) { 403 this.session.setEventMask(newMask); 404 } 405 } 406 407 private int sendEncryptedData() throws IOException { 408 if (!this.outEncrypted.hasData()) { 409 // If the buffer isn't acquired or is empty, call write() with an empty buffer. 410 // This will ensure that tests performed by write() still take place without 411 // having to acquire and release an empty buffer (e.g. connection closed, 412 // interrupted thread, etc..) 413 return this.session.channel().write(EMPTY_BUFFER); 414 } 415 416 // Acquire buffer 417 final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire(); 418 419 // Perform operation 420 outEncryptedBuf.flip(); 421 final int bytesWritten = this.session.channel().write(outEncryptedBuf); 422 outEncryptedBuf.compact(); 423 424 // Release if empty 425 if (outEncryptedBuf.position() == 0) { 426 this.outEncrypted.release(); 427 } 428 return bytesWritten; 429 } 430 431 private int receiveEncryptedData() throws IOException { 432 if (this.endOfStream) { 433 return -1; 434 } 435 436 // Acquire buffer 437 final ByteBuffer inEncryptedBuf = this.inEncrypted.acquire(); 438 439 // Perform operation 440 final int ret = this.session.channel().read(inEncryptedBuf); 441 442 // Release if empty 443 if (inEncryptedBuf.position() == 0) { 444 this.inEncrypted.release(); 445 } 446 return ret; 447 } 448 449 private boolean decryptData() throws SSLException { 450 boolean decrypted = false; 451 while (this.inEncrypted.hasData()) { 452 // Get buffers 453 final ByteBuffer inEncryptedBuf = this.inEncrypted.acquire(); 454 final ByteBuffer inPlainBuf = this.inPlain.acquire(); 455 456 // Perform operations 457 inEncryptedBuf.flip(); 458 final SSLEngineResult result = doUnwrap(inEncryptedBuf, inPlainBuf); 459 inEncryptedBuf.compact(); 460 461 try { 462 if (!inEncryptedBuf.hasRemaining() && result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) { 463 throw new SSLException("Unable to complete SSL handshake"); 464 } 465 final Status status = result.getStatus(); 466 if (status == Status.OK) { 467 decrypted = true; 468 } else { 469 if (status == Status.BUFFER_UNDERFLOW && this.endOfStream) { 470 throw new SSLException("Unable to decrypt incoming data due to unexpected end of stream"); 471 } 472 break; 473 } 474 if (result.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING) { 475 break; 476 } 477 } finally { 478 // Release inEncrypted if empty 479 if (this.inEncrypted.acquire().position() == 0) { 480 this.inEncrypted.release(); 481 } 482 } 483 } 484 return decrypted; 485 } 486 487 /** 488 * Reads encrypted data and returns whether the channel associated with 489 * this session has any decrypted inbound data available for reading. 490 * 491 * @throws IOException in case of an I/O error. 492 */ 493 public synchronized boolean isAppInputReady() throws IOException { 494 do { 495 final int bytesRead = receiveEncryptedData(); 496 if (bytesRead == -1) { 497 this.endOfStream = true; 498 } 499 doHandshake(); 500 final HandshakeStatus status = this.sslEngine.getHandshakeStatus(); 501 if (status == HandshakeStatus.NOT_HANDSHAKING || status == HandshakeStatus.FINISHED) { 502 decryptData(); 503 } 504 } while (this.sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_TASK); 505 // Some decrypted data is available or at the end of stream 506 return (this.appEventMask & SelectionKey.OP_READ) > 0 507 && (this.inPlain.hasData() 508 || (this.appBufferStatus != null && this.appBufferStatus.hasBufferedInput()) 509 || (this.endOfStream && this.status == ACTIVE)); 510 } 511 512 /** 513 * Returns whether the channel associated with this session is ready to 514 * accept outbound unecrypted data for writing. 515 * 516 * @throws IOException - not thrown currently 517 */ 518 public synchronized boolean isAppOutputReady() throws IOException { 519 return (this.appEventMask & SelectionKey.OP_WRITE) > 0 520 && this.status == ACTIVE 521 && this.sslEngine.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING; 522 } 523 524 /** 525 * Executes inbound SSL transport operations. 526 * 527 * @throws IOException - not thrown currently 528 */ 529 public synchronized void inboundTransport() throws IOException { 530 updateEventMask(); 531 } 532 533 /** 534 * Sends encrypted data and executes outbound SSL transport operations. 535 * 536 * @throws IOException in case of an I/O error. 537 */ 538 public synchronized void outboundTransport() throws IOException { 539 sendEncryptedData(); 540 doHandshake(); 541 updateEventMask(); 542 } 543 544 /** 545 * Returns whether the session will produce any more inbound data. 546 */ 547 public synchronized boolean isInboundDone() { 548 return this.sslEngine.isInboundDone(); 549 } 550 551 /** 552 * Returns whether the session will accept any more outbound data. 553 */ 554 public synchronized boolean isOutboundDone() { 555 return this.sslEngine.isOutboundDone(); 556 } 557 558 private synchronized int writePlain(final ByteBuffer src) throws IOException { 559 Args.notNull(src, "Byte buffer"); 560 if (this.status != ACTIVE) { 561 throw new ClosedChannelException(); 562 } 563 final ByteBuffer outEncryptedBuf = this.outEncrypted.acquire(); 564 final SSLEngineResult result = doWrap(src, outEncryptedBuf); 565 if (result.getStatus() == Status.CLOSED) { 566 this.status = CLOSED; 567 } 568 return result.bytesConsumed(); 569 } 570 571 private synchronized int readPlain(final ByteBuffer dst) { 572 Args.notNull(dst, "Byte buffer"); 573 if (this.inPlain.hasData()) { 574 // Acquire buffer 575 final ByteBuffer inPlainBuf = this.inPlain.acquire(); 576 577 // Perform opertaions 578 inPlainBuf.flip(); 579 final int n = Math.min(inPlainBuf.remaining(), dst.remaining()); 580 for (int i = 0; i < n; i++) { 581 dst.put(inPlainBuf.get()); 582 } 583 inPlainBuf.compact(); 584 585 // Release if empty 586 if (inPlainBuf.position() == 0) { 587 this.inPlain.release(); 588 } 589 return n; 590 } else { 591 if (this.endOfStream) { 592 return -1; 593 } else { 594 return 0; 595 } 596 } 597 } 598 599 @Override 600 public synchronized void close() { 601 if (this.status >= CLOSING) { 602 return; 603 } 604 this.status = CLOSING; 605 if (this.session.getSocketTimeout() == 0) { 606 this.session.setSocketTimeout(1000); 607 } 608 this.sslEngine.closeOutbound(); 609 try { 610 updateEventMask(); 611 } catch (final CancelledKeyException ex) { 612 shutdown(); 613 } 614 } 615 616 @Override 617 public synchronized void shutdown() { 618 if (this.status == CLOSED) { 619 return; 620 } 621 this.status = CLOSED; 622 this.session.shutdown(); 623 624 this.inEncrypted.release(); 625 this.outEncrypted.release(); 626 this.inPlain.release(); 627 628 } 629 630 @Override 631 public int getStatus() { 632 return this.status; 633 } 634 635 @Override 636 public boolean isClosed() { 637 return this.status >= CLOSING || this.session.isClosed(); 638 } 639 640 @Override 641 public ByteChannel channel() { 642 return this.channel; 643 } 644 645 @Override 646 public SocketAddress getLocalAddress() { 647 return this.session.getLocalAddress(); 648 } 649 650 @Override 651 public SocketAddress getRemoteAddress() { 652 return this.session.getRemoteAddress(); 653 } 654 655 @Override 656 public synchronized int getEventMask() { 657 return this.appEventMask; 658 } 659 660 @Override 661 public synchronized void setEventMask(final int ops) { 662 this.appEventMask = ops; 663 updateEventMask(); 664 } 665 666 @Override 667 public synchronized void setEvent(final int op) { 668 this.appEventMask = this.appEventMask | op; 669 updateEventMask(); 670 } 671 672 @Override 673 public synchronized void clearEvent(final int op) { 674 this.appEventMask = this.appEventMask & ~op; 675 updateEventMask(); 676 } 677 678 @Override 679 public int getSocketTimeout() { 680 return this.session.getSocketTimeout(); 681 } 682 683 @Override 684 public void setSocketTimeout(final int timeout) { 685 this.session.setSocketTimeout(timeout); 686 } 687 688 @Override 689 public synchronized boolean hasBufferedInput() { 690 return (this.appBufferStatus != null && this.appBufferStatus.hasBufferedInput()) 691 || this.inEncrypted.hasData() 692 || this.inPlain.hasData(); 693 } 694 695 @Override 696 public synchronized boolean hasBufferedOutput() { 697 return (this.appBufferStatus != null && this.appBufferStatus.hasBufferedOutput()) 698 || this.outEncrypted.hasData(); 699 } 700 701 @Override 702 public synchronized void setBufferStatus(final SessionBufferStatus status) { 703 this.appBufferStatus = status; 704 } 705 706 @Override 707 public Object getAttribute(final String name) { 708 return this.session.getAttribute(name); 709 } 710 711 @Override 712 public Object removeAttribute(final String name) { 713 return this.session.removeAttribute(name); 714 } 715 716 @Override 717 public void setAttribute(final String name, final Object obj) { 718 this.session.setAttribute(name, obj); 719 } 720 721 private static void formatOps(final StringBuilder buffer, final int ops) { 722 if ((ops & SelectionKey.OP_READ) > 0) { 723 buffer.append('r'); 724 } 725 if ((ops & SelectionKey.OP_WRITE) > 0) { 726 buffer.append('w'); 727 } 728 } 729 730 @Override 731 public String toString() { 732 final StringBuilder buffer = new StringBuilder(); 733 buffer.append(this.session); 734 buffer.append("["); 735 switch (this.status) { 736 case ACTIVE: 737 buffer.append("ACTIVE"); 738 break; 739 case CLOSING: 740 buffer.append("CLOSING"); 741 break; 742 case CLOSED: 743 buffer.append("CLOSED"); 744 break; 745 } 746 buffer.append("]["); 747 formatOps(buffer, this.appEventMask); 748 buffer.append("]["); 749 buffer.append(this.sslEngine.getHandshakeStatus()); 750 if (this.sslEngine.isInboundDone()) { 751 buffer.append("][inbound done]["); 752 } 753 if (this.sslEngine.isOutboundDone()) { 754 buffer.append("][outbound done]["); 755 } 756 if (this.endOfStream) { 757 buffer.append("][EOF]["); 758 } 759 buffer.append("]["); 760 buffer.append(!this.inEncrypted.hasData() ? 0 : inEncrypted.acquire().position()); 761 buffer.append("]["); 762 buffer.append(!this.inPlain.hasData() ? 0 : inPlain.acquire().position()); 763 buffer.append("]["); 764 buffer.append(!this.outEncrypted.hasData() ? 0 : outEncrypted.acquire().position()); 765 buffer.append("]"); 766 return buffer.toString(); 767 } 768 769 @Override 770 public Socket getSocket(){ 771 if (this.session instanceof SocketAccessor){ 772 return ((SocketAccessor) this.session).getSocket(); 773 } else { 774 return null; 775 } 776 } 777 778 private class InternalByteChannel implements ByteChannel { 779 780 @Override 781 public int write(final ByteBuffer src) throws IOException { 782 return SSLIOSession.this.writePlain(src); 783 } 784 785 @Override 786 public int read(final ByteBuffer dst) throws IOException { 787 return SSLIOSession.this.readPlain(dst); 788 } 789 790 @Override 791 public void close() throws IOException { 792 SSLIOSession.this.close(); 793 } 794 795 @Override 796 public boolean isOpen() { 797 return !SSLIOSession.this.isClosed(); 798 } 799 800 } 801 802}