You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by ri...@apache.org on 2008/01/08 18:55:41 UTC

svn commit: r610084 - in /geronimo/sandbox/AsyncHttpClient: ./ src/main/java/org/apache/ahc/ src/main/java/org/apache/ahc/codec/ src/main/java/org/apache/ahc/proxy/ src/test/java/org/apache/ahc/

Author: rickmcguire
Date: Tue Jan  8 09:55:22 2008
New Revision: 610084

URL: http://svn.apache.org/viewvc?rev=610084&view=rev
Log:
GERONIMO-3706 support for proxy
GERONIMO-3735 upgrade mina to 1.1.5


Added:
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java   (with props)
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyFilter.java   (with props)
    geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ProxyTest.java   (with props)
Modified:
    geronimo/sandbox/AsyncHttpClient/pom.xml
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java

Modified: geronimo/sandbox/AsyncHttpClient/pom.xml
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/pom.xml?rev=610084&r1=610083&r2=610084&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/pom.xml (original)
+++ geronimo/sandbox/AsyncHttpClient/pom.xml Tue Jan  8 09:55:22 2008
@@ -38,12 +38,12 @@
         <dependency>
             <groupId>org.apache.mina</groupId>
             <artifactId>mina-core</artifactId>
-            <version>1.1.2</version>
+            <version>1.1.5</version>
         </dependency>
         <dependency>
             <groupId>org.apache.mina</groupId>
             <artifactId>mina-filter-ssl</artifactId>
-            <version>1.1.2</version>
+            <version>1.1.5</version>
         </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
@@ -107,6 +107,10 @@
                 <artifactId>maven-surefire-plugin</artifactId>
                 <configuration>
                     <forkMode>pertest</forkMode>
+                    <excludes>
+                        <exclude>**/ProxyTest.java</exclude>
+                        <exclude>**/AbstractTest.java</exclude>
+                    </excludes>
                 </configuration>
             </plugin>
             <plugin>

Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java?rev=610084&r1=610083&r2=610084&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java Tue Jan  8 09:55:22 2008
@@ -20,6 +20,9 @@
 package org.apache.ahc;
 
 import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URL;
 import java.security.GeneralSecurityException;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Executor;
@@ -33,6 +36,7 @@
 import org.apache.ahc.codec.HttpRequestMessage;
 import org.apache.ahc.codec.ResponseFuture;
 import org.apache.ahc.codec.SessionCache;
+import org.apache.ahc.proxy.ProxyFilter;
 import org.apache.ahc.ssl.TrustManagerFactoryImpl;
 import org.apache.ahc.util.AsyncHttpClientException;
 import org.apache.mina.common.ConnectFuture;
@@ -144,6 +148,11 @@
     /** flag to make this as having been disposed of */
     private boolean destroyed = false; 
 
+    public static final String SSL_FILTER = "SSL";
+    public static final String PROTOCOL_FILTER = "protocolFilter";
+    public static final String PROXY_FILTER = "proxyFilter";
+    public static final String EVENT_THREAD_POOL_FILTER = "eventThreadPoolFilter";
+
     /**
      * Returns if it reuses established connections for more requests.
      * 
@@ -445,11 +454,13 @@
         // *IF* connection reuse is enabled, we should see if we have a cached 
         // connection first; if not, always open a new one
         ConnectFuture future = null;
-        if (reuseConnection) {
-            future = getCachedConnection(message);
-        } else {
-            // add the Connection close header explicitly
-            message.setHeader(HttpDecoder.CONNECTION, HttpDecoder.CLOSE);
+        if (!message.isProxyEnabled()) {
+            if (reuseConnection) {
+                future = getCachedConnection(message);
+            } else {
+                // add the Connection close header explicitly
+                message.setHeader(HttpDecoder.CONNECTION, HttpDecoder.CLOSE);
+            }
         }
         
         // if no cached connection is found or keep-alive is disabled, force a
@@ -458,7 +469,11 @@
             future = openConnection(message);
         }
         ResponseFuture response = message.getResponseFuture();
-        future.addListener(new FutureListener(message, response, connectionRetries));
+        FutureListener listener = 
+                message.isProxyEnabled() ? 
+                        new ProxyFutureListener(message, response) :
+                        new FutureListener(message, response);
+        future.addListener(listener);
         return response;
     }
     
@@ -472,13 +487,17 @@
      *                 attempt.  This should be one less than the count
      *                 used for the previous attempt.
      */
