org.httpobjects.tck.IntegrationTest.java Source code

Java tutorial

Introduction

Here is the source code for org.httpobjects.tck.IntegrationTest.java

Source

/**
 * Copyright (C) 2011, 2012 Commission Junction Inc.
 *
 * This file is part of httpobjects.
 *
 * httpobjects is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * httpobjects is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with httpobjects; see the file COPYING.  If not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 *
 * Linking this library statically or dynamically with other modules is
 * making a combined work based on this library.  Thus, the terms and
 * conditions of the GNU General Public License cover the whole
 * combination.
 *
 * As a special exception, the copyright holders of this library give you
 * permission to link this library with independent modules to produce an
 * executable, regardless of the license terms of these independent
 * modules, and to copy and distribute the resulting executable under
 * terms of your choice, provided that you also meet, for each linked
 * independent module, the terms and conditions of the license of that
 * module.  An independent module is a module which is not derived from
 * or based on this library.  If you modify this library, you may extend
 * this exception to your version of the library, but you are not
 * obligated to do so.  If you do not wish to do so, delete this
 * exception statement from your version.
 */
package org.httpobjects.tck;

import akka.dispatch.ExecutionContexts;
import akka.dispatch.Futures;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.*;
import org.httpobjects.*;
import org.httpobjects.header.DefaultHeaderFieldVisitor;
import org.httpobjects.header.GenericHeaderField;
import org.httpobjects.header.HeaderField;
import org.httpobjects.header.request.AuthorizationField;
import org.httpobjects.header.request.Cookie;
import org.httpobjects.header.request.CookieField;
import org.httpobjects.header.request.credentials.BasicCredentials;
import org.httpobjects.header.response.SetCookieField;
import org.httpobjects.header.response.WWWAuthenticateField.Method;
import org.httpobjects.path.Path;
import org.httpobjects.util.HttpObjectUtil;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import scala.concurrent.Future;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.regex.Pattern;

import static org.httpobjects.util.HttpObjectUtil.toAscii;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/**
 * Technology Compatibility Kit
 */
public abstract class IntegrationTest {

    protected abstract void serve(int port, HttpObject... objects);

    protected abstract void stopServing();

