org.ballerinalang.test.util.client.HttpClient.java Source code

Java tutorial

Introduction

Here is the source code for org.ballerinalang.test.util.client.HttpClient.java

Source

/*
 * Copyright (c) 2018, 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.ballerinalang.test.util.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.DefaultLastHttpContent;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.transport.http.netty.contract.Constants;

import java.net.InetSocketAddress;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * A simple Netty based HTTP client to be used in test cases.
 */
public class HttpClient {

    private static final Logger log = LoggerFactory.getLogger(HttpClient.class);
    private final String host;
    private final int port;
    private final ResponseHandler responseHandler;
    private Channel connectedChannel;
    private CountDownLatch waitForConnectionClosureLatch;

    public HttpClient(String host, int port) {
        this.host = host;
        this.port = port;
        this.responseHandler = new ResponseHandler();

        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap clientBootStrap = new Bootstrap();
            clientBootStrap.group(group).channel(NioSocketChannel.class)
                    .remoteAddress(new InetSocketAddress(host, port))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new HttpClientCodec());
                            ch.pipeline().addLast(new HttpObjectAggregator(1024 * 512));
                            ch.pipeline().addLast(responseHandler);
                        }
                    });
            connectedChannel = clientBootStrap.connect().sync().channel();
        } catch (Exception e) {
            log.error("Error while initializing the client", e);
        }
    }

    public FullHttpResponse sendChunkRequest(FullHttpRequest httpRequest) {
        httpRequest.headers().set(HttpHeaderNames.TRANSFER_ENCODING, Constants.CHUNKED);
        return send(httpRequest);
    }

    public FullHttpResponse sendRequest(FullHttpRequest httpRequest) {
        httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, httpRequest.content().readableBytes());
        return send(httpRequest);
    }

    private FullHttpResponse send(FullHttpRequest httpRequest) {
        CountDownLatch latch = new CountDownLatch(1);
        this.waitForConnectionClosureLatch = new CountDownLatch(1);
        this.responseHandler.setLatch(latch);
        this.responseHandler.setWaitForConnectionClosureLatch(this.waitForConnectionClosureLatch);

        httpRequest.headers().set(HttpHeaderNames.HOST, host + ":" + port);
        this.connectedChannel.writeAndFlush(httpRequest);
        try {
            latch.await();
        } catch (InterruptedException e) {
            log.warn("Interrupted before receiving the response");
        }
        return this.responseHandler.getHttpFullResponse();
    }

    public List<FullHttpResponse> sendExpectContinueRequest(DefaultHttpRequest httpRequest,
            DefaultLastHttpContent httpContent) {
        CountDownLatch latch = new CountDownLatch(1);
        this.waitForConnectionClosureLatch = new CountDownLatch(1);
        this.responseHandler.setLatch(latch);
        this.responseHandler.setWaitForConnectionClosureLatch(this.waitForConnectionClosureLatch);

        httpRequest.headers().set(HttpHeaderNames.HOST, host + ":" + port);
        httpRequest.headers().set(HttpHeaderNames.EXPECT, HttpHeaderValues.CONTINUE);
        this.connectedChannel.writeAndFlush(httpRequest);

        try {
            latch.await();
        } catch (InterruptedException e) {
            log.warn("Interrupted before receiving the response.");
        }

        FullHttpResponse response100Continue = this.responseHandler.getHttpFullResponse();

        if (response100Continue.status().equals(HttpResponseStatus.CONTINUE)) {
            latch = new CountDownLatch(1);
            this.responseHandler.setLatch(latch);
            this.connectedChannel.writeAndFlush(httpContent);
            try {
                latch.await();
            } catch (InterruptedException e) {
                log.warn("Interrupted before receiving the response.");
            }
        }

        return responseHandler.getHttpFullResponses();
    }

    public LinkedList<FullHttpResponse> sendTwoInPipeline(FullHttpRequest httpRequest) {
        CountDownLatch latch = new CountDownLatch(2);
        this.waitForConnectionClosureLatch = new CountDownLatch(2);
        this.responseHandler.setLatch(latch);
        this.responseHandler.setWaitForConnectionClosureLatch(this.waitForConnectionClosureLatch);

        httpRequest.headers().set(HttpHeaderNames.HOST, host + ":" + port);
        this.connectedChannel.writeAndFlush(httpRequest.copy());

        this.connectedChannel.writeAndFlush(httpRequest);
        try {
            latch.await();
        } catch (InterruptedException e) {
            log.warn("Interrupted before receiving the response");
        }
        return this.responseHandler.getHttpFullResponses();
    }

    /**
     * Send pipelined requests to the server.
     *
     * @param path Represents request path
     * @return A list of responses
     */
    public LinkedList<FullHttpResponse> sendPipeLinedRequests(String path) {
        CountDownLatch latch = new CountDownLatch(3);
        this.waitForConnectionClosureLatch = new CountDownLatch(3);
        this.responseHandler.setLatch(latch);
        this.responseHandler.setWaitForConnectionClosureLatch(this.waitForConnectionClosureLatch);

        FullHttpRequest firstRequest = getFullHttpRequest(path, "request-one");
        this.connectedChannel.writeAndFlush(firstRequest);

        FullHttpRequest secondRequest = getFullHttpRequest(path, "request-two");
        this.connectedChannel.writeAndFlush(secondRequest);

        FullHttpRequest thirdRequest = getFullHttpRequest(path, "request-three");
        this.connectedChannel.writeAndFlush(thirdRequest);

        try {
            latch.await();
        } catch (InterruptedException e) {
            log.warn("Interrupted before receiving the response");
        }
        return this.responseHandler.getHttpFullResponses();
    }

    /**
     * Send pipelined requests to the server and do not expect a response hence a latch shouldn't be set in response
     * handler for channel read.
     *
     * @param path Represents request path
     * @return A list of responses
     */
    public String sendMultiplePipelinedRequests(String path) {
        this.waitForConnectionClosureLatch = new CountDownLatch(1);
        this.responseHandler.setWaitForConnectionClosureLatch(this.waitForConnectionClosureLatch);

        FullHttpRequest firstRequest = getFullHttpRequest(path, "request-one");
        this.connectedChannel.writeAndFlush(firstRequest);

        FullHttpRequest secondRequest = getFullHttpRequest(path, "request-two");
        this.connectedChannel.writeAndFlush(secondRequest);

        FullHttpRequest thirdRequest = getFullHttpRequest(path, "request-three");
        this.connectedChannel.writeAndFlush(thirdRequest);

        FullHttpRequest fourthRequest = getFullHttpRequest(path, "request-four");
        this.connectedChannel.writeAndFlush(fourthRequest);

        FullHttpRequest fifthRequest = getFullHttpRequest(path, "request-five");
        this.connectedChannel.writeAndFlush(fifthRequest);

        FullHttpRequest sixthRequest = getFullHttpRequest(path, "request-six");
        this.connectedChannel.writeAndFlush(sixthRequest);

        try {
            this.waitForConnectionClosureLatch.await();
        } catch (InterruptedException e) {
            log.warn("Interrupted before receiving the response");
        }
        return this.responseHandler.getChannelEventMsg();
    }

    /**
     * Get a full http request.
     *
     * @param path      Represents request path
     * @param requestId A header value to identify the request
     * @return A full http request
     */
    private FullHttpRequest getFullHttpRequest(String path, String requestId) {
        FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
        request.headers().set(HttpHeaderNames.HOST, host + ":" + port);
        request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        request.headers().set("message-id", requestId);
        return request;
    }

    public boolean waitForChannelClose() {
        try {
            if (!this.waitForConnectionClosureLatch.await(5, TimeUnit.SECONDS)) {
                return false;
            }
        } catch (InterruptedException e) {
            log.warn("Interrupted before receiving the response");
            return false;
        }
        return true;
    }
}