com.springsource.insight.plugin.apache.http.hc4.HttpClientExecutionCollectionAspectTest.java Source code

Java tutorial

Introduction

Here is the source code for com.springsource.insight.plugin.apache.http.hc4.HttpClientExecutionCollectionAspectTest.java

Source

/**
 * Copyright (c) 2009-2011 VMware, Inc. All Rights Reserved.
 *
 * Licensed 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 com.springsource.insight.plugin.apache.http.hc4;

import java.io.IOException;
import java.io.Writer;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpMessage;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.RequestLine;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.HttpConnection;
import org.mortbay.jetty.Request;
import org.mortbay.jetty.Server;

import com.springsource.insight.collection.ObscuredValueSetMarker;
import com.springsource.insight.collection.http.HttpObfuscator;
import com.springsource.insight.collection.test.OperationCollectionAspectTestSupport;
import com.springsource.insight.intercept.operation.Operation;
import com.springsource.insight.intercept.operation.OperationFields;
import com.springsource.insight.intercept.operation.OperationList;
import com.springsource.insight.intercept.operation.OperationMap;
import com.springsource.insight.intercept.operation.OperationUtils;
import com.springsource.insight.util.ArrayUtil;
import com.springsource.insight.util.MapUtil;
import com.springsource.insight.util.StringFormatterUtils;
import com.springsource.insight.util.StringUtil;

/**
 * Tests {@link HttpClientExecutionCollectionAspect} using embedded
 * Jetty server
 */
public class HttpClientExecutionCollectionAspectTest extends OperationCollectionAspectTestSupport {
    private static final int TEST_PORT = 7365;
    private static final String TEST_URI = "http://localhost:" + TEST_PORT + "/";
    private static Server SERVER;
    private HttpObfuscator originalObfuscator;
    private final ObscuredValueSetMarker marker = new ObscuredValueSetMarker();

    public HttpClientExecutionCollectionAspectTest() {
        super();
    }

    @BeforeClass
    public static void startEmbeddedServer() throws Exception {
        SERVER = new Server(TEST_PORT);
        SERVER.setHandler(new TestHandler());
        System.out.println("Starting embedded server on port " + TEST_PORT);
        SERVER.start();
        System.out.println("Started embedded server on port " + TEST_PORT);
    }

    @AfterClass
    public static void stopEmbeddedServer() throws Exception {
        if (SERVER != null) {
            System.out.println("Stopping embedded server");
            SERVER.stop();
            System.out.println("Server stopped");
        }
    }

    @Before
    @Override
    public void setUp() {
        super.setUp();

        HttpClientExecutionCollectionAspect aspectInstance = getAspect();
        originalObfuscator = aspectInstance.getHttpHeadersObfuscator();
        marker.clear();
        aspectInstance.setHttpHeadersObfuscator(new HttpObfuscator(marker));
    }

    @After
    @Override
    public void restore() {
        HttpClientExecutionCollectionAspect aspectInstance = getAspect();
        aspectInstance.setHttpHeadersObfuscator(originalObfuscator);
        marker.clear();
        super.restore();
    }

    @Test
    public void testHttpUriRequest() throws IOException {
        runHttpUriRequestTest("testHttpUriRequest", null);
    }

    @Test
    public void testHttpUriRequestWithContext() throws IOException {
        runHttpUriRequestTest("testHttpUriRequestWithContext", new BasicHttpContext());
    }

    @Test
    public void testHttpHostUriRequest() throws Exception {
        runHttpHostRequestTest("testHttpHostUriRequest", null);
    }

    @Test
    public void testHttpHostUriRequestWithContext() throws Exception {
        runHttpHostRequestTest("testHttpHostUriRequestWithContext", new BasicHttpContext());
    }

    @Test
    public void testResponseHandler() throws IOException {
        runResponseHandlerTest("testResponseHandler", null);
    }

    @Test
    public void testResponseHandlerWithContext() throws IOException {
        runResponseHandlerTest("testResponseHandlerWithContext", new BasicHttpContext());
    }

    @Test
    public void testHostResponseHandler() throws Exception {
        runHostResponseHandlerTest("testHostResponseHandler", null);
    }

    @Test
    public void testHostResponseHandlerWithContext() throws Exception {
        runHostResponseHandlerTest("testHostResponseHandlerWithContext", new BasicHttpContext());
    }

    @Test
    public void testDefaultObfuscatedHeaders() throws Exception {
        runHeadersObfuscationTest("testDefaultObfuscatedHeaders", HttpObfuscator.DEFAULT_OBFUSCATED_HEADERS_LIST,
                true);
    }

