com.adito.replacementproxy.ProxiedRequestDispatcher.java Source code

Java tutorial

Introduction

Here is the source code for com.adito.replacementproxy.ProxiedRequestDispatcher.java

Source

/*
*  Adito
*
*  Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
*
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU General Public License
*  as published by the Free Software Foundation; either version 2 of
*  the License, or (at your option) any later version.
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public
*  License along with this program; if not, write to the Free Software
*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

package com.adito.replacementproxy;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.maverick.http.HttpAuthenticatorFactory;
import com.maverick.http.HttpClient;
import com.maverick.http.HttpResponse;
import com.maverick.http.PasswordCredentials;
import com.maverick.util.IOUtil;
import com.adito.boot.Branding;
import com.adito.boot.CaseInsensitiveMap;
import com.adito.boot.HttpConstants;
import com.adito.boot.SystemProperties;
import com.adito.boot.Util;
import com.adito.core.CookieItem;
import com.adito.core.CookieMap;
import com.adito.core.MultiMap;
import com.adito.core.RequestParameterMap;
import com.adito.core.RequestParameterMap.ProxyURIDetails;
import com.adito.core.stringreplacement.SessionInfoReplacer;
import com.adito.policyframework.LaunchSession;
import com.adito.reverseproxy.SessionClients;
import com.adito.security.Constants;
import com.adito.util.ProxiedHttpMethod;
import com.adito.webforwards.WebForwardDatabaseFactory;
import com.adito.webforwards.WebForwardTypes;

/**
 */
public class ProxiedRequestDispatcher {
    final static Log log = LogFactory.getLog(ProxiedRequestDispatcher.class);
    static CaseInsensitiveMap ignoreHeaders = new CaseInsensitiveMap();

    final static String sessionIdCookieName = SystemProperties.get("adito.cookie", "JSESSIONID");

    /**
     * Launch session attribute for storing whether authentication has been
     * posted yet
     */
    public static final String LAUNCH_ATTR_AUTH_POSTED = "authPosted";

    static {

        ignoreHeaders.put(HttpConstants.HDR_PROXY_CONNECTION, Boolean.TRUE);
        ignoreHeaders.put(HttpConstants.HDR_ACCEPT_ENCODING, Boolean.TRUE);
        ignoreHeaders.put(HttpConstants.HDR_TRANSFER_ENCODING, Boolean.TRUE);
        ignoreHeaders.put(HttpConstants.HDR_TE, Boolean.TRUE);
        ignoreHeaders.put(HttpConstants.HDR_TRAILER, Boolean.TRUE);
        ignoreHeaders.put(HttpConstants.HDR_PROXY_AUTHORIZATION, Boolean.TRUE);
        ignoreHeaders.put(HttpConstants.HDR_PROXY_AUTHENTICATE, Boolean.TRUE);
        ignoreHeaders.put(HttpConstants.HDR_UPGRADE, Boolean.TRUE);

    }

    private RequestProcessor requestProcessor;
    private LaunchSession launchSession;
    private HttpResponse serverResponse;
    private int responseCode;
    private String responseMessage;
    private CookieMap cookieMap;

    public ProxiedRequestDispatcher(RequestProcessor requestProcessor, LaunchSession launchSession,
            CookieMap cookieMap) {
        this.launchSession = launchSession;
        this.requestProcessor = requestProcessor;
        this.cookieMap = cookieMap;
    }

