com.zimbra.qa.unittest.TestZimbraHttpConnectionManager.java Source code

Java tutorial

Introduction

Here is the source code for com.zimbra.qa.unittest.TestZimbraHttpConnectionManager.java

Source

/*
 * ***** BEGIN LICENSE BLOCK *****
 * Zimbra Collaboration Suite Server
 * Copyright (C) 2009, 2010, 2011, 2013, 2014, 2016 Synacor, Inc.
 *
 * This program 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,
 * version 2 of the License.
 *
 * This program 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 this program.
 * If not, see <https://www.gnu.org/licenses/>.
 * ***** END LICENSE BLOCK *****
 */
package com.zimbra.qa.unittest;

/*
 * for SimpleHttpServer
 */
import java.net.*;
import java.io.*;
import java.util.*;

import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;

import com.zimbra.common.account.Key;
import com.zimbra.common.account.Key.DomainBy;
import com.zimbra.common.httpclient.HttpClientUtil;
import com.zimbra.common.localconfig.LC;
import com.zimbra.common.mime.MimeConstants;
import com.zimbra.common.service.ServiceException;
import com.zimbra.common.soap.AdminConstants;
import com.zimbra.common.util.ByteUtil;
import com.zimbra.common.util.CliUtil;
import com.zimbra.common.util.Constants;
import com.zimbra.common.util.ZimbraHttpConnectionManager;
import com.zimbra.cs.account.soap.SoapProvisioning;

public class TestZimbraHttpConnectionManager {

    @BeforeClass
    public static void init() throws Exception {
        CliUtil.toolSetup("INFO");
    }

    public static void dumpResponse(int respCode, HttpMethod method, String prefix) throws IOException {

        prefix = prefix + " - ";

        // status
        int statusCode = method.getStatusCode();
        String statusLine = method.getStatusLine().toString();

        System.out.println(prefix + "respCode=" + respCode);
        System.out.println(prefix + "statusCode=" + statusCode);
        System.out.println(prefix + "statusLine=" + statusLine);

        // headers
        System.out.println(prefix + "Headers");
        Header[] respHeaders = method.getResponseHeaders();
        for (int i = 0; i < respHeaders.length; i++) {
            String header = respHeaders[i].toString();
            // trim the CRLF at the end to save space
            System.out.println(prefix + header.trim());
        }

        // body
        byte[] bytes = ByteUtil.getContent(method.getResponseBodyAsStream(), 0);
        System.out.println(prefix + bytes.length + " bytes read");
        System.out.println(new String(bytes));
    }

    /**
     * A thread that performs a GET.
     */
    private static class TestGetThread extends Thread {

        private HttpClient mHttpClient;
        private GetMethod mMethod;
        private int mId;

        public TestGetThread(HttpClient httpClient, GetMethod method, int id) {
            mHttpClient = httpClient;
            mMethod = method;
            mId = id;
        }

        /**
         * Executes the GetMethod and prints some status information.
         */
        public void run() {
            long startTime = System.currentTimeMillis();
            long endTime;

            try {
                System.out.println(mId + " - about to get something from " + mMethod.getURI());
                // execute the method
                int respCode = HttpClientUtil.executeMethod(mHttpClient, mMethod);

                System.out.println(mId + " - get executed");
                // get the response body as an array of bytes
                // byte[] bytes = method.getResponseBody();
                // dumpResponse(respCode, mMethod, Integer.valueOf(mId).toString());

            } catch (Exception e) {
                System.out.println(mId + " - error: " + e);
                e.printStackTrace();
            } finally {

                endTime = System.currentTimeMillis();
                long elapsedTime = endTime - startTime;
                System.out.println("Finished, elapsedTime=" + elapsedTime + " milli seconds");

                // always release the connection after we're done 
                mMethod.releaseConnection();
                System.out.println(mId + " - connection released");
            }
        }
    }

