/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.core5.reactor; import java.io.IOException; import java.net.SocketAddress; import java.nio.channels.ByteChannel; import java.util.Queue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ssl.SSLBufferManagement; import org.apache.hc.core5.reactor.ssl.SSLIOSession; import org.apache.hc.core5.reactor.ssl.SSLMode; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.util.Asserts; /** * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) class ManagedIOSession implements IOSession, TransportSecurityLayer { private final NamedEndpoint namedEndpoint; private final IOSession ioSession; private final AtomicReference tlsSessionRef; private final Queue closedSessions; private final AtomicBoolean closed; private volatile long lastAccessTime; ManagedIOSession( final NamedEndpoint namedEndpoint, final IOSession ioSession, final Queue closedSessions) { this.namedEndpoint = namedEndpoint; this.ioSession = ioSession; this.closedSessions = closedSessions; this.tlsSessionRef = new AtomicReference<>(null); this.closed = new AtomicBoolean(false); updateAccessTime(); } void updateAccessTime() { this.lastAccessTime = System.currentTimeMillis(); } long getLastAccessTime() { return lastAccessTime; } private IOSession getSessionImpl() { final SSLIOSession tlsSession = tlsSessionRef.get(); if (tlsSession != null) { return tlsSession; } else { return ioSession; } } IOEventHandler getEventHandler() { final IOEventHandler handler = ioSession.getHandler(); Asserts.notNull(handler, "IO event handler"); return handler; } void onConnected() { try { final IOEventHandler handler = getEventHandler(); final SSLIOSession tlsSession = tlsSessionRef.get(); if (tlsSession != null) { try { if (!tlsSession.isInitialized()) { tlsSession.initialize(); } handler.connected(this); } catch (Exception ex) { handler.exception(tlsSession, ex); } } else { handler.connected(this); } } catch (RuntimeException ex) { shutdown(); throw ex; } } void onInputReady() { try { final IOEventHandler handler = getEventHandler(); final SSLIOSession tlsSession = tlsSessionRef.get(); if (tlsSession != null) { try { if (!tlsSession.isInitialized()) { tlsSession.initialize(); } if (tlsSession.isAppInputReady()) { do { handler.inputReady(this); } while (tlsSession.hasInputDate()); } tlsSession.inboundTransport(); } catch (final IOException ex) { handler.exception(tlsSession, ex); tlsSession.shutdown(); } } else { handler.inputReady(this); } } catch (RuntimeException ex) { shutdown(); throw ex; } } void onOutputReady() { try { final IOEventHandler handler = getEventHandler(); final SSLIOSession tlsSession = tlsSessionRef.get(); if (tlsSession != null) { try { if (!tlsSession.isInitialized()) { tlsSession.initialize(); } if (tlsSession.isAppOutputReady()) { handler.outputReady(this); } tlsSession.outboundTransport(); } catch (final IOException ex) { handler.exception(tlsSession, ex); tlsSession.shutdown(); } } else { handler.outputReady(this); } } catch (RuntimeException ex) { shutdown(); throw ex; } } void onTimeout() { try { final IOEventHandler handler = getEventHandler(); handler.timeout(this); final SSLIOSession tlsSession = tlsSessionRef.get(); if (tlsSession != null) { if (tlsSession.isOutboundDone() && !tlsSession.isInboundDone()) { // The session failed to terminate cleanly tlsSession.shutdown(); } } } catch (RuntimeException ex) { shutdown(); throw ex; } } void onDisconnected() { final IOEventHandler handler = getEventHandler(); handler.disconnected(this); } @Override public void start( final SSLContext sslContext, final SSLBufferManagement sslBufferManagement, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier) { if (!tlsSessionRef.compareAndSet(null, new SSLIOSession( namedEndpoint, ioSession, namedEndpoint != null ? SSLMode.CLIENT : SSLMode.SERVER, sslContext, sslBufferManagement, initializer, verifier))) { throw new IllegalStateException("TLS already activated"); } } @Override public SSLSession getSSLSession() { final SSLIOSession sslIoSession = tlsSessionRef.get(); return sslIoSession != null ? sslIoSession.getSSLSession() : null; } public void close() { if (closed.compareAndSet(false, true)) { try { getSessionImpl().close(); } finally { closedSessions.add(this); } } } @Override public void shutdown() { if (closed.compareAndSet(false, true)) { try { getSessionImpl().shutdown(); } finally { closedSessions.add(this); } } } @Override public int getStatus() { return getSessionImpl().getStatus(); } @Override public boolean isClosed() { return getSessionImpl().isClosed(); } @Override public IOEventHandler getHandler() { return ioSession.getHandler(); } public void setHandler(final IOEventHandler eventHandler) { ioSession.setHandler(eventHandler); } @Override public void addLast(final Command command) { getSessionImpl().addLast(command); } @Override public void addFirst(final Command command) { getSessionImpl().addFirst(command); } @Override public Command getCommand() { return getSessionImpl().getCommand(); } @Override public ByteChannel channel() { return getSessionImpl().channel(); } @Override public SocketAddress getRemoteAddress() { return ioSession.getRemoteAddress(); } @Override public SocketAddress getLocalAddress() { return ioSession.getLocalAddress(); } @Override public int getEventMask() { return getSessionImpl().getEventMask(); } @Override public void setEventMask(final int ops) { getSessionImpl().setEventMask(ops); } @Override public void setEvent(final int op) { getSessionImpl().setEvent(op); } @Override public void clearEvent(final int op) { getSessionImpl().clearEvent(op); } @Override public int getSocketTimeout() { return ioSession.getSocketTimeout(); } @Override public void setSocketTimeout(final int timeout) { ioSession.setSocketTimeout(timeout); } @Override public String toString() { return getSessionImpl().toString(); } }