org.wso2.carbon.transport.http.netty.contractimpl.HttpClientConnectorImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.transport.http.netty.contractimpl.HttpClientConnectorImpl.java

Source

/*
 *  Copyright (c) 2017, 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.wso2.carbon.transport.http.netty.contractimpl;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.transport.http.netty.common.Constants;
import org.wso2.carbon.transport.http.netty.common.HttpRoute;
import org.wso2.carbon.transport.http.netty.common.ProxyServerConfiguration;
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.contract.ClientConnectorException;
import org.wso2.carbon.transport.http.netty.contract.HttpClientConnector;
import org.wso2.carbon.transport.http.netty.contract.HttpResponseFuture;
import org.wso2.carbon.transport.http.netty.listener.SourceHandler;
import org.wso2.carbon.transport.http.netty.message.HTTPCarbonMessage;
import org.wso2.carbon.transport.http.netty.sender.channel.TargetChannel;
import org.wso2.carbon.transport.http.netty.sender.channel.pool.ConnectionManager;

/**
 * Implementation of the client connector.
 */
public class HttpClientConnectorImpl implements HttpClientConnector {

    private static final Logger log = LoggerFactory.getLogger(HttpClientConnector.class);

    private ConnectionManager connectionManager;
    private SSLConfig sslConfig;
    private int socketIdleTimeout;
    private boolean httpTraceLogEnabled;
    private boolean followRedirect;
    private int maxRedirectCount;
    private boolean chunkDisabled;
    private ProxyServerConfiguration proxyServerConfiguration;

    /*This needs to be refactored to hold all the channel properties in a separate bean as there are too many
     arguments here*/
    public HttpClientConnectorImpl(ConnectionManager connectionManager, SSLConfig sslConfig, int socketIdleTimeout,
            boolean httpTraceLogEnabled, boolean chunkDisabled, boolean followRedirect, int maxRedirectCount,
            ProxyServerConfiguration proxyServerConfiguration) {
        this.connectionManager = connectionManager;
        this.httpTraceLogEnabled = httpTraceLogEnabled;
        this.sslConfig = sslConfig;
        this.socketIdleTimeout = socketIdleTimeout;
        this.chunkDisabled = chunkDisabled;
        this.followRedirect = followRedirect;
        this.maxRedirectCount = maxRedirectCount;
        this.proxyServerConfiguration = proxyServerConfiguration;
    }

    @Override
    public HttpResponseFuture connect() {
        return null;
    }

