1   /*
2    * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/oac.hc3x/trunk/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java $
3    * $Revision$
4    * $Date$
5    * ====================================================================
6    *
7    *  Licensed to the Apache Software Foundation (ASF) under one or more
8    *  contributor license agreements.  See the NOTICE file distributed with
9    *  this work for additional information regarding copyright ownership.
10   *  The ASF licenses this file to You under the Apache License, Version 2.0
11   *  (the "License"); you may not use this file except in compliance with
12   *  the License.  You may obtain a copy of the License at
13   *
14   *      http://www.apache.org/licenses/LICENSE-2.0
15   *
16   *  Unless required by applicable law or agreed to in writing, software
17   *  distributed under the License is distributed on an "AS IS" BASIS,
18   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   *  See the License for the specific language governing permissions and
20   *  limitations under the License.
21   * ====================================================================
22   *
23   * This software consists of voluntary contributions made by many
24   * individuals on behalf of the Apache Software Foundation.  For more
25   * information on the Apache Software Foundation, please see
26   * <http://www.apache.org/>.
27   */
28  
29  package org.apache.commons.httpclient;
30  
31  import java.io.IOException;
32  import java.lang.ref.WeakReference;
33  import java.net.InetAddress;
34  import java.net.Socket;
35  import java.net.UnknownHostException;
36  
37  import junit.framework.Test;
38  import junit.framework.TestSuite;
39  
40  import org.apache.commons.httpclient.methods.GetMethod;
41  import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
42  import org.apache.commons.httpclient.params.HttpConnectionParams;
43  import org.apache.commons.httpclient.protocol.Protocol;
44  import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
45  import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
46  import org.apache.commons.httpclient.server.SimpleRequest;
47  import org.apache.commons.httpclient.server.SimpleResponse;
48  
49  /***
50   * Unit tests for {@link HttpConnectionManager}.
51   *
52   * @author Marc A. Saegesser
53   * @version $Id: TestHttpConnectionManager.java 608014 2008-01-02 05:48:53Z rolandw $
54   */
55  public class TestHttpConnectionManager extends HttpClientTestBase {
56  
57      // ------------------------------------------------------------ Constructor
58      public TestHttpConnectionManager(String testName) throws IOException {
59          super(testName);
60      }
61  
62      // ------------------------------------------------------------------- Main
63      public static void main(String args[]) {
64          String[] testCaseName = { TestHttpConnectionManager.class.getName() };
65          junit.textui.TestRunner.main(testCaseName);
66      }
67  
68      // ------------------------------------------------------- TestCase Methods
69  
70      public static Test suite() {
71          return new TestSuite(TestHttpConnectionManager.class);
72      }
73  
74  
75      // ----------------------------------------------------------- Test Methods
76  
77      /***
78       * Test that the ConnectMethod correctly releases connections when
79       * CONNECT fails.
80       */
81      public void testConnectMethodFailureRelease() throws Exception {
82          
83          MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
84          mgr.getParams().setIntParameter(
85              HttpConnectionManagerParams.MAX_TOTAL_CONNECTIONS, 1);
86          client.setHttpConnectionManager(mgr);
87          this.server.setHttpService(new RejectConnectService());
88          
89          // we're going to execute a connect method against the localhost, assuming
90          // that CONNECT is not supported.  This should test the fakeResponse()
91          // code on HttpMethodBase.
92          client.getHostConfiguration().setProxy(server.getLocalAddress(), server.getLocalPort());
93          // we must set the host to a secure destination or the CONNECT method
94          // will not be used
95          client.getHostConfiguration().setHost(
96              "notARealHost", 
97              1234, 
98              new Protocol(
99                  "https", 
100                 (ProtocolSocketFactory)new FakeSecureProtocolSocketFactory(), 
101                 443)
102         );
103         
104         GetMethod get = new GetMethod("/");
105         try {
106             assertTrue(client.executeMethod(get) != 200);
107         } catch (IOException e) {
108             e.printStackTrace();
109             fail("Error executing connect: " + e);
110         }
111 
112         // this should calling releaseConnection() releases the connection
113         try {
114             get.releaseConnection();
115             mgr.getConnectionWithTimeout(client.getHostConfiguration(), 1).releaseConnection();
116         } catch (ConnectTimeoutException e1) {
117             fail("Connection should have been available.");
118         }
119         
120         get = new GetMethod("/");
121         
122         try {
123             assertTrue(client.executeMethod(get) != 200);
124         } catch (IOException e) {
125             e.printStackTrace();
126             fail("Error executing connect: " + e);
127         }
128 
129         // make sure reading the response fully releases the connection        
130         try {
131             get.getResponseBodyAsString();
132             mgr.getConnectionWithTimeout(client.getHostConfiguration(), 1).releaseConnection();
133         } catch (ConnectTimeoutException e1) {
134             fail("Connection should have been available.");
135         }     
136         
137         get = new GetMethod("/");
138         
139         try {
140             assertTrue(client.executeMethod(get) != 200);
141         } catch (IOException e) {
142             e.printStackTrace();
143             fail("Error executing connect: " + e);
144         }
145 
146         // make sure closing the output stream releases the connection        
147         try {
148             get.getResponseBodyAsStream().close();
149             mgr.getConnectionWithTimeout(client.getHostConfiguration(), 1).releaseConnection();
150         } catch (ConnectTimeoutException e) {
151             fail("Connection should have been available.");
152         } catch (IOException e) {
153             e.printStackTrace();
154             fail("Close connection failed: " + e);   
155         }
156     }
157 
158     public void testGetConnection() {
159         MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
160 
161         HostConfiguration hostConfiguration = new HostConfiguration();
162         hostConfiguration.setHost("www.nosuchserver.com", 80, "http");
163 
164         // Create a new connection
165         HttpConnection conn = mgr.getConnection(hostConfiguration);
166         // Validate the connection properties
167         assertEquals("Host", "www.nosuchserver.com", conn.getHost());
168         assertEquals("Port", 80, conn.getPort());
169         // Release the connection
170         mgr.releaseConnection(conn);
171 
172         // Create a new connection
173         hostConfiguration.setHost("www.nosuchserver.com", -1, "https");
174         conn = mgr.getConnection(hostConfiguration);
175         // Validate the connection properties
176         assertEquals("Host", "www.nosuchserver.com", conn.getHost());
177         assertEquals("Port", 443, conn.getPort());
178         // Release the connection
179         mgr.releaseConnection(conn);
180 
181         // Create a new connection
182         hostConfiguration.setHost("www.nowhere.org", 8080, "http");
183         conn = mgr.getConnection(hostConfiguration);
184         // Validate the connection properties
185         assertEquals("Host", "www.nowhere.org", conn.getHost());
186         assertEquals("Port", 8080, conn.getPort());
187         // Release the connection
188         mgr.releaseConnection(conn);
189 
190     }
191 
192     public void testDroppedThread() throws Exception {
193 
194         this.server.setHttpService(new EchoService());
195 
196         MultiThreadedHttpConnectionManager mthcm = new MultiThreadedHttpConnectionManager();
197         client.setHttpConnectionManager(mthcm);
198         WeakReference wr = new WeakReference(mthcm);
199 
200         GetMethod method = new GetMethod("/");
201         client.executeMethod(method);
202         method.releaseConnection();
203 
204         mthcm = null;
205         client = null;
206         method = null;
207         
208         System.gc();
209 
210         // this sleep appears to be necessary in order to give the JVM
211         // time to clean up the miscellaneous pointers to the connection manager
212         try {
213             Thread.sleep(1000);
214         } catch (InterruptedException e) {
215             fail("shouldn't be interrupted.");
216         }
217 
218         Object connectionManager = wr.get();
219         assertNull("connectionManager should be null", connectionManager);
220     }    
221     
222     public void testWriteRequestReleaseConnection() {
223 
224         MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
225         connectionManager.getParams().setDefaultMaxConnectionsPerHost(1);
226 
227         client.setHttpConnectionManager(connectionManager);
228         
229         GetMethod get = new GetMethod("/") {
230             protected boolean writeRequestBody(HttpState state, HttpConnection conn)
231                 throws IOException, HttpException {
232                 throw new IOException("Oh no!!");
233             }
234         };
235         
236         try {
237             client.executeMethod(get);
238             fail("An exception should have occurred.");
239         } catch (HttpException e) {
240             e.printStackTrace();
241             fail("HttpException should not have occurred: " + e);
242         } catch (IOException e) {
243             // expected
244         }
245         
246         try {
247             connectionManager.getConnectionWithTimeout(client.getHostConfiguration(), 1);
248         } catch (ConnectTimeoutException e) {
249             e.printStackTrace();
250             fail("Connection was not released: " + e);
251         }
252         
253     }
254     
255     public void testReleaseConnection() {
256 
257         this.server.setHttpService(new EchoService());
258 
259         MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
260         connectionManager.getParams().setDefaultMaxConnectionsPerHost(1);
261 
262         client.setHttpConnectionManager(connectionManager);
263         // we shouldn't have to wait if a connection is available
264         client.getParams().setConnectionManagerTimeout(1);
265 
266         GetMethod getMethod = new GetMethod("/");
267 
268         try {
269             client.executeMethod(getMethod);
270         } catch (Exception e) {
271             fail("error reading from server: " + e);
272         }
273 
274         try {
275             // this should fail quickly since the connection has not been released
276             client.executeMethod(getMethod);
277             fail("a httpConnection should not be available");
278         } catch (ConnectTimeoutException e) {            
279         } catch (HttpException e) {
280             fail("error reading from server; " + e);
281         } catch (IOException e) {
282             e.printStackTrace();
283             fail("error reading from server; " + e);
284         }
285 
286         // this should release the connection
287         getMethod.releaseConnection();
288 
289         getMethod = new GetMethod("/");
290 
291         try {
292             // this should fail quickly if the connection has not been released
293             client.executeMethod(getMethod);
294         } catch (HttpException e) {
295             fail("httpConnection does not appear to have been released: " + e);
296         } catch (IOException e) {
297             fail("error reading from server; " + e);
298         }
299 
300     }
301 
302     /***
303      * Makes sure that a connection gets released after the content of the body
304      * is read.
305      */
306     public void testResponseAutoRelease() throws Exception  {
307 
308         this.server.setHttpService(new EchoService());
309 
310         MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
311         connectionManager.getParams().setDefaultMaxConnectionsPerHost(1);
312 
313         client.setHttpConnectionManager(connectionManager);
314         // we shouldn't have to wait if a connection is available
315         client.getParams().setConnectionManagerTimeout( 1 );
316 
317         GetMethod getMethod = new GetMethod("/");
318 
319         try {
320             client.executeMethod(getMethod);
321         } catch (Exception e) {
322             fail("error reading from server: " + e);
323         }
324         
325         // this should release the connection
326         getMethod.getResponseBody();
327 
328         getMethod = new GetMethod("/");
329 
330         try {
331             // this should fail quickly if the connection has not been released
332             client.executeMethod(getMethod);
333         } catch (HttpException e) {
334             fail("httpConnection does not appear to have been released: " + e);
335         } catch (IOException e) {
336             fail("error reading from server; " + e);
337         }
338 
339     }
340     
341     /***
342      * Tests the MultiThreadedHttpConnectionManager's ability to reclaim unused 
343      * connections.
344      */
345     public void testConnectionReclaiming() {
346         
347         MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
348         connectionManager.getParams().setDefaultMaxConnectionsPerHost(1);
349         connectionManager.getParams().setMaxTotalConnections(1);
350 
351         HostConfiguration host1 = new HostConfiguration();
352         host1.setHost("host1", -1, "http");
353 
354         HostConfiguration host2 = new HostConfiguration();
355         host2.setHost("host2", -1, "http");
356 
357         HttpConnection connection = connectionManager.getConnection(host1);
358         // now release this connection
359         connection.releaseConnection();
360         connection = null;
361         
362         try {
363             // the connection from host1 should be reclaimed
364             connection = connectionManager.getConnectionWithTimeout(host2, 100);
365         } catch (ConnectTimeoutException e) {
366             e.printStackTrace();
367             fail("a httpConnection should have been available: " + e);
368         }        
369     }
370     
371     /***
372      * Tests that {@link MultiThreadedHttpConnectionManager#shutdownAll()} closes all resources
373      * and makes all connection mangers unusable.
374      */
375     public void testShutdownAll() {
376 
377         MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
378         connectionManager.getParams().setDefaultMaxConnectionsPerHost(1);
379         connectionManager.getParams().setMaxTotalConnections(1);
380 
381         HostConfiguration host1 = new HostConfiguration();
382         host1.setHost("host1", -1, "http");
383 
384         // hold on to the only connection
385         HttpConnection connection = connectionManager.getConnection(host1);
386 
387         // wait for a connection on another thread
388         GetConnectionThread getConn = new GetConnectionThread(host1, connectionManager, 0);
389         getConn.start();
390         
391         MultiThreadedHttpConnectionManager.shutdownAll();
392         
393         // now release this connection, this should close the connection, but have no other effect
394         connection.releaseConnection();
395         connection = null;
396         
397         try {
398             getConn.join();
399         } catch (InterruptedException e) {
400             e.printStackTrace();
401         }
402         
403         // this thread should have caught an exception without getting a connection
404         assertNull("Not connection should have been checked out", getConn.getConnection());
405         assertNotNull("There should have been an exception", getConn.getException());
406         
407         try {
408             connectionManager.getConnection(host1);
409             fail("An exception should have occurred");
410         } catch (Exception e) {
411             // this is expected
412         }
413     }
414         
415     /***
416      * Tests that {@link MultiThreadedHttpConnectionManager#shutdown()} closes all resources
417      * and makes the connection manger unusable.
418      */
419     public void testShutdown() {
420 
421         MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
422         connectionManager.getParams().setDefaultMaxConnectionsPerHost(1);
423         connectionManager.getParams().setMaxTotalConnections(1);
424 
425         HostConfiguration host1 = new HostConfiguration();
426         host1.setHost("host1", -1, "http");
427 
428         // hold on to the only connection
429         HttpConnection connection = connectionManager.getConnection(host1);
430 
431         // wait for a connection on another thread
432         GetConnectionThread getConn = new GetConnectionThread(host1, connectionManager, 0);
433         getConn.start();
434         
435         connectionManager.shutdown();
436         
437         // now release this connection, this should close the connection, but have no other effect
438         connection.releaseConnection();
439         connection = null;
440         
441         try {
442             getConn.join();
443         } catch (InterruptedException e) {
444             e.printStackTrace();
445         }
446         
447         // this thread should have caught an exception without getting a connection
448         assertNull("Not connection should have been checked out", getConn.getConnection());
449         assertNotNull("There should have been an exception", getConn.getException());
450         
451         try {
452             connectionManager.getConnection(host1);
453             fail("An exception should have occurred");
454         } catch (Exception e) {
455             // this is expected
456         }
457     }
458     
459     /***
460      * Tests the MultiThreadedHttpConnectionManager's ability to restrict the maximum number 
461      * of connections.
462      */    
463     public void testMaxConnections() {
464         
465         MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
466         connectionManager.getParams().setDefaultMaxConnectionsPerHost(1);
467         connectionManager.getParams().setMaxTotalConnections(2);
468 
469         HostConfiguration host1 = new HostConfiguration();
470         host1.setHost("host1", -1, "http");
471 
472         HostConfiguration host2 = new HostConfiguration();
473         host2.setHost("host2", -1, "http");
474 
475         HttpConnection connection1 = connectionManager.getConnection(host1);
476         HttpConnection connection2 = connectionManager.getConnection(host2);
477     
478         try {
479             // this should fail quickly since the connection has not been released
480             connectionManager.getConnectionWithTimeout(host2, 100);
481             fail("ConnectionPoolTimeoutException should not be available");
482         } catch (ConnectionPoolTimeoutException e) {
483             // this should throw an exception
484         }
485         
486         // release one of the connections
487         connection2.releaseConnection();
488         connection2 = null;
489         
490         try {
491             // there should be a connection available now
492             connection2 = connectionManager.getConnectionWithTimeout(host2, 100);
493         } catch (ConnectionPoolTimeoutException e) {
494             e.printStackTrace();
495             fail("a httpConnection should have been available: " + e);
496         }
497     }    
498 
499     /***
500      * Tests the MultiThreadedHttpConnectionManager's ability to restrict the maximum number 
501      * of connections per host.
502      */    
503     public void testMaxConnectionsPerHost() throws Exception {
504         
505         MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
506         connectionManager.getParams().setDefaultMaxConnectionsPerHost(1);
507         connectionManager.getParams().setMaxTotalConnections(100);
508 
509         HostConfiguration host1 = new HostConfiguration();
510         host1.setHost("host1", -1, "http");
511 
512         HostConfiguration host2 = new HostConfiguration();
513         host2.setHost("host2", -1, "http");
514 
515         HostConfiguration host3 = new HostConfiguration();
516         host3.setHost("host3", -1, "http");
517 
518         connectionManager.getParams().setMaxConnectionsPerHost(host1, 3);
519         connectionManager.getParams().setMaxConnectionsPerHost(host2, 2);
520 
521         // Host1
522         HttpConnection connection1 = connectionManager.getConnectionWithTimeout(host1, 1000);
523         HttpConnection connection2 = connectionManager.getConnectionWithTimeout(host1, 1000);
524         HttpConnection connection3 = connectionManager.getConnectionWithTimeout(host1, 1000);
525         try {
526             // this should fail quickly since the connection has not been released
527             connectionManager.getConnectionWithTimeout(host1, 100);
528             fail("ConnectionPoolTimeoutException should not be available");
529         } catch (ConnectionPoolTimeoutException e) {
530             // expected
531         }
532         
533         // Host2
534         connection1 = connectionManager.getConnectionWithTimeout(host2, 1000);
535         connection2 = connectionManager.getConnectionWithTimeout(host2, 1000);
536         try {
537             // this should fail quickly since the connection has not been released
538             connectionManager.getConnectionWithTimeout(host2, 100);
539             fail("ConnectionPoolTimeoutException should not be available");
540         } catch (ConnectionPoolTimeoutException e) {
541             // expected
542         }
543 
544         // Host3 (should use the default per host value)
545         connection1 = connectionManager.getConnectionWithTimeout(host3, 1000);
546         try {
547             // this should fail quickly since the connection has not been released
548             connectionManager.getConnectionWithTimeout(host3, 100);
549             fail("ConnectionPoolTimeoutException should not be available");
550         } catch (ConnectionPoolTimeoutException e) {
551             // expected
552         }
553     }    
554 
555     public void testHostReusePreference() {
556         
557         final MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
558         connectionManager.getParams().setDefaultMaxConnectionsPerHost(1);
559         connectionManager.getParams().setMaxTotalConnections(1);
560 
561         final HostConfiguration host1 = new HostConfiguration();
562         host1.setHost("host1", -1, "http");
563 
564         final HostConfiguration host2 = new HostConfiguration();
565         host2.setHost("host2", -1, "http");
566 
567         HttpConnection connection = connectionManager.getConnection(host1);
568 
569         GetConnectionThread getHost1 = new GetConnectionThread(host1, connectionManager, 200);
570         GetConnectionThread getHost2 = new GetConnectionThread(host2, connectionManager, 200);
571         
572         getHost2.start();
573         getHost1.start();
574         
575         // give the threads some time to startup
576         try {
577             Thread.sleep(100);
578         } catch (InterruptedException e1) {
579             e1.printStackTrace();
580         }
581             
582         // after the connection to host1 is released it should be given to getHost1
583         connection.releaseConnection();
584         connection = null;
585 
586         try {
587             getHost1.join();
588             getHost2.join();
589         } catch (InterruptedException e) {
590             e.printStackTrace();
591         }
592 
593         assertNotSame(
594             "Connection should have been given to someone", 
595             getHost1.getConnection(),
596             getHost2.getConnection()
597         );        
598         assertNotNull("Connection should have been given to host1", getHost1.getConnection());
599         assertNull("Connection should NOT have been given to host2", getHost2.getConnection());
600         
601     } 
602     
603     public void testMaxConnectionsPerServer() {
604      
605         this.server.setHttpService(new EchoService());
606 
607         MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
608         connectionManager.getParams().setDefaultMaxConnectionsPerHost(1);
609 
610         client.setHttpConnectionManager(connectionManager);
611         // we shouldn't have to wait if a connection is available
612         client.getParams().setConnectionManagerTimeout( 1 );
613 
614         GetMethod getMethod = new GetMethod("/");
615 
616         try {
617             client.executeMethod(getMethod);
618         } catch (Exception e) {
619             fail("error reading from server: " + e);
620         }
621 
622         GetMethod getMethod2 = new GetMethod("/");
623 
624         try {
625             // this should fail quickly since the connection has not been released
626             client.executeMethod(getMethod2);
627             fail("a httpConnection should not be available");
628         } catch (ConnectTimeoutException e) {
629         } catch (HttpException e) {
630             fail("error reading from server; " + e);
631         } catch (IOException e) {
632             fail("error reading from server; " + e);
633         }
634                 
635     }
636     
637     public void testDeleteClosedConnections() {
638         
639         MultiThreadedHttpConnectionManager manager = new MultiThreadedHttpConnectionManager();
640         
641         HttpConnection conn = manager.getConnection(client.getHostConfiguration());
642         
643         assertEquals("connectionsInPool", manager.getConnectionsInPool(), 1);
644         assertEquals("connectionsInPool(host)", manager.getConnectionsInPool(client.getHostConfiguration()), 1);
645         
646         conn.close();
647         conn.releaseConnection();
648 
649         assertEquals("connectionsInPool", manager.getConnectionsInPool(), 1);
650         assertEquals("connectionsInPool(host)", manager.getConnectionsInPool(client.getHostConfiguration()), 1);
651 
652         manager.deleteClosedConnections();
653         
654         assertEquals("connectionsInPool", manager.getConnectionsInPool(), 0);
655         assertEquals("connectionsInPool(host)", manager.getConnectionsInPool(client.getHostConfiguration()), 0);
656     }
657     
658     /***
659      * Tests that thread waiting in the MultiThreadedHttpConnectionManager can be 
660      * interrupted.
661      */
662     public void testWaitingThreadInterrupted() {
663 
664         this.server.setHttpService(new EchoService());
665 
666         MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
667         connectionManager.getParams().setIntParameter(
668             HttpConnectionManagerParams.MAX_TOTAL_CONNECTIONS, 1);
669 
670         HostConfiguration host1 = new HostConfiguration();
671         host1.setHost("host1", -1, "http");
672 
673         // hold on to the only connection
674         HttpConnection connection = connectionManager.getConnection(host1);
675 
676         // wait for a connection on another thread
677         GetConnectionThread getConn = new GetConnectionThread(host1, connectionManager, 1000);
678         getConn.start();
679         
680         // give the thread a chance to block
681         synchronized (this) {
682             try {
683                 this.wait(500);
684             } catch (InterruptedException e) {
685                 e.printStackTrace();
686             }
687         }
688         
689         // interrupt the thread, this should cancel waiting with a RuntimeException
690         getConn.interrupt();
691         
692         try {
693             getConn.join();
694         } catch (InterruptedException e) {
695             e.printStackTrace();
696         }
697         
698         // make sure the correct exception was thrown
699         assertTrue(getConn.exception != null);
700         assertEquals(getConn.exception.getClass(), IllegalThreadStateException.class);
701         
702         // make sure the connection manager is still working
703         connection.releaseConnection();
704         try {
705             connectionManager.getConnectionWithTimeout(host1, 10);
706         } catch (ConnectionPoolTimeoutException e) {
707             fail("Connection not available");
708         }
709     }
710     
711     public void testReclaimUnusedConnection() {
712 
713         this.server.setHttpService(new EchoService());
714 
715         MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
716         connectionManager.getParams().setIntParameter(
717             HttpConnectionManagerParams.MAX_TOTAL_CONNECTIONS, 1);
718 
719         client.setHttpConnectionManager(connectionManager);
720         // we shouldn't have to wait if a connection is available
721         client.getParams().setConnectionManagerTimeout( 30000 );
722 
723         GetMethod getMethod = new GetMethod("/");
724 
725         try {
726             client.executeMethod(getMethod);
727         } catch (Exception e) {
728             fail("error reading from server: " + e);
729         }
730 
731         getMethod = new GetMethod("/");
732         
733         Runtime.getRuntime().gc();
734 
735         try {
736             // we didn't explicitly release the connection, but it should be 
737             // reclaimed by the garbage collector, we hope:)
738             client.executeMethod(getMethod);
739         } catch (HttpException e) {
740             fail("httpConnection does not appear to have been reclaimed by the GC: " + e);
741         } catch (IOException e) {
742             fail("error reading from server; " + e);
743         }
744 
745     }
746     
747     public void testGetFromMultipleThreads() {
748         
749         this.server.setHttpService(new EchoService());
750 
751         client.setHttpConnectionManager(new MultiThreadedHttpConnectionManager());
752         ExecuteMethodThread[] threads = new ExecuteMethodThread[10];
753         
754         for (int i = 0; i < threads.length; i++) {
755             GetMethod method = new GetMethod("/");
756             method.setFollowRedirects(true);
757             
758             threads[i] = new ExecuteMethodThread(method, client);
759             threads[i].start();
760         }
761         
762         for (int i = 0; i < threads.length; i++) {
763             try {
764                 // wait until this thread finishes. we'll give it 10 seconds,
765                 // but it shouldn't take that long
766                 threads[i].join(10000);
767             } catch (InterruptedException e) {
768             }
769             // make sure an exception did not occur
770             Exception e = threads[i].getException();
771             if (e != null) {
772                 fail("An error occured in the get: " + e);
773             }
774             // we should have a 200 status
775             assertEquals(threads[i].getMethod().getStatusCode(), HttpStatus.SC_OK);
776         }
777     }
778 
779     public void testTimeout() {
780         MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
781         mgr.getParams().setDefaultMaxConnectionsPerHost(2);
782         
783         try{
784             HostConfiguration hostConfig = new HostConfiguration();
785             hostConfig.setHost("www.nosuchserver.com", 80, "http");
786             
787             HttpConnection conn1 = mgr.getConnection(hostConfig);
788             HttpConnection conn2 = mgr.getConnection(hostConfig);
789             
790             HttpConnection conn3 = mgr.getConnectionWithTimeout(hostConfig, 1000);
791             fail("Expected an HttpException.");
792             
793         }catch(ConnectTimeoutException e){
794             //Expected result
795         }
796     }
797     
798     static class FakeSecureProtocolSocketFactory implements SecureProtocolSocketFactory {
799         
800         public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
801             throws IOException, UnknownHostException {
802             throw new IllegalStateException("createSocket() should never have been called.");
803         }
804         
805         public Socket createSocket(String host, int port)
806             throws IOException, UnknownHostException {
807             throw new IllegalStateException("createSocket() should never have been called.");
808         }
809         
810         public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort)
811             throws IOException, UnknownHostException {
812             throw new IllegalStateException("createSocket() should never have been called.");
813         }
814         
815         public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort, 
816             HttpConnectionParams params)
817             throws IOException, UnknownHostException {
818             throw new IllegalStateException("createSocket() should never have been called.");
819         }
820     }
821     
822     static class RejectConnectService extends EchoService {
823 		public boolean process(SimpleRequest request, SimpleResponse response)
824 				throws IOException {
825             if (request.getRequestLine().getMethod().equalsIgnoreCase("CONNECT")) {
826                 response.setStatusLine(request.getRequestLine().getHttpVersion(), HttpStatus.SC_METHOD_NOT_ALLOWED);
827                 response.setHeader(new Header("Connection", "close"));
828                 return true;
829             } else {
830                 return super.process(request, response);
831             }
832 		}
833     }
834     
835     static class GetConnectionThread extends Thread {
836         
837         private HostConfiguration hostConfiguration;
838         private MultiThreadedHttpConnectionManager connectionManager;
839         private HttpConnection connection;
840         private long timeout;
841         private Exception exception;
842         
843         public GetConnectionThread(
844             HostConfiguration hostConfiguration, 
845             MultiThreadedHttpConnectionManager connectionManager,
846             long timeout
847         ) {
848             this.hostConfiguration = hostConfiguration;
849             this.connectionManager = connectionManager; 
850             this.timeout = timeout;
851         }
852         
853         public void run() {
854             try {
855                 connection = connectionManager.getConnectionWithTimeout(hostConfiguration, timeout);
856             } catch (Exception e) {
857                 this.exception = e;
858             }            
859         }
860         
861         public Exception getException() {
862             return exception;
863         }
864         
865         public HttpConnection getConnection() {
866             return connection;
867         }
868 
869     }
870     
871 }
872