001package org.apache.activemq.transport.auto.nio;
002
003import java.io.IOException;
004import java.net.Socket;
005import java.net.URI;
006import java.net.URISyntaxException;
007import java.nio.ByteBuffer;
008import java.util.HashMap;
009import java.util.Set;
010import java.util.concurrent.Future;
011
012import javax.net.ServerSocketFactory;
013import javax.net.ssl.SSLContext;
014import javax.net.ssl.SSLEngine;
015
016import org.apache.activemq.broker.BrokerService;
017import org.apache.activemq.broker.BrokerServiceAware;
018import org.apache.activemq.broker.TransportConnector;
019import org.apache.activemq.transport.Transport;
020import org.apache.activemq.transport.auto.AutoTcpTransportServer;
021import org.apache.activemq.transport.nio.AutoInitNioSSLTransport;
022import org.apache.activemq.transport.nio.NIOSSLTransport;
023import org.apache.activemq.transport.tcp.TcpTransport;
024import org.apache.activemq.transport.tcp.TcpTransport.InitBuffer;
025import org.apache.activemq.transport.tcp.TcpTransportFactory;
026import org.apache.activemq.transport.tcp.TcpTransportServer;
027import org.apache.activemq.util.IntrospectionSupport;
028import org.apache.activemq.wireformat.WireFormat;
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031
032/**
033 * Licensed to the Apache Software Foundation (ASF) under one or more
034 * contributor license agreements.  See the NOTICE file distributed with
035 * this work for additional information regarding copyright ownership.
036 * The ASF licenses this file to You under the Apache License, Version 2.0
037 * (the "License"); you may not use this file except in compliance with
038 * the License.  You may obtain a copy of the License at
039 *
040 *      http://www.apache.org/licenses/LICENSE-2.0
041 *
042 * Unless required by applicable law or agreed to in writing, software
043 * distributed under the License is distributed on an "AS IS" BASIS,
044 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
045 * See the License for the specific language governing permissions and
046 * limitations under the License.
047 */
048public class AutoNIOSSLTransportServer extends AutoTcpTransportServer {
049
050    private static final Logger LOG = LoggerFactory.getLogger(AutoNIOSSLTransportServer.class);
051    private SSLContext context;
052
053    public AutoNIOSSLTransportServer(SSLContext context, TcpTransportFactory transportFactory, URI location, ServerSocketFactory serverSocketFactory,
054            BrokerService brokerService, Set<String> enabledProtocols) throws IOException, URISyntaxException {
055        super(transportFactory, location, serverSocketFactory, brokerService, enabledProtocols);
056
057        this.context = context;
058    }
059
060    private boolean needClientAuth;
061    private boolean wantClientAuth;
062
063    protected Transport createTransport(Socket socket, WireFormat format, SSLEngine engine,
064            InitBuffer initBuffer, ByteBuffer inputBuffer, TcpTransportFactory detectedFactory) throws IOException {
065        NIOSSLTransport transport = new NIOSSLTransport(format, socket, engine, initBuffer, inputBuffer);
066        if (context != null) {
067            transport.setSslContext(context);
068        }
069
070        transport.setNeedClientAuth(needClientAuth);
071        transport.setWantClientAuth(wantClientAuth);
072
073
074        return transport;
075    }
076
077    @Override
078    protected TcpTransport createTransport(Socket socket, WireFormat format) throws IOException {
079        throw new UnsupportedOperationException("method not supported");
080    }
081
082    @Override
083    public boolean isSslServer() {
084        return true;
085    }
086
087    public boolean isNeedClientAuth() {
088        return this.needClientAuth;
089    }
090
091    public void setNeedClientAuth(boolean value) {
092        this.needClientAuth = value;
093    }
094
095    public boolean isWantClientAuth() {
096        return this.wantClientAuth;
097    }
098
099    public void setWantClientAuth(boolean value) {
100        this.wantClientAuth = value;
101    }
102
103
104    @Override
105    protected TransportInfo configureTransport(final TcpTransportServer server, final Socket socket) throws Exception {
106        //The SSLEngine needs to be initialized and handshake done to get the first command and detect the format
107        //The wireformat doesn't need properties set here because we aren't using this format during the SSL handshake
108        final AutoInitNioSSLTransport in = new AutoInitNioSSLTransport(wireFormatFactory.createWireFormat(), socket);
109        if (context != null) {
110            in.setSslContext(context);
111        }
112        //We need to set the transport options on the init transport so that the SSL options are set
113        if (transportOptions != null) {
114            //Clone the map because we will need to set the options later on the actual transport
115            IntrospectionSupport.setProperties(in, new HashMap<>(transportOptions));
116        }
117
118        //Attempt to read enough bytes to detect the protocol until the timeout period
119        //is reached
120        Future<?> future = protocolDetectionExecutor.submit(new Runnable() {
121            @Override
122            public void run() {
123                try {
124                    in.start();
125                } catch (Exception error) {
126                    LOG.warn("Could not accept connection {}: {} ({})",
127                            (in.getRemoteAddress() == null ? "" : "from " + in.getRemoteAddress()), error.getMessage(),
128                            TransportConnector.getRootCause(error).getMessage());
129                    throw new IllegalStateException("Could not complete Transport start", error);
130                }
131
132                int attempts = 0;
133                do {
134                    if(attempts > 0) {
135                        try {
136                            //increase sleep period each attempt to prevent high cpu usage
137                            //if the client is hung and not sending bytes
138                            int sleep = attempts >= 1024 ? 1024 : 4 * attempts;
139                            Thread.sleep(sleep);
140                        } catch (InterruptedException e) {
141                            break;
142                        }
143                    }
144                    //In the future it might be better to register a nonblocking selector
145                    //to be told when bytes are ready
146                    in.serviceRead();
147                    attempts++;
148                } while(in.getReadSize().get() < 8 && !Thread.interrupted());
149            }
150        });
151
152        try {
153            //If this fails and throws an exception and the socket will be closed
154            waitForProtocolDetectionFinish(future, in.getReadSize());
155        } finally {
156            //call cancel in case task didn't complete which will interrupt the task
157            future.cancel(true);
158        }
159        in.stop();
160
161        InitBuffer initBuffer = new InitBuffer(in.getReadSize().get(), ByteBuffer.allocate(in.getReadData().length));
162        initBuffer.buffer.put(in.getReadData());
163
164        ProtocolInfo protocolInfo = detectProtocol(in.getReadData());
165
166        if (protocolInfo.detectedTransportFactory instanceof BrokerServiceAware) {
167            ((BrokerServiceAware) protocolInfo.detectedTransportFactory).setBrokerService(brokerService);
168        }
169
170        WireFormat format = protocolInfo.detectedWireFormatFactory.createWireFormat();
171        Transport transport = createTransport(socket, format, in.getSslSession(), initBuffer, in.getInputBuffer(), protocolInfo.detectedTransportFactory);
172
173        return new TransportInfo(format, transport, protocolInfo.detectedTransportFactory);
174    }
175
176}
177
178