You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by cm...@apache.org on 2011/01/30 18:29:43 UTC

svn commit: r1065326 - in /camel/trunk: components/camel-http4/src/main/java/org/apache/camel/component/http4/ components/camel-http4/src/main/java/org/apache/camel/component/http4/helper/ components/camel-http4/src/test/java/org/apache/camel/component...

Author: cmueller
Date: Sun Jan 30 17:29:42 2011
New Revision: 1065326

URL: http://svn.apache.org/viewvc?rev=1065326&view=rev
Log:
CAMEL-3585: Upgrade to Apache HttpClients 4.1

Modified:
    camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/DefaultHttpBinding.java
    camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpComponent.java
    camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpEndpoint.java
    camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpPollingConsumer.java
    camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpProducer.java
    camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/helper/HttpHelper.java
    camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpCompressionTest.java
    camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpConcurrentTest.java
    camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpEndpointOptionsNotChangeComponentTest.java
    camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpProxyServerTest.java
    camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpServerTestSupport.java
    camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpsAuthenticationTest.java
    camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpsGetTest.java
    camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/helper/GZIPHelperTest.java
    camel/trunk/parent/pom.xml
    camel/trunk/tests/camel-itest/src/test/java/org/apache/camel/itest/http/HttpTestServer.java

Modified: camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/DefaultHttpBinding.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/DefaultHttpBinding.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/DefaultHttpBinding.java (original)
+++ camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/DefaultHttpBinding.java Sun Jan 30 17:29:42 2011
@@ -25,6 +25,7 @@ import java.io.UnsupportedEncodingExcept
 import java.net.URLDecoder;
 import java.util.Enumeration;
 import java.util.Map;
+
 import javax.activation.DataHandler;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
@@ -69,8 +70,8 @@ public class DefaultHttpBinding implemen
         this.headerFilterStrategy = endpoint.getHeaderFilterStrategy();
     }
 
+    @SuppressWarnings("rawtypes")
     public void readRequest(HttpServletRequest request, HttpMessage message) {
-
         // lets force a parse of the body and headers
         message.getBody();
         // populate the headers from the request
@@ -132,6 +133,7 @@ public class DefaultHttpBinding implemen
         populateAttachments(request, message);
     }
 
+    @SuppressWarnings("rawtypes")
     protected void populateRequestParameters(HttpServletRequest request, HttpMessage message) throws UnsupportedEncodingException {
         //we populate the http request parameters without checking the request method
         Map<String, Object> headers = message.getHeaders();
@@ -164,6 +166,7 @@ public class DefaultHttpBinding implemen
         }
     }
 