    @Before
    public void setup() {

        ExecutionContexts.fromExecutor(Executors.newSingleThreadExecutor());

        serve(8080, new HttpObject("/app/inbox") {
            public Future<Response> post(Request req) {
                return OK(Text("Message Received")).toFuture();
            }
        }, new HttpObject("/app/inbox/abc") {
            public Future<Response> put(Request req) {
                return OK(req.representation()).toFuture();
            }
        }, new HttpObject("/app") {
            public Future<Response> get(Request req) {
                return OK(Text("Welcome to the app")).toFuture();
            }
        }, new HttpObject("/app/message") {
            public Future<Response> post(Request req) {
                return SEE_OTHER(Location("/app"), SetCookie("name", "frank")).toFuture();
            }
        }, new HttpObject("/nothing", null) {
        }, new HttpObject("/secure") {
            public Future<Response> get(Request req) {
                AuthorizationField authorization = req.header().authorization();
                if (authorization != null && authorization.method() == Method.Basic) {

                    BasicCredentials creds = authorization.basicCredentials();
                    if (creds.user().equals("Aladdin") && creds.password().equals("open sesame")) {
                        return OK(Text("You're In!")).toFuture();
                    }
                }
                return UNAUTHORIZED(BasicAuthentication("secure area"), Text("You must first log-in")).toFuture();
            }
        }, new HttpObject("/echoUrl/{id}/{name}") {
            @Override
            public Future<Response> get(Request req) {
                try {
                    final String query = req.query().toString();
                    return OK(Text(req.path().toString() + query)).toFuture();
                } catch (Exception e) {
                    e.printStackTrace();
                    return INTERNAL_SERVER_ERROR(e).toFuture();
                }
            }
        }, new HttpObject("/echoQuery") {
            @Override
            public Future<Response> get(Request req) {
                final StringBuffer text = new StringBuffer();
                final Query query = req.query();
                for (String name : query.paramNames()) {
                    if (text.length() > 0) {
                        text.append('\n');
                    }
                    text.append(name + "=" + query.valueFor(name));
                }
                return OK(Text(text.toString())).toFuture();
            }
        }, new HttpObject("/echoCookies") {
            public Future<Response> get(Request req) {

                final StringBuffer text = new StringBuffer();
                for (HeaderField next : req.header().fields()) {
                    next.accept(new DefaultHeaderFieldVisitor<Void>() {
                        @Override
                        public Void visit(CookieField cookieField) {
                            for (Cookie cookie : cookieField.cookies()) {
                                text.append(cookie.name + "=" + cookie.value);
                            }
                            return null;
                        }
                    });
                }

                return OK(Text(text.toString())).toFuture();
            }
        }, new HttpObject("/cookieSetter") {
            public Future<Response> get(Request req) {
                return OK(Text("Here are some cookies!"),
                        new SetCookieField("name", "cookie monster", "sesamestreet.com"),
                        new SetCookieField("specialGuest", "mr rogers", "mrrogers.com", "/myNeighborhood",
                                "Wed, 13-Jan-2021 22:23:01 GMT", true),
                        new SetCookieField("oldInsecureCookie", "yes", "the90sIntranet.com", "/images/animatedGifs",
                                "Wed, 13-Jan-1999 22:23:01 GMT", false)).toFuture();
            }
        }, new HttpObject("/subpathEcho/{subPath*}") {
            @Override
            public Future<Response> get(Request req) {
                return OK(Text(req.path().valueFor("subPath"))).toFuture();
            }
        }, new HttpObject("/echoHasRepresentation") {
            @Override
            public Future<Response> post(Request req) {
                return OK(Text(req.hasRepresentation() ? "yes" : "no")).toFuture();
            }
        }, new HttpObject("/pows/{name}/{rank}/{serialnumber}") {
            @Override
            public Future<Response> get(Request req) {
                final Path path = req.path();
                return OK(Text(
                        path.valueFor("rank") + " " + path.valueFor("name") + ", " + path.valueFor("serialnumber")))
                                .toFuture();
            }
        }, new HttpObject("/immutablecopy/{subpath*}") {
            @Override
            public Future<Response> post(Request req) {
                Request r = req.immutableCopy();
                final String firstPass = toString(r);
                final String secondPass = toString(r);
                return OK(Text(secondPass)).toFuture();
            }

            class HeadersByName implements Comparator<HeaderField> {
                @Override
                public int compare(HeaderField o1, HeaderField o2) {
                    return o1.name().compareTo(o2.name());
                }
            }

            private <T> List<T> sorted(List<T> items, Comparator<T> comparator) {
                List<T> sorted = new ArrayList<T>(items);
                Collections.sort(sorted, comparator);
                return sorted;
            }

            private String toString(Request r) {
                return "URI: " + r.path().toString() + "?" + r.query().toString() + "\n"
                        + toString(r.header().fields()) + toAscii(r.representation());
            }

            private String toString(List<HeaderField> fields) {
                StringBuffer text = new StringBuffer();
                for (HeaderField field : sorted(fields, new HeadersByName())) {
                    text.append(field.name() + "=" + field.value() + "\n");
                }
                return text.toString();
            }
        }, new HttpObject("/patchme") {
            public Future<Response> patch(org.httpobjects.Request req) {
                try {
                    final String input = new String(HttpObjectUtil.toByteArray(req.representation()), "UTF-8");
                    return OK(Text("You told me to patch!" + input)).toFuture();
                } catch (UnsupportedEncodingException e) {
                    return INTERNAL_SERVER_ERROR(e).toFuture();
                }
            }
        }, new HttpObject("/connectionInfo") {
            public Future<Response> get(Request req) {
                final ConnectionInfo connection = req.connectionInfo();
                return OK(Text("Local " + connection.localAddress + ":" + connection.localPort + ", " + "Remote "
                        + connection.remoteAddress + ":" + connection.remotePort)).toFuture();
            }
        }, new HttpObject("/head") {
            @Override
            public Future<Response> head(Request req) {
                return OK(Text(""), new GenericHeaderField("foo", "bar")).toFuture();
            }
        });
    }

    class PatchMethod extends EntityEnclosingMethod {

        public PatchMethod(String uri) {
            super(uri);
        }

        @Override
        public String getName() {
            return "PATCH";
        }
    }