-    private void retryConnection(HttpRequestMessage message, ResponseFuture response, int retries) {
+    private void retryConnection(HttpRequestMessage message, ResponseFuture response, FutureListener listener) {
         ConnectFuture future = openConnection(message);
-        future.addListener(new FutureListener(message, response, retries));
+        future.addListener(listener);
     }
     
     private ConnectFuture openConnection(HttpRequestMessage message) {
-        return connector.connect(new InetSocketAddress(message.getHost(), message.getPort()), handler);
+        InetSocketAddress remote = 
+                message.isProxyEnabled() ?
+                        message.getProxyConfiguration().getProxyAddress(message.getUrl()) :
+                        new InetSocketAddress(message.getHost(), message.getPort());
+        return connector.connect(remote, handler);
     }
     
     private ConnectFuture getCachedConnection(HttpRequestMessage message) {
@@ -543,17 +562,13 @@
      * connection occurs, it is also responsible for sending the request.
      */
     class FutureListener implements IoFutureListener {
-        static final String SSL_FILTER = "SSL";
-        static final String PROTOCOL_FILTER = "protocolFilter";
-        static final String EVENT_THREAD_POOL_FILTER = "eventThreadPoolFilter";
-
         /** The request. */
         final HttpRequestMessage request;
         /** The response future. */
         final ResponseFuture response;
         
         /** The count of additional retries for the connection */
-        int retries = 0; 
+        volatile int retries = getConnectionRetries(); 
 
         /**
          * Instantiates a new future listener for a connection.
@@ -561,10 +576,9 @@
          * @param request the <code>HttpRequestMessage</code> request that is to be sent.
          * @param response the response future object.
          */
-        public FutureListener(HttpRequestMessage request, ResponseFuture response, int retries) {
+        public FutureListener(HttpRequestMessage request, ResponseFuture response) {
             this.request = request;
             this.response = response;
-            this.retries = retries; 
         }
 
         /**
@@ -583,38 +597,18 @@
                 addSSLFilter(sess);
                 // add the protocol filter (if it's not there already like in a
                 // reused session)
-                if (!sess.getFilterChain().contains(PROTOCOL_FILTER)) {
-                    sess.getFilterChain().addLast(PROTOCOL_FILTER, new ProtocolCodecFilter(
-                            new HttpProtocolCodecFactory()));
-                }
+                addProtocolCodecFilter(sess);
                 // (optional) add the executor filter for the event thread pool 
                 // (if it's not there already like in a reused session)
-                if (eventThreadPool != null && 
-                        !sess.getFilterChain().contains(EVENT_THREAD_POOL_FILTER)) {
-                    sess.getFilterChain().addLast(EVENT_THREAD_POOL_FILTER, 
-                            new ExecutorFilter(eventThreadPool));
-                }
+                addEventThreadPoolFilter(sess);
                 
-                sess.setAttribute(HttpIoHandler.CURRENT_REQUEST, request);
-
-                sess.setAttachment(AsyncHttpClient.this);
-
-                //Set the socket parameters on successfully obtaining the session
-                SocketSessionConfig config = (SocketSessionConfig) sess.getConfig();
-                config.setKeepAlive(keepAlive);
-                config.setOobInline(oobInline);
-                config.setReceiveBufferSize(receiveBufferSize);
-                config.setReuseAddress(reuseAddress);
-                config.setSendBufferSize(sendBufferSize);
-                config.setSoLinger(soLinger);
-                config.setTcpNoDelay(tcpNoDelay);
-                config.setTrafficClass(trafficClass);
+                configureSession(sess);
 
                 sess.write(request);
             } else {
-                if (retries > 0) {
+                if (retries-- > 0) {
                     // go retry this connection 
-                    retryConnection(request, response, --retries); 
+                    retryConnection(request, response, this); 
                 }
                 else {
                     try {
@@ -628,6 +622,38 @@
             }
         }
 
+        protected void configureSession(IoSession sess) {
+            sess.setAttribute(HttpIoHandler.CURRENT_REQUEST, request);
+
+            sess.setAttachment(AsyncHttpClient.this);
+
+            //Set the socket parameters on successfully obtaining the session
+            SocketSessionConfig config = (SocketSessionConfig) sess.getConfig();
+            config.setKeepAlive(keepAlive);
+            config.setOobInline(oobInline);
+            config.setReceiveBufferSize(receiveBufferSize);
+            config.setReuseAddress(reuseAddress);
+            config.setSendBufferSize(sendBufferSize);
+            config.setSoLinger(soLinger);
+            config.setTcpNoDelay(tcpNoDelay);
+            config.setTrafficClass(trafficClass);
+        }
+
+        protected void addEventThreadPoolFilter(IoSession sess) {
+            if (eventThreadPool != null && 
+                    !sess.getFilterChain().contains(EVENT_THREAD_POOL_FILTER)) {
+                sess.getFilterChain().addLast(EVENT_THREAD_POOL_FILTER, 
+                        new ExecutorFilter(eventThreadPool));
+            }
+        }
+
+        protected void addProtocolCodecFilter(IoSession sess) {
+            if (!sess.getFilterChain().contains(PROTOCOL_FILTER)) {
+                sess.getFilterChain().addLast(PROTOCOL_FILTER, new ProtocolCodecFilter(
+                        new HttpProtocolCodecFactory()));
+            }
+        }
+
         private void addSSLFilter(IoSession sess) {
             String scheme = request.getUrl().getProtocol();
             
@@ -637,14 +663,7 @@
                 // session
                 if (!sess.getFilterChain().contains(SSL_FILTER)) {
                     try {
-                        SSLContext context = request.getSSLContext();
-                        if (context == null) {
-                            // if the caller did not provide an SSL context
-                            // create a default SSL context
-                            context = createDefaultSSLContext();
-                        }
-                        SSLFilter sslFilter = new SSLFilter(context);
-                        sslFilter.setUseClientMode(true);
+                        SSLFilter sslFilter = createSSLFilter();
                         sess.getFilterChain().addLast(SSL_FILTER, sslFilter);
                     } catch (GeneralSecurityException e) {
                         try {
@@ -657,6 +676,18 @@
             }
         }
 
+        protected SSLFilter createSSLFilter() throws GeneralSecurityException {
+            SSLContext context = request.getSSLContext();
+            if (context == null) {
+                // if the caller did not provide an SSL context
+                // create a default SSL context
+                context = createDefaultSSLContext();
+            }
+            SSLFilter sslFilter = new SSLFilter(context);
+            sslFilter.setUseClientMode(true);
+            return sslFilter;
+        }
+
         /**
          * Creates a default SSL context in case it was not provided by the
          * caller.
@@ -670,5 +701,70 @@
             return context;
         }
 
+    }
+    
+    class ProxyFutureListener extends FutureListener {
+        public ProxyFutureListener(HttpRequestMessage request, 
+                                   ResponseFuture response) {
+            super(request, response);
+        }
+
+        @Override
+        public void operationComplete(IoFuture future) {
+            ConnectFuture connectFuture = (ConnectFuture)future;
+            if (connectFuture.isConnected()) {
+                IoSession session = future.getSession();
+                // add the protocol filter (if it's not there already like in a
+                // reused session)
+                addProtocolCodecFilter(session);
+                addProxyFilter(session);
+                // (optional) add the executor filter for the event thread pool 
+                // (if it's not there already like in a reused session)
+                addEventThreadPoolFilter(session);
+                
+                configureSession(session);
+                
+                // write the connect request if the protocol is https
+                String protocol = request.getUrl().getProtocol();
+                if (protocol.toLowerCase().equals("https")) {
+                    session.write(createConnectRequest());
+                } else {
+                    session.write(request);
+                }
+            } else {
+                super.operationComplete(future);
+            }
+        }
+        
+        private HttpRequestMessage createConnectRequest() {
+            try {
+                HttpRequestMessage req = new HttpRequestMessage(new URL("http", request.getHost(), request.getPort(), ""), null);
+                req.setRequestMethod(HttpRequestMessage.REQUEST_CONNECT);
+                return req;
+            } catch (MalformedURLException e) {
+                e.printStackTrace();
+            } catch (ProtocolException e) {
+                e.printStackTrace();
+            }
+            // this can't happen
+            return null;
+        }
+
+        private void addProxyFilter(IoSession session) {
+            if (!session.getFilterChain().contains(PROXY_FILTER)) {
+                String scheme = request.getUrl().getProtocol();
+                ProxyFilter proxyFilter = null;
+                if (scheme.toLowerCase().equals("https")) {
+                    try {
+                        proxyFilter = new ProxyFilter(createSSLFilter());
+                    } catch (GeneralSecurityException e) {
+                        e.printStackTrace(); // this normally cannot happen
+                    }
+                } else {
+                    proxyFilter = new ProxyFilter();
+                }
+                session.getFilterChain().addLast(PROXY_FILTER, proxyFilter);
+            }
+        }
     }
 }

Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java?rev=610084&r1=610083&r2=610084&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java Tue Jan  8 09:55:22 2008
@@ -106,16 +106,26 @@
             CharsetEncoder encoder = Charset.forName(HttpMessage.HTTP_ELEMENT_CHARSET).newEncoder();
             buf.putString(msg.getRequestMethod(), encoder);
             buf.putString(" ", encoder);
-            buf.putString(msg.getUrl().getFile(), encoder);
-            //If its a GET, append the attributes
-            if (msg.getRequestMethod().equals(HttpRequestMessage.REQUEST_GET) && attrCount > 0) {
-                //If there is not already a ? in the query, append one, otherwise append a &
-                if (!msg.getUrl().getFile().contains("?")) {
-                    buf.putString("?", encoder);
+            if (msg.getRequestMethod().equals(HttpRequestMessage.REQUEST_CONNECT)) {
+                buf.putString(msg.getHost(), encoder);
+                buf.putString(":", encoder);
+                buf.putString(msg.getPort() + "", encoder);
+            } else {
+                if (msg.isProxyEnabled() && !msg.getProtocol().toLowerCase().equals("https")) {
+                    buf.putString(msg.getUrl().toString(), encoder);
                 } else {
-                    buf.putString("&", encoder);
+                    buf.putString(msg.getUrl().getFile(), encoder);
+                }
+                //If its a GET, append the attributes
+                if (msg.getRequestMethod().equals(HttpRequestMessage.REQUEST_GET) && attrCount > 0) {
+                    //If there is not already a ? in the query, append one, otherwise append a &
+                    if (!msg.getUrl().getFile().contains("?")) {
+                        buf.putString("?", encoder);
+                    } else {
+                        buf.putString("&", encoder);
+                    }
+                    buf.putString(urlAttrs, encoder);
                 }
-                buf.putString(urlAttrs, encoder);
             }
             buf.putString(" HTTP/1.1", encoder);
             buf.put(CRLF);

Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java?rev=610084&r1=610083&r2=610084&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java Tue Jan  8 09:55:22 2008
@@ -32,6 +32,7 @@
 import org.apache.ahc.auth.AuthScope;
 import org.apache.ahc.auth.AuthState;
 import org.apache.ahc.auth.Credentials;
+import org.apache.ahc.proxy.ProxyConfiguration;
 
 /**
  * The Class HttpRequestMessage. This is an object representation of an HTTP request.
@@ -82,6 +83,8 @@
      * The Constant REQUEST_TRACE.
      */
     public static final String REQUEST_TRACE = "TRACE";
+    
+    public static final String REQUEST_CONNECT = "CONNECT";
 
     /**
      * The request method.
@@ -153,6 +156,8 @@
      * SSL context for https
      */
     private SSLContext sslContext;
+    
+    private ProxyConfiguration proxyConfig;
 
     /**
      * Instantiates a new http request message.
@@ -249,7 +254,8 @@
             || requestMethod.equals(REQUEST_OPTIONS)
             || requestMethod.equals(REQUEST_PUT)
             || requestMethod.equals(REQUEST_DELETE)
-            || requestMethod.equals(REQUEST_TRACE)) {
+            || requestMethod.equals(REQUEST_TRACE)
+            || requestMethod.equals(REQUEST_CONNECT)) {
             this.requestMethod = requestMethod;
             return;
         }
@@ -538,5 +544,17 @@
      */
     public void setSSLContext(SSLContext sslContext) {
         this.sslContext = sslContext;
+    }
+    
+    public ProxyConfiguration getProxyConfiguration() {
+        return proxyConfig;
+    }
+    
+    public void setProxyConfiguration(ProxyConfiguration config) {
+        proxyConfig = config;
+    }
+    
+    public boolean isProxyEnabled() {
+        return proxyConfig != null && !proxyConfig.isExcluded(getUrl());
     }
 }

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java?rev=610084&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java Tue Jan  8 09:55:22 2008
@@ -0,0 +1,180 @@
+package org.apache.ahc.proxy;
+
+import java.net.InetSocketAddress;
+import java.net.InetAddress;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.ahc.auth.AuthScheme;
+
+public class ProxyConfiguration {
+    private final String httpProxyHost;
+    private final int httpProxyPort;
+    private final String httpsProxyHost;
+    private final int httpsProxyPort;
+    private String exclusionList = null;
+    private String proxyUser = null;
+    private String proxyPassword = null;
+    private AuthScheme scheme = null;
+    private List<String> wildCardExclusions = new ArrayList<String>(); 
+    private Map<String,String> directExclusions = new HashMap<String,String>(); 
+    
+    /**
+     * Construct a ProxyConfiguration instance.
+     * 
+     * @param proxyHost The host to be used for both http and https connections.
+     * @param proxyPort The port to be used for both http and https connections.
+     */
+    public ProxyConfiguration(String proxyHost, int proxyPort) {
+        this.httpProxyHost = proxyHost;
+        this.httpProxyPort = proxyPort;
+        this.httpsProxyHost = proxyHost;
+        this.httpsProxyPort = proxyPort;
+    }
+    
+    /**
+     * Construct a proxy configuration that uses separate 
+     * http and https proxy targets.
+     * 
+     * @param httpProxyHost
+     *               The host to be used for http requests.
+     * @param httpProxyPort
+     *               The port to be used for http requests.
+     * @param httpsProxyHost
+     *               The host to use for https requests.
+     * @param httpsProxyPort
+     */
+    public ProxyConfiguration(String httpProxyHost, int httpProxyPort, String httpsProxyHost, int httpsProxyPort) {
+        this.httpProxyHost = httpProxyHost;
+        this.httpProxyPort = httpProxyPort;
+        this.httpsProxyHost = httpsProxyHost;
+        this.httpsProxyPort = httpsProxyPort;
+    }
+    
+    
+    /**
+     * Get the target connection for a proxied request. 
+     * The target will depend on whether this is an http 
+     * or an https request.
+     * 
+     * @param target The target URL
+     * 
+     * @return An InetSocketAddress for the appropriate proxy target. 
+     */
+    public InetSocketAddress getProxyAddress(URL target) 
+    {
+        if (target.getProtocol().equalsIgnoreCase("https")) {
+            return new InetSocketAddress(getHttpsProxyHost(), getHttpsProxyPort()); 
+        }
+        else { 
+            return new InetSocketAddress(getHttpProxyHost(), getHttpProxyPort()); 
+        }
+    }
+    
+    public String getHttpProxyHost() {
+        return httpProxyHost;
+    }
+
+    public int getHttpProxyPort() {
+        return httpProxyPort;
+    }
+
+    public String getHttpsProxyHost() {
+        return httpsProxyHost;
+    }
+
+    public int getHttpsProxyPort() {
+        return httpsProxyPort;
+    }
+
+    public String getExclusionList() {
+        return exclusionList;
+    }
+
+    /**
+     * Set the exclusion list for the proxy configuration.
+     * The exclusion list is a set of explicit hosts and/or
+     * wildcard domains ("*.apache.org") separated by ";". 
+     * 
+     * @param exclusionList
+     */
+    public void setExclusionList(String exclusionList) {
+        this.exclusionList = exclusionList;
+        // we clear these out regardless 
+        wildCardExclusions.clear(); 
+        directExclusions.clear(); 
+        
+        if (exclusionList != null) {
+            // now divide the exclusion list into the explict and wildcard lists 
+            StringTokenizer tokenizer = new StringTokenizer(exclusionList, ";"); 
+            while (tokenizer.hasMoreTokens()) {
+                String domain = tokenizer.nextToken(); 
+                // wild card versions we just create a matching list that we run through 
+                if (domain.startsWith("*.")) {
+                    wildCardExclusions.add(domain.substring(1)); 
+                }
+                else {
+                    // the direct exlusions are names we can look up via a map. 
+                    directExclusions.put(domain, domain); 
+                }
+            }
+        }
+    }
+
+    public String getProxyUser() {
+        return proxyUser;
+    }
+
+    public void setProxyUser(String proxyUser) {
+        this.proxyUser = proxyUser;
+    }
+
+    public String getProxyPassword() {
+        return proxyPassword;
+    }
+
+    public void setProxyPassword(String proxyPassword) {
+        this.proxyPassword = proxyPassword;
+    }
+    
+    public AuthScheme getAuthScheme() {
+        return scheme;
+    }
+    
+    public void setAuthScheme(AuthScheme scheme) {
+        this.scheme = scheme;
+    }
+    
+    /**
+     * Tests if the host in a target URL is specified in 
+     * the proxy configuration exclusion list.
+     * 
+     * @param target The target URL of the connection.
+     * 
+     * @return true if the host is included in the configuration 
+     *         exclusion list.  false indicates the connection
+     *         needs to go through the proxy server.
+     */
+    public boolean isExcluded(URL target) {
+        String host = target.getHost(); 
+        
+        // if the host is explicitly listed, this is easy 
+        if (directExclusions.get(host) != null) {
+            return true; 
+        }
+        // the wildcard elements are stored as ".apache.org", so 
+        // a simple endsWith() test will gives a match on something 
+        // like "people.apache.org". 
+        for (String domain : wildCardExclusions) {
+            if (host.endsWith(domain)) {
+                return true; 
+            }
+        }
+        // not found in any host 
+        return false; 
+    }
+}

Propchange: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyFilter.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyFilter.java?rev=610084&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyFilter.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyFilter.java Tue Jan  8 09:55:22 2008
@@ -0,0 +1,81 @@
+package org.apache.ahc.proxy;
+
+import org.apache.ahc.AsyncHttpClient;
+import org.apache.ahc.auth.UsernamePasswordCredentials;
+import org.apache.ahc.codec.HttpIoHandler;
+import org.apache.ahc.codec.HttpRequestMessage;
+import org.apache.ahc.codec.HttpResponseMessage;
+import org.apache.mina.common.IoFilterAdapter;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.SSLFilter;
+
+public class ProxyFilter extends IoFilterAdapter {
+    public static final String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization";
+    
+    private volatile boolean connectHandshakeComplete;
+    private final SSLFilter sslFilter;
+
+    public ProxyFilter() {
+        this(null);
+    }
+    
+    public ProxyFilter(SSLFilter sslFilter) {
+        this.sslFilter = sslFilter;
+    }
+    
+    @Override
+    public void messageSent(NextFilter nextFilter, IoSession session,
+            Object message) throws Exception {
+        HttpRequestMessage request = (HttpRequestMessage)message;
+        ProxyConfiguration proxyConfig = request.getProxyConfiguration();
+        if (proxyConfig != null && 
+                proxyConfig.getProxyUser() != null && 
+                proxyConfig.getProxyPassword() != null &&
+                proxyConfig.getAuthScheme() != null) { // can proxy config ever be null?
+            // add the proxy authorization header
+            UsernamePasswordCredentials cred = 
+                    new UsernamePasswordCredentials(proxyConfig.getProxyUser(), 
+                            proxyConfig.getProxyPassword());
+            String authHeader = proxyConfig.getAuthScheme().authenticate(cred, request);
+            request.setHeader(PROXY_AUTHORIZATION_HEADER, authHeader);
+        }
+        // always forward
+        super.messageSent(nextFilter, session, message);
+    }
+
+    private HttpRequestMessage getRequest(IoSession session) {
+        HttpRequestMessage request = 
+                (HttpRequestMessage)session.getAttribute(HttpIoHandler.CURRENT_REQUEST);
+        return request;
+    }
+
+    @Override
+    public void messageReceived(NextFilter nextFilter, IoSession session,
+            Object message) throws Exception {
+        if (needConnectHandshake()) {
+            // we need to complete the connect handshake
+            handleConnectResponse(session, message);
+        } else {
+            super.messageReceived(nextFilter, session, message);
+        }
+    }
+    
+    private boolean needConnectHandshake() {
+        return (sslFilter != null && !connectHandshakeComplete);
+    }
+
+    private void handleConnectResponse(IoSession session, Object message) {
+        HttpResponseMessage response = (HttpResponseMessage)message;
+        int status = response.getStatusCode();
+        if (status == 200) {
+            // layer the SSL socket by inserting the SSL filter
+            session.getFilterChain().addBefore(AsyncHttpClient.PROTOCOL_FILTER, "SSL", sslFilter);
+            connectHandshakeComplete = true; // handshake is done
+            HttpRequestMessage request = getRequest(session);
+            // write the original request intended for the remote target
+            session.write(request);
+        } else {
+            session.close();
+        }
+    }
+}

Propchange: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyFilter.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/proxy/ProxyFilter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ProxyTest.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ProxyTest.java?rev=610084&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ProxyTest.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ProxyTest.java Tue Jan  8 09:55:22 2008
@@ -0,0 +1,117 @@
+package org.apache.ahc;
+
+import java.net.URL;
+import java.util.concurrent.Future;
+
+import org.apache.ahc.codec.HttpRequestMessage;
+import org.apache.ahc.codec.HttpResponseMessage;
+import org.apache.ahc.proxy.ProxyConfiguration;
+
+public class ProxyTest extends AbstractTest {
+    public void testHttpProxy() throws Exception {
+        AsyncHttpClient ahc = new AsyncHttpClient();
+        
+        HttpRequestMessage request = 
+                new HttpRequestMessage(new URL("http://localhost:8282/"), null);
+        ProxyConfiguration config = new ProxyConfiguration("localhost", 8888);
+        request.setProxyConfiguration(config);
+        
+        Future<HttpResponseMessage> future = ahc.sendRequest(request);
+        
+        HttpResponseMessage response = future.get();
+        assertEquals("\nHello World!", response.getStringContent());
+    }
+    
+    
+    public void testHttpProxyIP() throws Exception {
+        AsyncHttpClient ahc = new AsyncHttpClient();
+        
+        HttpRequestMessage request = 
+                new HttpRequestMessage(new URL("http://127.0.0.1:8282/"), null);
+        ProxyConfiguration config = new ProxyConfiguration("localhost", 8888);
+        request.setProxyConfiguration(config);
+        
+        Future<HttpResponseMessage> future = ahc.sendRequest(request);
+        
+        HttpResponseMessage response = future.get();
+        assertEquals("\nHello World!", response.getStringContent());
+    }
+    
+    public void testHttpsProxySame() throws Exception {
+        AsyncHttpClient ahc = new AsyncHttpClient();
+        
+        HttpRequestMessage request = 
+                new HttpRequestMessage(new URL("https://localhost:8383/"), null);
+        ProxyConfiguration config = new ProxyConfiguration("localhost", 8888);
+        request.setProxyConfiguration(config);
+        
+        Future<HttpResponseMessage> future = ahc.sendRequest(request);
+        
+        HttpResponseMessage response = future.get();
+        assertEquals("\nHello World!", response.getStringContent());
+    }
+    
+    
+    public void testHttpsProxyDifferent() throws Exception {
+        AsyncHttpClient ahc = new AsyncHttpClient();
+        
+        HttpRequestMessage request = 
+                new HttpRequestMessage(new URL("https://localhost:8383/"), null);
+        ProxyConfiguration config = new ProxyConfiguration("people.apache.org", 8889, "localhost", 8888);
+        request.setProxyConfiguration(config);
+        
+        Future<HttpResponseMessage> future = ahc.sendRequest(request);
+        
+        HttpResponseMessage response = future.get();
+        assertEquals("\nHello World!", response.getStringContent());
+    }
+    
+    public void testHttpExclusion() throws Exception {
+        AsyncHttpClient ahc = new AsyncHttpClient();
+        
+        HttpRequestMessage request = 
+                new HttpRequestMessage(new URL("http://localhost:8282/"), null);
+        // NOTE:  The proxy server config is invalid, so this will fail if the 
+        // exclusion doesn't work. 
+        ProxyConfiguration config = new ProxyConfiguration("localhost", 8889);
+        config.setExclusionList("localhost");
+        request.setProxyConfiguration(config);
+        
+        Future<HttpResponseMessage> future = ahc.sendRequest(request);
+        
+        HttpResponseMessage response = future.get();
+        assertEquals("\nHello World!", response.getStringContent());
+    }
+    
+    public void testHttpExclusionIP() throws Exception {
+        AsyncHttpClient ahc = new AsyncHttpClient();
+        
+        HttpRequestMessage request = 
+                new HttpRequestMessage(new URL("http://127.0.0.1:8282/"), null);
+        // NOTE:  The proxy server config is invalid, so this will fail if the 
+        // exclusion doesn't work. 
+        ProxyConfiguration config = new ProxyConfiguration("localhost", 8889);
+        config.setExclusionList("127.0.0.1;*.apache.org");
+        request.setProxyConfiguration(config);
+        
+        Future<HttpResponseMessage> future = ahc.sendRequest(request);
+        
+        HttpResponseMessage response = future.get();
+        assertEquals("\nHello World!", response.getStringContent());
+    }
+    
+    public void testHttpsExclusion() throws Exception {
+        AsyncHttpClient ahc = new AsyncHttpClient();
+        
+        HttpRequestMessage request = 
+                new HttpRequestMessage(new URL("https://localhost:8383/"), null);
+        ProxyConfiguration config = new ProxyConfiguration("localhost", 8889);
+        config.setExclusionList("localhost;*.apache.org");
+        request.setProxyConfiguration(config);
+        
+        Future<HttpResponseMessage> future = ahc.sendRequest(request);
+        
+        HttpResponseMessage response = future.get();
+        assertEquals("\nHello World!", response.getStringContent());
+    }
+}

Propchange: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ProxyTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ProxyTest.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ProxyTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain