/* * $Header: /home/data/cvs/rt/org.eclipse.ecf/tests/bundles/org.eclipse.ecf.tests.apache.httpclient.server/src/org/apache/commons/httpclient/TestHttpConnectionManager.java,v 1.1 2009/02/13 18:07:49 slewis Exp $ * $Revision: 1.1 $ * $Date: 2009/02/13 18:07:49 $ * ==================================================================== * * Copyright 1999-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package org.apache.commons.httpclient; import java.io.IOException; import java.lang.ref.WeakReference; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import junit.framework.Test; import junit.framework.TestSuite; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.params.HttpConnectionManagerParams; import org.apache.commons.httpclient.params.HttpConnectionParams; import org.apache.commons.httpclient.protocol.Protocol; import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; import org.apache.commons.httpclient.server.SimpleRequest; import org.apache.commons.httpclient.server.SimpleResponse; /** * Unit tests for {@link HttpConnectionManager}. * * @author Marc A. Saegesser * @version $Id: TestHttpConnectionManager.java,v 1.1 2009/02/13 18:07:49 slewis Exp $ */ public class TestHttpConnectionManager extends HttpClientTestBase { // ------------------------------------------------------------ Constructor public TestHttpConnectionManager(String testName) throws IOException { super(testName); } // ------------------------------------------------------------------- Main public static void main(String args[]) { String[] testCaseName = { TestHttpConnectionManager.class.getName() }; junit.textui.TestRunner.main(testCaseName); } // ------------------------------------------------------- TestCase Methods public static Test suite() { return new TestSuite(TestHttpConnectionManager.class); } // ----------------------------------------------------------- Test Methods /** * Test that the ConnectMethod correctly releases connections when * CONNECT fails. */ public void testConnectMethodFailureRelease() throws Exception { MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager(); mgr.getParams().setIntParameter( HttpConnectionManagerParams.MAX_TOTAL_CONNECTIONS, 1); client.setHttpConnectionManager(mgr); this.server.setHttpService(new RejectConnectService()); // we're going to execute a connect method against the localhost, assuming // that CONNECT is not supported. This should test the fakeResponse() // code on HttpMethodBase. client.getHostConfiguration().setProxy(server.getLocalAddress(), server.getLocalPort()); // we must set the host to a secure destination or the CONNECT method // will not be used client.getHostConfiguration().setHost( "notARealHost", 1234, new Protocol( "https", (ProtocolSocketFactory)new FakeSecureProtocolSocketFactory(), 443) ); GetMethod get = new GetMethod("/"); try { assertTrue(client.executeMethod(get) != 200); } catch (IOException e) { e.printStackTrace(); fail("Error executing connect: " + e); } // this should calling releaseConnection() releases the connection try { get.releaseConnection(); mgr.getConnectionWithTimeout(client.getHostConfiguration(), 1).releaseConnection(); } catch (ConnectTimeoutException e1) { fail("Connection should have been available."); } get = new GetMethod("/"); try { assertTrue(client.executeMethod(get) != 200); } catch (IOException e) { e.printStackTrace(); fail("Error executing connect: " + e); } // make sure reading the response fully releases the connection try { get.getResponseBodyAsString(); mgr.getConnectionWithTimeout(client.getHostConfiguration(), 1).releaseConnection(); } catch (ConnectTimeoutException e1) { fail("Connection should have been available."); } get = new GetMethod("/"); try { assertTrue(client.executeMethod(get) != 200); } catch (IOException e) { e.printStackTrace(); fail("Error executing connect: " + e); } // make sure closing the output stream releases the connection try { get.getResponseBodyAsStream().close(); mgr.getConnectionWithTimeout(client.getHostConfiguration(), 1).releaseConnection(); } catch (ConnectTimeoutException e) { fail("Connection should have been available."); } catch (IOException e) { e.printStackTrace(); fail("Close connection failed: " + e); } } public void testGetConnection() { MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager(); HostConfiguration hostConfiguration = new HostConfiguration(); hostConfiguration.setHost("www.nosuchserver.com", 80, "http"); // Create a new connection HttpConnection conn = mgr.getConnection(hostConfiguration); // Validate the connection properties assertEquals("Host", "www.nosuchserver.com", conn.getHost()); assertEquals("Port", 80, conn.getPort()); // Release the connection mgr.releaseConnection(conn); // Create a new connection hostConfiguration.setHost("www.nosuchserver.com", -1, "https"); conn = mgr.getConnection(hostConfiguration); // Validate the connection properties assertEquals("Host", "www.nosuchserver.com", conn.getHost()); assertEquals("Port", 443, conn.getPort()); // Release the connection mgr.releaseConnection(conn); // Create a new connection hostConfiguration.setHost("www.nowhere.org", 8080, "http"); conn = mgr.getConnection(hostConfiguration); // Validate the connection properties assertEquals("Host", "www.nowhere.org", conn.getHost()); assertEquals("Port", 8080, conn.getPort()); // Release the connection mgr.releaseConnection(conn); } public void testDroppedThread() throws Exception { this.server.setHttpService(new EchoService()); MultiThreadedHttpConnectionManager mthcm = new MultiThreadedHttpConnectionManager(); client.setHttpConnectionManager(mthcm); WeakReference wr = new WeakReference(mthcm); GetMethod method = new GetMethod("/"); client.executeMethod(method); method.releaseConnection(); mthcm = null; client = null; method = null; System.gc(); // this sleep appears to be necessary in order to give the JVM // time to clean up the miscellaneous pointers to the connection manager try { Thread.sleep(1000); } catch (InterruptedException e) { fail("shouldn't be interrupted."); } Object connectionManager = wr.get(); assertNull("connectionManager should be null", connectionManager); } public void testWriteRequestReleaseConnection() { MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); connectionManager.getParams().setDefaultMaxConnectionsPerHost(1); client.setHttpConnectionManager(connectionManager); GetMethod get = new GetMethod("/") { protected boolean writeRequestBody(HttpState state, HttpConnection conn) throws IOException, HttpException { throw new IOException("Oh no!!"); } }; try { client.executeMethod(get); fail("An exception should have occurred."); } catch (HttpException e) { e.printStackTrace(); fail("HttpException should not have occurred: " + e); } catch (IOException e) { // expected } try { connectionManager.getConnectionWithTimeout(client.getHostConfiguration(), 1); } catch (ConnectTimeoutException e) { e.printStackTrace(); fail("Connection was not released: " + e); } } public void testReleaseConnection() { this.server.setHttpService(new EchoService()); MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); connectionManager.getParams().setDefaultMaxConnectionsPerHost(1); client.setHttpConnectionManager(connectionManager); // we shouldn't have to wait if a connection is available client.getParams().setConnectionManagerTimeout(1); GetMethod getMethod = new GetMethod("/"); try { client.executeMethod(getMethod); } catch (Exception e) { fail("error reading from server: " + e); } try { // this should fail quickly since the connection has not been released client.executeMethod(getMethod); fail("a httpConnection should not be available"); } catch (ConnectTimeoutException e) { } catch (HttpException e) { fail("error reading from server; " + e); } catch (IOException e) { e.printStackTrace(); fail("error reading from server; " + e); } // this should release the connection getMethod.releaseConnection(); getMethod = new GetMethod("/"); try { // this should fail quickly if the connection has not been released client.executeMethod(getMethod); } catch (HttpException e) { fail("httpConnection does not appear to have been released: " + e); } catch (IOException e) { fail("error reading from server; " + e); } } /** * Makes sure that a connection gets released after the content of the body * is read. */ public void testResponseAutoRelease() throws Exception { this.server.setHttpService(new EchoService()); MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); connectionManager.getParams().setDefaultMaxConnectionsPerHost(1); client.setHttpConnectionManager(connectionManager); // we shouldn't have to wait if a connection is available client.getParams().setConnectionManagerTimeout( 1 ); GetMethod getMethod = new GetMethod("/"); try { client.executeMethod(getMethod); } catch (Exception e) { fail("error reading from server: " + e); } // this should release the connection getMethod.getResponseBody(); getMethod = new GetMethod("/"); try { // this should fail quickly if the connection has not been released client.executeMethod(getMethod); } catch (HttpException e) { fail("httpConnection does not appear to have been released: " + e); } catch (IOException e) { fail("error reading from server; " + e); } } /** * Tests the MultiThreadedHttpConnectionManager's ability to reclaim unused * connections. */ public void testConnectionReclaiming() { MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); connectionManager.getParams().setDefaultMaxConnectionsPerHost(1); connectionManager.getParams().setMaxTotalConnections(1); HostConfiguration host1 = new HostConfiguration(); host1.setHost("host1", -1, "http"); HostConfiguration host2 = new HostConfiguration(); host2.setHost("host2", -1, "http"); HttpConnection connection = connectionManager.getConnection(host1); // now release this connection connection.releaseConnection(); connection = null; try { // the connection from host1 should be reclaimed connection = connectionManager.getConnectionWithTimeout(host2, 100); } catch (ConnectTimeoutException e) { e.printStackTrace(); fail("a httpConnection should have been available: " + e); } } /** * Tests that {@link MultiThreadedHttpConnectionManager#shutdownAll()} closes all resources * and makes all connection mangers unusable. */ public void testShutdownAll() { MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); connectionManager.getParams().setDefaultMaxConnectionsPerHost(1); connectionManager.getParams().setMaxTotalConnections(1); HostConfiguration host1 = new HostConfiguration(); host1.setHost("host1", -1, "http"); // hold on to the only connection HttpConnection connection = connectionManager.getConnection(host1); // wait for a connection on another thread GetConnectionThread getConn = new GetConnectionThread(host1, connectionManager, 0); getConn.start(); MultiThreadedHttpConnectionManager.shutdownAll(); // now release this connection, this should close the connection, but have no other effect connection.releaseConnection(); connection = null; try { getConn.join(); } catch (InterruptedException e) { e.printStackTrace(); } // this thread should have caught an exception without getting a connection assertNull("Not connection should have been checked out", getConn.getConnection()); assertNotNull("There should have been an exception", getConn.getException()); try { connectionManager.getConnection(host1); fail("An exception should have occurred"); } catch (Exception e) { // this is expected } } /** * Tests that {@link MultiThreadedHttpConnectionManager#shutdown()} closes all resources * and makes the connection manger unusable. */ public void testShutdown() { MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); connectionManager.getParams().setDefaultMaxConnectionsPerHost(1); connectionManager.getParams().setMaxTotalConnections(1); HostConfiguration host1 = new HostConfiguration(); host1.setHost("host1", -1, "http"); // hold on to the only connection HttpConnection connection = connectionManager.getConnection(host1); // wait for a connection on another thread GetConnectionThread getConn = new GetConnectionThread(host1, connectionManager, 0); getConn.start(); connectionManager.shutdown(); // now release this connection, this should close the connection, but have no other effect connection.releaseConnection(); connection = null; try { getConn.join(); } catch (InterruptedException e) { e.printStackTrace(); } // this thread should have caught an exception without getting a connection assertNull("Not connection should have been checked out", getConn.getConnection()); assertNotNull("There should have been an exception", getConn.getException()); try { connectionManager.getConnection(host1); fail("An exception should have occurred"); } catch (Exception e) { // this is expected } } /** * Tests the MultiThreadedHttpConnectionManager's ability to restrict the maximum number * of connections. */ public void testMaxConnections() { MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); connectionManager.getParams().setDefaultMaxConnectionsPerHost(1); connectionManager.getParams().setMaxTotalConnections(2); HostConfiguration host1 = new HostConfiguration(); host1.setHost("host1", -1, "http"); HostConfiguration host2 = new HostConfiguration(); host2.setHost("host2", -1, "http"); HttpConnection connection1 = connectionManager.getConnection(host1); HttpConnection connection2 = connectionManager.getConnection(host2); try { // this should fail quickly since the connection has not been released connectionManager.getConnectionWithTimeout(host2, 100); fail("ConnectionPoolTimeoutException should not be available"); } catch (ConnectionPoolTimeoutException e) { // this should throw an exception } // release one of the connections connection2.releaseConnection(); connection2 = null; try { // there should be a connection available now connection2 = connectionManager.getConnectionWithTimeout(host2, 100); } catch (ConnectionPoolTimeoutException e) { e.printStackTrace(); fail("a httpConnection should have been available: " + e); } } /** * Tests the MultiThreadedHttpConnectionManager's ability to restrict the maximum number * of connections per host. */ public void testMaxConnectionsPerHost() throws Exception { MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); connectionManager.getParams().setDefaultMaxConnectionsPerHost(1); connectionManager.getParams().setMaxTotalConnections(100); HostConfiguration host1 = new HostConfiguration(); host1.setHost("host1", -1, "http"); HostConfiguration host2 = new HostConfiguration(); host2.setHost("host2", -1, "http"); HostConfiguration host3 = new HostConfiguration(); host3.setHost("host3", -1, "http"); connectionManager.getParams().setMaxConnectionsPerHost(host1, 3); connectionManager.getParams().setMaxConnectionsPerHost(host2, 2); // Host1 HttpConnection connection1 = connectionManager.getConnectionWithTimeout(host1, 1000); HttpConnection connection2 = connectionManager.getConnectionWithTimeout(host1, 1000); HttpConnection connection3 = connectionManager.getConnectionWithTimeout(host1, 1000); try { // this should fail quickly since the connection has not been released connectionManager.getConnectionWithTimeout(host1, 100); fail("ConnectionPoolTimeoutException should not be available"); } catch (ConnectionPoolTimeoutException e) { // expected } // Host2 connection1 = connectionManager.getConnectionWithTimeout(host2, 1000); connection2 = connectionManager.getConnectionWithTimeout(host2, 1000); try { // this should fail quickly since the connection has not been released connectionManager.getConnectionWithTimeout(host2, 100); fail("ConnectionPoolTimeoutException should not be available"); } catch (ConnectionPoolTimeoutException e) { // expected } // Host3 (should use the default per host value) connection1 = connectionManager.getConnectionWithTimeout(host3, 1000); try { // this should fail quickly since the connection has not been released connectionManager.getConnectionWithTimeout(host3, 100); fail("ConnectionPoolTimeoutException should not be available"); } catch (ConnectionPoolTimeoutException e) { // expected } } public void testHostReusePreference() { final MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); connectionManager.getParams().setDefaultMaxConnectionsPerHost(1); connectionManager.getParams().setMaxTotalConnections(1); final HostConfiguration host1 = new HostConfiguration(); host1.setHost("host1", -1, "http"); final HostConfiguration host2 = new HostConfiguration(); host2.setHost("host2", -1, "http"); HttpConnection connection = connectionManager.getConnection(host1); GetConnectionThread getHost1 = new GetConnectionThread(host1, connectionManager, 200); GetConnectionThread getHost2 = new GetConnectionThread(host2, connectionManager, 200); getHost2.start(); getHost1.start(); // give the threads some time to startup try { Thread.sleep(100); } catch (InterruptedException e1) { e1.printStackTrace(); } // after the connection to host1 is released it should be given to getHost1 connection.releaseConnection(); connection = null; try { getHost1.join(); getHost2.join(); } catch (InterruptedException e) { e.printStackTrace(); } assertNotSame( "Connection should have been given to someone", getHost1.getConnection(), getHost2.getConnection() ); assertNotNull("Connection should have been given to host1", getHost1.getConnection()); assertNull("Connection should NOT have been given to host2", getHost2.getConnection()); } public void testMaxConnectionsPerServer() { this.server.setHttpService(new EchoService()); MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); connectionManager.getParams().setDefaultMaxConnectionsPerHost(1); client.setHttpConnectionManager(connectionManager); // we shouldn't have to wait if a connection is available client.getParams().setConnectionManagerTimeout( 1 ); GetMethod getMethod = new GetMethod("/"); try { client.executeMethod(getMethod); } catch (Exception e) { fail("error reading from server: " + e); } GetMethod getMethod2 = new GetMethod("/"); try { // this should fail quickly since the connection has not been released client.executeMethod(getMethod2); fail("a httpConnection should not be available"); } catch (ConnectTimeoutException e) { } catch (HttpException e) { fail("error reading from server; " + e); } catch (IOException e) { fail("error reading from server; " + e); } } public void testDeleteClosedConnections() { MultiThreadedHttpConnectionManager manager = new MultiThreadedHttpConnectionManager(); HttpConnection conn = manager.getConnection(client.getHostConfiguration()); assertEquals("connectionsInPool", manager.getConnectionsInPool(), 1); assertEquals("connectionsInPool(host)", manager.getConnectionsInPool(client.getHostConfiguration()), 1); conn.close(); conn.releaseConnection(); assertEquals("connectionsInPool", manager.getConnectionsInPool(), 1); assertEquals("connectionsInPool(host)", manager.getConnectionsInPool(client.getHostConfiguration()), 1); manager.deleteClosedConnections(); assertEquals("connectionsInPool", manager.getConnectionsInPool(), 0); assertEquals("connectionsInPool(host)", manager.getConnectionsInPool(client.getHostConfiguration()), 0); } public void testReclaimUnusedConnection() { this.server.setHttpService(new EchoService()); MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); connectionManager.getParams().setIntParameter( HttpConnectionManagerParams.MAX_TOTAL_CONNECTIONS, 1); client.setHttpConnectionManager(connectionManager); // we shouldn't have to wait if a connection is available client.getParams().setConnectionManagerTimeout( 30000 ); GetMethod getMethod = new GetMethod("/"); try { client.executeMethod(getMethod); } catch (Exception e) { fail("error reading from server: " + e); } getMethod = new GetMethod("/"); Runtime.getRuntime().gc(); try { // we didn't explicitly release the connection, but it should be // reclaimed by the garbage collector, we hope:) client.executeMethod(getMethod); } catch (HttpException e) { fail("httpConnection does not appear to have been reclaimed by the GC: " + e); } catch (IOException e) { fail("error reading from server; " + e); } } public void testGetFromMultipleThreads() { this.server.setHttpService(new EchoService()); client.setHttpConnectionManager(new MultiThreadedHttpConnectionManager()); ExecuteMethodThread[] threads = new ExecuteMethodThread[10]; for (int i = 0; i < threads.length; i++) { GetMethod method = new GetMethod("/"); method.setFollowRedirects(true); threads[i] = new ExecuteMethodThread(method, client); threads[i].start(); } for (int i = 0; i < threads.length; i++) { try { // wait until this thread finishes. we'll give it 10 seconds, // but it shouldn't take that long threads[i].join(10000); } catch (InterruptedException e) { } // make sure an exception did not occur Exception e = threads[i].getException(); if (e != null) { fail("An error occured in the get: " + e); } // we should have a 200 status assertEquals(threads[i].getMethod().getStatusCode(), HttpStatus.SC_OK); } } public void testTimeout() { MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager(); mgr.getParams().setDefaultMaxConnectionsPerHost(2); try{ HostConfiguration hostConfig = new HostConfiguration(); hostConfig.setHost("www.nosuchserver.com", 80, "http"); HttpConnection conn1 = mgr.getConnection(hostConfig); HttpConnection conn2 = mgr.getConnection(hostConfig); HttpConnection conn3 = mgr.getConnectionWithTimeout(hostConfig, 1000); fail("Expected an HttpException."); }catch(ConnectTimeoutException e){ //Expected result } } static class FakeSecureProtocolSocketFactory implements SecureProtocolSocketFactory { public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { throw new IllegalStateException("createSocket() should never have been called."); } public Socket createSocket(String host, int port) throws IOException, UnknownHostException { throw new IllegalStateException("createSocket() should never have been called."); } public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException { throw new IllegalStateException("createSocket() should never have been called."); } public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort, HttpConnectionParams params) throws IOException, UnknownHostException { throw new IllegalStateException("createSocket() should never have been called."); } } static class RejectConnectService extends EchoService { public boolean process(SimpleRequest request, SimpleResponse response) throws IOException { if (request.getRequestLine().getMethod().equalsIgnoreCase("CONNECT")) { response.setStatusLine(request.getRequestLine().getHttpVersion(), HttpStatus.SC_METHOD_NOT_ALLOWED); response.setHeader(new Header("Connection", "close")); return true; } else { return super.process(request, response); } } } static class GetConnectionThread extends Thread { private HostConfiguration hostConfiguration; private MultiThreadedHttpConnectionManager connectionManager; private HttpConnection connection; private long timeout; private Exception exception; public GetConnectionThread( HostConfiguration hostConfiguration, MultiThreadedHttpConnectionManager connectionManager, long timeout ) { this.hostConfiguration = hostConfiguration; this.connectionManager = connectionManager; this.timeout = timeout; } public void run() { try { connection = connectionManager.getConnectionWithTimeout(hostConfiguration, timeout); } catch (Exception e) { this.exception = e; } } public Exception getException() { return exception; } public HttpConnection getConnection() { return connection; } } }