    @Test
    public void supportsHead() throws Exception {
        // given
        HttpClient client = new HttpClient();
        HeadMethod request = new HeadMethod("http://127.0.0.2:8080/head");

        //when
        int responseCode = client.executeMethod(request);

        // then

        assertEquals(200, responseCode);
        assertEquals("bar", request.getResponseHeader("foo").getValue());
    }

    @Test
    public void returnsConnectionInfo() throws Exception {
        // given
        String url = "http://127.0.0.2:8080/connectionInfo";

        //when
        final String result = getFrom("127.0.0.3", url);

        // then
        Pattern expectedPattern = Pattern.compile("Local 127.0.0.2:8080, Remote 127.0.0.3:([0-9].*)");
        assertTrue("'" + result + " should match '" + expectedPattern, expectedPattern.matcher(result).matches());
    }

    @Test
    public void hasRepresentation() throws Exception {
        // given
        PostMethod request = new PostMethod("http://localhost:8080/echoHasRepresentation");
        request.setRequestEntity(new StringRequestEntity("foo bar", "text/plain", "UTF-8"));

        // then/when
        assertResource(request, "yes", 200);
    }

    @Test
    public void immutableCopies() throws Exception {
        // given
        PostMethod request = new PostMethod("http://localhost:8080/immutablecopy/no/mutation/allowed");
        request.setRequestEntity(new StringRequestEntity("foo bar", "text/plain", "UTF-8"));

        // then/when
        assertResource(request,
                "URI: /immutablecopy/no/mutation/allowed?\n" + "Content-Length=7\n"
                        + "Content-Type=text/plain; charset=UTF-8\n" + "Host=localhost:8080\n"
                        + "User-Agent=Jakarta Commons-HttpClient/3.1\n" + "foo bar",
                200);

    }

    @Test
    public void parsesPathVars() throws Exception {
        // given
        GetMethod request = new GetMethod("http://localhost:8080/pows/marty/private/abc123");

        // then/when
        assertResource(request, "private marty, abc123", 200);
    }

    @Test
    public void parsesSubpaths() throws Exception {
        // given
        GetMethod request = new GetMethod("http://localhost:8080/subpathEcho/i/am/my/own/grandpa");

        // then/when
        assertResource(request, "i/am/my/own/grandpa", 200);
    }

    @Test
    public void supportsPatch() throws Exception {
        // given
        PatchMethod request = new PatchMethod("http://localhost:8080/patchme");
        request.setRequestEntity(new StringRequestEntity(" foo bar", "text/plain", "UTF-8"));

        // then/when
        assertResource(request, "You told me to patch! foo bar", 200);
    }

    @Test
    public void setCookieHeadersAreTranslated() throws Exception {
        // given
        GetMethod request = new GetMethod("http://localhost:8080/cookieSetter");
        HttpClient client = new HttpClient();

        // when
        int response = client.executeMethod(request);

        // then
        assertEquals(200, response);
        List<Header> setCookies = sortByValue(Arrays.asList(request.getResponseHeaders("Set-Cookie")));
        assertEquals(3, setCookies.size());

        {
            String value = setCookies.get(0).getValue();
            SetCookieField cookie = SetCookieField.fromHeaderValue(value);
            assertEquals("name", cookie.name);
            assertEquals("cookie monster", cookie.value);
            assertEquals("sesamestreet.com", cookie.domain);
        }

        {
            String value = setCookies.get(1).getValue();
            SetCookieField cookie = SetCookieField.fromHeaderValue(value);
            assertEquals("oldInsecureCookie", cookie.name);
            assertEquals("yes", cookie.value);
            assertEquals("the90sintranet.com", cookie.domain.toLowerCase());
            assertEquals("/images/animatedGifs", cookie.path);
            assertEquals(null, cookie.secure);
        }

        {
            String value = setCookies.get(2).getValue();
            SetCookieField cookie = SetCookieField.fromHeaderValue(value);
            assertEquals("specialGuest", cookie.name);
            assertEquals("mr rogers", cookie.value);
            assertEquals("mrrogers.com", cookie.domain);
            assertEquals("/myNeighborhood", cookie.path);
            assertEquals(Boolean.TRUE, cookie.secure);
        }
    }

