package org.apache.activemq.transport.auto.nio; import java.io.IOException; import java.net.Socket; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Set; import java.util.concurrent.Future; import javax.net.ServerSocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import org.apache.activemq.broker.BrokerService; import org.apache.activemq.broker.BrokerServiceAware; import org.apache.activemq.transport.Transport; import org.apache.activemq.transport.auto.AutoTcpTransportServer; import org.apache.activemq.transport.nio.AutoInitNioSSLTransport; import org.apache.activemq.transport.nio.NIOSSLTransport; import org.apache.activemq.transport.tcp.TcpTransport; import org.apache.activemq.transport.tcp.TcpTransport.InitBuffer; import org.apache.activemq.transport.tcp.TcpTransportFactory; import org.apache.activemq.transport.tcp.TcpTransportServer; import org.apache.activemq.util.IntrospectionSupport; import org.apache.activemq.wireformat.WireFormat; /** * 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. */ public class AutoNIOSSLTransportServer extends AutoTcpTransportServer { private SSLContext context; public AutoNIOSSLTransportServer(SSLContext context, TcpTransportFactory transportFactory, URI location, ServerSocketFactory serverSocketFactory, BrokerService brokerService, Set<String> enabledProtocols) throws IOException, URISyntaxException { super(transportFactory, location, serverSocketFactory, brokerService, enabledProtocols); this.context = context; } private boolean needClientAuth; private boolean wantClientAuth; protected Transport createTransport(Socket socket, WireFormat format, SSLEngine engine, InitBuffer initBuffer, ByteBuffer inputBuffer, TcpTransportFactory detectedFactory) throws IOException { NIOSSLTransport transport = new NIOSSLTransport(format, socket, engine, initBuffer, inputBuffer); if (context != null) { transport.setSslContext(context); } transport.setNeedClientAuth(needClientAuth); transport.setWantClientAuth(wantClientAuth); return transport; } @Override protected TcpTransport createTransport(Socket socket, WireFormat format) throws IOException { throw new UnsupportedOperationException("method not supported"); } @Override public boolean isSslServer() { return true; } public boolean isNeedClientAuth() { return this.needClientAuth; } public void setNeedClientAuth(boolean value) { this.needClientAuth = value; } public boolean isWantClientAuth() { return this.wantClientAuth; } public void setWantClientAuth(boolean value) { this.wantClientAuth = value; } @Override protected TransportInfo configureTransport(final TcpTransportServer server, final Socket socket) throws Exception { //The SSLEngine needs to be initialized and handshake done to get the first command and detect the format //The wireformat doesn't need properties set here because we aren't using this format during the SSL handshake final AutoInitNioSSLTransport in = new AutoInitNioSSLTransport(wireFormatFactory.createWireFormat(), socket); if (context != null) { in.setSslContext(context); } //We need to set the transport options on the init transport so that the SSL options are set if (transportOptions != null) { //Clone the map because we will need to set the options later on the actual transport IntrospectionSupport.setProperties(in, new HashMap<>(transportOptions)); } in.start(); SSLEngine engine = in.getSslSession(); //Attempt to read enough bytes to detect the protocol until the timeout period //is reached Future<?> future = protocolDetectionExecutor.submit(new Runnable() { @Override public void run() { int attempts = 0; do { if(attempts > 0) { try { //increase sleep period each attempt to prevent high cpu usage //if the client is hung and not sending bytes int sleep = attempts >= 1024 ? 1024 : 4 * attempts; Thread.sleep(sleep); } catch (InterruptedException e) { break; } } //In the future it might be better to register a nonblocking selector //to be told when bytes are ready in.serviceRead(); attempts++; } while(in.getReadSize().get() < 8 && !Thread.interrupted()); } }); try { //If this fails and throws an exception and the socket will be closed waitForProtocolDetectionFinish(future, in.getReadSize()); } finally { //call cancel in case task didn't complete which will interrupt the task future.cancel(true); } in.stop(); InitBuffer initBuffer = new InitBuffer(in.getReadSize().get(), ByteBuffer.allocate(in.getReadData().length)); initBuffer.buffer.put(in.getReadData()); ProtocolInfo protocolInfo = detectProtocol(in.getReadData()); if (protocolInfo.detectedTransportFactory instanceof BrokerServiceAware) { ((BrokerServiceAware) protocolInfo.detectedTransportFactory).setBrokerService(brokerService); } WireFormat format = protocolInfo.detectedWireFormatFactory.createWireFormat(); Transport transport = createTransport(socket, format, engine, initBuffer, in.getInputBuffer(), protocolInfo.detectedTransportFactory); return new TransportInfo(format, transport, protocolInfo.detectedTransportFactory); } }