+    @SuppressWarnings("rawtypes")
     protected void populateAttachments(HttpServletRequest request, HttpMessage message) {
         // check if there is multipart files, if so will put it into DataHandler
         Enumeration names = request.getAttributeNames();

Modified: camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpComponent.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpComponent.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpComponent.java (original)
+++ camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpComponent.java Sun Jan 30 17:29:42 2011
@@ -17,6 +17,7 @@
 package org.apache.camel.component.http4;
 
 import java.net.URI;
+import java.security.NoSuchAlgorithmException;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -32,13 +33,13 @@ import org.apache.http.auth.params.AuthP
 import org.apache.http.client.params.ClientParamBean;
 import org.apache.http.conn.ClientConnectionManager;
 import org.apache.http.conn.params.ConnConnectionParamBean;
-import org.apache.http.conn.params.ConnManagerParamBean;
-import org.apache.http.conn.params.ConnPerRouteBean;
 import org.apache.http.conn.params.ConnRouteParamBean;
 import org.apache.http.conn.scheme.PlainSocketFactory;
 import org.apache.http.conn.scheme.Scheme;
 import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
 import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
 import org.apache.http.cookie.params.CookieSpecParamBean;
 import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
 import org.apache.http.params.BasicHttpParams;
@@ -58,6 +59,7 @@ public class HttpComponent extends Heade
     protected HttpClientConfigurer httpClientConfigurer;
     protected ClientConnectionManager clientConnectionManager;
     protected HttpBinding httpBinding;
+    protected X509HostnameVerifier x509HostnameVerifier = new BrowserCompatHostnameVerifier();
 
     // options to the default created http connection manager
     protected int maxTotalConnections = 200;
@@ -93,40 +95,54 @@ public class HttpComponent extends Heade
         if (configurer == null) {
             // try without ref
             configurer = resolveAndRemoveReferenceParameter(parameters, "httpClientConfigurer", HttpClientConfigurer.class);
+            
+            if (configurer == null) {
+                // fallback to component configured
+                configurer = getHttpClientConfigurer();
+            }
         }
-        if (configurer == null) {
-            // fallback to component configured
-            configurer = getHttpClientConfigurer();
-        }
 
-        // check the user name and password for basic authentication
+        configurer = configureBasicAuthentication(parameters, configurer);
+        configurer = configureHttpProxy(parameters, configurer);
+
+        return configurer;
+    }
+
+    private HttpClientConfigurer configureBasicAuthentication(Map<String, Object> parameters, HttpClientConfigurer configurer) {
         String username = getAndRemoveParameter(parameters, "username", String.class);
         String password = getAndRemoveParameter(parameters, "password", String.class);
-        String domain = getAndRemoveParameter(parameters, "domain", String.class);
-        String host = getAndRemoveParameter(parameters, "host", String.class);
+
         if (username != null && password != null) {
-            configurer = CompositeHttpConfigurer.combineConfigurers(
+            String domain = getAndRemoveParameter(parameters, "domain", String.class);
+            String host = getAndRemoveParameter(parameters, "host", String.class);
+            
+            return CompositeHttpConfigurer.combineConfigurers(
                     configurer,
                     new BasicAuthenticationHttpClientConfigurer(username, password, domain, host));
         }
+        
+        return configurer;
+    }
 
-        // check the proxy details for proxy configuration
+    private HttpClientConfigurer configureHttpProxy(Map<String, Object> parameters, HttpClientConfigurer configurer) {
         String proxyHost = getAndRemoveParameter(parameters, "proxyHost", String.class);
         Integer proxyPort = getAndRemoveParameter(parameters, "proxyPort", Integer.class);
+        
         if (proxyHost != null && proxyPort != null) {
             String proxyUsername = getAndRemoveParameter(parameters, "proxyUsername", String.class);
             String proxyPassword = getAndRemoveParameter(parameters, "proxyPassword", String.class);
             String proxyDomain = getAndRemoveParameter(parameters, "proxyDomain", String.class);
             String proxyNtHost = getAndRemoveParameter(parameters, "proxyNtHost", String.class);
+            
             if (proxyUsername != null && proxyPassword != null) {
-                configurer = CompositeHttpConfigurer.combineConfigurers(
+                return CompositeHttpConfigurer.combineConfigurers(
                         configurer, new ProxyHttpClientConfigurer(proxyHost, proxyPort, proxyUsername, proxyPassword, proxyDomain, proxyNtHost));
             } else {
-                configurer = CompositeHttpConfigurer.combineConfigurers(
+                return CompositeHttpConfigurer.combineConfigurers(
                         configurer, new ProxyHttpClientConfigurer(proxyHost, proxyPort));
             }
         }
-
+        
         return configurer;
     }
 
@@ -139,21 +155,21 @@ public class HttpComponent extends Heade
         Map<String, Object> httpClientParameters = new HashMap<String, Object>(parameters);
         // http client can be configured from URI options
         HttpParams clientParams = configureHttpParams(parameters);
-
-        // must extract well known parameters before we create the endpoint
-        HttpBinding binding = resolveAndRemoveReferenceParameter(parameters, "httpBindingRef", HttpBinding.class);
-        if (binding == null) {
-            // try without ref
-            binding = resolveAndRemoveReferenceParameter(parameters, "httpBinding", HttpBinding.class);
-        }
-        Boolean throwExceptionOnFailure = getAndRemoveParameter(parameters, "throwExceptionOnFailure", Boolean.class);
-        Boolean transferException = getAndRemoveParameter(parameters, "transferException", Boolean.class);
-        Boolean bridgeEndpoint = getAndRemoveParameter(parameters, "bridgeEndpoint", Boolean.class);
-        Boolean matchOnUriPrefix = getAndRemoveParameter(parameters, "matchOnUriPrefix", Boolean.class);
-        Boolean disableStreamCache = getAndRemoveParameter(parameters, "disableStreamCache", Boolean.class);
-
         // validate that we could resolve all httpClient. parameters as this component is lenient
         validateParameters(uri, parameters, "httpClient.");
+        
+        HttpBinding httpBinding = resolveAndRemoveReferenceParameter(parameters, "httpBindingRef", HttpBinding.class);
+        if (httpBinding == null) {
+            httpBinding = resolveAndRemoveReferenceParameter(parameters, "httpBinding", HttpBinding.class);
+        }
+        
+        HttpClientConfigurer httpClientConfigurer = resolveAndRemoveReferenceParameter(parameters, "httpClientConfigurerRef", HttpClientConfigurer.class);
+        if (httpClientConfigurer == null) {
+            httpClientConfigurer = resolveAndRemoveReferenceParameter(parameters, "httpClientConfigurer", HttpClientConfigurer.class);
+        }
+        
+        x509HostnameVerifier = resolveAndRemoveReferenceParameter(parameters, "x509HostnameVerifier", X509HostnameVerifier.class);
+        
         // create the configurer to use for this endpoint
         HttpClientConfigurer configurer = createHttpClientConfigurer(parameters);
         URI endpointUri = URISupport.createRemainingURI(new URI(addressUri), CastUtils.cast(httpClientParameters));
@@ -173,39 +189,20 @@ public class HttpComponent extends Heade
         // register port on schema registry
         boolean secure = isSecureConnection(uri);
         int port = getPort(httpUri);
-        registerPort(secure, port);
-
+        registerPort(secure, x509HostnameVerifier, port);
+        
         // create the endpoint
         HttpEndpoint endpoint = new HttpEndpoint(endpointUri.toString(), this, httpUri, clientParams, clientConnectionManager, configurer);
+        setProperties(endpoint, parameters);
         setEndpointHeaderFilterStrategy(endpoint);
-
-        // prefer to use endpoint configured over component configured
-        if (binding == null) {
-            // fallback to component configured
-            binding = getHttpBinding();
-        }
-        if (binding != null) {
-            endpoint.setBinding(binding);
-        }
-        // should we use an exception for failed error codes?
-        if (throwExceptionOnFailure != null) {
-            endpoint.setThrowExceptionOnFailure(throwExceptionOnFailure);
+        endpoint.setBinding(getHttpBinding());
+        if (httpBinding != null) {
+            endpoint.setHttpBinding(httpBinding);
         }
-        // should we transfer exception as serialized object
-        if (transferException != null) {
-            endpoint.setTransferException(transferException);
-        }
-        if (bridgeEndpoint != null) {
-            endpoint.setBridgeEndpoint(bridgeEndpoint);
-        }
-        if (matchOnUriPrefix != null) {
-            endpoint.setMatchOnUriPrefix(matchOnUriPrefix);
-        }
-        if (disableStreamCache != null) {
-            endpoint.setDisableStreamCache(disableStreamCache);
+        if (httpClientConfigurer != null) {
+            endpoint.setHttpClientConfigurer(httpClientConfigurer);
         }
 
-        setProperties(endpoint, parameters);
         return endpoint;
     }
 
@@ -223,19 +220,25 @@ public class HttpComponent extends Heade
         return port;
     }
 
-    protected void registerPort(boolean secure, int port) {
+    protected void registerPort(boolean secure, X509HostnameVerifier x509HostnameVerifier, int port) throws NoSuchAlgorithmException {
         SchemeRegistry registry = clientConnectionManager.getSchemeRegistry();
         if (secure) {
             // must register both https and https4
-            registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), port));
+            SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
+            socketFactory.setHostnameVerifier(x509HostnameVerifier);
+            registry.register(new Scheme("https4", port, socketFactory));
+            registry.register(new Scheme("https", port, SSLSocketFactory.getSocketFactory()));
             LOG.info("Registering SSL scheme https on port " + port);
-            registry.register(new Scheme("https4", SSLSocketFactory.getSocketFactory(), port));
+            
+            socketFactory = SSLSocketFactory.getSocketFactory();
+            socketFactory.setHostnameVerifier(x509HostnameVerifier);
+            registry.register(new Scheme("https4", port, socketFactory));
             LOG.info("Registering SSL scheme https4 on port " + port);
         } else {
             // must register both http and http4
-            registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), port));
+            registry.register(new Scheme("http", port, new PlainSocketFactory()));
             LOG.info("Registering PLAIN scheme http on port " + port);
