brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [2/9] incubator-brooklyn git commit: Updates in response to review comments on the pull request.
Date Wed, 04 Nov 2015 15:47:47 GMT
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/03dbd281/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpAsserts.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpAsserts.java b/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpAsserts.java
new file mode 100644
index 0000000..bbfdbfb
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpAsserts.java
@@ -0,0 +1,282 @@
+/*
+ * 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 org.apache.brooklyn.util.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSession;
+
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.crypto.SslTrustUtils;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.time.Time;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+/**
+ * Utility methods to aid testing HTTP.
+ * 
+ * @author aled
+ */
+public class HttpAsserts {
+
+    // TODO Delete methods from TestUtils, to just have them here (or switch so TestUtils delegates here,
+    // and deprecate methods in TestUtils until deleted).
+
+    private static final Logger LOG = LoggerFactory.getLogger(HttpAsserts.class);
+
+    public static void assertHealthyStatusCode(int code) {
+        if (code>=200 && code<=299) return;
+        Asserts.fail("Wrong status code: " + code);
+    }
+    
+    public static int getHttpStatusCode(String url) throws Exception {
+        URLConnection connection = HttpTool.connectToUrl(url);
+        long startTime = System.currentTimeMillis();
+        int status = ((HttpURLConnection) connection).getResponseCode();
+        
+        // read fully if possible, then close everything, trying to prevent cached threads at server
+        HttpTool.consumeAndCloseQuietly((HttpURLConnection) connection);
+        
+        if (LOG.isDebugEnabled())
+            LOG.debug("connection to {} ({}ms) gives {}", new Object[] { url, (System.currentTimeMillis()-startTime), status });
+        return status;
+    }
+
+    /**
+     * Asserts that gets back any "valid" response - i.e. not an exception. This could be an unauthorized,
+     * a redirect, a 404, or anything else that implies there is web-server listening on that port.
+     */
+    public static void assertUrlReachable(String url) {
+        try {
+            getHttpStatusCode(url);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException("Interrupted for "+url+" (in assertion that is reachable)", e);
+        } catch (Exception e) {
+            throw new IllegalStateException("Server at "+url+" Asserts.failed to respond (in assertion that is reachable): "+e, e);
+        }
+    }
+
+    public static void assertUrlUnreachable(String url) {
+        try {
+            int statusCode = getHttpStatusCode(url);
+            Asserts.fail("Expected url " + url + " unreachable, but got status code " + statusCode);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException("Interrupted for "+url+" (in assertion that unreachable)", e);
+        } catch (Exception e) {
+            IOException cause = Exceptions.getFirstThrowableOfType(e, IOException.class);
+            if (cause != null) {
+                // success; clean shutdown transitioning from 400 to error
+            } else {
+                Throwables.propagate(e);
+            }                        
+        }
+    }
+
+    public static void assertUrlUnreachableEventually(final String url) {
+        assertUrlUnreachableEventually(Maps.newLinkedHashMap(), url);
+    }
+    
+    public static void assertUrlUnreachableEventually(Map flags, final String url) {
+        Asserts.succeedsEventually(flags, new Runnable() {
+            public void run() {
+                assertUrlUnreachable(url);
+            }
+         });
+    }
+
+    public static void assertHttpStatusCodeEquals(String url, int... acceptableReturnCodes) {
+        List<Integer> acceptableCodes = Lists.newArrayList();
+        for (int code : acceptableReturnCodes) {
+            acceptableCodes.add((Integer)code);
+        }
+        try {
+            int actualCode = getHttpStatusCode(url);
+            Asserts.assertTrue(acceptableCodes.contains(actualCode), "code=" + actualCode + "; expected=" + acceptableCodes + "; url=" + url);
+            
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException("Interrupted for "+url+" (in assertion that result code is "+acceptableCodes+")", e);
+        } catch (Exception e) {
+            throw new IllegalStateException("Server at "+url+" Asserts.failed to respond (in assertion that result code is "+acceptableCodes+"): "+e, e);
+        }
+    }
+
+    public static void assertHttpStatusCodeEventuallyEquals(final String url, final int expectedCode) {
+        assertHttpStatusCodeEventuallyEquals(Maps.newLinkedHashMap(),  url, expectedCode);
+    }
+
+    public static void assertHttpStatusCodeEventuallyEquals(Map flags, final String url, final int expectedCode) {
+        Asserts.succeedsEventually(flags, new Runnable() {
+            public void run() {
+                assertHttpStatusCodeEquals(url, expectedCode);
+            }
+         });
+    }
+
+    public static void assertContentContainsText(final String url, final String phrase, final String ...additionalPhrases) {
+        try {
+            String contents = HttpTool.getContent(url);
+            Asserts.assertTrue(contents != null && contents.length() > 0);
+            for (String text: Lists.asList(phrase, additionalPhrases)) {
+                if (!contents.contains(text)) {
+                    LOG.warn("CONTENTS OF URL "+url+" MISSING TEXT: "+text+"\n"+contents);
+                    Asserts.fail("URL "+url+" does not contain text: "+text);
+                }
+            }
+        } catch (Exception e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    public static void assertContentNotContainsText(final String url, final String phrase, final String ...additionalPhrases) {
+        try {
+            String contents = HttpTool.getContent(url);
+            Asserts.assertTrue(contents != null);
+            for (String text: Lists.asList(phrase, additionalPhrases)) {
+                if (contents.contains(text)) {
+                    LOG.warn("CONTENTS OF URL "+url+" HAS TEXT: "+text+"\n"+contents);
+                    Asserts.fail("URL "+url+" contain text: "+text);
+                }
+            }
+        } catch (Exception e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    public static void assertErrorContentContainsText(final String url, final String phrase, final String ...additionalPhrases) {
+        try {
+            String contents = HttpTool.getErrorContent(url);
+            Asserts.assertTrue(contents != null && contents.length() > 0);
+            for (String text: Lists.asList(phrase, additionalPhrases)) {
+                if (!contents.contains(text)) {
+                    LOG.warn("CONTENTS OF URL "+url+" MISSING TEXT: "+text+"\n"+contents);
+                    Asserts.fail("URL "+url+" does not contain text: "+text);
+                }
+            }
+        } catch (Exception e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+
+    public static void assertErrorContentNotContainsText(final String url, final String phrase, final String ...additionalPhrases) {
+        try {
+            String err = HttpTool.getErrorContent(url);
+            Asserts.assertTrue(err != null);
+            for (String text: Lists.asList(phrase, additionalPhrases)) {
+                if (err.contains(text)) {
+                    LOG.warn("CONTENTS OF URL "+url+" HAS TEXT: "+text+"\n"+err);
+                    Asserts.fail("URL "+url+" contain text: "+text);
+                }
+            }
+        } catch (Exception e) {
+            throw Throwables.propagate(e);
+        }
+    }
+    
+    public static void assertContentEventuallyContainsText(final String url, final String phrase, final String ...additionalPhrases) {
+        assertContentEventuallyContainsText(MutableMap.of(), url, phrase, additionalPhrases);
+    }
+    
+    public static void assertContentEventuallyContainsText(Map flags, final String url, final String phrase, final String ...additionalPhrases) {
+        Asserts.succeedsEventually(flags, new Runnable() {
+            public void run() {
+                assertContentContainsText(url, phrase, additionalPhrases);
+            }
+         });
+    }
+    
+    public static void assertContentMatches(String url, String regex) {
+        String contents = HttpTool.getContent(url);
+        Asserts.assertNotNull(contents);
+        Asserts.assertTrue(contents.matches(regex), "Contents does not match expected regex ("+regex+"): "+contents);
+    }
+
+    public static void assertContentEventuallyMatches(final String url, final String regex) {
+        Asserts.succeedsEventually(new Runnable() {
+            @Override
+            public void run() {
+                assertContentMatches(url, regex);
+            }
+        });
+    }
+
+
+
+    /**
+     * Schedules (with the given executor) a poller that repeatedly accesses the given url, to confirm it always gives
+     * back the expected status code.
+     * 
+     * Expected usage is to query the future, such as:
+     * 
+     * <pre>
+     * {@code
+     * Future<?> future = assertAsyncHttpStatusCodeContinuallyEquals(executor, url, 200);
+     * // do other stuff...
+     * if (future.isDone()) future.get(); // get exception if it's Asserts.failed
+     * }
+     * </pre>
+     * 
+     * For stopping it, you can either do future.cancel(true), or you can just do executor.shutdownNow().
+     * 
+     * TODO Look at difference between this and WebAppMonitor, to decide if this should be kept.
+     */
+    public static ListenableFuture<?> assertAsyncHttpStatusCodeContinuallyEquals(ListeningExecutorService executor, final String url, final int expectedStatusCode) {
+        return executor.submit(new Runnable() {
+            @Override public void run() {
+                // TODO Need to drop logging; remove sleep when that's done.
+                while (!Thread.currentThread().isInterrupted()) {
+                    assertHttpStatusCodeEquals(url, expectedStatusCode);
+                    try {
+                        Thread.sleep(1000);
+                    } catch (InterruptedException e) {
+                        return; // graceful return
+                    }
+                }
+            }
+        });
+    }
+    
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/03dbd281/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpTool.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpTool.java b/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpTool.java
new file mode 100644
index 0000000..8811a32
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpTool.java
@@ -0,0 +1,525 @@
+/*
+ * 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 org.apache.brooklyn.util.http;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.google.common.base.Throwables;
+import org.apache.brooklyn.util.crypto.SslTrustUtils;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.net.URLParamEncoder;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Time;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeSocketFactory;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.LaxRedirectStrategy;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSession;
+
+public class HttpTool {
+
+    private static final Logger LOG = LoggerFactory.getLogger(HttpTool.class);
+
+    static final ExecutorService executor = Executors.newCachedThreadPool();
+
+    /**
+     * Connects to the given url and returns the connection.
+     * Caller should {@code connection.getInputStream().close()} the result of this
+     * (especially if they are making heavy use of this method).
+     */
+    public static URLConnection connectToUrl(String u) throws Exception {
+        final URL url = new URL(u);
+        final AtomicReference<Exception> exception = new AtomicReference<Exception>();
+
+        // sometimes openConnection hangs, so run in background
+        Future<URLConnection> f = executor.submit(new Callable<URLConnection>() {
+            public URLConnection call() {
+                try {
+                    HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
+                        @Override
+                        public boolean verify(String s, SSLSession sslSession) {
+                            return true;
+                        }
+                    });
+                    URLConnection connection = url.openConnection();
+                    TrustingSslSocketFactory.configure(connection);
+                    connection.connect();
+
+                    connection.getContentLength(); // Make sure the connection is made.
+                    return connection;
+                } catch (Exception e) {
+                    exception.set(e);
+                    LOG.debug("Error connecting to url "+url+" (propagating): "+e, e);
+                }
+                return null;
+            }
+        });
+        try {
+            URLConnection result = null;
+            try {
+                result = f.get(60, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                throw e;
+            } catch (Exception e) {
+                LOG.debug("Error connecting to url "+url+", probably timed out (rethrowing): "+e);
+                throw new IllegalStateException("Connect to URL not complete within 60 seconds, for url "+url+": "+e);
+            }
+            if (exception.get() != null) {
+                LOG.debug("Error connecting to url "+url+", thread caller of "+exception, new Throwable("source of rethrown error "+exception));
+                throw exception.get();
+            } else {
+                return result;
+            }
+        } finally {
+            f.cancel(true);
+        }
+    }
+
+
+
+    public static int getHttpStatusCode(String url) throws Exception {
+        URLConnection connection = connectToUrl(url);
+        long startTime = System.currentTimeMillis();
+        int status = ((HttpURLConnection) connection).getResponseCode();
+
+        // read fully if possible, then close everything, trying to prevent cached threads at server
+        consumeAndCloseQuietly((HttpURLConnection) connection);
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("connection to {} ({}ms) gives {}", new Object[] { url, (System.currentTimeMillis()-startTime), status });
+        return status;
+    }
+
+
+    public static String getContent(String url) {
+        try {
+            return Streams.readFullyString(SslTrustUtils.trustAll(new URL(url).openConnection()).getInputStream());
+        } catch (Exception e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    public static String getErrorContent(String url) {
+        try {
+            HttpURLConnection connection = (HttpURLConnection) connectToUrl(url);
+            long startTime = System.currentTimeMillis();
+
+            String err;
+            int status;
+            try {
+                InputStream errStream = connection.getErrorStream();
+                err = Streams.readFullyString(errStream);
+                status = connection.getResponseCode();
+            } finally {
+                closeQuietly(connection);
+            }
+
+            if (LOG.isDebugEnabled())
+                LOG.debug("read of err {} ({}ms) complete; http code {}", new Object[] { url, Time.makeTimeStringRounded(System.currentTimeMillis() - startTime), status});
+            return err;
+
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    /**
+     * Consumes the input stream entirely and then cleanly closes the connection.
+     * Ignores all exceptions completely, not even logging them!
+     *
+     * Consuming the stream fully is useful for preventing idle TCP connections.
+     * @see <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/net/http-keepalive.html">Persistent Connections</a>
+     */
+    public static void consumeAndCloseQuietly(HttpURLConnection connection) {
+        try { Streams.readFully(connection.getInputStream()); } catch (Exception e) {}
+        closeQuietly(connection);
+    }
+
+    /**
+     * Closes all streams of the connection, and disconnects it. Ignores all exceptions completely,
+     * not even logging them!
+     */
+    public static void closeQuietly(HttpURLConnection connection) {
+        try { connection.disconnect(); } catch (Exception e) {}
+        try { connection.getInputStream().close(); } catch (Exception e) {}
+        try { connection.getOutputStream().close(); } catch (Exception e) {}
+        try { connection.getErrorStream().close(); } catch (Exception e) {}
+    }
+
+    /** Apache HTTP commons utility for trusting all.
+     * <p>
+     * For generic java HTTP usage, see {@link SslTrustUtils#trustAll(java.net.URLConnection)} 
+     * and static constants in the same class. */
+    public static class TrustAllStrategy implements TrustStrategy {
+        @Override
+        public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+            return true;
+        }
+    }
+
+    public static HttpClientBuilder httpClientBuilder() {
+        return new HttpClientBuilder();
+    }
+    
+    public static class HttpClientBuilder {
+        private ClientConnectionManager clientConnectionManager;
+        private HttpParams httpParams;
+        private URI uri;
+        private Integer port;
+        private Credentials credentials;
+        private boolean laxRedirect;
+        private Boolean https;
+        private SchemeSocketFactory socketFactory;
+        private ConnectionReuseStrategy reuseStrategy;
+        private boolean trustAll;
+        private boolean trustSelfSigned;
+
+        public HttpClientBuilder clientConnectionManager(ClientConnectionManager val) {
+            this.clientConnectionManager = checkNotNull(val, "clientConnectionManager");
+            return this;
+        }
+        public HttpClientBuilder httpParams(HttpParams val) {
+            checkState(httpParams == null, "Must not call httpParams multiple times, or after other methods like connectionTimeout");
+            this.httpParams = checkNotNull(val, "httpParams");
+            return this;
+        }
+        public HttpClientBuilder connectionTimeout(Duration val) {
+            if (httpParams == null) httpParams = new BasicHttpParams();
+            long millis = checkNotNull(val, "connectionTimeout").toMilliseconds();
+            if (millis > Integer.MAX_VALUE) throw new IllegalStateException("HttpClient only accepts upto max-int millis for connectionTimeout, but given "+val);
+            HttpConnectionParams.setConnectionTimeout(httpParams, (int) millis);
+            return this;
+        }
+        public HttpClientBuilder socketTimeout(Duration val) {
+            if (httpParams == null) httpParams = new BasicHttpParams();
+            long millis = checkNotNull(val, "socketTimeout").toMilliseconds();
+            if (millis > Integer.MAX_VALUE) throw new IllegalStateException("HttpClient only accepts upto max-int millis for socketTimeout, but given "+val);
+            HttpConnectionParams.setSoTimeout(httpParams, (int) millis);
+            return this;
+        }
+        public HttpClientBuilder reuseStrategy(ConnectionReuseStrategy val) {
+            this.reuseStrategy = checkNotNull(val, "reuseStrategy");
+            return this;
+        }
+        public HttpClientBuilder uri(String val) {
+            return uri(URI.create(checkNotNull(val, "uri")));
+        }
+        public HttpClientBuilder uri(URI val) {
+            this.uri = checkNotNull(val, "uri");
+            if (https == null) https = ("https".equalsIgnoreCase(uri.getScheme()));
+            return this;
+        }
+        public HttpClientBuilder port(int val) {
+            this.port = val;
+            return this;
+        }
+        public HttpClientBuilder credentials(Credentials val) {
+            this.credentials = checkNotNull(val, "credentials");
+            return this;
+        }
+        public void credential(Optional<Credentials> val) {
+            if (val.isPresent()) credentials = val.get();
+        }
+        /** similar to curl --post301 -L` */
+        public HttpClientBuilder laxRedirect(boolean val) {
+            this.laxRedirect = val;
+            return this;
+        }
+        public HttpClientBuilder https(boolean val) {
+            this.https = val;
+            return this;
+        }
+        public HttpClientBuilder socketFactory(SchemeSocketFactory val) {
+            this.socketFactory = checkNotNull(val, "socketFactory");
+            return this;
+        }
+        public HttpClientBuilder trustAll() {
+            this.trustAll = true;
+            return this;
+        }
+        public HttpClientBuilder trustSelfSigned() {
+            this.trustSelfSigned = true;
+            return this;
+        }
+        public HttpClient build() {
+            final DefaultHttpClient httpClient = new DefaultHttpClient(clientConnectionManager);
+            httpClient.setParams(httpParams);
+    
+            // support redirects for POST (similar to `curl --post301 -L`)
+            // http://stackoverflow.com/questions/3658721/httpclient-4-error-302-how-to-redirect
+            if (laxRedirect) {
+                httpClient.setRedirectStrategy(new LaxRedirectStrategy());
+            }
+            if (reuseStrategy != null) {
+                httpClient.setReuseStrategy(reuseStrategy);
+            }
+            if (https == Boolean.TRUE || (uri!=null && uri.toString().startsWith("https:"))) {
+                try {
+                    if (port == null) {
+                        port = (uri != null && uri.getPort() >= 0) ? uri.getPort() : 443;
+                    }
+                    if (socketFactory == null) {
+                        if (trustAll) {
+                            TrustStrategy trustStrategy = new TrustAllStrategy();
+                            X509HostnameVerifier hostnameVerifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
+                            socketFactory = new SSLSocketFactory(trustStrategy, hostnameVerifier);
+                        } else if (trustSelfSigned) {
+                            TrustStrategy trustStrategy = new TrustSelfSignedStrategy();
+                            X509HostnameVerifier hostnameVerifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
+                            socketFactory = new SSLSocketFactory(trustStrategy, hostnameVerifier);
+                        } else {
+                            // Using default https scheme: based on default java truststore, which is pretty strict!
+                        }
+                    }
+                    if (socketFactory != null) {
+                        Scheme sch = new Scheme("https", port, socketFactory);
+                        httpClient.getConnectionManager().getSchemeRegistry().register(sch);
+                    }
+                } catch (Exception e) {
+                    LOG.warn("Error setting trust for uri {}", uri);
+                    throw Exceptions.propagate(e);
+                }
+            }
+    
+            // Set credentials
+            if (uri != null && credentials != null) {
+                String hostname = uri.getHost();
+                int port = uri.getPort();
+                httpClient.getCredentialsProvider().setCredentials(new AuthScope(hostname, port), credentials);
+            }
+            if (uri==null && credentials!=null) {
+                LOG.warn("credentials have no effect in builder unless URI for host is specified");
+            }
+    
+            return httpClient;
+        }
+    }
+
+    protected static abstract class HttpRequestBuilder<B extends HttpRequestBuilder<B, R>, R extends HttpRequest> {
+        protected R req;
+        
+        protected HttpRequestBuilder(R req) {
+            this.req = req;
+        }
+        @SuppressWarnings("unchecked")
+        protected B self() {
+            return (B) this;
+        }
+        public B headers(Map<String,String> headers) {
+            if (headers!=null) {
+                for (Map.Entry<String,String> entry : headers.entrySet()) {
+                    req.addHeader(entry.getKey(), entry.getValue());
+                }
+            }
+            return self();
+        }
+        public B headers(Multimap<String,String> headers) {
+            if (headers!=null) {
+                for (Map.Entry<String,String> entry : headers.entries()) {
+                    req.addHeader(entry.getKey(), entry.getValue());
+                }
+            }
+            return self();
+        }
+        public R build() {
+            return req;
+        }
+    }
+    
+    protected static abstract class HttpEntityEnclosingRequestBaseBuilder<B extends HttpEntityEnclosingRequestBaseBuilder<B,R>, R extends HttpEntityEnclosingRequestBase> extends HttpRequestBuilder<B, R> {
+        protected HttpEntityEnclosingRequestBaseBuilder(R req) {
+            super(req);
+        }
+        public B body(byte[] body) {
+            if (body != null) {
+                HttpEntity httpEntity = new ByteArrayEntity(body);
+                req.setEntity(httpEntity);
+            }
+            return self();
+        }
+    }
+    
+    public static class HttpGetBuilder extends HttpRequestBuilder<HttpGetBuilder, HttpGet> {
+        public HttpGetBuilder(URI uri) {
+            super(new HttpGet(uri));
+        }
+    }
+    
+    public static class HttpHeadBuilder extends HttpRequestBuilder<HttpHeadBuilder, HttpHead> {
+        public HttpHeadBuilder(URI uri) {
+            super(new HttpHead(uri));
+        }
+    }
+    
+    public static class HttpDeleteBuilder extends HttpRequestBuilder<HttpDeleteBuilder, HttpDelete> {
+        public HttpDeleteBuilder(URI uri) {
+            super(new HttpDelete(uri));
+        }
+    }
+    
+    public static class HttpPostBuilder extends HttpEntityEnclosingRequestBaseBuilder<HttpPostBuilder, HttpPost> {
+        HttpPostBuilder(URI uri) {
+            super(new HttpPost(uri));
+        }
+    }
+
+    public static class HttpFormPostBuilder extends HttpRequestBuilder<HttpFormPostBuilder, HttpPost> {
+        HttpFormPostBuilder(URI uri) {
+            super(new HttpPost(uri));
+        }
+
+        public HttpFormPostBuilder params(Map<String, String> params) {
+            if (params != null) {
+                Collection<NameValuePair> httpParams = new ArrayList<NameValuePair>(params.size());
+                for (Entry<String, String> param : params.entrySet()) {
+                    httpParams.add(new BasicNameValuePair(param.getKey(), param.getValue()));
+                }
+                req.setEntity(new UrlEncodedFormEntity(httpParams));
+            }
+            return self();
+        }
+    }
+
+    public static class HttpPutBuilder extends HttpEntityEnclosingRequestBaseBuilder<HttpPutBuilder, HttpPut> {
+        public HttpPutBuilder(URI uri) {
+            super(new HttpPut(uri));
+        }
+    }
+    
+    public static HttpToolResponse httpGet(HttpClient httpClient, URI uri, Map<String,String> headers) {
+        HttpGet req = new HttpGetBuilder(uri).headers(headers).build();
+        return execAndConsume(httpClient, req);
+    }
+
+    public static HttpToolResponse httpPost(HttpClient httpClient, URI uri, Map<String,String> headers, byte[] body) {
+        HttpPost req = new HttpPostBuilder(uri).headers(headers).body(body).build();
+        return execAndConsume(httpClient, req);
+    }
+
+    public static HttpToolResponse httpPut(HttpClient httpClient, URI uri, Map<String, String> headers, byte[] body) {
+        HttpPut req = new HttpPutBuilder(uri).headers(headers).body(body).build();
+        return execAndConsume(httpClient, req);
+    }
+
+    public static HttpToolResponse httpPost(HttpClient httpClient, URI uri, Map<String,String> headers, Map<String, String> params) {
+        HttpPost req = new HttpFormPostBuilder(uri).headers(headers).params(params).build();
+        return execAndConsume(httpClient, req);
+    }
+
+    public static HttpToolResponse httpDelete(HttpClient httpClient, URI uri, Map<String,String> headers) {
+        HttpDelete req = new HttpDeleteBuilder(uri).headers(headers).build();
+        return execAndConsume(httpClient, req);
+    }
+    
+    public static HttpToolResponse httpHead(HttpClient httpClient, URI uri, Map<String,String> headers) {
+        HttpHead req = new HttpHeadBuilder(uri).headers(headers).build();
+        return execAndConsume(httpClient, req);
+    }
+    
+    public static HttpToolResponse execAndConsume(HttpClient httpClient, HttpUriRequest req) {
+        long startTime = System.currentTimeMillis();
+        try {
+            HttpResponse httpResponse = httpClient.execute(req);
+            
+            try {
+                return new HttpToolResponse(httpResponse, startTime);
+            } finally {
+                EntityUtils.consume(httpResponse.getEntity());
+            }
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+    
+    public static boolean isStatusCodeHealthy(int code) { return (code>=200 && code<=299); }
+
+    public static String toBasicAuthorizationValue(UsernamePasswordCredentials credentials) {
+        return "Basic "+Base64.encodeBase64String( (credentials.getUserName()+":"+credentials.getPassword()).getBytes() );
+    }
+
+    public static String encodeUrlParams(Map<?,?> data) {
+        if (data==null) return "";
+        Iterable<String> args = Iterables.transform(data.entrySet(), 
+            new Function<Map.Entry<?,?>,String>() {
+            @Override public String apply(Map.Entry<?,?> entry) {
+                Object k = entry.getKey();
+                Object v = entry.getValue();
+                return URLParamEncoder.encode(Strings.toString(k)) + (v != null ? "=" + URLParamEncoder.encode(Strings.toString(v)) : "");
+            }
+        });
+        return Joiner.on("&").join(args);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/03dbd281/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpToolResponse.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpToolResponse.java b/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpToolResponse.java
new file mode 100644
index 0000000..70bcb17
--- /dev/null
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpToolResponse.java
@@ -0,0 +1,183 @@
+/*
+ * 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 org.apache.brooklyn.util.http;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Time;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.io.ByteStreams;
+
+public class HttpToolResponse {
+
+    private static final Logger log = LoggerFactory.getLogger(HttpToolResponse.class);
+    
+    private final Object mutex = new Object();
+    private final HttpResponse response;
+    private final long startTime;
+    private final long durationMillisOfFirstResponse;
+    private final long durationMillisOfFullContent;
+    private int responseCode;
+    private String reasonPhrase;
+    private Map<String,List<String>> headerLists;
+    private byte[] content;
+
+
+    public HttpToolResponse(HttpResponse response, long startTime) {
+        this.response = response;
+        this.startTime = startTime; 
+        
+        try {
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            HttpEntity entity = response.getEntity();
+            if (entity != null) {
+                entity.getContentLength();
+                durationMillisOfFirstResponse = Duration.sinceUtc(startTime).toMilliseconds();
+
+                ByteStreams.copy(entity.getContent(), out);
+                content = out.toByteArray();
+
+                entity.getContentLength();
+            } else {
+                durationMillisOfFirstResponse = Duration.sinceUtc(startTime).toMilliseconds();
+                content = new byte[0];
+            }
+            durationMillisOfFullContent = Duration.sinceUtc(startTime).toMilliseconds();
+            if (log.isTraceEnabled())
+                log.trace("HttpPollValue latency "+Time.makeTimeStringRounded(durationMillisOfFirstResponse)+" / "+Time.makeTimeStringRounded(durationMillisOfFullContent)+", content size "+content.length);
+        } catch (IOException e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    public HttpToolResponse(int responseCode, Map<String,? extends List<String>> headers, byte[] content,
+            long startTime, long durationMillisOfFirstResponse, long durationMillisOfFullContent) {
+        this.response = null;
+        this.responseCode = responseCode;
+        this.headerLists = ImmutableMap.copyOf(headers);
+        this.content = content;
+        this.startTime = startTime;
+        this.durationMillisOfFirstResponse = durationMillisOfFirstResponse;
+        this.durationMillisOfFullContent = durationMillisOfFullContent;
+    }
+    
+    public int getResponseCode() {
+        synchronized (mutex) {
+            if (responseCode == 0) {
+                responseCode = response.getStatusLine().getStatusCode();
+            }
+        }
+        return responseCode;
+    }
+
+    public String getReasonPhrase() {
+        synchronized (mutex) {
+            if (reasonPhrase == null) {
+                reasonPhrase = response.getStatusLine().getReasonPhrase();
+            }
+        }
+        return reasonPhrase;
+    }
+
+    /** returns the timestamp (millis since 1970) when this request was started */ 
+    public long getStartTime() {
+        return startTime;
+    }
+    
+    /** returns latency, in milliseconds, if value was initialized with a start time */
+    public long getLatencyFullContent() {
+        return durationMillisOfFullContent;
+    }
+    
+    /** returns latency, in milliseconds, before response started coming in */
+    public long getLatencyFirstResponse() {
+        return durationMillisOfFirstResponse;
+    }
+    
+    public Map<String, List<String>> getHeaderLists() {
+        synchronized (mutex) {
+            if (headerLists == null) {
+                Map<String, List<String>> headerListsMutable = Maps.newLinkedHashMap();
+                for (Header header : response.getAllHeaders()) {
+                    List<String> vals = headerListsMutable.get(header.getName());
+                    if (vals == null) {
+                        vals = new ArrayList<String>();
+                        headerListsMutable.put(header.getName(), vals);
+                    }
+                    vals.add(header.getValue());
+                }
+                headerLists = Collections.unmodifiableMap(headerListsMutable);
+            }
+        }
+        return headerLists;
+    }
+    
+    public byte[] getContent() {
+        synchronized (mutex) {
+            if (content == null) {
+                InputStream in = null;
+                try {
+                    in = response.getEntity().getContent();
+                    ByteArrayOutputStream out = new ByteArrayOutputStream();
+                    ByteStreams.copy(in, out);
+                    content = out.toByteArray();
+                } catch (IOException e) {
+                    throw Throwables.propagate(e);
+                } finally {
+                    Streams.closeQuietly(in);
+                }
+            }
+        }
+        return content;
+    }
+
+    public String getContentAsString() {
+        return new String(getContent());
+    }
+    
+    public Maybe<HttpResponse> getResponse() {
+        return Maybe.fromNullable(response);
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(getClass())
+                .add("responseCode", responseCode)
+                .toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/03dbd281/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpUtils.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpUtils.java b/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpUtils.java
deleted file mode 100644
index 8eeef19..0000000
--- a/utils/common/src/main/java/org/apache/brooklyn/util/http/HttpUtils.java
+++ /dev/null
@@ -1,387 +0,0 @@
-/*
- * 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 org.apache.brooklyn.util.http;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLSession;
-
-import org.apache.brooklyn.test.Asserts;
-import org.apache.brooklyn.util.collections.MutableMap;
-import org.apache.brooklyn.util.crypto.SslTrustUtils;
-import org.apache.brooklyn.util.exceptions.Exceptions;
-import org.apache.brooklyn.util.stream.Streams;
-import org.apache.brooklyn.util.time.Time;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Throwables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-
-/**
- * Utility methods to aid testing HTTP.
- * 
- * @author aled
- */
-public class HttpUtils {
-
-    // TODO Delete methods from TestUtils, to just have them here (or switch so TestUtils delegates here,
-    // and deprecate methods in TestUtils until deleted).
-
-    private static final Logger LOG = LoggerFactory.getLogger(HttpUtils.class);
-
-    static final ExecutorService executor = Executors.newCachedThreadPool();
-    
-    /**
-     * Connects to the given url and returns the connection.
-     * Caller should {@code connection.getInputStream().close()} the result of this
-     * (especially if they are making heavy use of this method).
-     */
-    public static URLConnection connectToUrl(String u) throws Exception {
-        final URL url = new URL(u);
-        final AtomicReference<Exception> exception = new AtomicReference<Exception>();
-        
-        // sometimes openConnection hangs, so run in background
-        Future<URLConnection> f = executor.submit(new Callable<URLConnection>() {
-            public URLConnection call() {
-                try {
-                    HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
-                        @Override public boolean verify(String s, SSLSession sslSession) {
-                            return true;
-                        }
-                    });
-                    URLConnection connection = url.openConnection();
-                    TrustingSslSocketFactory.configure(connection);
-                    connection.connect();
-    
-                    connection.getContentLength(); // Make sure the connection is made.
-                    return connection;
-                } catch (Exception e) {
-                    exception.set(e);
-                    LOG.debug("Error connecting to url "+url+" (propagating): "+e, e);
-                }
-                return null;
-            }
-        });
-        try {
-            URLConnection result = null;
-            try {
-                result = f.get(60, TimeUnit.SECONDS);
-            } catch (InterruptedException e) {
-                throw e;
-            } catch (Exception e) {
-                LOG.debug("Error connecting to url "+url+", probably timed out (rethrowing): "+e);
-                throw new IllegalStateException("Connect to URL not complete within 60 seconds, for url "+url+": "+e);
-            }
-            if (exception.get() != null) {
-                LOG.debug("Error connecting to url "+url+", thread caller of "+exception, new Throwable("source of rethrown error "+exception));
-                throw exception.get();
-            } else {
-                return result;
-            }
-        } finally {
-            f.cancel(true);
-        }
-    }
-
-    public static void assertHealthyStatusCode(int code) {
-        if (code>=200 && code<=299) return;
-        Asserts.fail("Wrong status code: " + code);
-    }
-    
-    public static int getHttpStatusCode(String url) throws Exception {
-        URLConnection connection = connectToUrl(url);
-        long startTime = System.currentTimeMillis();
-        int status = ((HttpURLConnection) connection).getResponseCode();
-        
-        // read fully if possible, then close everything, trying to prevent cached threads at server
-        consumeAndCloseQuietly((HttpURLConnection) connection);
-        
-        if (LOG.isDebugEnabled())
-            LOG.debug("connection to {} ({}ms) gives {}", new Object[] { url, (System.currentTimeMillis()-startTime), status });
-        return status;
-    }
-
-    /**
-     * Asserts that gets back any "valid" response - i.e. not an exception. This could be an unauthorized,
-     * a redirect, a 404, or anything else that implies there is web-server listening on that port.
-     */
-    public static void assertUrlReachable(String url) {
-        try {
-            getHttpStatusCode(url);
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            throw new RuntimeException("Interrupted for "+url+" (in assertion that is reachable)", e);
-        } catch (Exception e) {
-            throw new IllegalStateException("Server at "+url+" Asserts.failed to respond (in assertion that is reachable): "+e, e);
-        }
-    }
-
-    public static void assertUrlUnreachable(String url) {
-        try {
-            int statusCode = getHttpStatusCode(url);
-            Asserts.fail("Expected url " + url + " unreachable, but got status code " + statusCode);
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            throw new RuntimeException("Interrupted for "+url+" (in assertion that unreachable)", e);
-        } catch (Exception e) {
-            IOException cause = Exceptions.getFirstThrowableOfType(e, IOException.class);
-            if (cause != null) {
-                // success; clean shutdown transitioning from 400 to error
-            } else {
-                Throwables.propagate(e);
-            }                        
-        }
-    }
-
-    public static void assertUrlUnreachableEventually(final String url) {
-        assertUrlUnreachableEventually(Maps.newLinkedHashMap(), url);
-    }
-    
-    public static void assertUrlUnreachableEventually(Map flags, final String url) {
-        Asserts.succeedsEventually(flags, new Runnable() {
-            public void run() {
-                assertUrlUnreachable(url);
-            }
-         });
-    }
-
-    public static void assertHttpStatusCodeEquals(String url, int... acceptableReturnCodes) {
-        List<Integer> acceptableCodes = Lists.newArrayList();
-        for (int code : acceptableReturnCodes) {
-            acceptableCodes.add((Integer)code);
-        }
-        try {
-            int actualCode = getHttpStatusCode(url);
-            Asserts.assertTrue(acceptableCodes.contains(actualCode), "code=" + actualCode + "; expected=" + acceptableCodes + "; url=" + url);
-            
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            throw new RuntimeException("Interrupted for "+url+" (in assertion that result code is "+acceptableCodes+")", e);
-        } catch (Exception e) {
-            throw new IllegalStateException("Server at "+url+" Asserts.failed to respond (in assertion that result code is "+acceptableCodes+"): "+e, e);
-        }
-    }
-
-    public static void assertHttpStatusCodeEventuallyEquals(final String url, final int expectedCode) {
-        assertHttpStatusCodeEventuallyEquals(Maps.newLinkedHashMap(),  url, expectedCode);
-    }
-
-    public static void assertHttpStatusCodeEventuallyEquals(Map flags, final String url, final int expectedCode) {
-        Asserts.succeedsEventually(flags, new Runnable() {
-            public void run() {
-                assertHttpStatusCodeEquals(url, expectedCode);
-            }
-         });
-    }
-
-    public static void assertContentContainsText(final String url, final String phrase, final String ...additionalPhrases) {
-        try {
-            String contents = getContent(url);
-            Asserts.assertTrue(contents != null && contents.length() > 0);
-            for (String text: Lists.asList(phrase, additionalPhrases)) {
-                if (!contents.contains(text)) {
-                    LOG.warn("CONTENTS OF URL "+url+" MISSING TEXT: "+text+"\n"+contents);
-                    Asserts.fail("URL "+url+" does not contain text: "+text);
-                }
-            }
-        } catch (Exception e) {
-            throw Throwables.propagate(e);
-        }
-    }
-
-    public static void assertContentNotContainsText(final String url, final String phrase, final String ...additionalPhrases) {
-        try {
-            String contents = getContent(url);
-            Asserts.assertTrue(contents != null);
-            for (String text: Lists.asList(phrase, additionalPhrases)) {
-                if (contents.contains(text)) {
-                    LOG.warn("CONTENTS OF URL "+url+" HAS TEXT: "+text+"\n"+contents);
-                    Asserts.fail("URL "+url+" contain text: "+text);
-                }
-            }
-        } catch (Exception e) {
-            throw Throwables.propagate(e);
-        }
-    }
-
-    public static void assertErrorContentContainsText(final String url, final String phrase, final String ...additionalPhrases) {
-        try {
-            String contents = getErrorContent(url);
-            Asserts.assertTrue(contents != null && contents.length() > 0);
-            for (String text: Lists.asList(phrase, additionalPhrases)) {
-                if (!contents.contains(text)) {
-                    LOG.warn("CONTENTS OF URL "+url+" MISSING TEXT: "+text+"\n"+contents);
-                    Asserts.fail("URL "+url+" does not contain text: "+text);
-                }
-            }
-        } catch (Exception e) {
-            throw Throwables.propagate(e);
-        }
-    }
-
-
-    public static void assertErrorContentNotContainsText(final String url, final String phrase, final String ...additionalPhrases) {
-        try {
-            String err = getErrorContent(url);
-            Asserts.assertTrue(err != null);
-            for (String text: Lists.asList(phrase, additionalPhrases)) {
-                if (err.contains(text)) {
-                    LOG.warn("CONTENTS OF URL "+url+" HAS TEXT: "+text+"\n"+err);
-                    Asserts.fail("URL "+url+" contain text: "+text);
-                }
-            }
-        } catch (Exception e) {
-            throw Throwables.propagate(e);
-        }
-    }
-    
-    public static void assertContentEventuallyContainsText(final String url, final String phrase, final String ...additionalPhrases) {
-        assertContentEventuallyContainsText(MutableMap.of(), url, phrase, additionalPhrases);
-    }
-    
-    public static void assertContentEventuallyContainsText(Map flags, final String url, final String phrase, final String ...additionalPhrases) {
-        Asserts.succeedsEventually(flags, new Runnable() {
-            public void run() {
-                assertContentContainsText(url, phrase, additionalPhrases);
-            }
-         });
-    }
-    
-    public static void assertContentMatches(String url, String regex) {
-        String contents = getContent(url);
-        Asserts.assertNotNull(contents);
-        Asserts.assertTrue(contents.matches(regex), "Contents does not match expected regex ("+regex+"): "+contents);
-    }
-
-    public static void assertContentEventuallyMatches(final String url, final String regex) {
-        Asserts.succeedsEventually(new Runnable() {
-            @Override
-            public void run() {
-                assertContentMatches(url, regex);
-            }
-        });
-    }
-    
-    public static String getErrorContent(String url) {
-        try {
-            HttpURLConnection connection = (HttpURLConnection) connectToUrl(url);
-            long startTime = System.currentTimeMillis();
-            
-            String err;
-            int status;
-            try {
-                InputStream errStream = connection.getErrorStream();
-                err = Streams.readFullyString(errStream);
-                status = connection.getResponseCode();
-            } finally {
-                closeQuietly(connection);
-            }
-            
-            if (LOG.isDebugEnabled())
-                LOG.debug("read of err {} ({}ms) complete; http code {}", new Object[] { url, Time.makeTimeStringRounded(System.currentTimeMillis()-startTime), status});
-            return err;
-
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-    
-    public static String getContent(String url) {
-        try {
-            return Streams.readFullyString(SslTrustUtils.trustAll(new URL(url).openConnection()).getInputStream());
-        } catch (Exception e) {
-            throw Throwables.propagate(e);
-        }
-    }
-
-    /**
-     * Schedules (with the given executor) a poller that repeatedly accesses the given url, to confirm it always gives
-     * back the expected status code.
-     * 
-     * Expected usage is to query the future, such as:
-     * 
-     * <pre>
-     * {@code
-     * Future<?> future = assertAsyncHttpStatusCodeContinuallyEquals(executor, url, 200);
-     * // do other stuff...
-     * if (future.isDone()) future.get(); // get exception if it's Asserts.failed
-     * }
-     * </pre>
-     * 
-     * For stopping it, you can either do future.cancel(true), or you can just do executor.shutdownNow().
-     * 
-     * TODO Look at difference between this and WebAppMonitor, to decide if this should be kept.
-     */
-    public static ListenableFuture<?> assertAsyncHttpStatusCodeContinuallyEquals(ListeningExecutorService executor, final String url, final int expectedStatusCode) {
-        return executor.submit(new Runnable() {
-            @Override public void run() {
-                // TODO Need to drop logging; remove sleep when that's done.
-                while (!Thread.currentThread().isInterrupted()) {
-                    assertHttpStatusCodeEquals(url, expectedStatusCode);
-                    try {
-                        Thread.sleep(1000);
-                    } catch (InterruptedException e) {
-                        return; // graceful return
-                    }
-                }
-            }
-        });
-    }
-    
-    /**
-     * Consumes the input stream entirely and then cleanly closes the connection.
-     * Ignores all exceptions completely, not even logging them!
-     * 
-     * Consuming the stream fully is useful for preventing idle TCP connections.
-     * @see <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/net/http-keepalive.html">Persistent Connections</a>
-     */
-    public static void consumeAndCloseQuietly(HttpURLConnection connection) {
-        try { Streams.readFully(connection.getInputStream()); } catch (Exception e) {}
-        closeQuietly(connection);
-    }
-    
-    /**
-     * Closes all streams of the connection, and disconnects it. Ignores all exceptions completely,
-     * not even logging them!
-     */
-    public static void closeQuietly(HttpURLConnection connection) {
-        try { connection.disconnect(); } catch (Exception e) {}
-        try { connection.getInputStream().close(); } catch (Exception e) {}
-        try { connection.getOutputStream().close(); } catch (Exception e) {}
-        try { connection.getErrorStream().close(); } catch (Exception e) {}
-    }
-}


Mime
View raw message