gobblin.tunnel.TunnelTest.java Source code

Java tutorial

Introduction

Here is the source code for gobblin.tunnel.TunnelTest.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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 gobblin.tunnel;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.mockserver.integration.ClientAndServer;
import org.mockserver.model.HttpForward;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;

/**
 * Tests for HTTP traffic through Tunnel via an HTTP proxy.
 *
 * @author navteniev@linkedin.com
 */
@Test(groups = { "gobblin.tunnel", "disabledOnTravis" })
public class TunnelTest {

    private ClientAndServer mockServer;
    int PORT = 0;

    @BeforeClass
    public void startProxy() throws IOException {
        mockServer = ClientAndServer.startClientAndServer(0);
        PORT = mockServer.getPort();
    }

    @AfterClass
    public void stopProxy() {
        mockServer.stop();
    }

    @AfterMethod
    public void reset() {
        mockServer.reset();
    }

    @Test
    public void mustBuildTunnelAndStartAcceptingConnections() throws Exception {
        Tunnel tunnel = Tunnel.build("example.org", 80, "localhost", PORT);

        try {
            int tunnelPort = tunnel.getPort();
            assertTrue(tunnelPort > 0);
        } finally {
            tunnel.close();
        }
    }

    @Test
    public void mustHandleClientDisconnectingWithoutClosingTunnel() throws Exception {
        mockExample();
        Tunnel tunnel = Tunnel.build("example.org", 80, "localhost", PORT);

        try {
            int tunnelPort = tunnel.getPort();
            SocketChannel client = SocketChannel.open();

            client.connect(new InetSocketAddress("localhost", tunnelPort));
            client.write(ByteBuffer
                    .wrap("GET / HTTP/1.1%nUser-Agent: GobblinTunnel%nConnection:keep - alive %n%n".getBytes()));
            client.close();

            assertNotNull(fetchContent(tunnelPort));
        } finally {
            tunnel.close();
        }
    }

    @Test
    public void mustHandleConnectionToExternalResource() throws Exception {

        mockExample();
        Tunnel tunnel = Tunnel.build("example.org", 80, "localhost", PORT);

        try {
            String content = fetchContent(tunnel.getPort());

            assertNotNull(content);
        } finally {
            tunnel.close();
        }
    }

    @Test
    public void mustHandleMultipleConnections() throws Exception {
        mockExample();
        Tunnel tunnel = Tunnel.build("example.org", 80, "localhost", PORT);
        int clients = 5;

        final CountDownLatch startSignal = new CountDownLatch(1);
        final CountDownLatch doneSignal = new CountDownLatch(clients);

        ExecutorService executor = Executors.newFixedThreadPool(clients);
        try {
            final int tunnelPort = tunnel.getPort();

            List<Future<String>> results = new ArrayList<Future<String>>();

            for (int i = 0; i < clients; i++) {
                Future<String> result = executor.submit(new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        startSignal.await();

                        try {
                            return fetchContent(tunnelPort);
                        } finally {
                            doneSignal.countDown();
                        }
                    }
                });

                results.add(result);
            }

            startSignal.countDown();
            doneSignal.await();

            for (Future<String> result : results) {
                assertNotNull(result.get());
            }
        } finally {
            tunnel.close();
        }
    }

    @Test(expectedExceptions = SocketException.class)
    public void mustRefuseConnectionWhenProxyIsUnreachable() throws Exception {

        Tunnel tunnel = Tunnel.build("example.org", 80, "localhost", 1);

        try {
            int tunnelPort = tunnel.getPort();

            fetchContent(tunnelPort);
        } finally {
            tunnel.close();
        }
    }

    @Test(expectedExceptions = SocketException.class)
    public void mustRefuseConnectionWhenProxyRefuses() throws Exception {
        mockServer.when(HttpRequest.request().withMethod("CONNECT").withPath("www.us.apache.org:80"))
                .respond(HttpResponse.response().withStatusCode(403));

        Tunnel tunnel = Tunnel.build("example.org", 80, "localhost", PORT);

        try {
            int tunnelPort = tunnel.getPort();

            fetchContent(tunnelPort);
        } finally {
            tunnel.close();
        }
    }

    @Test(expectedExceptions = SocketException.class)
    public void mustRefuseConnectionWhenProxyTimesOut() throws Exception {
        mockServer.when(HttpRequest.request().withMethod("CONNECT").withPath("www.us.apache.org:80"))
                .respond(HttpResponse.response().withDelay(TimeUnit.SECONDS, 2).withStatusCode(200));

        Tunnel tunnel = Tunnel.build("example.org", 80, "localhost", PORT);

        try {
            int tunnelPort = tunnel.getPort();

            fetchContent(tunnelPort);
        } finally {
            tunnel.close();
        }
    }

    @Test(enabled = false)
    public void mustDownloadLargeFiles() throws Exception {

        mockServer.when(HttpRequest.request().withMethod("CONNECT").withPath("www.us.apache.org:80"))
                .respond(HttpResponse.response().withStatusCode(200));
        mockServer
                .when(HttpRequest.request().withMethod("GET")
                        .withPath("/dist//httpcomponents/httpclient/binary/httpcomponents-client-4.5.1-bin.tar.gz"))
                .forward(HttpForward.forward().withHost("www.us.apache.org").withPort(80));

        Tunnel tunnel = Tunnel.build("www.us.apache.org", 80, "localhost", PORT);
        try {
            IOUtils.copyLarge(
                    (InputStream) new URL("http://localhost:" + tunnel.getPort()
                            + "/dist//httpcomponents/httpclient/binary/httpcomponents-client-4.5.1-bin.tar.gz")
                                    .getContent(new Class[] { InputStream.class }),
                    new FileOutputStream(File.createTempFile("httpcomponents-client-4.5.1-bin", "tar.gz")));
        } finally {
            tunnel.close();
        }
    }

    private String fetchContent(int tunnelPort) throws IOException {
        InputStream content = (InputStream) new URL(String.format("http://localhost:%s/", tunnelPort))
                .openConnection().getContent(new Class[] { InputStream.class });
        return IOUtils.toString(content);
    }

    private void mockExample() throws IOException {
        mockServer.when(HttpRequest.request().withMethod("CONNECT").withPath("example.org:80"))
                .respond(HttpResponse.response().withStatusCode(200));
        mockServer.when(HttpRequest.request().withMethod("GET").withPath("/")).respond(
                HttpResponse.response(IOUtils.toString(getClass().getResourceAsStream("/example.org.html"))));
    }

}