-            registry.register(new Scheme("http4", PlainSocketFactory.getSocketFactory(), port));
+            registry.register(new Scheme("http4", port, new PlainSocketFactory()));
             LOG.info("Registering PLAIN scheme http4 on port " + port);
         }
     }
@@ -243,17 +246,13 @@ public class HttpComponent extends Heade
     protected ClientConnectionManager createConnectionManager() {
         SchemeRegistry schemeRegistry = new SchemeRegistry();
 
-        // configure additional configurations
-        HttpParams params = new BasicHttpParams();
-        ConnManagerParamBean param = new ConnManagerParamBean(params);
+        ThreadSafeClientConnManager answer = new ThreadSafeClientConnManager(schemeRegistry);
         if (getMaxTotalConnections() > 0) {
-            param.setMaxTotalConnections(getMaxTotalConnections());
+            answer.setMaxTotal(getMaxTotalConnections());
         }
         if (getConnectionsPerRoute() > 0) {
-            param.setConnectionsPerRoute(new ConnPerRouteBean(getConnectionsPerRoute()));
+            answer.setDefaultMaxPerRoute(getConnectionsPerRoute());
         }
-
-        ThreadSafeClientConnManager answer = new ThreadSafeClientConnManager(params, schemeRegistry);
         LOG.info("Created ClientConnectionManager " + answer);
 
         return answer;
@@ -271,9 +270,6 @@ public class HttpComponent extends Heade
         ConnConnectionParamBean connConnectionParamBean = new ConnConnectionParamBean(clientParams);
         IntrospectionSupport.setProperties(connConnectionParamBean, parameters, "httpClient.");
 
-        ConnManagerParamBean connManagerParamBean = new ConnManagerParamBean(clientParams);
-        IntrospectionSupport.setProperties(connManagerParamBean, parameters, "httpClient.");
-
         ConnRouteParamBean connRouteParamBean = new ConnRouteParamBean(clientParams);
         IntrospectionSupport.setProperties(connRouteParamBean, parameters, "httpClient.");
 
@@ -339,21 +335,21 @@ public class HttpComponent extends Heade
     }
 
     @Override