    @Test
    public void testNonDefaultObfuscatedHeaders() throws Exception {
        runHeadersObfuscationTest("testNonDefaultObfuscatedHeaders", Arrays.asList("X-Hdr1", "X-Hdr2", "X-Hdr3"),
                false);
    }

    /**
     * This test is designed to ensure that when {@link HttpClient}'s
     * <code>execute</code> methods are called with a <code>null</code>
     * {@link HttpUriRequest} argument the aspect fails with {@link NullPointerException}
     * instead of {@link IllegalStateException} as it should have
     */
    @Test
    public void testMissingRequestArgument() throws IOException, ClientProtocolException {
        HttpClient httpClient = new DefaultHttpClient();
        for (MissingRequestTestRunner runner : MissingRequestTestRunner.values()) {
            String excValue = null;
            try {
                HttpResponse rsp = runner.execute(httpClient);
                fail("Unexpected sucess of " + runner + ": " + rsp);
            } catch (IllegalArgumentException e) {
                // expected - thrown by HttpClient when null request supplied
                excValue = StringFormatterUtils.formatStackTrace(e);
            }

            Operation op = assertExecutionResult(HttpClientDefinitions.PLACEHOLDER_URI_VALUE,
                    HttpPlaceholderRequest.PLACEHOLDER, null, true);
            assertEquals("Mismatched exception value for " + runner, excValue,
                    op.get(OperationFields.EXCEPTION, String.class));
            Mockito.reset(spiedOperationCollector); // prepare for next iteration
        }
    }

    static enum MissingRequestTestRunner {
        REQONLY {
            @Override
            public HttpResponse execute(HttpClient httpClient) throws IOException, ClientProtocolException {
                return httpClient.execute(null);
            }
        },
        REQANDCONTEXT {
            @Override
            public HttpResponse execute(HttpClient httpClient) throws IOException, ClientProtocolException {
                return httpClient.execute(null, Mockito.mock(HttpContext.class));
            }
        },
        HOSTANDREQ {
            @Override
            public HttpResponse execute(HttpClient httpClient) throws IOException, ClientProtocolException {
                return httpClient.execute(new HttpHost("localhost", TEST_PORT, "http"), null);
            }
        },
        HOSTREQCONTEXT {
            @Override
            public HttpResponse execute(HttpClient httpClient) throws IOException, ClientProtocolException {
                return httpClient.execute(new HttpHost("localhost", TEST_PORT, "http"), null,
                        Mockito.mock(HttpContext.class));
            }
        };

        public abstract HttpResponse execute(HttpClient httpClient) throws IOException, ClientProtocolException;
    }

    private Map<String, String> runHeadersObfuscationTest(String testName, Collection<String> headerSet,
            boolean defaultHeaders) throws IOException {
        HttpClient httpClient = new DefaultHttpClient();
        String uri = createTestUri(testName);
        HttpGet request = new HttpGet(uri);
        for (String name : headerSet) {
            if ("WWW-Authenticate".equalsIgnoreCase(name)) {
                continue; // this is a response header
            }
            request.setHeader(name, name);
        }

        HttpClientExecutionCollectionAspect aspectInstance = getAspect();
        HttpObfuscator obfuscator = aspectInstance.getHttpHeadersObfuscator();
        if (!defaultHeaders) {
            for (String name : HttpObfuscator.DEFAULT_OBFUSCATED_HEADERS_LIST) {
                if ("WWW-Authenticate".equalsIgnoreCase(name)) {
                    continue; // this is a response header
                }
                request.setHeader(name, name);
            }
            obfuscator.incrementalUpdate(HttpObfuscator.OBFUSCATED_HEADERS_SETTING,
                    StringUtil.implode(headerSet, ","));
        }

        HttpResponse response = httpClient.execute(request);
        Operation op = assertExecutionResult(uri, request, response, false);
        OperationMap reqDetails = op.get("request", OperationMap.class);
        OperationList reqHeaders = reqDetails.get("headers", OperationList.class);
        Map<String, String> requestHeaders = toHeadersMap(reqHeaders);
        OperationMap rspDetails = op.get("response", OperationMap.class);
        OperationList rspHeaders = rspDetails.get("headers", OperationList.class);
        Map<String, String> responseHeaders = toHeadersMap(rspHeaders);
        Map<String, String> hdrsMap = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
        if (MapUtil.size(requestHeaders) > 0) {
            hdrsMap.putAll(requestHeaders);
        }
        if (MapUtil.size(responseHeaders) > 0) {
            hdrsMap.putAll(responseHeaders);
        }

        Collection<?> obscuredValues = (ObscuredValueSetMarker) obfuscator.getSensitiveValueMarker();
        for (String name : headerSet) {
            String value = hdrsMap.get(name);
            assertNotNull("Missing header=" + name, value);
            assertTrue("Unobscured value of " + name, obscuredValues.contains(value));
        }

        if (!defaultHeaders) {
            for (String name : HttpObfuscator.DEFAULT_OBFUSCATED_HEADERS_LIST) {
                assertFalse("Un-necessarily obscured value of " + name, obscuredValues.contains(name));
            }
        }

        return hdrsMap;
    }

