# NettyConfiguration.java

    /*
     *  Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
     *
     *  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
     *
     *  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 org.apache.synapse.transport.netty.config;

    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.apache.synapse.transport.netty.BridgeConstants;
    import org.apache.synapse.transport.util.ConfigurationBuilderUtil;

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Properties;

    /**
     * This class encapsulates netty transport tuning configurations specified via a
     * configurations file or system properties.
     */
    public class NettyConfiguration {

        private static final Log LOG = LogFactory.getLog(NettyConfiguration.class);

        /**
         * Default tuning parameter values.
         */
        public static final int DEFAULT_WORKER_POOL_SIZE_CORE = 400;
        public static final int DEFAULT_WORKER_POOL_SIZE_MAX = 400;
        public static final int DEFAULT_WORKER_THREAD_KEEPALIVE_SEC = 60;
        public static final int DEFAULT_WORKER_POOL_QUEUE_LENGTH = -1;
        public static final int DEFAULT_HTTP_SOCKET_TIMEOUT = 180000;
        public static final int DEFAULT_CONNECTION_POOLING_MAX_IDLE_CONNECTIONS = 100;
        public static final int DEFAULT_CONNECTION_POOLING_MAX_ACTIVE_CONNECTIONS = -1;
        public static final int DEFAULT_CONNECTION_POOLING_WAIT_TIME = 30;
        public static final int DEFAULT_CLIENT_ENDPOINT_SOCKET_TIMEOUT = 60;
        public static final int DEFAULT_MAX_STATUS_LINE_LENGTH = -1;
        public static final int DEFAULT_MAX_HEADER_SIZE = -1;
        public static final int DEFAULT_MAX_ENTITY_BODY_SIZE = -1;
        public static final int DEFAULT_MAX_CLIENT_REQUEST_STATUS_LINE_LENGTH = -1;
        public static final int DEFAULT_MAX_CLIENT_REQUEST_HEADER_SIZE = -1;
        public static final int DEFAULT_MAX_CLIENT_REQUEST_ENTITY_BODY_SIZE = -1;
        public static final String HTTP_WORKER_THREAD_GROUP_NAME = "HTTP Worker Thread Group";
        public static final String HTTP_WORKER_THREAD_ID = "HTTPWorker";
        public static final String REVERSE_PROXY_MODE_SYSTEM_PROPERTY = "reverseProxyMode";

        private Boolean isKeepAliveDisabled = null;

        private Boolean reverseProxyMode = null;

        private Properties props;

        private static final NettyConfiguration instance = new NettyConfiguration();

        private NettyConfiguration() {

            try {
                props = loadNettyProperties();
            } catch (Exception ignored) {
                // ignore
            }
        }

        public static NettyConfiguration getInstance() {

            return instance;
        }

        public int getWorkerPoolCoreSize() {

            return ConfigurationBuilderUtil.getIntProperty(NettyConfigPropertyNames.WORKER_POOL_SIZE_CORE,
                    DEFAULT_WORKER_POOL_SIZE_CORE, props);
        }

        public int getWorkerPoolMaxSize() {

            return ConfigurationBuilderUtil.getIntProperty(NettyConfigPropertyNames.WORKER_POOL_SIZE_MAX,
                    DEFAULT_WORKER_POOL_SIZE_MAX, props);
        }

        public int getWorkerThreadKeepaliveSec() {

            return ConfigurationBuilderUtil.getIntProperty(NettyConfigPropertyNames.WORKER_THREAD_KEEP_ALIVE_SEC,
                    DEFAULT_WORKER_THREAD_KEEPALIVE_SEC, props);
        }

        public int getWorkerPoolQueueLen() {

            return ConfigurationBuilderUtil.getIntProperty(NettyConfigPropertyNames.WORKER_POOL_QUEUE_LENGTH,
                    DEFAULT_WORKER_POOL_QUEUE_LENGTH, props);
        }

        public boolean isRequestLimitsValidationEnabled() {

            return ConfigurationBuilderUtil.getBooleanProperty(NettyConfigPropertyNames.REQUEST_LIMIT_VALIDATION,
                    false, props);
        }

        public int getMaxStatusLineLength() {

            return ConfigurationBuilderUtil.getIntProperty(NettyConfigPropertyNames.MAX_STATUS_LINE_LENGTH,
                    DEFAULT_MAX_STATUS_LINE_LENGTH, props);
        }

        public int getMaxHeaderSize() {

            return ConfigurationBuilderUtil.getIntProperty(NettyConfigPropertyNames.MAX_HEADER_SIZE,
                    DEFAULT_MAX_HEADER_SIZE, props);
        }

        public int getMaxEntityBodySize() {

            return ConfigurationBuilderUtil.getIntProperty(NettyConfigPropertyNames.MAX_ENTITY_BODY_SIZE,
                    DEFAULT_MAX_ENTITY_BODY_SIZE, props);
        }

        public boolean isClientRequestLimitsValidationEnabled() {

            return ConfigurationBuilderUtil.getBooleanProperty(NettyConfigPropertyNames.CLIENT_REQUEST_LIMIT_VALIDATION,
                    false, props);
        }

        public int getClientRequestMaxStatusLineLength() {

            return ConfigurationBuilderUtil.getIntProperty(NettyConfigPropertyNames.MAX_CLIENT_REQUEST_STATUS_LINE_LENGTH,
                    DEFAULT_MAX_CLIENT_REQUEST_STATUS_LINE_LENGTH, props);
        }

        public int getClientRequestMaxHeaderSize() {

            return ConfigurationBuilderUtil.getIntProperty(NettyConfigPropertyNames.MAX_CLIENT_REQUEST_HEADER_SIZE,
                    DEFAULT_MAX_CLIENT_REQUEST_HEADER_SIZE, props);
        }

        public int getClientRequestMaxEntityBodySize() {

            return ConfigurationBuilderUtil.getIntProperty(NettyConfigPropertyNames.MAX_CLIENT_REQUEST_ENTITY_BODY_SIZE,
                    DEFAULT_MAX_CLIENT_REQUEST_ENTITY_BODY_SIZE, props);
        }

        public int getSocketTimeout() {

            return ConfigurationBuilderUtil.getIntProperty(NettyConfigPropertyNames.HTTP_SOCKET_TIMEOUT,
                    DEFAULT_HTTP_SOCKET_TIMEOUT, props);
        }

        public boolean isCustomConnectionPoolConfigsEnabled() {

            return ConfigurationBuilderUtil.getBooleanProperty(
                    NettyConfigPropertyNames.ENABLE_CUSTOM_CONNECTION_POOL_CONFIG, false, props);
        }

        public int getConnectionPoolingMaxActiveConnections() {

            return ConfigurationBuilderUtil.getIntProperty(
                    NettyConfigPropertyNames.CONNECTION_POOLING_MAX_ACTIVE_CONNECTIONS,
                    DEFAULT_CONNECTION_POOLING_MAX_ACTIVE_CONNECTIONS, props);
        }

        public int getConnectionPoolingMaxIdleConnections() {

            return ConfigurationBuilderUtil.getIntProperty(
                    NettyConfigPropertyNames.CONNECTION_POOLING_MAX_IDLE_CONNECTIONS,
                    DEFAULT_CONNECTION_POOLING_MAX_IDLE_CONNECTIONS, props);
        }

        public int getConnectionPoolingWaitTime() {

            return ConfigurationBuilderUtil.getIntProperty(NettyConfigPropertyNames.CONNECTION_POOLING_WAIT_TIME,
                    DEFAULT_CONNECTION_POOLING_WAIT_TIME, props);
        }

        public int getClientEndpointSocketTimeout() {

            return ConfigurationBuilderUtil.getIntProperty(NettyConfigPropertyNames.CLIENT_ENDPOINT_SOCKET_TIMEOUT,
                    DEFAULT_CLIENT_ENDPOINT_SOCKET_TIMEOUT, props);
        }

        public boolean isKeepAliveDisabled() {

            if (isKeepAliveDisabled == null) {
                isKeepAliveDisabled = ConfigurationBuilderUtil
                        .getBooleanProperty(NettyConfigPropertyNames.DISABLE_KEEPALIVE, false, props);
            }
            return isKeepAliveDisabled;
        }

        public String getServerHostname() {

            return ConfigurationBuilderUtil
                    .getStringProperty(NettyConfigPropertyNames.HTTP_SERVER_HOSTNAME, null, props);

        }

        public String getHttpGetRequestProcessorClass() {

            return ConfigurationBuilderUtil
                    .getStringProperty(NettyConfigPropertyNames.HTTP_GET_REQUEST_PROCESSOR, "", props);

        }

        public String getHttpTransportMediationInterceptorClass() {

            return ConfigurationBuilderUtil
                    .getStringProperty(NettyConfigPropertyNames.HTTP_TRANSPORT_MEDIATION_INTERCEPTOR, "", props);

        }

        public boolean isPreserveUserAgentHeader() {

            return ConfigurationBuilderUtil.getBooleanProperty(NettyConfigPropertyNames.USER_AGENT_HEADER_PRESERVE,
                    false, props);
        }

        public boolean isPreserveServerHeader() {

            return ConfigurationBuilderUtil.getBooleanProperty(NettyConfigPropertyNames.SERVER_HEADER_PRESERVE,
                    false, props);
        }

        public String getPreserveHttpHeaders() {

            return ConfigurationBuilderUtil.getStringProperty(NettyConfigPropertyNames.HTTP_HEADERS_PRESERVE,
                    "", props);
        }

        public String isServiceListBlocked() {

            return ConfigurationBuilderUtil.getStringProperty(NettyConfigPropertyNames.HTTP_BLOCK_SERVICE_LIST,
                    "false", props);
        }

        public String getResponsePreserveHttpHeaders() {

            return ConfigurationBuilderUtil.getStringProperty(NettyConfigPropertyNames.HTTP_RESPONSE_HEADERS_PRESERVE,
                    "", props);
        }

        /**
         * Check for reverse proxy mode.
         *
         * @return whether reverse proxy mode is enabled
         */
        public boolean isReverseProxyMode() {
            if (reverseProxyMode == null) {
                reverseProxyMode = Boolean.parseBoolean(System.getProperty(REVERSE_PROXY_MODE_SYSTEM_PROPERTY));
            }
            return reverseProxyMode;
        }

        public boolean isForcedMessageBuildEnabled() {

            return ConfigurationBuilderUtil.getBooleanProperty(NettyConfigPropertyNames.FORCE_MESSAGE_BUILDER,
                    true, props);
        }

        public boolean isForcedXmlMessageValidationEnabled() {

            return ConfigurationBuilderUtil.getBooleanProperty(NettyConfigPropertyNames.FORCE_XML_MESSAGE_VALIDATION,
                    false, props);
        }

        public boolean isForcedJSONMessageValidationEnabled() {

            return ConfigurationBuilderUtil.getBooleanProperty(NettyConfigPropertyNames.FORCE_JSON_MESSAGE_VALIDATION,
                    false, props);
        }

        /**
         * Load the properties from the netty.properties file.
         *
         * @return Properties loaded from netty.properties file
         */
        private static Properties loadNettyProperties() {

            String filePath = "netty.properties";

            Properties properties = new Properties();
            ClassLoader cl = Thread.currentThread().getContextClassLoader();

            if (LOG.isDebugEnabled()) {
                LOG.debug("Loading the file '" + filePath + "' from classpath");
            }

            InputStream in = null;

            //if we reach to this assume that the we may have to looking to the customer provided external location for the
            //given properties
            if (System.getProperty(BridgeConstants.CONF_LOCATION) != null) {
                try {
                    in = new FileInputStream(System.getProperty(BridgeConstants.CONF_LOCATION)
                            + File.separator + filePath);
                } catch (FileNotFoundException e) {
                    String msg = "Error loading properties from a file at from the System defined location: " + filePath;
                    LOG.warn(msg);
                }
            }

            if (in == null) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Unable to load file  '" + filePath + "'");
                }

                filePath = "conf" + File.separatorChar + filePath;
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Loading the file '" + filePath + "'");
                }

                in = cl.getResourceAsStream(filePath);
                if (in == null) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Unable to load file  '" + filePath + "'");
                    }
                }
            }
            if (in != null) {
                try {
                    properties.load(in);
                } catch (IOException e) {
                    String msg = "Error loading properties from a file at : " + filePath;
                    LOG.error(msg, e);
                }
            }
            return properties;
        }

    }