    /**
     * Send the request to the target server.
     * 
     * @return request successful
     * @throws Exception on any error
     */
    public boolean sendProxiedRequest() throws Exception {

        byte[] content = null;
        OutputStream serverOut = null;
        HttpClient client;
        SessionClients clients = null;
        HttpSession session = requestProcessor.getSession();

        // Manage the sessions clients
        synchronized (session) {
            clients = (SessionClients) session.getAttribute(Constants.HTTP_CLIENTS);
            if (clients == null) {
                clients = new SessionClients();
                session.setAttribute(Constants.HTTP_CLIENTS, clients);
            }
        }

        RequestParameterMap requestParameters = requestProcessor.getRequestParameters();
        URL proxiedURL = requestParameters.getProxiedURIDetails().getProxiedURL();

        synchronized (clients) {
            String key = proxiedURL.getHost() + ":"
                    + (proxiedURL.getPort() > 0 ? proxiedURL.getPort()
                            : proxiedURL.getProtocol().equals("https") ? 443 : 80)
                    + ":" + proxiedURL.getProtocol().equals("https") + ":"
                    + requestProcessor.getWebForward().getResourceId() + Thread.currentThread().getName();
            client = (HttpClient) clients.get(key);

            if (client == null) {
                client = new HttpClient(proxiedURL.getHost(),
                        (proxiedURL.getPort() > 0 ? proxiedURL.getPort()
                                : proxiedURL.getProtocol().equals("https") ? 443 : 80),
                        proxiedURL.getProtocol().equals("https"));

                if (!requestProcessor.getWebForward().getPreferredAuthenticationScheme()
                        .equals(HttpAuthenticatorFactory.NONE)
                        && !requestProcessor.getWebForward().getAuthenticationUsername().equals("")
                        && !requestProcessor.getWebForward().getAuthenticationPassword().equals("")) {
                    PasswordCredentials pwd = new PasswordCredentials();
                    pwd.setUsername(SessionInfoReplacer.replace(requestProcessor.getSessionInfo(),
                            requestProcessor.getWebForward().getAuthenticationUsername()));
                    pwd.setPassword(SessionInfoReplacer.replace(requestProcessor.getSessionInfo(),
                            requestProcessor.getWebForward().getAuthenticationPassword()));
                    client.setCredentials(pwd);
                }

                // Set the preferred scheme
                client.setPreferredAuthentication(
                        requestProcessor.getWebForward().getPreferredAuthenticationScheme());

                // Do not track cookies, browser will instead
                client.setIncludeCookies(false);

                // If we're using basic authentication then preempt the 401
                // response
                client.setPreemtiveAuthentication(requestProcessor.getWebForward()
                        .getPreferredAuthenticationScheme().equalsIgnoreCase("BASIC"));
                clients.put(key, client);
            }
        }

        if (log.isDebugEnabled())
            log.debug("Connecting to [" + proxiedURL + "] ");

        ProxiedHttpMethod method;

        if (!requestProcessor.getWebForward().getFormType().equals(WebForwardTypes.FORM_SUBMIT_NONE)
                && !requestProcessor.getWebForward().getFormType().equals("")
                && !requestProcessor.getWebForward().getFormType().equals(WebForwardTypes.FORM_SUBMIT_JAVASCRIPT)
                && !Boolean.TRUE.equals(launchSession.getAttribute(LAUNCH_ATTR_AUTH_POSTED))) {

            /**
             * This code will automatically submit form parameters.
             * 
             * LDP - Use the full URI with parameters as we need to ensure parameters are sent as they are received.
             */
            method = new ProxiedHttpMethod(requestProcessor.getWebForward().getFormType(),
                    SessionInfoReplacer.replace(requestProcessor.getSessionInfo(),
                            requestProcessor.getUriEncoded()),
                    requestProcessor.getWebForward().getFormType().equals(WebForwardTypes.FORM_SUBMIT_POST)
                            ? new MultiMap()
                            : requestParameters,
                    requestProcessor.getSessionInfo(),
                    requestProcessor.getWebForward().getFormType().equals(WebForwardTypes.FORM_SUBMIT_POST));

            if (requestProcessor.getWebForward().getEncoding() != null
                    && !requestProcessor.getWebForward().getEncoding().equals(WebForwardTypes.DEFAULT_ENCODING))
                method.setCharsetEncoding(requestProcessor.getWebForward().getEncoding());

            StringTokenizer tokens = new StringTokenizer(requestProcessor.getWebForward().getFormParameters(),
                    "\n");
            int idx;
            String param;
            while (tokens.hasMoreTokens()) {
                param = SessionInfoReplacer.replace(requestProcessor.getLaunchSession().getSession(),
                        tokens.nextToken().trim());
                idx = param.indexOf('=');
                if (idx > -1 && idx < param.length() - 1) {
                    method.addParameter(param.substring(0, idx), param.substring(idx + 1));
                } else
                    method.addParameter(param, "");
            }

            launchSession.setAttribute(LAUNCH_ATTR_AUTH_POSTED, Boolean.TRUE);
        } else {
            /**
              * LDP - Use the full URI with parameters as we need to ensure parameters are sent as they are received.
             */
            method = new ProxiedHttpMethod(requestProcessor.getMethod(),
                    SessionInfoReplacer.replace(requestProcessor.getSessionInfo(),
                            requestProcessor.getUriEncoded()),
                    requestParameters, requestProcessor.getSessionInfo(),
                    requestProcessor.getRequest().getContentType() != null && requestProcessor.getRequest()
                            .getContentType().startsWith("application/x-www-form-urlencoded"));

            if (requestProcessor.getWebForward().getEncoding() != null
                    && !requestProcessor.getWebForward().getEncoding().equals(WebForwardTypes.DEFAULT_ENCODING))
                method.setCharsetEncoding(requestProcessor.getWebForward().getEncoding());
        }

        int contentLength = 0;
        String contentType = null;

        for (Enumeration e = requestProcessor.getHeaderNames(); e.hasMoreElements();) {

            String hdr = (String) e.nextElement();

            if (ignoreHeaders.containsKey(hdr)) {
                if (log.isDebugEnabled())
                    log.debug("Ignoring " + hdr + " = " + requestProcessor.getHeader(hdr));
                continue;
            }

            // See if there any replacements for this header
            List replacements = WebForwardDatabaseFactory.getInstance().getReplacementsForContent(
                    launchSession.getSession().getUser().getPrincipalName(),
                    Replacement.REPLACEMENT_TYPE_SENT_HEADER, hdr, proxiedURL.toExternalForm());

            Enumeration vals = requestProcessor.getHeaders(hdr);
            while (vals.hasMoreElements()) {
                String val = (String) vals.nextElement();

                // Do the replacements
                for (Iterator i = replacements.iterator(); i.hasNext();) {
                    Replacement r = (Replacement) i.next();
                    val = val.replaceAll(r.getMatchPattern(), r.getReplacePattern());
                }

                if (val != null) {
                    if (hdr.equalsIgnoreCase(HttpConstants.HDR_HOST)) {
                        if (proxiedURL.getPort() == -1) {
                            val = proxiedURL.getHost();
                        } else {
                            val = proxiedURL.getHost() + ":" + proxiedURL.getPort();
                        }
                    } else if (hdr.equalsIgnoreCase(HttpConstants.HDR_COOKIE)) {
                        // We shouldnt supply our local cookies
                        if (log.isDebugEnabled())
                            log.debug(" Splitting cookie " + val);
                        String[] cookieVals = val.split("\\;");
                        StringBuffer newVal = new StringBuffer();
                        for (int i = 0; i < cookieVals.length; i++) {
                            if (log.isDebugEnabled())
                                log.debug("Cookie = " + cookieVals[i]);
                            int idx = cookieVals[i].indexOf('=');
                            String cn = "";
                            String cv = "";
                            if (idx == -1) {
                                cn = Util.trimBoth(cookieVals[i]);
                            } else if (idx < cookieVals[i].length() - 1) {
                                cn = Util.trimBoth(cookieVals[i].substring(0, idx));
                                cv = Util.trimBoth(cookieVals[i].substring(idx + 1));
                            } else {
                                cn = Util.trimBoth(cookieVals[i].substring(0, idx));
                            }
                            if (cn.equals("webForward") || cn.equals(Constants.LOGON_TICKET)
                                    || cn.equals(Constants.DOMAIN_LOGON_TICKET) || (cn.equals(sessionIdCookieName)
                                            && cv.equals(requestProcessor.getSession().getId()))) {
                                if (log.isDebugEnabled())
                                    log.debug("  Omiting cookie " + cn + "=" + cv);
                            } else {
                                // TODO is it ok to store the cookie map in
                                // memory?
                                CookieItem cookie = cookieMap.getByFakeCookieName(cn);
                                if (cookie == null) {
                                    if (log.isDebugEnabled())
                                        log.debug("  Cookie " + cn + " unmapped, ignoring");
                                    // Un-mapped cookie, ignore
                                } else {
                                    if (log.isDebugEnabled())
                                        log.debug("  Including cookie " + cn + "=" + cv);
                                    if (newVal.length() > 0) {
                                        newVal.append("; ");
                                    }
                                    newVal.append(cookie.getRealCookieName());
                                    newVal.append("=");
                                    newVal.append(Util.urlDecode(cv));
                                }
                            }
                        }
                        if (newVal.length() == 0) {
                            if (log.isDebugEnabled())
                                log.debug("Send no cookies");
                            val = null;
                        } else {
                            val = newVal.toString();
                            if (log.isDebugEnabled())
                                log.debug("Using cooking val of " + val);
                        }
                    }
                    // Change the refererer
                    else if (hdr.equalsIgnoreCase(HttpConstants.HDR_REFERER)) {
                        try {
                            URL refUrl = new URL(val);
                            refUrl.getQuery();
                            if (log.isDebugEnabled())
                                log.debug("Splitting refererer query string [" + val + "] " + refUrl.getQuery());
                            if (refUrl.getFile() != null) {

                                ProxyURIDetails uriDetails = RequestParameterMap.parseProxyPath(refUrl.getFile(),
                                        "UTF-8");
                                if (uriDetails.getProxiedURL() == null) {
                                    /* If the referer is not a proxied URL then don't send a referer. This
                                     * way a target server won't know its a request from Adito by
                                     * examining the referer
                                     */
                                    val = null;
                                } else {
                                    val = uriDetails.getProxiedURL().toExternalForm();
                                }
                            }
                        } catch (MalformedURLException murle) {

                        }
                    } else if (hdr.equalsIgnoreCase(HttpConstants.HDR_CONTENT_LENGTH)) {
                        contentLength = Integer.parseInt(val);
                        continue;
                    } else if (hdr.equalsIgnoreCase(HttpConstants.HDR_CONTENT_TYPE)) {
                        contentType = val;
                        continue;
                    } else if (hdr.equalsIgnoreCase(HttpConstants.HDR_CONNECTION)) {
                        // Handled by the Maverick HTTP client
                        continue;
                    }

                    if (val != null) {
                        method.getProxiedRequest().addHeaderField(hdr, val);
                    }

                    if (log.isDebugEnabled())
                        log.debug("Adding request property " + hdr + " = " + val);
                }
            }
        }

        // Proxy headers
        method.getProxiedRequest().setHeaderField("Via", Branding.PRODUCT_NAME);

        if (requestParameters.isMultipart() && requestParameters.getMultipartDataLength() > 0) {
            method.setContent(getDebugStream(requestParameters.getMultipartData()),
                    requestParameters.getMultipartDataLength(), requestParameters.getOriginalContentType());
        } else if (!requestParameters.isWwwFormURLEncoded() && contentLength > 0) {
            method.setContent(getDebugStream(requestProcessor.getRequest().getInputStream()),
                    requestParameters.getOriginalContentLength(), requestParameters.getOriginalContentType());
        }

        serverResponse = client.execute(method);

        responseCode = serverResponse.getStatus();
        responseMessage = serverResponse.getReason();

        return true;
    }

    InputStream getDebugStream(InputStream in) throws IOException {
        if (log.isDebugEnabled()) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            IOUtil.copy(in, baos);
            byte[] buf = baos.toByteArray();
            log.debug("Sending content :-\n" + new String(buf));
            return new ByteArrayInputStream(buf);
        }
        return in;
    }

    public int getResponseCode() {
        return responseCode;
    }

    public HttpResponse getServerResponse() {
        return serverResponse;
    }

    public String getResponseMessage() {
        return responseMessage;
    }
}