Index: modules/jetty/src/test/java/org/mortbay/jetty/handler/ReverseProxyAwareHandlerWrapperTest.java
===================================================================
--- modules/jetty/src/test/java/org/mortbay/jetty/handler/ReverseProxyAwareHandlerWrapperTest.java	(révision 0)
+++ modules/jetty/src/test/java/org/mortbay/jetty/handler/ReverseProxyAwareHandlerWrapperTest.java	(révision 0)
@@ -0,0 +1,200 @@
+package org.mortbay.jetty.handler;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestCase;
+
+import org.mortbay.jetty.Connector;
+import org.mortbay.jetty.LocalConnector;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.handler.AbstractHandler;
+import org.mortbay.jetty.handler.HandlerWrapper;
+
+/**
+ * Test ReverseProxyAwareHandlerWrapper by creating typical requests
+ * from a reverse proxy (especially mod_proxy_http from Apache HTTPD).
+ */
+public class ReverseProxyAwareHandlerWrapperTest extends TestCase
+{
+    /**
+     * Constructor.
+     * @param name the test case name.
+     */
+    public ReverseProxyAwareHandlerWrapperTest(String name)
+    {
+        super(name);
+    }
+
+    /**
+     * Test dynamic reverse proxy configuration.
+     * @throws Exception if an error occurs.
+     */
+    public void testDynamicReverseProxyConfiguration() throws Exception
+    {
+        // Default mode
+        ReverseProxyAwareHandlerWrapper handlerWrapper = new ReverseProxyAwareHandlerWrapper();
+        
+        // Classic ProxyPass from example.com:80 to localhost:8080
+        testRequest(handlerWrapper,
+            "Host: localhost:8080\n" +
+            "X-Forwarded-For: 10.20.30.40\n" +
+            "X-Forwarded-Host: example.com\n" +
+            "X-Forwarded-Server: example.com",
+            new RequestValidator()
+        {
+            public void validate(HttpServletRequest request)
+            {
+                assertEquals("example.com", request.getServerName());
+                assertEquals(80, request.getServerPort());
+                assertEquals("10.20.30.40", request.getRemoteAddr());
+                assertEquals("example.com", request.getHeader("Host"));
+            }
+        });
+        
+        // ProxyPass from example.com:81 to localhost:8080
+        testRequest(handlerWrapper,
+            "Host: localhost:8080\n" +
+            "X-Forwarded-For: 10.20.30.40\n" +
+            "X-Forwarded-Host: example.com:81\n" +
+            "X-Forwarded-Server: example.com",
+            new RequestValidator()
+        {
+            public void validate(HttpServletRequest request)
+            {
+                assertEquals("example.com", request.getServerName());
+                assertEquals(81, request.getServerPort());
+                assertEquals("10.20.30.40", request.getRemoteAddr());
+                assertEquals("example.com:81", request.getHeader("Host"));
+            }
+        });
+    }
+
+    /**
+     * Test static reverse proxy configuration.
+     * @throws Exception if an error occurs.
+     */
+    public void testStaticReverseProxyConfiguration() throws Exception
+    {
+        ReverseProxyAwareHandlerWrapper handlerWrapper = new ReverseProxyAwareHandlerWrapper();
+        
+        // Set the static server name to use
+        handlerWrapper.setServerName("example.com");
+        
+        // Forced proxy from example.com:80 to localhost:8080
+        testRequest(handlerWrapper,
+            "Host: localhost:8080",
+            new RequestValidator()
+        {
+            public void validate(HttpServletRequest request)
+            {
+                assertEquals("example.com", request.getServerName());
+                // Default port is 80
+                assertEquals(80, request.getServerPort());
+            }
+        });
+        
+        // The static server port to use
+        handlerWrapper.setServerPort(81);
+        
+        // Forced proxy from example.com:81 to localhost:8080
+        testRequest(handlerWrapper,
+            "Host: localhost:8080",
+            new RequestValidator()
+        {
+            public void validate(HttpServletRequest request)
+            {
+                assertEquals("example.com", request.getServerName());
+                assertEquals(81, request.getServerPort());
+            }
+        });
+    }
+
+    private void testRequest(HandlerWrapper handlerWrapper, String headers, RequestValidator requestValidator) throws Exception
+    {
+        Server server = new Server();
+        LocalConnector connector = new LocalConnector();
+        server.setConnectors(new Connector[] {connector});
+        
+        ValidationHandler validationHandler = new ValidationHandler(requestValidator);
+        
+        handlerWrapper.setHandler(validationHandler);
+        
+        server.setHandler(handlerWrapper);
+        
+        try
+        {
+            server.start();
+            connector.getResponses("GET / HTTP/1.1\n" + headers + "\n\n");
+            
+            Error error = validationHandler.getError();
+            
+            if (error != null)
+            {
+                throw error;
+            }
+        }
+        finally
+        {
+            server.stop();
+        }
+    }
+
+    /**
+     * Interface for validate a wrapped request.
+     */
+    private static interface RequestValidator
+    {
+        /**
+         * Validate the current request.
+         * @param request the request.
+         */
+        void validate(HttpServletRequest request);
+    }
+
+    /**
+     * Handler for validation.
+     */
+    private static class ValidationHandler extends AbstractHandler
+    {
+        private RequestValidator _requestValidator;
+        private Error _error;
+        
+        /**
+         * Create the validation handler with a request validator.
+         * @param requestValidator the request validator.
+         */
+        public ValidationHandler(RequestValidator requestValidator)
+        {
+            _requestValidator = requestValidator;
+        }
+        
+        /**
+         * Retrieve the validation error.
+         * @return the validation error or <code>null</code> if there was no error.
+         */
+        public Error getError()
+        {
+            return _error;
+        }
+        
+        public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException
+        {
+            try
+            {
+                _requestValidator.validate(request);
+            }
+            catch (Error e)
+            {
+                _error = e;
+            }
+            catch (Throwable e)
+            {
+                _error = new Error(e);
+            }
+        }
+    }
+}
Index: modules/jetty/src/main/java/org/mortbay/jetty/handler/ReverseProxyAwareHandlerWrapper.java
===================================================================
--- modules/jetty/src/main/java/org/mortbay/jetty/handler/ReverseProxyAwareHandlerWrapper.java	(révision 0)
+++ modules/jetty/src/main/java/org/mortbay/jetty/handler/ReverseProxyAwareHandlerWrapper.java	(révision 0)
@@ -0,0 +1,451 @@
+package org.mortbay.jetty.handler;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+
+import org.mortbay.jetty.Handler;
+import org.mortbay.jetty.handler.HandlerWrapper;
+import org.mortbay.log.Log;
+
+/**
+ * Handler wrapper for providing information from the original request after
+ * run-through a reverse proxy.<br>
+ * Comes in handy when Jetty is behind an Apache server with
+ * mod_proxy_http.<p>
+ * The following headers are used by default:
+ * <dl>
+ *  <dt>X-Forwarded-Host</dt>
+ *  <dd>for retrieving the original Host header</dd>
+ * </dl>
+ * <dl>
+ *  <dt>X-Forwarded-Server</dt>
+ *  <dd>for retrieving the server name and the server port</dd>
+ * </dl>
+ * <dl>
+ *  <dt>X-Forwarded-For</dt>
+ *  <dd>for retrieving the remote address and the remote hostname</dd>
+ * </dl>
+ * See <a href="http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#x-headers">
+ * Apache HTTPD documentation</a>  for more information<p>
+ * The server name and the server port can also be set and not computed
+ * at each request (like <code>proxyName</code> and <code>proxyPort</code> in
+ * <a href="http://tomcat.apache.org/tomcat-5.5-doc/proxy-howto.html">
+ * Tomcat</a>).<p>
+ * Still need enhancements for HTTPS management (
+ * <code>request.isSecure()</code>, server port and retrieve client
+ * certificate information).
+ */
+public class ReverseProxyAwareHandlerWrapper extends HandlerWrapper
+{
+    private String _forwardedHostHeader = "X-Forwarded-Host";
+    private String _forwardedServerHeader = "X-Forwarded-Server";
+    private String _forwardedRemoteAddressHeader = "X-Forwarded-For";
+    private boolean _useDns = false;
+    private String _host;
+    private String _serverName;
+    private int _serverPort = 80;
+
+    /**
+     * Set the forwarded host header to use.
+     * @param hostHeader the forwarded host header.
+     */
+    public void setForwardedHostHeader(String hostHeader) {
+        _forwardedHostHeader = hostHeader;
+    }
+
+    /**
+     * Retrieve the forwarded host header to use.
+     * @return  the forwarded host header.
+     */
+    public String getForwardedHostHeader() {
+        return _forwardedHostHeader;
+    }
+
+    /**
+     * Set the forwarded server header to use.
+     * @param serverHeader the forwarded server header.
+     */
+    public void setForwardedServerHeader(String serverHeader) {
+        _forwardedServerHeader = serverHeader;
+    }
+
+    /**
+     * Retrieve the forwarded server header to use.
+     * @return  the forwarded server header.
+     */
+    public String getForwardedServerHeader() {
+        return _forwardedServerHeader;
+    }
+
+    /**
+     * Set the forwarded remote address header to use.
+     * @param remoteAddressHeader the forwarded remote address header.
+     */
+    public void setForwardedRemoteAddressHeader(String remoteAddressHeader) {
+        _forwardedRemoteAddressHeader = remoteAddressHeader;
+    }
+
+    /**
+     * Retrieve the forwarded remote address header to use.
+     * @return  the forwarded remote address header.
+     */
+    public String getForwardedRemoteAddressHeader() {
+        return _forwardedRemoteAddressHeader;
+    }
+
+    /**
+     * Set whether to use DNS or not.
+     * @param dns the DNS status.
+     */
+    public void setUseDns(boolean dns)
+    {
+        _useDns = dns;
+    }
+
+    /**
+     * Retrieve if whether to use DNS or not.
+     * @return the DNS status.
+     */
+    public boolean isUseDns()
+    {
+        return _useDns;
+    }
+
+    /**
+     * Set the server name to use.
+     * @param name the server name.
+     */
+    public void setServerName(String name)
+    {
+        _serverName = name;
+        _host = _getHost(_serverName, _serverPort);
+    }
+
+    /**
+     * Retrieve the server name to use.
+     * @return the server name.
+     */
+    public String getServerName()
+    {
+        return _serverName;
+    }
+
+    /**
+     * Set the server port to use.
+     * @param port the server port.
+     */
+    public void setServerPort(int port)
+    {
+        _serverPort = port;
+        _host = _getHost(_serverName, _serverPort);
+    }
+
+    /**
+     * Retrieve the server port to use.
+     * @return the server port.
+     */
+    public int getServerPort()
+    {
+        return _serverPort;
+    }
+
+    public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException
+    {
+        Handler handler = getHandler();
+        
+        if (handler !=null && isStarted())
+        {
+            if (dispatch == REQUEST)
+            {
+                // Provide reverse proxy informations
+                handler.handle(target, _getWrappedRequest(request), response, dispatch);
+            }
+            else
+            {
+                // Do not wrap because the current handler must already have been processed 
+                handler.handle(target, request, response, dispatch);
+            }
+        }
+    }
+
+    /**
+     * Wrap the current request.
+     * @param request the current request.
+     * @return the request wrapped.
+     */
+    protected HttpServletRequest _getWrappedRequest(HttpServletRequest request)
+    {
+        ReverseProxyAwareHttpServletRequestWrapper requestWrapper = new ReverseProxyAwareHttpServletRequestWrapper(request);
+        
+        if (_serverName == null)
+        {
+            // Retrieving mod_proxy_http headers from the request
+            String hostHeader = request.getHeader(_forwardedHostHeader);
+            String server = request.getHeader(_forwardedServerHeader);
+            String remoteAddr = request.getHeader(_forwardedRemoteAddressHeader);
+            
+            requestWrapper.setHostHeader(hostHeader);
+            
+            if (hostHeader != null)
+            {
+                int colonIndex = hostHeader.indexOf(":");
+                
+                if (colonIndex == -1)
+                {
+                    // Prefer host name dedicated header
+                    if (server != null)
+                    {
+                        requestWrapper.setServerName(server);
+                    }
+                    else
+                    {
+                        requestWrapper.setServerName(hostHeader);
+                    }
+                    
+                    // TODO handle https protocol
+                    requestWrapper.setServerPort(80);
+                }
+                else
+                {
+                    // Prefer host name dedicated header
+                    if (server != null)
+                    {
+                        requestWrapper.setServerName(server);
+                    }
+                    else
+                    {
+                        requestWrapper.setServerName(hostHeader.substring(0, colonIndex));
+                    }
+                    
+                    String serverPort = hostHeader.substring(colonIndex + 1);
+                    
+                    try
+                    {
+                        requestWrapper.setServerPort(Integer.valueOf(serverPort).intValue());
+                    }
+                    catch (NumberFormatException e)
+                    {
+                        throw new RuntimeException("Unable to compute port from server: " + server, e);
+                    }
+                }
+            }
+            else
+            {
+                // Use provided server name
+                requestWrapper.setServerName(server);
+            }
+            
+            if (remoteAddr != null)
+            {
+                requestWrapper.setRemoteAddr(remoteAddr);
+                
+                if (_useDns)
+                {
+                    InetAddress inetAddress = null;
+                    
+                    try
+                    {
+                        inetAddress = InetAddress.getByName(remoteAddr);
+                    }
+                    catch (UnknownHostException e)
+                    {
+                        Log.ignore(e);
+                    }
+                    
+                    if (inetAddress != null)
+                    {
+                        requestWrapper.setRemoteHost(inetAddress.getHostName());
+                    }
+                }
+            }
+        }
+        else
+        {
+            // Using static configuration
+            requestWrapper.setHostHeader(_host);
+            requestWrapper.setServerName(_serverName);
+            requestWrapper.setServerPort(_serverPort);
+        }
+        
+        return requestWrapper;
+    }
+
+    private String _getHost(String name, int port)
+    {
+        StringBuffer host = new StringBuffer();
+        
+        host.append(name);
+        
+        // TODO handle https protocol
+        if (port != 80)
+        {
+            host.append(":");
+            host.append(port);
+        }
+        
+        return host.toString();
+    }
+
+    /**
+     * HttpServletRequestWrapper for adding original request informations.
+     */
+    public static class ReverseProxyAwareHttpServletRequestWrapper extends HttpServletRequestWrapper
+    {
+        private static final String HOST_HEADER = "Host";
+        private String _hostHeader;
+        private String _serverName;
+        private int _serverPort = -1;
+        private String _remoteAddr;
+        private String _remoteHost;
+
+        /** 
+         * Constructs a request object wrapping the given request.
+         * @param request the request to wrap.
+         */
+        public ReverseProxyAwareHttpServletRequestWrapper(HttpServletRequest request)
+        {
+            super(request);
+        }
+
+        /**
+         * Set the HOST header.
+         * @param headerValue the HOST header value.
+         */
+        public void setHostHeader(String headerValue)
+        {
+            _hostHeader = headerValue;
+        }
+
+        /**
+         * Set the server name.
+         * @param name the server name.
+         */
+        public void setServerName(String name)
+        {
+            _serverName = name;
+        }
+
+        /**
+         * Set the server port.
+         * @param port the server port.
+         */
+        public void setServerPort(int port)
+        {
+            _serverPort = port;
+        }
+
+        /**
+         * Set the remote address.
+         * @param address the remote address.
+         */
+        public void setRemoteAddr(String address)
+        {
+            _remoteAddr = address;
+        }
+
+        /**
+         * Set the remote host.
+         * @param host the emote host.
+         */
+        public void setRemoteHost(String host)
+        {
+            _remoteHost = host;
+        }
+
+        public String getHeader(String name)
+        {
+            if (HOST_HEADER.equals(name))
+            {
+                if (_hostHeader != null)
+                {
+                    return _hostHeader;
+                }
+            }
+            
+            return super.getHeader(name);
+        }
+
+        public Enumeration getHeaderNames()
+        {
+            List headers = new ArrayList();
+            boolean isHostHeaderPresent = false;
+            Enumeration enumeration = super.getHeaderNames();
+            
+            while (enumeration.hasMoreElements())
+            {
+                String headerName = (String) enumeration.nextElement();
+                
+                if (HOST_HEADER.equals(headerName) && _hostHeader != null)
+                {
+                    isHostHeaderPresent = true;
+                }
+                else
+                {
+                    headers.add(headerName);
+                }
+            }
+            
+            if (isHostHeaderPresent)
+            {
+              headers.add(_hostHeader);
+            }
+            
+            return Collections.enumeration(headers);
+        }
+
+        public String getServerName()
+        {
+            if (_serverName != null)
+            {
+                return _serverName;
+            }
+            
+            return super.getServerName();
+        }
+
+        public int getServerPort()
+        {
+            if (_serverPort != -1)
+            {
+                return _serverPort;
+            }
+            
+            return super.getServerPort();
+        }
+
+        public String getRemoteAddr()
+        {
+            if (_remoteAddr != null)
+            {
+                return _remoteAddr;
+            }
+            
+            return super.getRemoteAddr();
+        }
+
+        public String getRemoteHost()
+        {
+            if (_remoteHost != null)
+            {
+                return _remoteHost;
+            }
+            
+            if (_remoteAddr != null)
+            {
+                return _remoteAddr;
+            }
+            
+            return super.getRemoteHost();
+        }
+    }
+}