    /* This test has to be run manually.
     *    
     * before running this test, do:
          
      zmlocalconfig -e httpclient_connmgr_idle_reaper_sleep_interval_external=10000
      zmlocalconfig -e httpclient_connmgr_idle_reaper_connection_timeout_external=2000
          
    */
    // @Test
    public void testReaper() throws Exception {

        // create an array of URIs to perform GETs on
        String[] urisToGet = { "http://localhost:7070/zimbra/public/empty.html",
                "http://localhost:7071/service/admin/soap",
                /*
                "http://hc.apache.org:80/",
                "http://hc.apache.org:80/httpclient-3.x/status.html",
                "http://hc.apache.org:80/httpclient-3.x/methods/",
                "http://svn.apache.org/viewvc/httpcomponents/oac.hc3x/"
                */
        };

        ZimbraHttpConnectionManager connMgr = ZimbraHttpConnectionManager.getExternalHttpConnMgr();

        // create a thread for each URI
        TestGetThread[] threads = new TestGetThread[urisToGet.length];
        for (int i = 0; i < threads.length; i++) {
            GetMethod get = new GetMethod(urisToGet[i]);
            get.setFollowRedirects(true);
            threads[i] = new TestGetThread(connMgr.newHttpClient(), get, i + 1);
        }

        ZimbraHttpConnectionManager.startReaperThread(); // comment out to reproduce the CLOSE_WAIT

        // start the threads
        for (int j = 0; j < threads.length; j++) {
            threads[j].start();
        }

        /*
         * This test has to be run manually.
         * 
         * not sure how to automate this:
         * 
         * if ZimbraHttpConnectionManager.startReaperThread() was run:
         * after httpclient_connmgr_idle_reaper_sleep_interval,
         * netstat | grep CLOSE_WAIT | grep apache
         * should print nothing
         * 
         * if ZimbraHttpConnectionManager.startReaperThread() is *not* running:
         * netstat | grep CLOSE_WAIT | grep apache
         * will show:
         * tcp4       0      0  goodbyewhen-lm.c.62910 eos.apache.org.http    CLOSE_WAIT
         * tcp4       0      0  goodbyewhen-lm.c.62909 eos.apache.org.http    CLOSE_WAIT
         * tcp4       0      0  goodbyewhen-lm.c.62908 eris.apache.org.http   CLOSE_WAIT
         * tcp4       0      0  goodbyewhen-lm.c.62907 eos.apache.org.http    CLOSE_WAIT
         * 
         * for very long time.
         */

        // wait, so we can watch the CLOSE_WAIT going away
        Thread.sleep(Constants.MILLIS_PER_HOUR);
    }

    private static class SimpleHttpServer implements Runnable {

        // server control vars
        private static Thread mServerThread = null;
        private static SimpleHttpServer sServer = null;

        // server vars
        private int mPort;
        private ServerSocket mServerSocket;
        private boolean mShutdownRequested = false;

        private enum DelayWhen {
            BEFORE_READING_REQ_LINE, BEFORE_READING_HEADERS, GET_BEFORE_FETCHING_RESOURCE, POST_BEFORE_READING_BODY, BEFORE_WRITING_RESPONSE_HEADERS, BEFORE_WRITING_RESPONSE_BODY, DURING_WRITING_RESPONSE_BODY, AFTER_WRITING_RESPONSE_BODY
        }

        private synchronized static void start(int port) {
            if (mServerThread != null) {
                log("start server: server already started");
                return;
            }

            log("starting server");
            sServer = new SimpleHttpServer(port);
            mServerThread = new Thread(sServer);
            mServerThread.start();
        }

        private synchronized static void shutdown() {
            if (mServerThread == null) {
                log("shutdown server: server is not running");
                return;
            }

            sServer.requestShutdown();
            // mServerThread.interrupt();  It turns out that the ServerSocket.accept() is not interruptible.  Just use the close socket hack.
            mServerThread = null;
        }

        private SimpleHttpServer(int port) {
            mPort = port;
        }

        private synchronized void requestShutdown() {
            mShutdownRequested = true;
            try {
                mServerSocket.close();
            } catch (IOException e) {
                // just what we expect
            }
        }

        private static void log(String msg) {
            System.out.println("*** SimpleHttpServer: " + msg);
        }

        public void run() {
            try {
                //print out the port number for user
                mServerSocket = new ServerSocket(mPort);
                log("started on port " + mServerSocket.getLocalPort());

                // server infinite loop
                while (true && !mShutdownRequested) {
                    Socket socket = mServerSocket.accept();
                    log("new connection accepted " + socket.getInetAddress() + ":" + socket.getPort());

                    // Construct handler to process the HTTP request message.
                    try {
                        HttpRequestHandler request = new HttpRequestHandler(socket);
                        // Create a new thread to process the request.
                        Thread thread = new Thread(request);
                        // Start the thread.
                        thread.start();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            } catch (IOException e) {
                if (!mShutdownRequested)
                    e.printStackTrace();
                // else, the IOException is expected
            } finally {
                log("exiting server");
            }
        }
    }

    private static class HttpRequestHandler implements Runnable {

        final static String CRLF = "\r\n";

        private static final String SERVER_LINE = "Server: brain dead java httpServer" + CRLF;
        private static final String STATUS_LINE_200 = "HTTP/1.0 200 OK" + CRLF;
        private static final String STATUS_LINE_404 = "HTTP/1.0 404 Not Found" + CRLF;

        private static final String HEADER_CONTENT_LENGTH = "Content-Length";
        private static final String HEADER_CONTENT_TYPE = "Content-Type";

        Socket socket;
        InputStream input;
        OutputStream output;
        BufferedReader reader;

        private String url;
        private Map<String, String> headers = new HashMap<String, String>();
        private Map<String, String> queryParams = new HashMap<String, String>();

        private HttpRequestHandler(Socket socket) throws Exception {
            this.socket = socket;
            this.input = socket.getInputStream();
            this.output = socket.getOutputStream();
            this.reader = new BufferedReader(new InputStreamReader(input));
        }

        // Implement the run() method of the Runnable interface.
        public void run() {
            try {
                processRequest();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        private void getHeaders() throws Exception {
            delayIfRequested(SimpleHttpServer.DelayWhen.BEFORE_READING_HEADERS);

            String header;

            while (true) {
                header = reader.readLine();

                if (header.equals(CRLF) || header.equals("")) {
                    break;
                }

                String[] parts = header.split(":");
                headers.put(parts[0].trim(), parts[1].trim());
            }

        }

        private void parseRequestLine(String req) {
            String[] parts = req.split("\\?");
            url = parts[0];

            String qp = null;
            long waitInServer = 0;
            if (parts.length == 2) {
                qp = parts[1];

                String[] params = qp.split("&");
                for (String param : params) {
                    String[] nameValue = param.split("=");
                    queryParams.put(nameValue[0], nameValue[1]);
                }
            }
        }

        private void delayIfRequested(SimpleHttpServer.DelayWhen delayWhen) throws Exception {
            String delay = queryParams.get(delayWhen.name());
            if (delay != null) {
                int delayMilliSecs = Integer.valueOf(delay);
                if (delayMilliSecs != 0) {
                    SimpleHttpServer.log("Delaying " + delay + " milli seconds: " + delayWhen.name());
                    Thread.sleep(delayMilliSecs);
                }
            }
        }

        private static void sendBytes(FileInputStream fis, OutputStream os) throws Exception {
            // Construct a 1K buffer to hold bytes on their way to the socket.
            byte[] buffer = new byte[1024];
            int bytes = 0;

            // Copy requested file into the socket's output stream.
            while ((bytes = fis.read(buffer)) != -1) {
                os.write(buffer, 0, bytes);
            }
        }

        private void doGet() throws Exception {

            delayIfRequested(SimpleHttpServer.DelayWhen.GET_BEFORE_FETCHING_RESOURCE);

            // Open the requested file.
            String fileName = url;
            FileInputStream fis = null;
            boolean fileExists = true;
            try {
                fis = new FileInputStream(fileName);
            } catch (FileNotFoundException e) {
                fileExists = false;
            }

            String statusLine = null;
            String contentTypeLine = null;
            String entityBody = null;
            String contentLengthLine = null;
            if (fileExists) {
                statusLine = STATUS_LINE_200;
                contentTypeLine = HEADER_CONTENT_TYPE + ": " + "text/plain" + CRLF;
                contentLengthLine = HEADER_CONTENT_LENGTH + ": " + (new Integer(fis.available())).toString() + CRLF;
            } else {
                statusLine = STATUS_LINE_404;
                contentTypeLine = HEADER_CONTENT_TYPE + ": " + "text/html" + CRLF;
                entityBody = "<HTML>" + "<HEAD><TITLE>404 Not Found</TITLE></HEAD>" + "<BODY>404 Not Found"
                        + "<br>File " + fileName + "</BODY></HTML>";

                contentLengthLine = HEADER_CONTENT_LENGTH + ": " + +entityBody.length() + CRLF;
            }

            delayIfRequested(SimpleHttpServer.DelayWhen.BEFORE_WRITING_RESPONSE_HEADERS);

            // Send the status line.
            output.write(statusLine.getBytes());

            // Send the server line.
            output.write(SERVER_LINE.getBytes());

            // Send the content type line.
            output.write(contentTypeLine.getBytes());

            // Send the Content-Length
            output.write(contentLengthLine.getBytes());

            // Send a blank line to indicate the end of the header lines.
            output.write(CRLF.getBytes());

            delayIfRequested(SimpleHttpServer.DelayWhen.BEFORE_WRITING_RESPONSE_BODY);

            // Send the entity body.
            if (fileExists) {
                sendBytes(fis, output);
                fis.close();
            } else {
                output.write(entityBody.getBytes());
            }

            delayIfRequested(SimpleHttpServer.DelayWhen.AFTER_WRITING_RESPONSE_BODY);
        }

        private void doPost() throws Exception {

            delayIfRequested(SimpleHttpServer.DelayWhen.POST_BEFORE_READING_BODY);

            int contentLength = Integer.valueOf(headers.get(HEADER_CONTENT_LENGTH));
            int firstHalfLen = contentLength / 2;
            int secondHalfLen = contentLength - firstHalfLen;

            byte[] bodyFirstHalf = new byte[firstHalfLen];
            input.read(bodyFirstHalf);

            // wait while reading
            // waitIfRequested();

            byte[] bodySecondHalf = new byte[secondHalfLen];
            input.read(bodySecondHalf);

            String entityBody = "all is well!";
            String contentTypeLine = HEADER_CONTENT_TYPE + ": " + "text/plain" + CRLF;
            String contentLengthLine = HEADER_CONTENT_LENGTH + ": " + entityBody.getBytes().length + CRLF;

            delayIfRequested(SimpleHttpServer.DelayWhen.BEFORE_WRITING_RESPONSE_HEADERS);

            // Send the status line.
            output.write(STATUS_LINE_200.getBytes());

            // Send the server line.
            output.write(SERVER_LINE.getBytes());

            // Send the content type line.
            output.write(contentTypeLine.getBytes());

            // Send the Content-Length
            output.write(contentLengthLine.getBytes());

            // Send a blank line to indicate the end of the header lines.
            output.write(CRLF.getBytes());

            delayIfRequested(SimpleHttpServer.DelayWhen.BEFORE_WRITING_RESPONSE_BODY);

            // Send the entity body.
            output.write(entityBody.getBytes());

            delayIfRequested(SimpleHttpServer.DelayWhen.AFTER_WRITING_RESPONSE_BODY);
        }

        private void processRequest() throws Exception {

            while (true) {
                delayIfRequested(SimpleHttpServer.DelayWhen.BEFORE_READING_REQ_LINE);
                String reqLine = reader.readLine();
                SimpleHttpServer.log(reqLine);
                if (reqLine.equals(CRLF) || reqLine.equals("")) {
                    break;
                }

                getHeaders();

                StringTokenizer s = new StringTokenizer(reqLine);
                String method = s.nextToken();

                String req = s.nextToken();
                parseRequestLine(req);

                if (method.equals("GET")) {
                    doGet();
                } else if (method.equals("POST")) {
                    doPost();
                }

                // our thread only process one request  :)
                break;
            }

            try {
                output.close();
                reader.close();
                socket.close();
            } catch (Exception e) {
            }
        }

    }

    // @Test
    public void testSoTimeoutViaHttpMethod() throws Exception {

        int serverPort = 7778;
        String resourceToGet = "/opt/zimbra/unittest/rights-unittest.xml";
        long delayInServer = 10000; // delay 10 seconds in server
        int soTimeout = 3000; // 3 seconds

        String qp = "?" + SimpleHttpServer.DelayWhen.BEFORE_WRITING_RESPONSE_HEADERS.name() + "=" + delayInServer;
        String uri = "http://localhost:" + serverPort + resourceToGet + qp;

        // start a http server for testing
        SimpleHttpServer.start(serverPort);

        // HttpClient httpClient = ZimbraHttpConnectionManager.getExternalHttpConnMgr().newHttpClient();
        HttpClient httpClient = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();

        GetMethod method = new GetMethod(uri);

        // method.getParams().setParameter(HttpConnectionParams.SO_TIMEOUT, Integer.valueOf(soTimeout));
        method.getParams().setSoTimeout(soTimeout);

        long startTime = System.currentTimeMillis();
        long endTime;
        try {
            // int respCode = HttpClientUtil.executeMethod(httpClient, method);
            int respCode = httpClient.executeMethod(method);

            dumpResponse(respCode, method, "");
            Assert.fail(); // nope, it should have timed out
        } catch (java.net.SocketTimeoutException e) {
            // good, just what we want
            endTime = System.currentTimeMillis();
            long elapsedTime = endTime - startTime;
            System.out.println("Client timed out after " + elapsedTime + " msecs");
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail();
        } finally {
            method.releaseConnection();
        }

        // shutdown the server
        SimpleHttpServer.shutdown();
    }

    /*
     * Before running this test:
     * 
     * zmlocalconfig -e httpclient_connmgr_so_timeout_external=3000  (3 seconds)
     */
    // @Test
    public void testSoTimeoutViaConnMgrParam() throws Exception {

        int serverPort = 7778;
        String resourceToGet = "/opt/zimbra/unittest/rights-unittest.xml";
        long delayInServer = 10000; // delay 10 seconds in server
        int soTimeout = 3000; // 3 seconds

        String qp = "?" + SimpleHttpServer.DelayWhen.BEFORE_WRITING_RESPONSE_HEADERS.name() + "=" + delayInServer;
        String uri = "http://localhost:" + serverPort + resourceToGet + qp;

        // start a http server for testing
        SimpleHttpServer.start(serverPort);

        HttpClient httpClient = ZimbraHttpConnectionManager.getExternalHttpConnMgr().newHttpClient();

        GetMethod method = new GetMethod(uri);

        long startTime = System.currentTimeMillis();
        long endTime;
        try {
            // int respCode = HttpClientUtil.executeMethod(httpClient, method);
            int respCode = httpClient.executeMethod(method);

            dumpResponse(respCode, method, "");
            Assert.fail(); // nope, it should have timed out
        } catch (java.net.SocketTimeoutException e) {
            // good, just what we want
            endTime = System.currentTimeMillis();
            long elapsedTime = endTime - startTime;
            System.out.println("Client timed out after " + elapsedTime + " msecs");
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail();
        } finally {
            method.releaseConnection();
        }

        // shutdown the server
        SimpleHttpServer.shutdown();
    }

    // @Test
    public void testSoTimeoutViaHttpPostMethod() throws Exception {

        int serverPort = 7778;
        String resourceToPost = "/opt/zimbra/unittest/rights-unittest.xml";
        long delayInServer = 100000; // delay 10 seconds in server
        int soTimeout = 60000; // 3000;  // 3 seconds, 0 for infinite wait

        String qp = "?" + SimpleHttpServer.DelayWhen.BEFORE_WRITING_RESPONSE_HEADERS.name() + "=" + delayInServer;
        String uri = "http://localhost:" + serverPort + resourceToPost + qp;

        // start a http server for testing
        SimpleHttpServer.start(serverPort);

        // post the exported content to the target server
        HttpClient httpClient = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
        PostMethod method = new PostMethod(uri);
        method.getParams().setSoTimeout(soTimeout); // infinite wait because it can take a long time to import a large mailbox

        File file = new File(resourceToPost);
        FileInputStream fis = null;

        long startTime = System.currentTimeMillis();
        long endTime;
        try {
            fis = new FileInputStream(file);
            InputStreamRequestEntity isre = new InputStreamRequestEntity(fis, file.length(),
                    MimeConstants.CT_APPLICATION_OCTET_STREAM);
            method.setRequestEntity(isre);
            int respCode = httpClient.executeMethod(method);

            dumpResponse(respCode, method, "");
            Assert.fail(); // nope, it should have timed out
        } catch (java.net.SocketTimeoutException e) {
            e.printStackTrace();
            // good, just what we want
            endTime = System.currentTimeMillis();
            long elapsedTime = endTime - startTime;
            System.out.println("Client timed out after " + elapsedTime + " msecs");
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail();
        } finally {
            method.releaseConnection();
        }

        // shutdown the server
        SimpleHttpServer.shutdown();
    }

    /*
     * before running this test:
     * 
     * zmlocalconfig -e httpclient_connmgr_max_total_connections_external=1  // the connection manager can only hand out one connection
     * zmlocalconfig -e httpclient_client_connection_timeout_external=5000   // time to get a connection from the connection manager
     */
    // @Test
    public void testHttpClientConnectionManagerTimeout() throws Exception {

        int serverPort = 7778;
        String path = "/Users/pshao/p4/main/ZimbraServer/src/java/com/zimbra/qa/unittest/TestZimbraHttpConnectionManager.java"; // this file
        long delayInServer = 10000;
        String qp = "?" + SimpleHttpServer.DelayWhen.GET_BEFORE_FETCHING_RESOURCE.name() + "=" + delayInServer;

        // start a server for testing
        SimpleHttpServer.start(serverPort);

        ZimbraHttpConnectionManager connMgr = ZimbraHttpConnectionManager.getExternalHttpConnMgr();

        // first thread
        GetMethod method1 = new GetMethod("http://localhost:" + serverPort + path + qp);
        TestGetThread thread1 = new TestGetThread(connMgr.newHttpClient(), method1, 1);
        thread1.start(); // this thread will hog the only one connection this conn mgr can offer for 10 seconds

        Thread.sleep(1000); // wait one second let thread one get a head start to grab the one and only connection

        // second thread
        GetMethod method2 = new GetMethod("http://localhost:" + serverPort + path + qp);
        TestGetThread thread2 = new TestGetThread(connMgr.newHttpClient(), method1, 2);
        thread2.start(); // this thread should timeout (ConnectionPoolTimeoutException) after 5000 milli seconds, 
                         // because zmlocalconfig -e httpclient_client_connection_timeout_external=5000 

        Thread.sleep(60000); // wait a little so we can observe the threads run

        // shutdown the server
        SimpleHttpServer.shutdown();
    }

    private static void runSoapProv(String msg) {
        System.out.println(msg);
        SoapProvisioning sp = new SoapProvisioning();
        String uri = LC.zimbra_admin_service_scheme.value() + LC.zimbra_zmprov_default_soap_server.value() + ":"
                + LC.zimbra_admin_service_port.intValue() + AdminConstants.ADMIN_SERVICE_URI;
        sp.soapSetURI(uri);
        try {
            sp.getDomainInfo(Key.DomainBy.name, "phoebe.mac");
        } catch (ServiceException e) {
            e.printStackTrace();
        }
    }

    private static class SoapProvThread extends Thread {

        private String mId;

        public SoapProvThread(String id) {
            mId = id;
        }

        public void run() {
            runSoapProv(mId);
            try {
                // wait 1 hour
                Thread.sleep(3600000);
            } catch (InterruptedException e) {
            }
        }
    }

    private void runSoapProvParallel(int num) {
        for (int i = 0; i < num; i++) {
            SoapProvThread thread = new SoapProvThread(Integer.valueOf(i).toString());
            thread.start();
        }
    }

    private void runSoapProvSerial(int num) {
        for (int i = 0; i < num; i++) {
            runSoapProv(Integer.valueOf(i).toString());
        }
    }

    // Test
    public void testSoapProv() throws Exception {
        // runSoapProvSerial(3);
        runSoapProvParallel(3);

    }

    private void runTest(HttpClient httpClient, String id, boolean authPreemp) {

        GetMethod method = new GetMethod("http://localhost:7070/");

        long startTime = System.currentTimeMillis();
        long endTime;

        try {
            System.out.println(id + " - about to get something from " + method.getURI());
            // execute the method

            if (authPreemp) {
                httpClient.getParams().setAuthenticationPreemptive(true);
            }

            int respCode = HttpClientUtil.executeMethod(httpClient, method);

            System.out.println(id + " - get executed");
            // get the response body as an array of bytes
            // byte[] bytes = method.getResponseBody();
            // dumpResponse(respCode, mMethod, Integer.valueOf(mId).toString());

        } catch (Exception e) {
            System.out.println(id + " - error: " + e);
            e.printStackTrace();
        } finally {

            endTime = System.currentTimeMillis();
            long elapsedTime = endTime - startTime;
            // System.out.println(id + " - Finished, elapsedTime=" + elapsedTime + " milli seconds");

            // always release the connection after we're done 
            method.releaseConnection();
            System.out.println(id + " - connection released");
        }
    }

    // bug 47488
    // @Test
    public void testAuthenticationPreemptive() throws Exception {

        ZimbraHttpConnectionManager.startReaperThread();

        for (int i = 0; i < 10; i++) {
            // runTest(new HttpClient(), "PLAIN"+i, true);
            runTest(ZimbraHttpConnectionManager.getExternalHttpConnMgr().newHttpClient(), "EXT-authPreemp" + i,
                    true);
            runTest(ZimbraHttpConnectionManager.getExternalHttpConnMgr().newHttpClient(), "EXT" + i, false);
            runTest(ZimbraHttpConnectionManager.getInternalHttpConnMgr().getDefaultHttpClient(), "INT" + i, false);
        }
    }

    @Test
    public void junk() throws Exception {

        String uri = "http://phoebe.mbp:7070/service/soap/AuthRequest";

        HttpClient httpClient = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();

        GetMethod method = new GetMethod(uri);

        try {
            // int respCode = HttpClientUtil.executeMethod(httpClient, method);
            int respCode = httpClient.executeMethod(method);

            dumpResponse(respCode, method, "");
            Assert.fail(); // nope, it should have timed out
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail();
        } finally {
            method.releaseConnection();
        }

    }

}