-    public void start() throws Exception {
-        super.start();
+    public void doStart() throws Exception {
+        super.doStart();
         if (clientConnectionManager == null) {
             clientConnectionManager = createConnectionManager();
         }
     }
 
     @Override
-    public void stop() throws Exception {
+    public void doStop() throws Exception {
         // shutdown connection manager
         if (clientConnectionManager != null) {
             LOG.info("Shutting down ClientConnectionManager: " + clientConnectionManager);
             clientConnectionManager.shutdown();
             clientConnectionManager = null;
         }
-        super.stop();
+        super.doStop();
     }
 }

Modified: camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpEndpoint.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpEndpoint.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpEndpoint.java (original)
+++ camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpEndpoint.java Sun Jan 30 17:29:42 2011
@@ -196,6 +196,22 @@ public class HttpEndpoint extends Defaul
     public void setBinding(HttpBinding binding) {
         this.binding = binding;
     }
+    
+    /**
+     * Used from the IntrospectionSupport in HttpComponent.
+     * @param binding
+     */
+    public void setHttpBinding(HttpBinding binding) {
+        this.binding = binding;
+    }
+    
+    /**
+     * Used from the IntrospectionSupport in HttpComponent.
+     * @param binding
+     */
+    public void setHttpBindingRef(HttpBinding binding) {
+        this.binding = binding;
+    }
 
     public String getPath() {
         return httpUri.getPath();

Modified: camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpPollingConsumer.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpPollingConsumer.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpPollingConsumer.java (original)
+++ camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpPollingConsumer.java Sun Jan 30 17:29:42 2011
@@ -31,6 +31,7 @@ import org.apache.http.client.HttpClient
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpRequestBase;
 import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.util.EntityUtils;
 
 /**
  * A polling HTTP consumer which by default performs a GET
@@ -102,7 +103,7 @@ public class HttpPollingConsumer extends
         } finally {
             if (responeEntity != null) {
                 try {
-                    responeEntity.consumeContent();
+                    EntityUtils.consume(responeEntity);
                 } catch (IOException e) {
                     // nothing what we can do
                 }

Modified: camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpProducer.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpProducer.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpProducer.java (original)
+++ camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpProducer.java Sun Jan 30 17:29:42 2011
@@ -29,7 +29,6 @@ import java.util.Map;
 
 import org.apache.camel.CamelExchangeException;
 import org.apache.camel.Exchange;
-import org.apache.camel.InvalidPayloadException;
 import org.apache.camel.Message;
 import org.apache.camel.component.file.GenericFile;
 import org.apache.camel.component.http4.helper.GZIPHelper;
@@ -54,6 +53,7 @@ import org.apache.http.entity.FileEntity
 import org.apache.http.entity.InputStreamEntity;
 import org.apache.http.entity.StringEntity;
 import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.util.EntityUtils;
 
 /**
  * @version $Revision$
@@ -110,9 +110,9 @@ public class HttpProducer extends Defaul
                 populateResponse(exchange, httpRequest, httpResponse, in, strategy, responseCode);
             }
         } finally {
-            if (httpResponse != null && httpResponse.getEntity() != null) {
+            if (httpResponse != null) {
                 try {
-                    httpResponse.getEntity().consumeContent();
+                    EntityUtils.consume(httpResponse.getEntity());
                 } catch (IOException e) {
                     // nothing we could do
                 }

Modified: camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/helper/HttpHelper.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/helper/HttpHelper.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/helper/HttpHelper.java (original)
+++ camel/trunk/components/camel-http4/src/main/java/org/apache/camel/component/http4/helper/HttpHelper.java Sun Jan 30 17:29:42 2011
@@ -65,7 +65,6 @@ public final class HttpHelper {
      */
     public static void writeObjectToServletResponse(ServletResponse response, Object target) throws IOException {
         response.setContentType(HttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT);
-        ObjectOutputStream oos = new ObjectOutputStream(response.getOutputStream());
         writeObjectToStream(response.getOutputStream(), target);
     }
 

Modified: camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpCompressionTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpCompressionTest.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpCompressionTest.java (original)
+++ camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpCompressionTest.java Sun Jan 30 17:29:42 2011
@@ -127,6 +127,11 @@ public class HttpCompressionTest extends
             public long getContentLength() {
                 return -1;
             }
+            
+            @Override
+            public boolean isStreaming() {
+                return false;
+            }
         }
     }
 
@@ -160,7 +165,11 @@ public class HttpCompressionTest extends
             public long getContentLength() {
                 return -1;
             }
+            
+            @Override
+            public boolean isStreaming() {
+                return false;
+            }
         }
     }
-    
 }
\ No newline at end of file

