# RequestResponseUtils.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.util;

    import io.netty.channel.ChannelHandlerContext;
    import io.netty.util.AttributeKey;
    import org.apache.axiom.om.OMElement;
    import org.apache.axiom.soap.SOAP11Constants;
    import org.apache.axiom.soap.SOAP12Constants;
    import org.apache.axiom.util.UIDGenerator;
    import org.apache.axis2.AxisFault;
    import org.apache.axis2.Constants;
    import org.apache.axis2.addressing.EndpointReference;
    import org.apache.axis2.context.ConfigurationContext;
    import org.apache.axis2.context.MessageContext;
    import org.apache.axis2.description.Parameter;
    import org.apache.axis2.description.TransportInDescription;
    import org.apache.axis2.description.java2wsdl.Java2WSDLConstants;
    import org.apache.axis2.transport.RequestResponseTransport;
    import org.apache.axis2.transport.TransportUtils;
    import org.apache.axis2.transport.base.BaseConstants;
    import org.apache.axis2.transport.http.HTTPConstants;
    import org.apache.commons.lang.ArrayUtils;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.apache.synapse.transport.netty.BridgeConstants;
    import org.apache.synapse.transport.netty.config.BaseConfiguration;
    import org.apache.synapse.transport.netty.config.NettyConfiguration;
    import org.apache.synapse.transport.netty.config.SourceConfiguration;
    import org.apache.synapse.transport.nhttp.HttpCoreRequestResponseTransport;
    import org.apache.synapse.transport.nhttp.util.SecureVaultValueReader;
    import org.apache.synapse.transport.util.HttpMessageHandler;
    import org.wso2.securevault.SecretResolver;
    import org.wso2.transport.http.netty.contract.config.InboundMsgSizeValidationConfig;
    import org.wso2.transport.http.netty.contract.config.ListenerConfiguration;
    import org.wso2.transport.http.netty.contract.config.SslConfiguration;
    import org.wso2.transport.http.netty.message.HttpCarbonMessage;

    import java.net.InetAddress;
    import java.net.InetSocketAddress;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.stream.Collectors;
    import javax.xml.namespace.QName;

    /**
     * {@code RequestResponseUtils} contains utilities used in request and response message flow.
     */
    public class RequestResponseUtils {

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

        /**
         * Create an Axis2 message context for the given HttpCarbonMessage. The carbon message may be in the
         * process of being streamed.
         *
         * @param incomingCarbonMsg   the http carbon message to be used to create the corresponding Axis2 message context
         * @param sourceConfiguration source configuration
         * @return the Axis2 message context created
         */
        public static MessageContext convertCarbonMsgToAxis2MsgCtx(HttpCarbonMessage incomingCarbonMsg,
                                                                   SourceConfiguration sourceConfiguration) {

            MessageContext msgCtx = new MessageContext();

            String transportName;
            if (sourceConfiguration.getScheme().isSSL()) {
                transportName = BridgeConstants.TRANSPORT_HTTPSWSS;
            } else {
                transportName = BridgeConstants.TRANSPORT_HTTPWS;
            }
            ConfigurationContext configurationContext = sourceConfiguration.getConfigurationContext();
            msgCtx.setConfigurationContext(configurationContext);
            msgCtx.setTransportOut(configurationContext.getAxisConfiguration().getTransportOut(transportName));
            msgCtx.setTransportIn(sourceConfiguration.getInDescription());
            msgCtx.setIncomingTransportName(transportName);
            msgCtx.setServerSide(true);

            //TODO: once the correlation id support is brought, the correlationID should be set as the messageID.
            // Refer to https://github.com/wso2-support/wso2-synapse/commit/2c86e14151d48ae3bb814be19b874800bd7468e5
            msgCtx.setMessageID(UIDGenerator.generateURNString());

            //TODO: set correlation id here

            msgCtx.setProperty(BaseConstants.INTERNAL_TRANSACTION_COUNTED,
                    ((ChannelHandlerContext) incomingCarbonMsg.getProperty(BridgeConstants.CHANNEL_HANDLER_CONTEXT))
                            .channel().attr(AttributeKey.valueOf(BaseConstants.INTERNAL_TRANSACTION_COUNTED)).get());

            msgCtx.setProperty(Constants.Configuration.TRANSPORT_IN_URL, incomingCarbonMsg.getProperty(BridgeConstants.TO));
            msgCtx.setProperty(MessageContext.CLIENT_API_NON_BLOCKING, Boolean.FALSE);
            msgCtx.setProperty(RequestResponseTransport.TRANSPORT_CONTROL, new HttpCoreRequestResponseTransport(msgCtx));
            msgCtx.setProperty(BridgeConstants.HTTP_SOURCE_CONFIGURATION, sourceConfiguration);

            // Following section is required for throttling to work
            InetAddress remoteAddress = ((InetSocketAddress) incomingCarbonMsg.getProperty(
                    org.wso2.transport.http.netty.contract.Constants.REMOTE_ADDRESS)).getAddress();
            msgCtx.setProperty(MessageContext.REMOTE_ADDR, remoteAddress.getHostAddress());
            msgCtx.setProperty(BridgeConstants.REMOTE_HOST, getHostName(remoteAddress));

            // http transport header names are case insensitive
            Map<String, String> headers = new HashMap<>();
            incomingCarbonMsg.getHeaders().forEach(entry -> headers.put(entry.getKey(), entry.getValue()));
            msgCtx.setProperty(MessageContext.TRANSPORT_HEADERS, headers);

            // Set the original incoming carbon message as a property
            msgCtx.setProperty(BridgeConstants.HTTP_CARBON_MESSAGE, incomingCarbonMsg);
            // This property is used when responding back to the client
            msgCtx.setProperty(BridgeConstants.HTTP_CLIENT_REQUEST_CARBON_MESSAGE, incomingCarbonMsg);
            msgCtx.setProperty(BridgeConstants.TRANSPORT_MESSAGE_HANDLER, new HttpMessageHandler());
            return msgCtx;
        }

        /**
         * This method tries to determine the hostname of the given InetAddress without
         * triggering a reverse DNS lookup.  {@link java.net.InetAddress#getHostName()}
         * triggers a reverse DNS lookup which can be very costly in cases where reverse
         * DNS fails. Tries to parse a symbolic hostname from {@link java.net.InetAddress#toString()},
         * which is documented to return a String of the form "hostname / literal IP address"
         * with 'hostname' blank if not already computed & stored in <code>address</code>.
         * <p/>
         * If the hostname cannot be determined from InetAddress.toString(),
         * the value of {@link java.net.InetAddress#getHostAddress()} is returned.
         *
         * @param address The InetAddress whose hostname has to be determined
         * @return hostsname, if it can be determined. hostaddress, if not.
         */
        public static String getHostName(InetAddress address) {
            String result;
            String hostAddress = address.getHostAddress();
            String inetAddress = address.toString();
            int index1 = inetAddress.lastIndexOf('/');
            int index2 = inetAddress.indexOf(hostAddress);
            if (index2 == index1 + 1) {
                if (index1 == 0) {
                    result = hostAddress;
                } else {
                    result = inetAddress.substring(0, index1);
                }
            } else {
                result = hostAddress;
            }
            return result;
        }

        /**
         * Check if the HTTP_CARBON_MESSAGE is present in the message context.
         *
         * @param msgContext axis2 message context
         * @return true if HTTP_CARBON_MESSAGE is present in the message context, false otherwise
         */
        public static boolean isHttpCarbonMessagePresent(MessageContext msgContext) {

            return Objects.nonNull(msgContext.getProperty(BridgeConstants.HTTP_CARBON_MESSAGE));
        }

        /**
         * Checks if the request/response body should be written using the appropriate message
         * formatter.
         *
         * @param msgContext axis2 message context
         * @return true if the request was initiated from a non-http client or if the message has been built.
         * Otherwise, return false if this is pass-through.
         */
        public static boolean shouldInvokeFormatterToWriteBody(MessageContext msgContext) {

            // If the property HTTP_CARBON_MESSAGE is null, it means the message was initiated from
            // a non-http client. In such cases, the message body will always be available in the
            // envelope itself. Hence, returning true.
            if (!RequestResponseUtils.isHttpCarbonMessagePresent(msgContext)) {
                return true;
            }
            if (msgContext.getEnvelope().getBody().getFirstElement() != null
                    || msgContext.isPropertyTrue(BridgeConstants.NO_ENTITY_BODY)) {
                return true;
            }
            return msgContext.isPropertyTrue(BridgeConstants.MESSAGE_BUILDER_INVOKED);
        }

        /**
         * Check whether the content type is multipart or not.
         *
         * @param contentType value of the content type
         * @return true for multipart content types
         */
        public static boolean isMultipartContent(String contentType) {

            return contentType.contains(HTTPConstants.MEDIA_TYPE_MULTIPART_FORM_DATA)
                    || contentType.contains(HTTPConstants.HEADER_ACCEPT_MULTIPART_RELATED);
        }

        /**
         * Generate the REST_URL_POSTFIX from the request URI.
         *
         * @param uri         the Request URI as a string
         * @param servicePath service path
         * @return REST_URL_POSTFIX as a string
         */
        public static String getRestUrlPostfix(String uri, String servicePath) {

            String contextServicePath = "/" + servicePath;
            if (uri.startsWith(contextServicePath)) {
                // discard upto servicePath
                uri = uri.substring(uri.indexOf(contextServicePath) +
                        contextServicePath.length());
                // discard [proxy] service name if any
                int pos = uri.indexOf("/", 1);
                if (pos > 0) {
                    uri = uri.substring(pos);
                } else {
                    pos = uri.indexOf("?");
                    if (pos != -1) {
                        uri = uri.substring(pos);
                    } else {
                        uri = "";
                    }
                }
            } else {
                // remove any absolute prefix if any
                int pos = uri.indexOf("://");
                //compute index of beginning of Query Parameter
                int indexOfQueryStart = uri.indexOf("?");

                //Check if there exist a absolute prefix '://' and it is before query parameters
                //To allow query parameters with URLs. ex: /test?a=http://asddd
                if (pos != -1 && ((indexOfQueryStart == -1 || pos < indexOfQueryStart))) {
                    uri = uri.substring(pos + 3);
                }
                pos = uri.indexOf("/");
                if (pos != -1) {
                    uri = uri.substring(pos + 1);
                }
                // Remove the service prefix
                if (uri.startsWith(servicePath)) {
                    // discard upto servicePath
                    uri = uri.substring(uri.indexOf(contextServicePath)
                            + contextServicePath.length());
                    // discard [proxy] service name if any
                    pos = uri.indexOf("/", 1);
                    if (pos > 0) {
                        uri = uri.substring(pos);
                    } else {
                        pos = uri.indexOf("?");
                        if (pos != -1) {
                            uri = uri.substring(pos);
                        } else {
                            uri = "";
                        }
                    }
                }
            }

            return uri;
        }

        /**
         * Checks if the HTTP request is REST based on the given Content-Type header.
         *
         * @param contentType Content-Type header
         * @return whether the HTTP request is REST or not
         */
        public static boolean isRESTSupportedMediaType(String contentType) {

            // TODO: verify for text/xml
            return contentType != null && !contentType.contains(SOAP11Constants.SOAP_11_CONTENT_TYPE)
                    && !contentType.contains(SOAP12Constants.SOAP_12_CONTENT_TYPE);
        }

        /**
         * Populates the SOAP version based on the given Content-Type.
         *
         * @param msgContext  axis2 message context
         * @param contentType Content type of the request.
         * @return 0 if the content type is null. return 2 if the content type is application/soap+xml. Otherwise 1
         */
        public static int populateSOAPVersion(MessageContext msgContext, String contentType) {

            int soapVersion = 0;
            if (contentType != null) {
                if (contentType.contains(SOAP12Constants.SOAP_12_CONTENT_TYPE)) {
                    soapVersion = 2;
                    TransportUtils.processContentTypeForAction(contentType, msgContext);
                } else if (contentType.contains(SOAP11Constants.SOAP_11_CONTENT_TYPE)) {
                    soapVersion = 1;
                } else if (isRESTSupportedMediaType(contentType)) {
                    soapVersion = 1;
                }
            }
            return soapVersion;
        }

        /**
         * Checks if the request should be considered as REST.
         *
         * @param msgContext       axis2 message context
         * @param contentType      content-type of the request
         * @param soapVersion      SOAP version
         * @param soapActionHeader SOAPAction header
         * @return whether the request should be considered as REST
         */
        public static boolean isRESTRequest(MessageContext msgContext, String contentType, int soapVersion,
                                            String soapActionHeader) {

            if (isRESTSupportedMediaType(contentType)) {
                return true;
            }
            if (soapVersion == 1) {
                Parameter disableREST = msgContext.getParameter(Java2WSDLConstants.DISABLE_BINDING_REST);
                return soapActionHeader == null && disableREST != null && "false".equals(disableREST.getValue());
            }
            return false;
        }

        /**
         * Get the EPR for the message passed in.
         *
         * @param msgContext the message context
         * @return the destination EPR
         */
        public static EndpointReference getDestinationEPR(MessageContext msgContext) {

            // Transport URL can be different from the WSA-To
            String transportURL = (String) msgContext.getProperty(
                    Constants.Configuration.TRANSPORT_URL);

            if (transportURL != null) {
                return new EndpointReference(transportURL);
            } else if (
                    (msgContext.getTo() != null) && !msgContext.getTo().hasAnonymousAddress()) {
                return msgContext.getTo();
            }
            return null;
        }

        /**
         * Returns Listener configuration instance populated with source configuration.
         *
         * @param sourceConfiguration source configuration
         * @return transport listener configuration instance
         */
        public static ListenerConfiguration getListenerConfig(SourceConfiguration sourceConfiguration, boolean sslEnabled)
                throws AxisFault {

            ListenerConfiguration listenerConfiguration = new ListenerConfiguration();
            listenerConfiguration.setPort(sourceConfiguration.getPort());
            listenerConfiguration.setHost(sourceConfiguration.getHost());
            String protocol = getProtocolVersion(sourceConfiguration);
            sourceConfiguration.setProtocol(protocol);
            listenerConfiguration.setVersion(protocol);
            NettyConfiguration globalConfig = NettyConfiguration.getInstance();

            // Set Request validation limits.
            boolean isRequestLimitsValidationEnabled = globalConfig.isRequestLimitsValidationEnabled();
            if (isRequestLimitsValidationEnabled) {
                setInboundMgsSizeValidationConfig(globalConfig.getMaxStatusLineLength(), globalConfig.getMaxHeaderSize(),
                        globalConfig.getMaxEntityBodySize(), listenerConfiguration.getMsgSizeValidationConfig());
            }

            int idleTimeout = globalConfig.getSocketTimeout();
            if (idleTimeout < 0) {
                throw new AxisFault("Idle timeout cannot be negative. If you want to disable the " +
                        "timeout please use value 0");
            }
            listenerConfiguration.setSocketIdleTimeout(idleTimeout);

            listenerConfiguration.setPipeliningEnabled(false); //Pipelining is disabled all the time

            if (isHTTPTraceLoggerEnabled()) {
                listenerConfiguration.setHttpTraceLogEnabled(true);
            }

            if (isHTTPAccessLoggerEnabled()) {
                listenerConfiguration.setHttpAccessLogEnabled(true);
            }

            if (sslEnabled) {
                return setSslConfig(sourceConfiguration.getInDescription(), listenerConfiguration, sourceConfiguration);
            }

            return listenerConfiguration;
        }

        public static String getProtocolVersion(SourceConfiguration sourceConfiguration) {

            String[] protocols = sourceConfiguration.getProtocol().split(",");
            String protocol = Arrays.stream(protocols).sorted().collect(Collectors.toList()).get(protocols.length - 1);
            return protocol;
        }

        public static boolean isHTTPTraceLoggerEnabled() {

            return Boolean.parseBoolean(System.getProperty(BridgeConstants.HTTP_TRACE_LOG_ENABLED));
        }

        public static boolean isHTTPAccessLoggerEnabled() {

            return Boolean.parseBoolean(System.getProperty(BridgeConstants.HTTP_ACCESS_LOG_ENABLED));
        }

        public static ListenerConfiguration setSslConfig(TransportInDescription transportIn,
                                                         ListenerConfiguration listenerConfiguration,
                                                         BaseConfiguration sourceConfiguration) throws AxisFault {

            List<org.wso2.transport.http.netty.contract.config.Parameter> serverParamList = new ArrayList<>();
            listenerConfiguration.setScheme(BridgeConstants.PROTOCOL_HTTPS);

            // evaluate keystore field
            Parameter keyParam = transportIn.getParameter(BridgeConstants.KEY_STORE);
            OMElement keyStoreEl = keyParam != null ? keyParam.getParameterElement() : null;

            SecretResolver secretResolver = sourceConfiguration.getConfigurationContext()
                    .getAxisConfiguration().getSecretResolver();

            populateKeyStoreConfigs(keyStoreEl, listenerConfiguration, secretResolver);

            // evaluate truststore field
            Parameter trustParam = transportIn.getParameter(BridgeConstants.TRUST_STORE);
            OMElement trustStoreEl = trustParam != null ? trustParam.getParameterElement() : null;
            populateTrustStoreConfigs(trustStoreEl, listenerConfiguration, secretResolver);

            // evaluate SSLVerifyClient field
            Parameter clientAuthParam = transportIn.getParameter(BridgeConstants.SSL_VERIFY_CLIENT);
            OMElement clientAuthEl = clientAuthParam != null ? clientAuthParam.getParameterElement() : null;
            final String s = clientAuthEl != null ? clientAuthEl.getText() : "";
            listenerConfiguration.setVerifyClient(s);

            // evaluate HttpsProtocols and SSLProtocol fields
            Parameter httpsProtocolsParam = transportIn.getParameter(BridgeConstants.HTTPS_PROTOCOL);
            OMElement httpsProtocolsEl = httpsProtocolsParam != null ? httpsProtocolsParam.getParameterElement() : null;
            Parameter sslParameter = transportIn.getParameter(BridgeConstants.SSL_PROTOCOL);
            String sslProtocol = sslParameter != null && sslParameter.getValue() != null
                    ? sslParameter.getValue().toString() : BridgeConstants.TLS_PROTOCOL;
            populateProtocolConfigs(httpsProtocolsEl, sslProtocol, listenerConfiguration, serverParamList);

            // evaluate PreferredCiphers field
            Parameter preferredCiphersParam = transportIn.getParameter(BridgeConstants.PREFERRED_CIPHERS);
            OMElement preferredCiphersEl = preferredCiphersParam != null
                    ? preferredCiphersParam.getParameterElement() : null;
            populateCiphersConfigs(preferredCiphersEl, serverParamList);

            // evaluate CertificateRevocationVerifier field
            Parameter cvpParam = transportIn.getParameter(BridgeConstants.CLIENT_REVOCATION);
            OMElement cvpEl = cvpParam != null ? cvpParam.getParameterElement() : null;
            populateCertValidationConfigs(cvpEl, listenerConfiguration);

            // evaluate common fields
            Parameter sessionTimeoutParam = transportIn.getParameter(BridgeConstants.SSL_SESSION_TIMEOUT);
            Parameter handshakeTimeoutParam = transportIn.getParameter(BridgeConstants.SSL_HANDSHAKE_TIMEOUT);
            String sessionTimeoutEl = sessionTimeoutParam != null && sessionTimeoutParam.getValue() != null
                    ? sessionTimeoutParam.getValue().toString() : null;
            String handshakeTimeoutEl = handshakeTimeoutParam != null && handshakeTimeoutParam.getValue() != null
                    ? handshakeTimeoutParam.getValue().toString() : null;
            populateTimeoutConfigs(sessionTimeoutEl, handshakeTimeoutEl, listenerConfiguration);

            if (!serverParamList.isEmpty()) {
                listenerConfiguration.setParameters(serverParamList);
            }

            listenerConfiguration.setId(getListenerInterface(listenerConfiguration.getHost(),
                    listenerConfiguration.getPort()));
            return listenerConfiguration;
        }

        public static void populateKeyStoreConfigs(OMElement keyStoreEl, SslConfiguration sslConfiguration,
                                                   SecretResolver secretResolver) throws AxisFault {

            if (keyStoreEl != null) {
                String location = getValueOfElementWithLocalName(keyStoreEl, BridgeConstants.STORE_LOCATION);
                String type = getValueOfElementWithLocalName(keyStoreEl, BridgeConstants.TYPE);
                OMElement storePasswordEl = keyStoreEl.getFirstChildWithName(
                        new QName(keyStoreEl.getNamespace().getNamespaceURI(), BridgeConstants.PASSWORD));
                OMElement keyPasswordEl = keyStoreEl.getFirstChildWithName(
                        new QName(keyStoreEl.getNamespace().getNamespaceURI(), BridgeConstants.KEY_PASSWORD));

                if (Objects.isNull(location) || location.isEmpty()) {
                    throw new AxisFault("KeyStore file location must be provided for secure connection");
                }
                if (storePasswordEl == null) {
                    throw new AxisFault("KeyStore password must be provided for secure connection");
                }
                if (keyPasswordEl == null) {
                    throw new AxisFault("Cannot proceed because KeyPassword element is missing in KeyStore");
                }
                String storePassword = SecureVaultValueReader.getSecureVaultValue(secretResolver, storePasswordEl);
                String keyPassword = SecureVaultValueReader.getSecureVaultValue(secretResolver, keyPasswordEl);

                sslConfiguration.setKeyStoreFile(location);
                sslConfiguration.setKeyStorePass(storePassword);
                sslConfiguration.setCertPass(keyPassword);
                sslConfiguration.setTLSStoreType(type);
            }
        }

        public static void populateTrustStoreConfigs(OMElement trustStoreEl, SslConfiguration sslConfiguration,
                                                     SecretResolver secretResolver) throws AxisFault {

            String location = getValueOfElementWithLocalName(trustStoreEl, BridgeConstants.STORE_LOCATION);
            String type = getValueOfElementWithLocalName(trustStoreEl, BridgeConstants.TYPE);
            OMElement storePasswordEl = trustStoreEl.getFirstChildWithName(
                    new QName(trustStoreEl.getNamespace().getNamespaceURI(), BridgeConstants.PASSWORD));
            if (storePasswordEl == null) {
                throw new AxisFault("Cannot proceed because Password element is missing in TrustStore");
            }
            String storePassword = SecureVaultValueReader.getSecureVaultValue(secretResolver, storePasswordEl);

            sslConfiguration.setTrustStoreFile(location);
            sslConfiguration.setTrustStoreType(type);
            sslConfiguration.setTrustStorePass(storePassword);
        }

        /**
         * Sets the configuration for the inbound request and response size validation.
         *
         * @param maxInitialLineLength The maximum length of the initial line (e.g. {@code "GET / HTTP/1.0"}
         *                             or {@code "HTTP/1.0 200 OK"})
         * @param maxHeaderSize        The maximum length of all headers.
         * @param maxEntityBodySize    The maximum length of the content or each chunk.
         * @param sizeValidationConfig instance that represents the configuration for the inbound request and
         *                             response size validation.
         * @throws AxisFault when the given values are invalid.
         */
        public static void setInboundMgsSizeValidationConfig(int maxInitialLineLength, int maxHeaderSize,
                                                             int maxEntityBodySize,
                                                             InboundMsgSizeValidationConfig sizeValidationConfig)
                throws AxisFault {

            if (maxInitialLineLength >= 0) {
                sizeValidationConfig.setMaxInitialLineLength(Math.toIntExact(maxInitialLineLength));
            }

            if (maxHeaderSize >= 0) {
                sizeValidationConfig.setMaxHeaderSize(Math.toIntExact(maxHeaderSize));
            }

            if (maxEntityBodySize != -1) {
                if (maxEntityBodySize >= 0) {
                    sizeValidationConfig.setMaxEntityBodySize(maxEntityBodySize);
                } else {
                    throw new AxisFault("Invalid configuration found for maxEntityBodySize : " + maxEntityBodySize);
                }
            }
        }

        public static String getValueOfElementWithLocalName(OMElement element, String localName) {

            Iterator iterator = element.getChildrenWithLocalName(localName);
            String value = null;
            Object obj = iterator.next();
            if (obj instanceof OMElement) {
                value = ((OMElement) obj).getText();
            }
            return value;
        }

        private static void populateCertValidationConfigs(OMElement cvpEl, SslConfiguration sslConfiguration) {

            final String cvEnable = cvpEl != null ?
                    cvpEl.getAttribute(new QName("enable")).getAttributeValue() : null;

            if ("true".equalsIgnoreCase(cvEnable)) {
                sslConfiguration.setValidateCertEnabled(true);
                String cacheSizeString = cvpEl.getFirstChildWithName(
                        new QName(cvpEl.getNamespace().getNamespaceURI(), BridgeConstants.CACHE_SIZE)).getText();
                String cacheDelayString = cvpEl.getFirstChildWithName(
                        new QName(cvpEl.getNamespace().getNamespaceURI(), BridgeConstants.CACHE_DELAY)).getText();
                Integer cacheSize = null;
                Integer cacheDelay = null;
                try {
                    cacheSize = new Integer(cacheSizeString);
                    cacheDelay = new Integer(cacheDelayString);
                } catch (NumberFormatException e) {
                    //
                }

                if (Objects.nonNull(cacheDelay) && cacheDelay != 0) {
                    sslConfiguration.setCacheValidityPeriod(Math.toIntExact(cacheDelay));
                }
                if (Objects.nonNull(cacheSize) && cacheSize != 0) {
                    sslConfiguration.setCacheSize(Math.toIntExact(cacheSize));
                }
            }
        }

        private static void populateTimeoutConfigs(String sessionTimeoutStr, String handshakeTimeoutStr,
                                                   SslConfiguration sslConfiguration) {

            if (Objects.nonNull(sessionTimeoutStr) && !sessionTimeoutStr.isEmpty()) {
                try {
                    int sessionTimeout = new Integer(sessionTimeoutStr);
                    if (sessionTimeout > 0) {
                        sslConfiguration.setSslSessionTimeOut(sessionTimeout);
                    } else {
                        LOG.warn("SessionTimeout should be a valid positive number. But found : " + sessionTimeoutStr
                                + ". Hence, using the default value of 86400s/24h");
                    }
                } catch (NumberFormatException e) {
                    LOG.warn("Invalid number found for SSL SessionTimeout : " + sessionTimeoutStr
                            + ". Hence, using the default value of 86400s/24h");
                }
            }

            if (Objects.nonNull(handshakeTimeoutStr) && !handshakeTimeoutStr.isEmpty()) {
                try {
                    int handshakeTimeout = new Integer(handshakeTimeoutStr);
                    if (handshakeTimeout > 0) {
                        sslConfiguration.setSslHandshakeTimeOut(handshakeTimeout);
                    } else {
                        LOG.warn("HandshakeTimeout should be a valid positive number. But found : " + handshakeTimeoutStr
                                + ". Hence, using the default value of 10s");
                    }
                } catch (NumberFormatException e) {
                    LOG.warn("Invalid number found for ssl handshakeTimeoutStr : " + handshakeTimeoutStr +
                            ". Hence, using the default value of 10s");
                }
            }
        }

        private static void populateProtocolConfigs(
                OMElement httpsProtocolsEl, String sslProtocol, SslConfiguration sslConfiguration,
                List<org.wso2.transport.http.netty.contract.config.Parameter> paramList) {

            if (httpsProtocolsEl != null) {
                String configuredHttpsProtocols = httpsProtocolsEl.getText().replaceAll("\\s", "");

                if (!configuredHttpsProtocols.isEmpty()) {
                    org.wso2.transport.http.netty.contract.config.Parameter serverProtocols
                            = new org.wso2.transport.http.netty.contract.config.Parameter(
                            BridgeConstants.SSL_ENABLED_PROTOCOLS, configuredHttpsProtocols);
                    paramList.add(serverProtocols);
                }
            }

            if (Objects.isNull(sslProtocol) || sslProtocol.isEmpty()) {
                sslProtocol = BridgeConstants.TLS_PROTOCOL;
            }
            sslConfiguration.setSSLProtocol(sslProtocol);
        }

        private static void populateCiphersConfigs(
                OMElement preferredCiphersEl, List<org.wso2.transport.http.netty.contract.config.Parameter> paramList) {

            if (preferredCiphersEl != null) {
                String preferredCiphers = preferredCiphersEl.getText().replaceAll("\\s", "");

                if (!preferredCiphers.isEmpty()) {
                    org.wso2.transport.http.netty.contract.config.Parameter serverParameters
                            = new org.wso2.transport.http.netty.contract.config.Parameter(
                            BridgeConstants.CIPHERS, preferredCiphers);
                    paramList.add(serverParameters);
                }
            }
        }

        public static String getListenerInterface(String host, int port) {

            host = host != null ? host : BridgeConstants.HTTP_DEFAULT_HOST;
            return host + ":" + port;
        }

        public static void handleException(String s, Exception e) throws AxisFault {

            LOG.error(s, e);
            throw new AxisFault(s, e);
        }

        public static void handleException(String msg) throws AxisFault {

            LOG.error(msg);
            throw new AxisFault(msg);
        }
    }