    @Override
    public HttpResponseFuture send(HTTPCarbonMessage httpCarbonRequest) {
        HttpResponseFuture httpResponseFuture = new HttpResponseFutureImpl();

        SourceHandler srcHandler = (SourceHandler) httpCarbonRequest.getProperty(Constants.SRC_HANDLER);
        if (srcHandler == null) {
            if (log.isDebugEnabled()) {
                log.debug(Constants.SRC_HANDLER + " property not found in the message."
                        + " Message is not originated from the HTTP Server connector");
            }
        }

        try {
            final HttpRoute route = getTargetRoute(httpCarbonRequest);
            Util.setupTransferEncodingForRequest(httpCarbonRequest, chunkDisabled);
            TargetChannel targetChannel = connectionManager.borrowTargetChannel(route, srcHandler, sslConfig,
                    httpTraceLogEnabled, chunkDisabled, followRedirect, maxRedirectCount, proxyServerConfiguration);
            targetChannel.getChannelFuture().addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture channelFuture) throws Exception {
                    if (isValidateChannel(channelFuture)) {
                        targetChannel.setChannel(channelFuture.channel());
                        targetChannel.configTargetHandler(httpCarbonRequest, httpResponseFuture);
                        targetChannel.setEndPointTimeout(socketIdleTimeout, followRedirect);
                        targetChannel.setCorrelationIdForLogging();
                        targetChannel.setChunkDisabled(chunkDisabled);
                        targetChannel.setRequestWritten(true);
                        if (followRedirect) {
                            setChannelAttributes(channelFuture.channel(), httpCarbonRequest, httpResponseFuture,
                                    targetChannel);
                        }
                        targetChannel.writeContent(httpCarbonRequest);
                    } else {
                        notifyErrorState(channelFuture);
                    }
                }

                private boolean isValidateChannel(ChannelFuture channelFuture) throws Exception {
                    if (channelFuture.isDone() && channelFuture.isSuccess()) {
                        if (log.isDebugEnabled()) {
                            log.debug("Created the connection to address: {}", route.toString() + " "
                                    + "Original Channel ID is : " + channelFuture.channel().id());
                        }
                        return true;
                    }
                    return false;
                }

                private void notifyErrorState(ChannelFuture channelFuture) {
                    ClientConnectorException cause;

                    if (channelFuture.isDone() && channelFuture.isCancelled()) {
                        cause = new ClientConnectorException("Request Cancelled, " + route.toString(),
                                HttpResponseStatus.BAD_GATEWAY.code());
                    } else if (!channelFuture.isDone() && !channelFuture.isSuccess() && !channelFuture.isCancelled()
                            && (channelFuture.cause() == null)) {
                        cause = new ClientConnectorException("Connection timeout, " + route.toString(),
                                HttpResponseStatus.BAD_GATEWAY.code());
                    } else {
                        cause = new ClientConnectorException("Connection refused, " + route.toString(),
                                HttpResponseStatus.BAD_GATEWAY.code());
                    }

                    if (channelFuture.cause() != null) {
                        cause.initCause(channelFuture.cause());
                    }

                    httpResponseFuture.notifyHttpListener(cause);
                }
            });
            Util.prepareBuiltMessageForTransfer(httpCarbonRequest);
        } catch (Exception failedCause) {
            httpResponseFuture.notifyHttpListener(failedCause);
        }

        return httpResponseFuture;
    }

    @Override
    public boolean close() {
        return false;
    }

    private HttpRoute getTargetRoute(HTTPCarbonMessage httpCarbonMessage) {
        // Fetch Host
        String host;
        Object hostProperty = httpCarbonMessage.getProperty(Constants.HOST);
        if (hostProperty != null && hostProperty instanceof String) {
            host = (String) hostProperty;
        } else {
            host = Constants.LOCALHOST;
            httpCarbonMessage.setProperty(Constants.HOST, Constants.LOCALHOST);
            log.debug("Cannot find property HOST of type string, hence using localhost as the host");
        }

        // Fetch Port
        int port;
        Object intProperty = httpCarbonMessage.getProperty(Constants.PORT);
        if (intProperty != null && intProperty instanceof Integer) {
            port = (int) intProperty;
        } else {
            port = sslConfig != null ? Constants.DEFAULT_HTTPS_PORT : Constants.DEFAULT_HTTP_PORT;
            httpCarbonMessage.setProperty(Constants.PORT, port);
            log.debug("Cannot find property PORT of type integer, hence using " + port);
        }

        return new HttpRoute(host, port);
    }

    /**
     * Set following attributes to original channel when redirect is on.
     *
     * @param channel            Original channel
     * @param httpCarbonRequest  Http request
     * @param httpResponseFuture Response future
     * @param targetChannel      Target channel
     */
    private void setChannelAttributes(Channel channel, HTTPCarbonMessage httpCarbonRequest,
            HttpResponseFuture httpResponseFuture, TargetChannel targetChannel) {
        channel.attr(Constants.ORIGINAL_REQUEST).set(httpCarbonRequest);
        channel.attr(Constants.RESPONSE_FUTURE_OF_ORIGINAL_CHANNEL).set(httpResponseFuture);
        channel.attr(Constants.TARGET_CHANNEL_REFERENCE).set(targetChannel);
        channel.attr(Constants.ORIGINAL_CHANNEL_START_TIME).set(System.currentTimeMillis());
        channel.attr(Constants.ORIGINAL_CHANNEL_TIMEOUT).set(socketIdleTimeout);
    }
}