    private void runResponseHandlerTest(String testName, HttpContext context) throws IOException {
        runResponseHandlerTest(testName, null, context);
    }

    private void runHostResponseHandlerTest(String testName, HttpContext context) throws Exception {
        runResponseHandlerTest(testName, URIUtils.extractHost(new URI(createTestUri(testName))), context);
    }

    private void runResponseHandlerTest(String testName, HttpHost host, HttpContext context) throws IOException {
        HttpClient httpClient = new DefaultHttpClient();
        String uri = createTestUri(testName);
        HttpGet request = new HttpGet(uri);
        // must be final or the anonymous class cannot reference it...
        final AtomicReference<HttpResponse> rspRef = new AtomicReference<HttpResponse>(null);
        ResponseHandler<HttpResponse> handler = new ResponseHandler<HttpResponse>() {
            public HttpResponse handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
                HttpResponse prevValue = rspRef.getAndSet(response);
                assertNull("Duplicate response handling", prevValue);
                return response;
            }
        };

        HttpResponse response;
        if (host == null) {
            response = (context == null) ? httpClient.execute(request, handler)
                    : httpClient.execute(request, handler, context);
        } else {
            response = (context == null) ? httpClient.execute(host, request, handler)
                    : httpClient.execute(host, request, handler, context);
        }