Modified: camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpConcurrentTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpConcurrentTest.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpConcurrentTest.java (original)
+++ camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpConcurrentTest.java Sun Jan 30 17:29:42 2011
@@ -71,10 +71,10 @@ public class HttpConcurrentTest extends 
 
     private void doSendMessages(int files, int poolSize) throws Exception {
         ExecutorService executor = Executors.newFixedThreadPool(poolSize);
-        Map<Integer, Future> responses = new ConcurrentHashMap<Integer, Future>();
+        Map<Integer, Future<Object>> responses = new ConcurrentHashMap<Integer, Future<Object>>();
         for (int i = 0; i < files; i++) {
             final int index = i;
-            Future out = executor.submit(new Callable<Object>() {
+            Future<Object> out = executor.submit(new Callable<Object>() {
                 public Object call() throws Exception {
                     return template.requestBody("http4://" + getHostName() + ":" + getPort(), null, String.class);
                 }
@@ -86,7 +86,7 @@ public class HttpConcurrentTest extends 
 
         // get all responses
         Set<Object> unique = new HashSet<Object>();
-        for (Future future : responses.values()) {
+        for (Future<Object> future : responses.values()) {
             unique.add(future.get());
         }
 

Modified: camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpEndpointOptionsNotChangeComponentTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpEndpointOptionsNotChangeComponentTest.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpEndpointOptionsNotChangeComponentTest.java (original)
+++ camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpEndpointOptionsNotChangeComponentTest.java Sun Jan 30 17:29:42 2011
@@ -72,5 +72,4 @@ public class HttpEndpointOptionsNotChang
 
     private class MyOtherBinding extends DefaultHttpBinding {
     }
-
 }

Modified: camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpProxyServerTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpProxyServerTest.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpProxyServerTest.java (original)
+++ camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpProxyServerTest.java Sun Jan 30 17:29:42 2011
@@ -184,11 +184,11 @@ public class HttpProxyServerTest extends
     }
 
     private String getProxyHost() {
-        return proxy.getServiceHostName();
+        return proxy.getServiceAddress().getHostName();
     }
 
     private int getProxyPort() {
-        return proxy.getServicePort();
+        return proxy.getServiceAddress().getPort();
     }
 
     class RequestProxyBasicAuth implements HttpRequestInterceptor {

Modified: camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpServerTestSupport.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpServerTestSupport.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpServerTestSupport.java (original)
+++ camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpServerTestSupport.java Sun Jan 30 17:29:42 2011
@@ -20,9 +20,11 @@ import javax.net.ssl.SSLContext;
 
 import org.apache.camel.test.junit4.CamelTestSupport;
 import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpResponseFactory;
 import org.apache.http.localserver.LocalTestServer;
 import org.apache.http.params.HttpParams;
 import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.HttpExpectationVerifier;
 import org.junit.After;
 import org.junit.Before;
 
@@ -43,6 +45,8 @@ public abstract class HttpServerTestSupp
         localServer = new LocalTestServer(
                 getBasicHttpProcessor(),
                 getConnectionReuseStrategy(),
+                getHttpResponseFactory(),
+                getHttpExpectationVerifier(),
                 getHttpParams(),
                 getSSLContext());
         registerHandler(localServer);
@@ -80,6 +84,26 @@ public abstract class HttpServerTestSupp
     protected ConnectionReuseStrategy getConnectionReuseStrategy() {
         return null;
     }
+    
+    /**
+     * Returns the org.apache.http.HttpResponseFactory which should be used
+     * by the server.
+     *
+     * @return httpResponseFactory
+     */
+    protected HttpResponseFactory getHttpResponseFactory() {
+        return null;
+    }
+    
+    /**
+     * Returns the org.apache.http.protocol.HttpExpectationVerifier which should be used
+     * by the server.
+     *
+     * @return httpExpectationVerifier
+     */
+    protected HttpExpectationVerifier getHttpExpectationVerifier() {
+        return null;
+    }
 
     /**
      * Returns the org.apache.http.params.HttpParams which should be used by
@@ -116,7 +140,7 @@ public abstract class HttpServerTestSupp
      * @return hostName
      */
     protected String getHostName() {
-        return localServer.getServiceHostName();
+        return localServer.getServiceAddress().getHostName();
     }
 
     /**
@@ -125,6 +149,6 @@ public abstract class HttpServerTestSupp
      * @return port
      */
     protected int getPort() {
-        return localServer.getServicePort();
+        return localServer.getServiceAddress().getPort();
     }
 }
\ No newline at end of file

Modified: camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpsAuthenticationTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpsAuthenticationTest.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpsAuthenticationTest.java (original)
+++ camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpsAuthenticationTest.java Sun Jan 30 17:29:42 2011
@@ -19,6 +19,8 @@ package org.apache.camel.component.http4
 import org.apache.camel.Exchange;
 import org.apache.camel.Processor;
 import org.apache.camel.component.http4.handler.AuthenticationValidationHandler;
+import org.apache.camel.impl.JndiRegistry;
+import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
 import org.apache.http.localserver.RequestBasicAuth;
 import org.apache.http.localserver.ResponseBasicUnauthorized;
 import org.apache.http.protocol.BasicHttpProcessor;
@@ -34,11 +36,19 @@ public class HttpsAuthenticationTest ext
     private String user = "camel";
     private String password = "password";
 
+    @Override
+    protected JndiRegistry createRegistry() throws Exception {
+        JndiRegistry registry = super.createRegistry();
+        registry.bind("x509HostnameVerifier", new AllowAllHostnameVerifier());
+
+        return registry;
+    }
+
     @Test
     public void httpsGetWithAuthentication() throws Exception {
         localServer.register("/", new AuthenticationValidationHandler("GET", null, null, getExpectedContent(), user, password));
 
-        Exchange exchange = template.request("https4://127.0.0.1:" + getPort() + "/?username=camel&password=password", new Processor() {
+        Exchange exchange = template.request("https4://127.0.0.1:" + getPort() + "/?username=camel&password=password&x509HostnameVerifier=x509HostnameVerifier", new Processor() {
             public void process(Exchange exchange) throws Exception {
             }
         });

Modified: camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpsGetTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpsGetTest.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpsGetTest.java (original)
+++ camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpsGetTest.java Sun Jan 30 17:29:42 2011
@@ -19,6 +19,8 @@ package org.apache.camel.component.http4
 import org.apache.camel.Exchange;
 import org.apache.camel.Processor;
 import org.apache.camel.component.http4.handler.BasicValidationHandler;
+import org.apache.camel.impl.JndiRegistry;
+import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
 import org.junit.Test;
 
 /**
@@ -27,11 +29,19 @@ import org.junit.Test;
  */
 public class HttpsGetTest extends BaseHttpsTest {
 
+    @Override
+    protected JndiRegistry createRegistry() throws Exception {
+        JndiRegistry registry = super.createRegistry();
+        registry.bind("x509HostnameVerifier", new AllowAllHostnameVerifier());
+
+        return registry;
+    }
+
     @Test
     public void httpsGet() throws Exception {
         localServer.register("/mail/", new BasicValidationHandler("GET", null, null, getExpectedContent()));
 
-        Exchange exchange = template.request("https4://127.0.0.1:" + getPort() + "/mail/", new Processor() {
+        Exchange exchange = template.request("https4://127.0.0.1:" + getPort() + "/mail/?x509HostnameVerifier=x509HostnameVerifier", new Processor() {
             public void process(Exchange exchange) throws Exception {
             }
         });

Modified: camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/helper/GZIPHelperTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/helper/GZIPHelperTest.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/helper/GZIPHelperTest.java (original)
+++ camel/trunk/components/camel-http4/src/test/java/org/apache/camel/component/http4/helper/GZIPHelperTest.java Sun Jan 30 17:29:42 2011
@@ -23,15 +23,15 @@ import java.io.InputStream;
 import org.apache.camel.Message;
 import org.apache.camel.converter.IOConverter;
 import org.apache.camel.impl.DefaultMessage;
-import org.junit.Assert;
 import org.junit.Test;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-public class GZIPHelperTest extends Assert {
+public class GZIPHelperTest {
 
     private static byte[] sampleBytes = new byte[]{1, 2, 3, 1, 2, 3};
     private static String sampleString = "<Hello>World</Hello>";

Modified: camel/trunk/parent/pom.xml
URL: http://svn.apache.org/viewvc/camel/trunk/parent/pom.xml?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/parent/pom.xml (original)
+++ camel/trunk/parent/pom.xml Sun Jan 30 17:29:42 2011
@@ -81,8 +81,8 @@
     <hibernate-entitymanager-version>3.2.1.ga</hibernate-entitymanager-version>
     <hsqldb-version>1.8.0.7</hsqldb-version>
     <httpunit-version>1.6.2</httpunit-version>
-    <httpcore4-version>4.0.1</httpcore4-version>
-    <httpclient4-version>4.0.3</httpclient4-version>
+    <httpcore4-version>4.1</httpcore4-version>
+    <httpclient4-version>4.1</httpclient4-version>
     <httpclient-version>3.1</httpclient-version>
     <icu4j-version>4.0.1</icu4j-version>
     <jackson-version>1.6.4</jackson-version>

Modified: camel/trunk/tests/camel-itest/src/test/java/org/apache/camel/itest/http/HttpTestServer.java
URL: http://svn.apache.org/viewvc/camel/trunk/tests/camel-itest/src/test/java/org/apache/camel/itest/http/HttpTestServer.java?rev=1065326&r1=1065325&r2=1065326&view=diff
==============================================================================
--- camel/trunk/tests/camel-itest/src/test/java/org/apache/camel/itest/http/HttpTestServer.java (original)
+++ camel/trunk/tests/camel-itest/src/test/java/org/apache/camel/itest/http/HttpTestServer.java Sun Jan 30 17:29:42 2011
@@ -16,50 +16,391 @@
  */
 package org.apache.camel.itest.http;
 
+import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 
-import org.apache.http.localserver.LocalTestServer;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocketFactory;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpServerConnection;
+import org.apache.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.http.impl.DefaultHttpResponseFactory;
+import org.apache.http.impl.DefaultHttpServerConnection;
+import org.apache.http.localserver.EchoHandler;
+import org.apache.http.localserver.RandomHandler;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.CoreProtocolPNames;
 import org.apache.http.params.HttpParams;
+import org.apache.http.params.SyncBasicHttpParams;
+import org.apache.http.protocol.BasicHttpContext;
 import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpExpectationVerifier;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.http.protocol.HttpRequestHandlerRegistry;
+import org.apache.http.protocol.HttpService;
+import org.apache.http.protocol.ImmutableHttpProcessor;
+import org.apache.http.protocol.ResponseConnControl;
+import org.apache.http.protocol.ResponseContent;
+import org.apache.http.protocol.ResponseDate;
+import org.apache.http.protocol.ResponseServer;
 
 /**
- * Subclass the org.apache.http.localserver.LocalTestServer to choose a specific
- * port.
+ * Copy of org.apache.http.localserver.LocalTestServer to use a specific port.
  * 
  * @author muellerc
  */
-public class HttpTestServer extends LocalTestServer {
+public class HttpTestServer {
 
     /**
-     * The local address to bind to. The host is an IP number rather than
-     * "localhost" to avoid surprises on hosts that map "localhost" to an IPv6
-     * address or something else. The port is 18080 by default.
+     * The local address to bind to.
+     * The host is an IP number rather than "localhost" to avoid surprises
+     * on hosts that map "localhost" to an IPv6 address or something else.
+     * The port is 0 to let the system pick one.
      */
-    public static final InetSocketAddress TEST_SERVER_ADDR = new InetSocketAddress(
-            "127.0.0.1", 18080);
+    public final static InetSocketAddress TEST_SERVER_ADDR =
+        new InetSocketAddress("localhost", 18080);
+
+    /** The request handler registry. */
+    private final HttpRequestHandlerRegistry handlerRegistry;
+
+    private final HttpService httpservice;
+
+    /** Optional SSL context */
+    private final SSLContext sslcontext;
+
+    /** The server socket, while being served. */
+    private volatile ServerSocket servicedSocket;
+
+    /** The request listening thread, while listening. */
+    private volatile ListenerThread listenerThread;
+
+    /** Set of active worker threads */
+    private final Set<Worker> workers;
 
-    public HttpTestServer(BasicHttpProcessor proc, HttpParams params) {
-        super(proc, params);
+    /** The number of connections this accepted. */
+    private final AtomicInteger acceptedConnections = new AtomicInteger(0);
+
+    /**
+     * Creates a new test server.
+     *
+     * @param proc      the HTTP processors to be used by the server, or
+     *                  <code>null</code> to use a
+     *                  {@link #newProcessor default} processor
+     * @param reuseStrat the connection reuse strategy to be used by the
+     *                  server, or <code>null</code> to use
+     *                  {@link #newConnectionReuseStrategy() default}
+     *                  strategy.
+     * @param params    the parameters to be used by the server, or
+     *                  <code>null</code> to use
+     *                  {@link #newDefaultParams default} parameters
+     * @param sslcontext optional SSL context if the server is to leverage
+     *                   SSL/TLS transport security
+     */
+    public HttpTestServer(
+            final BasicHttpProcessor proc,
+            final ConnectionReuseStrategy reuseStrat,
+            final HttpResponseFactory responseFactory,
+            final HttpExpectationVerifier expectationVerifier,
+            final HttpParams params,
+            final SSLContext sslcontext) {
+        super();
+        this.handlerRegistry = new HttpRequestHandlerRegistry();
+        this.workers = Collections.synchronizedSet(new HashSet<Worker>());
+        this.httpservice = new HttpService(
+            proc != null ? proc : newProcessor(),
+            reuseStrat != null ? reuseStrat: newConnectionReuseStrategy(),
+            responseFactory != null ? responseFactory: newHttpResponseFactory(),
+            handlerRegistry,
+            expectationVerifier,
+            params != null ? params : newDefaultParams());
+        this.sslcontext = sslcontext;
     }
 
     /**
-     * Starts this test server. Use {@link #getServicePort getServicePort} to
-     * obtain the port number afterwards.
+     * Creates a new test server with SSL/TLS encryption.
+     *
+     * @param sslcontext SSL context
+     */
+    public HttpTestServer(final SSLContext sslcontext) {
+        this(null, null, null, null, null, sslcontext);
+    }
+
+    /**
+     * Creates a new test server.
+     *
+     * @param proc      the HTTP processors to be used by the server, or
+     *                  <code>null</code> to use a
+     *                  {@link #newProcessor default} processor
+     * @param params    the parameters to be used by the server, or
+     *                  <code>null</code> to use
+     *                  {@link #newDefaultParams default} parameters
+     */
+    public HttpTestServer(
+            BasicHttpProcessor proc,
+            HttpParams params) {
+        this(proc, null, null, null, params, null);
+    }
+
+    /**
+     * Obtains an HTTP protocol processor with default interceptors.
+     *
+     * @return  a protocol processor for server-side use
+     */
+    protected HttpProcessor newProcessor() {
+        return new ImmutableHttpProcessor(
+                new HttpResponseInterceptor[] {
+                        new ResponseDate(),
+                        new ResponseServer(),
+                        new ResponseContent(),
+                        new ResponseConnControl()
+                });
+    }
+
+
+    /**
+     * Obtains a set of reasonable default parameters for a server.
+     *
+     * @return  default parameters
+     */
+    protected HttpParams newDefaultParams() {
+        HttpParams params = new SyncBasicHttpParams();
+        params
+            .setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 60000)
+            .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
+            .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
+            .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
+            .setParameter(CoreProtocolPNames.ORIGIN_SERVER,
+                          "LocalTestServer/1.1");
+        return params;
+    }
+
+    protected ConnectionReuseStrategy newConnectionReuseStrategy() {
+        return new DefaultConnectionReuseStrategy();
+    }
+
+    protected HttpResponseFactory newHttpResponseFactory() {
+        return new DefaultHttpResponseFactory();
+    }
+
+    /**
+     * Returns the number of connections this test server has accepted.
+     */
+    public int getAcceptedConnectionCount() {
+        return acceptedConnections.get();
+    }
+
+    /**
+     * {@link #register Registers} a set of default request handlers.
+     * <pre>
+     * URI pattern      Handler
+     * -----------      -------
+     * /echo/*          {@link EchoHandler EchoHandler}
+     * /random/*        {@link RandomHandler RandomHandler}
+     * </pre>
+     */
+    public void registerDefaultHandlers() {
+        handlerRegistry.register("/echo/*", new EchoHandler());
+        handlerRegistry.register("/random/*", new RandomHandler());
+    }
+
+
+    /**
+     * Registers a handler with the local registry.
+     *
+     * @param pattern   the URL pattern to match
+     * @param handler   the handler to apply
+     */
+    public void register(String pattern, HttpRequestHandler handler) {
+        handlerRegistry.register(pattern, handler);
+    }
+
+
+    /**
+     * Unregisters a handler from the local registry.
+     *
+     * @param pattern   the URL pattern
+     */
+    public void unregister(String pattern) {
+        handlerRegistry.unregister(pattern);
+    }
+
+
+    /**
+     * Starts this test server.
      */
-    @Override
     public void start() throws Exception {
         if (servicedSocket != null) {
             throw new IllegalStateException(this.toString() + " already running");
         }
+        ServerSocket ssock;
+        if (sslcontext != null) {
+            SSLServerSocketFactory sf = sslcontext.getServerSocketFactory();
+            ssock = sf.createServerSocket();
+        } else {
+            ssock = new ServerSocket();
+        }
 
-        ServerSocket ssock = new ServerSocket();
         ssock.setReuseAddress(true); // probably pointless for port '0'
         ssock.bind(TEST_SERVER_ADDR);
         servicedSocket = ssock;
 
-        listenerThread = new Thread(new RequestListener());
+        listenerThread = new ListenerThread();
         listenerThread.setDaemon(false);
         listenerThread.start();
     }
+
+    /**
+     * Stops this test server.
+     */
+    public void stop() throws Exception {
+        if (servicedSocket == null) {
+            return; // not running
+        }
+        ListenerThread t = listenerThread;
+        if (t != null) {
+            t.shutdown();
+        }
+        synchronized (workers) {
+            for (Iterator<Worker> it = workers.iterator(); it.hasNext(); ) {
+                Worker worker = it.next();
+                worker.shutdown();
+            }
+        }
+    }
+
+    public void awaitTermination(long timeMs) throws InterruptedException {
+        if (listenerThread != null) {
+            listenerThread.join(timeMs);
+        }
+    }
+
+    @Override
+    public String toString() {
+        ServerSocket ssock = servicedSocket; // avoid synchronization
+        StringBuilder sb = new StringBuilder(80);
+        sb.append("LocalTestServer/");
+        if (ssock == null)
+            sb.append("stopped");
+        else
+            sb.append(ssock.getLocalSocketAddress());
+        return sb.toString();
+    }
+
+    /**
+     * Obtains the local address the server is listening on
+     *
+     * @return the service address
+     */
+    public InetSocketAddress getServiceAddress() {
+        ServerSocket ssock = servicedSocket; // avoid synchronization
+        if (ssock == null) {
+            throw new IllegalStateException("not running");
+        }
+        return (InetSocketAddress) ssock.getLocalSocketAddress();
+    }
+
+    /**
+     * The request listener.
+     * Accepts incoming connections and launches a service thread.
+     */
+    class ListenerThread extends Thread {
+
+        private volatile Exception exception;
+
+        ListenerThread() {
+            super();
+        }
+
+        @Override
+        public void run() {
+            try {
+                while (!interrupted()) {
+                    Socket socket = servicedSocket.accept();
+                    acceptedConnections.incrementAndGet();
+                    DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
+                    conn.bind(socket, httpservice.getParams());
+                    // Start worker thread
+                    Worker worker = new Worker(conn);
+                    workers.add(worker);
+                    worker.setDaemon(true);
+                    worker.start();
+                }
+            } catch (Exception ex) {
+                this.exception = ex;
+            } finally {
+                try {
+                    servicedSocket.close();
+                } catch (IOException ignore) {
+                }
+            }
+        }
+
+        public void shutdown() {
+            interrupt();
+            try {
+                servicedSocket.close();
+            } catch (IOException ignore) {
+            }
+        }
+
+        public Exception getException() {
+            return this.exception;
+        }
+
+    }
+
+    class Worker extends Thread {
+
+        private final HttpServerConnection conn;
+
+        private volatile Exception exception;
+
+        public Worker(final HttpServerConnection conn) {
+            this.conn = conn;
+        }
+
+        @Override
+        public void run() {
+            HttpContext context = new BasicHttpContext();
+            try {
+                while (this.conn.isOpen() && !Thread.interrupted()) {
+                    httpservice.handleRequest(this.conn, context);
+                }
+            } catch (Exception ex) {
+                this.exception = ex;
+            } finally {
+                workers.remove(this);
+                try {
+                    this.conn.shutdown();
+                } catch (IOException ignore) {
+                }
+            }
+        }
+
+        public void shutdown() {
+            interrupt();
+            try {
+                this.conn.shutdown();
+            } catch (IOException ignore) {
+            }
+        }
+
+        public Exception getException() {
+            return this.exception;
+        }
+
+    }
+
 }
\ No newline at end of file