/** * Copyright (c) 2015, WSO2 Inc. (http://wso2.com) All Rights Reserved. * <p> * WSO2 Inc. 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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * 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 org.wso2.carbon.transport.http.netty.listener; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.carbon.messaging.BufferFactory; import org.wso2.carbon.messaging.CarbonMessageProcessor; import org.wso2.carbon.messaging.TransportListener; import org.wso2.carbon.messaging.TransportListenerManager; import org.wso2.carbon.transport.http.netty.common.Constants; import org.wso2.carbon.transport.http.netty.common.Util; import org.wso2.carbon.transport.http.netty.common.ssl.SSLConfig; import org.wso2.carbon.transport.http.netty.config.ListenerConfiguration; import org.wso2.carbon.transport.http.netty.config.TransportProperty; import org.wso2.carbon.transport.http.netty.internal.HTTPTransportContextHolder; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; /** * A class that starts the HTTP Server Bootstrap in given port and capable of binding interfaces to Server Bootstrap. * @deprecated use {@link HTTPServerConnector} instead. */ @Deprecated public class HTTPTransportListener extends TransportListener { private static final Logger log = LoggerFactory.getLogger(HTTPTransportListener.class); private int bossGroupSize; private int workerGroupSize; private ServerBootstrap bootstrap; private ListenerConfiguration defaultListenerConfig; private Map<Integer, ChannelFuture> channelFutureMap = new ConcurrentHashMap<>(); //Map used for cache listener configurations private Map<String, ListenerConfiguration> listenerConfigurationMap = new HashMap<>(); //Map used for map listener configurations with port as key for used in channel initializer private Map<String, ListenerConfiguration> listenerConfigMapWithPort = new HashMap<>(); private Map<String, SSLConfig> sslConfigMap = new ConcurrentHashMap<>(); private Set<TransportProperty> transportPropertiesSet; private Map<String, Object> transportProperties = new HashMap<>(); public HTTPTransportListener(Set<TransportProperty> transportPropertiesSet, Set<ListenerConfiguration> listenerConfigurationSet) { super(listenerConfigurationSet.iterator().next().getId()); if (listenerConfigurationSet.isEmpty()) { log.error("Cannot find registered listener configurations hence cannot start the transport listeners"); return; } this.transportPropertiesSet = transportPropertiesSet; listenerConfigurationMap = listenerConfigurationSet.stream() .collect(Collectors.toMap(ListenerConfiguration::getId, config -> config)); listenerConfigurationSet.forEach(config -> { int port = config.getPort(); String id = String.valueOf(port); //TODO currently only the port is used as the key, may be need to improve with host + port //TODO to support foo.com:9090, bar.com:9090 with two different configuration. listenerConfigMapWithPort.put(id, config); }); Iterator itr = listenerConfigurationSet.iterator(); if (itr.hasNext()) { defaultListenerConfig = (ListenerConfiguration) itr.next(); } } @Override public void start() { log.info("Starting HTTP Transport Listener"); startTransport(); } //configure bootstrap and bind bootstrap for default configuration private void startTransport() { if (transportPropertiesSet != null && !transportPropertiesSet.isEmpty()) { transportProperties = transportPropertiesSet.stream().collect( Collectors.toMap(TransportProperty::getName, TransportProperty::getValue)); } //Create Bootstrap Configuration from listener parameters ServerBootstrapConfiguration.createBootStrapConfiguration(transportProperties); ServerBootstrapConfiguration serverBootstrapConfiguration = ServerBootstrapConfiguration.getInstance(); //boss group is for accepting channels EventLoopGroup bossGroup = HTTPTransportContextHolder.getInstance().getBossGroup(); if (bossGroup == null) { int bossGroupSize = Util.getIntProperty(transportProperties, Constants.SERVER_BOOTSTRAP_BOSS_GROUP_SIZE, Runtime.getRuntime().availableProcessors()); bossGroup = new NioEventLoopGroup(bossGroupSize); HTTPTransportContextHolder.getInstance().setBossGroup(bossGroup); } //worker group is for processing IO EventLoopGroup workerGroup = HTTPTransportContextHolder.getInstance().getWorkerGroup(); if (workerGroup == null) { int workerGroupSize = Util.getIntProperty(transportProperties, Constants.SERVER_BOOTSTRAP_WORKER_GROUP_SIZE, Runtime.getRuntime().availableProcessors() * 2); workerGroup = new NioEventLoopGroup(workerGroupSize); HTTPTransportContextHolder.getInstance().setWorkerGroup(workerGroup); } log.debug("Netty Boss group size " + bossGroup); log.debug("Netty Worker group Size" + workerGroup); bootstrap = new ServerBootstrap(); bootstrap.option(ChannelOption.SO_BACKLOG, serverBootstrapConfiguration.getSoBackLog()); log.debug("Netty Server Socket BACKLOG " + serverBootstrapConfiguration.getSoBackLog()); bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class); addChannelInitializer(); int bufferSize = Util.getIntProperty(transportProperties, Constants.OUTPUT_CONTENT_BUFFER_SIZE, 0); if (bufferSize != 0) { BufferFactory.createInstance(bufferSize); } bootstrap.childOption(ChannelOption.TCP_NODELAY, serverBootstrapConfiguration.isTcpNoDelay()); log.debug("Netty Server Socket TCP_NODELAY " + serverBootstrapConfiguration.isTcpNoDelay()); bootstrap.option(ChannelOption.SO_KEEPALIVE, serverBootstrapConfiguration.isKeepAlive()); log.debug("Netty Server Socket SO_KEEPALIVE " + serverBootstrapConfiguration.isKeepAlive()); bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, serverBootstrapConfiguration.getConnectTimeOut()); log.debug(" Netty Server Socket CONNECT_TIMEOUT_MILLIS " + serverBootstrapConfiguration.getConnectTimeOut()); bootstrap.option(ChannelOption.SO_SNDBUF, serverBootstrapConfiguration.getSendBufferSize()); log.debug("Netty Server Socket SO_SNDBUF " + serverBootstrapConfiguration.getSendBufferSize()); bootstrap.option(ChannelOption.SO_RCVBUF, serverBootstrapConfiguration.getReceiveBufferSize()); log.debug("Netty Server Socket SO_RCVBUF " + serverBootstrapConfiguration.getReceiveBufferSize()); bootstrap.childOption(ChannelOption.SO_RCVBUF, serverBootstrapConfiguration.getReceiveBufferSize()); log.debug("Netty Server Socket SO_RCVBUF " + serverBootstrapConfiguration.getReceiveBufferSize()); bootstrap.childOption(ChannelOption.SO_SNDBUF, serverBootstrapConfiguration.getSendBufferSize()); log.debug("Netty Server Socket SO_SNDBUF " + serverBootstrapConfiguration.getSendBufferSize()); if (defaultListenerConfig.isBindOnStartup()) { bindInterface(defaultListenerConfig); } TransportListenerManager transportListenerManager = HTTPTransportContextHolder.getInstance().getManager(); if (transportListenerManager != null) { transportListenerManager.registerTransportListener(this); } } //Channel Initializer is responsible for create channel pipeline private void addChannelInitializer() { CarbonHTTPServerInitializer handler = new CarbonHTTPServerInitializer(listenerConfigMapWithPort); handler.setSslConfig(defaultListenerConfig.getSslConfig()); handler.setSslConfigMap(sslConfigMap); handler.setupConnectionManager(transportProperties); bootstrap.childHandler(handler); } @Override public void stop() { log.info("Stopping HTTP transport " + id + " on port " + defaultListenerConfig.getPort()); shutdownEventLoops(); } @Override public void beginMaintenance() { log.info("Putting HTTP transport " + id + " on port " + defaultListenerConfig.getPort() + " into maintenance mode"); shutdownEventLoops(); } @Override public void endMaintenance() { log.info("Ending maintenance mode for HTTP transport " + id + " running on port " + defaultListenerConfig .getPort()); startTransport(); } private void shutdownEventLoops() { try { EventLoopGroup bossGroup = HTTPTransportContextHolder.getInstance().getBossGroup(); if (bossGroup != null) { bossGroup.shutdownGracefully().sync(); HTTPTransportContextHolder.getInstance().setBossGroup(null); } EventLoopGroup workerGroup = HTTPTransportContextHolder.getInstance().getWorkerGroup(); if (workerGroup != null) { workerGroup.shutdownGracefully().sync(); HTTPTransportContextHolder.getInstance().setWorkerGroup(null); } log.info("HTTP transport " + id + " on port " + defaultListenerConfig.getPort() + " stopped successfully"); } catch (InterruptedException e) { log.error("HTTP transport " + id + " on port " + defaultListenerConfig.getPort() + " could not be stopped successfully " + e.getMessage()); } } @Override public void setMessageProcessor(CarbonMessageProcessor carbonMessageProcessor) { } @Override public boolean bind(String interfaceId) { ListenerConfiguration listenerConfiguration = listenerConfigurationMap.get(interfaceId); if (listenerConfiguration != null) { if (!listenerConfiguration.isBindOnStartup()) { return bindInterface(listenerConfiguration); } else { log.debug("Interface is already binned at the startup, hence ignoring"); return true; } } else { log.error("Cannot find defined Listener interface for Listener id " + interfaceId); } return false; } private boolean bindInterface(ListenerConfiguration listenerConfiguration) { try { String id = String.valueOf(listenerConfiguration.getPort()); SSLConfig sslConfig = listenerConfiguration.getSslConfig(); if (sslConfig != null) { sslConfigMap.put(id, sslConfig); } ChannelFuture future = bootstrap.bind(new InetSocketAddress(listenerConfiguration.getHost(), listenerConfiguration.getPort())).sync(); if (future.isSuccess()) { channelFutureMap.put(listenerConfiguration.getPort(), future); if (listenerConfiguration.getSslConfig() == null) { log.info("HTTP Interface " + listenerConfiguration.getId() + " starting on host " + listenerConfiguration.getHost() + " and port " + listenerConfiguration.getPort()); } else { log.info("HTTPS Interface " + listenerConfiguration.getId() + " starting on host " + listenerConfiguration.getHost() + " and port " + listenerConfiguration.getPort()); } return true; } else { log.error("Cannot bind port for host " + listenerConfiguration.getHost() + " port " + listenerConfiguration.getPort()); } } catch (InterruptedException e) { log.error(e.getMessage(), e); } return false; } @Override public boolean unBind(String interfaceId) { ListenerConfiguration listenerConfiguration = listenerConfigurationMap.get(interfaceId); if (listenerConfiguration != null && !defaultListenerConfig.getId().equals(listenerConfiguration.getId())) { String id = String.valueOf(listenerConfiguration.getPort()); //Remove cached channels and close them. ChannelFuture future = channelFutureMap.remove(listenerConfiguration.getPort()); if (future != null) { if (sslConfigMap.get(id) != null) { sslConfigMap.remove(id); } future.channel().close(); if (listenerConfiguration.getSslConfig() == null) { log.info("HTTP Listener stopped on listening interface " + interfaceId + " attached to host " + listenerConfiguration.getHost() + " and port " + listenerConfiguration.getPort()); } else { log.info("HTTPS Listener stopped on listening interface " + interfaceId + " attached to host " + listenerConfiguration.getHost() + " and port " + listenerConfiguration.getPort()); } return true; } } return false; } }