        assertSame("Mismatched reference and return value", response, rspRef.get());
        handleResponse(testName, uri, request, response, true);
    }

    private void runHttpUriRequestTest(String testName, HttpContext context) throws IOException {
        runHttpRequestTest(testName, null, context);
    }

    private void runHttpHostRequestTest(String testName, HttpContext context) throws Exception {
        runHttpRequestTest(testName, URIUtils.extractHost(new URI(createTestUri(testName))), context);
    }

    private void runHttpRequestTest(String testName, HttpHost host, HttpContext context) throws IOException {
        HttpClient httpClient = new DefaultHttpClient();
        String uri = createTestUri(testName);
        HttpGet request = new HttpGet(uri);
        HttpResponse response;
        if (host == null) {
            response = (context == null) ? httpClient.execute(request) : httpClient.execute(request, context);
        } else {
            response = (context == null) ? httpClient.execute(host, request)
                    : httpClient.execute(host, request, context);
        }

        handleResponse(testName, uri, request, response, true);
    }

    private Operation handleResponse(String testName, String uri, HttpRequest request, HttpResponse response,
            boolean checkHeaders) throws IOException {
        HttpEntity entity = response.getEntity();
        try {
            return assertExecutionResult(uri, request, response, checkHeaders);
        } finally {
            EntityUtils.consume(entity);
        }
    }

    private Operation assertExecutionResult(String uri, HttpRequest request, HttpResponse response,
            boolean checkHeaders) {
        Operation op = getLastEntered();
        assertNotNull("No operation extracted", op);
        assertEquals("Mismatched operation type for " + uri, HttpClientDefinitions.TYPE, op.getType());
        assertRequestDetails(uri, op.get("request", OperationMap.class), request, checkHeaders);
        assertResponseDetails(uri, op.get("response", OperationMap.class), response, checkHeaders);
        return op;
    }

    private OperationMap assertRequestDetails(String uri, OperationMap details, HttpRequest request,
            boolean checkHeaders) {
        assertNotNull("No request details for " + uri, details);

        RequestLine reqLine = request.getRequestLine();
        assertEquals("Mismatched method", reqLine.getMethod(), details.get("method"));
        assertEquals("Mismatched URI", reqLine.getUri(), details.get(OperationFields.URI));
        assertEquals("Mismatched protocol",
                HttpClientExecutionCollectionAspect.createVersionValue(reqLine.getProtocolVersion()),
                details.get("protocol"));
        if (checkHeaders) {
            assertHeadersContents(uri, "request", details, request);
        }

        return details;
    }

    private OperationMap assertResponseDetails(String uri, OperationMap details, HttpResponse response,
            boolean checkHeaders) {
        if (response == null) {
            return details;
        }

        StatusLine statusLine = response.getStatusLine();
        assertEquals("Mismatched status code", statusLine.getStatusCode(), details.getInt("statusCode", (-1)));
        assertEquals("Mismatched reason phrase", statusLine.getReasonPhrase(),
                details.get("reasonPhrase", String.class));
        if (checkHeaders) {
            assertHeadersContents(uri, "response", details, response);
        }

        return details;
    }

    private void assertHeadersContents(String uri, String type, OperationMap details, HttpMessage message) {
        OperationList headers = details.get("headers", OperationList.class);
        assertNotNull("No " + type + " headers for " + uri, headers);

        Header[] hdrArray = message.getAllHeaders();
        int numHdrs = (hdrArray == null) ? 0 : hdrArray.length;
        assertEquals("Mismatched " + type + " number of headers", numHdrs, headers.size());
        if (numHdrs <= 0) {
            return;
        }

        Map<String, String> opHdrs = toHeadersMap(headers);
        Map<String, String> msgHdrs = toHeadersMap(hdrArray);
        assertEquals("Mismatched " + type + " headers map size", msgHdrs.size(), opHdrs.size());

        for (Map.Entry<String, String> hdrValue : msgHdrs.entrySet()) {
            String name = hdrValue.getKey();
            assertEquals("Mismatched " + type + " value for " + name + " header", hdrValue.getValue(),
                    opHdrs.get(name));
        }
    }

    private Map<String, String> toHeadersMap(Header... headers) {
        if (ArrayUtil.length(headers) <= 0) {
            return Collections.emptyMap();
        }

        Map<String, String> hdrsMap = new TreeMap<String, String>();
        for (Header hdrValue : headers) {
            String name = hdrValue.getName();
            String value = hdrValue.getValue();
            hdrsMap.put(name, value);
        }

        return hdrsMap;
    }

    private Map<String, String> toHeadersMap(OperationList headers) {
        if ((headers == null) || (headers.size() <= 0)) {
            return Collections.emptyMap();
        }

        Map<String, String> hdrsMap = new TreeMap<String, String>();
        for (int index = 0; index < headers.size(); index++) {
            OperationMap hdrValue = headers.get(index, OperationMap.class);
            String name = hdrValue.get(OperationUtils.NAME_KEY, String.class);
            String value = hdrValue.get(OperationUtils.VALUE_KEY, String.class);
            hdrsMap.put(name, value);
        }

        return hdrsMap;
    }

    @Override
    public HttpClientExecutionCollectionAspect getAspect() {
        return HttpClientExecutionCollectionAspect.aspectOf();
    }

    private static final class TestHandler implements Handler {
        private Server server;
        private boolean started;

        protected TestHandler() {
            super();
        }

        public void addLifeCycleListener(Listener listener) {
            // ignored
        }

        public void removeLifeCycleListener(Listener listener) {
            // ignored
        }

        public void stop() throws Exception {
            if (!started) {
                throw new IllegalStateException("Not started");
            }

            started = false;
        }

        public void start() throws Exception {
            if (started) {
                throw new IllegalStateException("Double start");
            }

            started = true;
        }

        public boolean isStopping() {
            return true;
        }

        public boolean isStopped() {
            return !started;
        }

        public boolean isStarting() {
            return true;
        }

        public boolean isStarted() {
            return started;
        }

        public boolean isRunning() {
            return started;
        }

        public boolean isFailed() {
            return false;
        }

        public Server getServer() {
            return this.server;
        }

        public void setServer(Server s) {
            this.server = s;
        }

        public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)
                throws IOException, ServletException {
            int namePos = target.lastIndexOf('/');
            String testName = target.substring(namePos + 1);
            response.setStatus(HttpServletResponse.SC_OK);
            response.setContentType("text/xml;charset=utf-8");
            response.addHeader("X-Test-Name", testName);
            response.addHeader("WWW-Authenticate", "allowed");

            Writer writer = response.getWriter();
            try {
                writer.append(createResponseContent(testName));
            } finally {
                writer.close();
            }

            Request baseRequest = (request instanceof Request) ? (Request) request
                    : HttpConnection.getCurrentConnection().getRequest();
            baseRequest.setHandled(true);
        }

        public void destroy() {
            if (this.server != null)
                this.server = null;
        }
    }

    static String createResponseContent(String testName) {
        return "<test name=\"" + testName + "\" />";
    }

    static String createTestUri(String testName) {
        return TEST_URI + testName;
    }
}