/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * Licensed 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. */ package com.hazelcast.nio.tcp; import com.hazelcast.client.impl.protocol.util.ClientMessageChannelInboundHandler; import com.hazelcast.config.SSLConfig; import com.hazelcast.internal.networking.Channel; import com.hazelcast.internal.networking.ChannelInboundHandler; import com.hazelcast.internal.networking.ChannelInitializer; import com.hazelcast.internal.networking.ChannelOutboundHandler; import com.hazelcast.internal.networking.ChannelReader; import com.hazelcast.internal.networking.ChannelWriter; import com.hazelcast.internal.networking.InitResult; import com.hazelcast.logging.ILogger; import com.hazelcast.nio.IOService; import com.hazelcast.nio.Protocols; import com.hazelcast.nio.ascii.TextChannelInboundHandler; import com.hazelcast.nio.ascii.TextChannelOutboundHandler; import java.io.EOFException; import java.io.IOException; import java.net.SocketException; import java.nio.ByteBuffer; import java.util.concurrent.ConcurrentMap; import static com.hazelcast.nio.ConnectionType.MEMBER; import static com.hazelcast.nio.IOService.KILO_BYTE; import static com.hazelcast.nio.IOUtil.newByteBuffer; import static com.hazelcast.nio.Protocols.CLIENT_BINARY_NEW; import static com.hazelcast.nio.Protocols.CLUSTER; import static com.hazelcast.nio.Protocols.TEXT; import static com.hazelcast.util.StringUtil.bytesToString; import static com.hazelcast.util.StringUtil.stringToBytes; public class MemberChannelInitializer implements ChannelInitializer<TcpIpConnection> { private static final String PROTOCOL_BUFFER = "protocolbuffer"; private final ILogger logger; public MemberChannelInitializer(ILogger logger) { this.logger = logger; } @Override public InitResult<ChannelInboundHandler> initInbound(TcpIpConnection connection, ChannelReader reader) throws IOException { TcpIpConnectionManager connectionManager = connection.getConnectionManager(); final IOService ioService = connectionManager.getIoService(); Channel channel = reader.getChannel(); ByteBuffer protocolBuffer = getProtocolBuffer(channel); int readBytes = channel.read(protocolBuffer); if (readBytes == -1) { throw new EOFException("Could not read protocol type!"); } if (readBytes == 0 && isSslEnabled(ioService)) { // when using SSL, we can read 0 bytes since data read from socket can be handshake data. return null; } if (protocolBuffer.hasRemaining()) { // we have not yet received all protocol bytes return null; } // since the protocol is complete; we can remove the protocol-buffer. channel.attributeMap().remove(PROTOCOL_BUFFER); ChannelInboundHandler inboundHandler; String protocol = bytesToString(protocolBuffer.array()); ChannelWriter channelWriter = connection.getChannelWriter(); ByteBuffer inputBuffer; if (CLUSTER.equals(protocol)) { inputBuffer = initInputBuffer(connection, ioService.getSocketReceiveBufferSize()); connection.setType(MEMBER); channelWriter.setProtocol(CLUSTER); inboundHandler = ioService.createReadHandler(connection); } else if (CLIENT_BINARY_NEW.equals(protocol)) { inputBuffer = initInputBuffer(connection, ioService.getSocketClientReceiveBufferSize()); channelWriter.setProtocol(CLIENT_BINARY_NEW); inboundHandler = new ClientMessageChannelInboundHandler( reader.getNormalFramesReadCounter(), new MessageHandlerImpl(connection, ioService.getClientEngine())); } else { inputBuffer = initInputBuffer(connection, ioService.getSocketReceiveBufferSize()); channelWriter.setProtocol(TEXT); inputBuffer.put(protocolBuffer.array()); inboundHandler = new TextChannelInboundHandler(connection); connectionManager.incrementTextConnections(); } if (inboundHandler == null) { throw new IOException("Could not initialize ChannelInboundHandler!"); } return new InitResult<ChannelInboundHandler>(inputBuffer, inboundHandler); } private static ByteBuffer getProtocolBuffer(Channel channel) { ConcurrentMap attributeMap = channel.attributeMap(); ByteBuffer protocolBuffer = (ByteBuffer) attributeMap.get(PROTOCOL_BUFFER); if (protocolBuffer == null) { protocolBuffer = ByteBuffer.allocate(3); attributeMap.put(PROTOCOL_BUFFER, protocolBuffer); } return protocolBuffer; } private ByteBuffer initInputBuffer(TcpIpConnection connection, int sizeKb) { boolean directBuffer = connection.getConnectionManager().getIoService().useDirectSocketBuffer(); int sizeBytes = sizeKb * KILO_BYTE; ByteBuffer inputBuffer = newByteBuffer(sizeBytes, directBuffer); try { connection.getChannel().socket().setReceiveBufferSize(sizeBytes); } catch (SocketException e) { logger.finest("Failed to adjust TCP receive buffer of " + connection + " to " + sizeBytes + " B.", e); } return inputBuffer; } private static boolean isSslEnabled(IOService ioService) { SSLConfig config = ioService.getSSLConfig(); return config != null && config.isEnabled(); } @Override public InitResult<ChannelOutboundHandler> initOutbound(TcpIpConnection connection, ChannelWriter writer, String protocol) { logger.fine("Initializing ChannelWriter ChannelOutboundHandler with " + Protocols.toUserFriendlyString(protocol)); ChannelOutboundHandler handler = newOutboundHandler(connection, protocol); ByteBuffer outputBuffer = newOutputBuffer(connection, protocol); return new InitResult<ChannelOutboundHandler>(outputBuffer, handler); } private ChannelOutboundHandler newOutboundHandler(TcpIpConnection connection, String protocol) { if (CLUSTER.equals(protocol)) { IOService ioService = connection.getConnectionManager().getIoService(); return ioService.createWriteHandler(connection); } else if (CLIENT_BINARY_NEW.equals(protocol)) { return new ClientChannelOutboundHandler(); } else { return new TextChannelOutboundHandler(connection); } } private ByteBuffer newOutputBuffer(TcpIpConnection connection, String protocol) { IOService ioService = connection.getConnectionManager().getIoService(); int sizeKb = CLUSTER.equals(protocol) ? ioService.getSocketSendBufferSize() : ioService.getSocketClientSendBufferSize(); int size = KILO_BYTE * sizeKb; ByteBuffer outputBuffer = newByteBuffer(size, ioService.useDirectSocketBuffer()); if (CLUSTER.equals(protocol)) { outputBuffer.put(stringToBytes(CLUSTER)); } try { connection.getChannel().socket().setSendBufferSize(size); } catch (SocketException e) { logger.finest("Failed to adjust TCP send buffer of " + connection + " to " + size + " B.", e); } return outputBuffer; } }