    private List<Header> sortByValue(final List<Header> cookies) {
        List<Header> result = new ArrayList<Header>(cookies);
        Collections.sort(result, new Comparator<Header>() {
            @Override
            public int compare(Header o1, Header o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
        });
        return result;
    }

    @Test
    public void requestCookiesAreTranslated() throws Exception {
        // WHEN
        GetMethod get = new GetMethod("http://localhost:8080/echoCookies");
        get.setRequestHeader("Cookie", "Larry=Moe");

        assertResource(get, "Larry=Moe", 200);
    }

    @Test
    public void basicAuthentication() {
        // without authorization header
        assertResource(new GetMethod("http://localhost:8080/secure"), "You must first log-in", 401,
                new HeaderSpec("WWW-Authenticate", "Basic realm=secure area"));

        // with authorization header
        GetMethod get = new GetMethod("http://localhost:8080/secure");
        get.setRequestHeader("Authorization", "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
        assertResource(get, "You're In!", 200);
    }

    @Test
    public void nullResponsesAreTreatedAsNotFound() {
        assertResource(new GetMethod("http://localhost:8080/nothing"), 404);
    }

    @Test
    public void returnsNotFoundIfThereIsNoMatchingPattern() {
        assertResource(new GetMethod("http://localhost:8080/bob"), 404);
    }

    @Test
    public void happyPathForGet() {
        assertResource(new GetMethod("http://localhost:8080/app"), "Welcome to the app", 200);
    }

    @Test
    public void happyPathForPost() {
        assertResource(new PostMethod("http://localhost:8080/app/inbox"), "Message Received", 200);
    }

    @Test
    public void happyPathForPut() {
        assertResource(withBody(new PutMethod("http://localhost:8080/app/inbox/abc"), "hello world"), "hello world",
                200);
    }

    @Test
    public void queryParameters() {
        assertResource(new GetMethod("http://localhost:8080/echoQuery?a=1&b=2"), "a=1\nb=2", 200);
    }

    @Test
    public void urlToString() {
        assertResource(new GetMethod("http://localhost:8080/echoUrl/34/marty?a=1&b=2"), "/echoUrl/34/marty?a=1&b=2",
                200);
        assertResource(new GetMethod("http://localhost:8080/echoUrl/44/foo"), "/echoUrl/44/foo", 200);
    }

    @Test
    public void methodNotAllowed() {
        assertResource(new GetMethod("http://localhost:8080/app/inbox"), "405 Client Error: Method Not Allowed",
                405);
    }

    @Test
    public void redirectsAndSetsCookies() {

        assertResource(new PostMethod("http://localhost:8080/app/message"), 303, new HeaderSpec("Location", "/app"),
                new HeaderSpec("Set-Cookie", "name=frank"));
    }

    @SuppressWarnings("deprecation")
    private static <T extends EntityEnclosingMethod> T withBody(T m, String body) {
        m.setRequestBody(body);
        return m;
    }

    private void assertResource(HttpMethod method, int expectedResponseCode, HeaderSpec... header) {
        assertResource(method, null, expectedResponseCode, header);
    }

    private void assertResource(HttpMethod method, String expectedBody, int expectedResponseCode,
            HeaderSpec... header) {
        try {
            HttpClient client = new HttpClient();
            int response = client.executeMethod(method);

            Assert.assertEquals(expectedResponseCode, response);
            if (expectedBody != null)
                Assert.assertEquals(expectedBody, method.getResponseBodyAsString());

            if (header != null) {
                for (HeaderSpec next : header) {
                    Header h = method.getResponseHeader(next.name);
                    Assert.assertNotNull("Expected a \"" + next.name + "\" value of \"" + next.value + "\"", h);
                    Assert.assertEquals(next.value, h.getValue());
                }
            }
        } catch (HttpException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private class HeaderSpec {
        final String name;
        final String value;

        private HeaderSpec(String name, String value) {
            super();
            this.name = name;
            this.value = value;
        }

    }

    private String getFrom(String address, String url) {
        try {
            HttpClient client = new HttpClient();
            client.getHostConfiguration().setLocalAddress(InetAddress.getByName(address));
            GetMethod request = new GetMethod(url);
            int responseCode = client.executeMethod(request);
            String result = request.getResponseBodyAsString();
            return result;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private String get(String url) throws IOException, HttpException {
        HttpClient client = new HttpClient();
        GetMethod request = new GetMethod(url);
        int responseCode = client.executeMethod(request);
        String result = request.getResponseBodyAsString();
        return result;
    }

    @After
    public void tearDown() throws Exception {
        stopServing();
    }
}