/*
* 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.reverseproxy;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.servlet.http.Cookie;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.maverick.http.AuthenticationCancelledException;
import com.maverick.http.HttpAuthenticatorFactory;
import com.maverick.http.HttpClient;
import com.maverick.http.HttpException;
import com.maverick.http.HttpResponse;
import com.maverick.http.PasswordCredentials;
import com.maverick.http.UnsupportedAuthenticationException;
import com.maverick.util.URLUTF8Encoder;
import com.adito.boot.ContextHolder;
import com.adito.boot.HttpConstants;
import com.adito.boot.RequestHandler;
import com.adito.boot.RequestHandlerException;
import com.adito.boot.RequestHandlerRequest;
import com.adito.boot.RequestHandlerResponse;
import com.adito.boot.SystemProperties;
import com.adito.boot.Util;
import com.adito.core.MultiMap;
import com.adito.core.stringreplacement.SessionInfoReplacer;
import com.adito.core.stringreplacement.VariableReplacement;
import com.adito.policyframework.LaunchSession;
import com.adito.policyframework.LaunchSessionFactory;
import com.adito.security.Constants;
import com.adito.security.LogonControllerFactory;
import com.adito.security.SessionInfo;
import com.adito.util.ProxiedHttpMethod;
import com.adito.vfs.webdav.DAVUtilities;
import com.adito.webforwards.AbstractAuthenticatingWebForwardHandler;
import com.adito.webforwards.ReverseProxyWebForward;
import com.adito.webforwards.WebForwardPlugin;
import com.adito.webforwards.WebForwardTypes;
/**
* Request handler that deals with both <i>Reverse Proxy</i> and <i>Replacement
* Proxy</i> web forwards.
*/
public class ReverseProxyMethodHandler extends AbstractAuthenticatingWebForwardHandler implements RequestHandler {
/**
* Launch session attribute for storing whether authentication has been
* posted yet
*/
public static final String LAUNCH_ATTR_AUTH_POSTED = "authPosted";
final static String sessionCookie = SystemProperties.get("adito.cookie", "JSESSIONID");
static HashSet<String> ignoredHeaders = new HashSet<String>();
static {
ignoredHeaders.add("Location".toUpperCase());
ignoredHeaders.add("Server".toUpperCase());
ignoredHeaders.add("Date".toUpperCase());
}
static Log log = LogFactory.getLog(ReverseProxyMethodHandler.class);
public boolean handle(String pathInContext, String pathParams, RequestHandlerRequest request, RequestHandlerResponse response)
throws RequestHandlerException, IOException {
if (log.isDebugEnabled())
log.debug("Check if Reverse Proxy Request for: " + pathInContext);
/*
* First try and locate the session, if there is no session then this is
* definitely not a reverse proxy request
*/
LaunchSession launchSession = null;
SessionInfo session = locateSession(request, response);
if (session == null) {
// If we have no session, then this cannot be a reverse proxy
// request
if (log.isDebugEnabled())
log.debug("No session, not a reverse proxy.");
return false;
}
try {
// Perhaps this is a reverse proxy?
String host = request.getHost();
ReverseProxyWebForward wf = null;
// Active Proxy
if (host != null && !host.equals("") && host.indexOf('.') > -1) {
int idx = host.indexOf('.');
if (idx != -1) {
try {
String uniqueId = host.substring(0, idx);
launchSession = LaunchSessionFactory.getInstance().getLaunchSession(session, uniqueId);
if (launchSession != null) {
wf = (ReverseProxyWebForward) launchSession.getResource();
launchSession.checkAccessRights(null, session);
if (!((ReverseProxyWebForward) wf).getActiveDNS()) {
throw new Exception("Appears to be an active DNS request but the associated web forward is not active DNS. Is someone trying something funny???");
}
LogonControllerFactory.getInstance().addCookies(request, response, session.getLogonTicket(), session);
return handleReverseProxy(pathInContext, pathParams, request, response, launchSession);
}
} catch (Exception ex) {
if (log.isDebugEnabled())
log.debug("Active DNS web forward lookup failed", ex);
}
} else {
if (log.isDebugEnabled())
log.debug("Not active DNS.");
}
}
String hostHeader = request.getHost();
int idx = hostHeader.indexOf(':');
if (idx > -1)
hostHeader = hostHeader.substring(0, idx);
/* Ordinary reverse proxy? There can only ever be one launch session per reverse proxy
* as there is no way of maintaining the session across requests. If a user launches the
* resource more than once, the old launch session will be removed
*/
for (LaunchSession rs : LaunchSessionFactory.getInstance().getLaunchSessionsForType(session,
WebForwardPlugin.WEBFORWARD_RESOURCE_TYPE)) {
if (rs.getResource() instanceof ReverseProxyWebForward) {
wf = (ReverseProxyWebForward) rs.getResource();
// Check that its not reverseProxyRedirect.jsp because if we don't it breaks access after first attempt in same session
if (wf.isValidPath(pathInContext) || (wf.getHostHeader() != null && wf.getHostHeader().equals(hostHeader) && !pathInContext.startsWith("/reverseProxyRedirect.jsp"))) {
rs.checkAccessRights(null, session);
return handleReverseProxy(pathInContext, pathParams, request, response, rs);
}
}
}
} catch (Exception e) {
log.error("Failed to process web forward.", e);
if (session != null) {
session.getHttpSession().setAttribute(Constants.EXCEPTION, e);
response.sendRedirect("/showPopupException.do");
} else {
throw new RequestHandlerException("Failed to process web forward.", 500);
}
return true;
}
return false;
}
private boolean handleReverseProxy(String path, String params, RequestHandlerRequest request, RequestHandlerResponse response,
LaunchSession launchSession) throws IOException {
ReverseProxyWebForward webForward = (ReverseProxyWebForward) launchSession.getResource();
boolean connectionError = true;
/* Because we are in a request handler, the session's last access time
* does not get updated by the container
*/
launchSession.getSession().access();
/***
* LDP - DO NOT use request parameter map until the encoding has been set. If you
* call getParameters it decodes the parameters so this can only be done once the
* character set has been set.
*/
try {
URL target = getTarget(launchSession, request);
setRequestEncoding(launchSession, target, request);
HttpClient client = getClient(launchSession, target);
ProxiedHttpMethod method = getMethod(client, launchSession, request, target);
processPortsAndXForwarding(method, request);
checkProcessedContent(launchSession, method, request);
addCustomHeaders(webForward, method);
/* If this webforward has JavaScript form authentication and this hasn't
* yet been processed, then we make sure the content that comes back from
* the target is not encoded using gzip or any other compression methods.
* This is because we will be tacking on some content and its easy to
* deal with unencoded.
*
* TODO We may want to support at least Gzip at some point
*/
if (webForward.getFormType().equals(WebForwardTypes.FORM_SUBMIT_JAVASCRIPT)
&& !Boolean.TRUE.equals(launchSession.getAttribute(LAUNCH_ATTR_AUTH_POSTED))) {
method.getProxiedRequest().removeFields("Accept-Encoding");
}
com.maverick.http.HttpResponse clientResponse = doExecute(client, method);
connectionError = false;
checkInsecureIIS(webForward, clientResponse);
filterUnsupportedAuthMethods(clientResponse);
processStatus(clientResponse, response);
processRedirects(clientResponse, request, response);
processHeaders(clientResponse, response);
/*
* If the content type is HTML, this webforward is configured for
* automatic JavaScript authentication and authentication has not
* yet been performed, then tack the JavaScript on to the end of the
* content. This requires that the content is read into memory and
* the content length adjusted
*/
if (clientResponse.getStatus() == 200 && webForward.getFormType().equals(WebForwardTypes.FORM_SUBMIT_JAVASCRIPT)
&& "text/html".equals(clientResponse.getContentTypeWithoutParameter())
&& !Boolean.TRUE.equals(launchSession.getAttribute(LAUNCH_ATTR_AUTH_POSTED))) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Util.copy(clientResponse.getInputStream(), baos, -1, 16384);
addJavaScriptAuthenticationCode(launchSession, baos, 0);
byte[] arr = baos.toByteArray();
if (clientResponse.getHeaderField("Content-Length") != null) {
response.setField("Content-Length", String.valueOf(arr.length));
}
response.getOutputStream().write(arr);
launchSession.setAttribute(LAUNCH_ATTR_AUTH_POSTED, Boolean.TRUE);
} else {
Util.copy(clientResponse.getInputStream(), response.getOutputStream(), -1, 16384);
}
clientResponse.close();
return true;
} catch (UnsupportedAuthenticationException ex) {
log.error("?", ex);
} catch (com.maverick.http.HttpException ex) {
log.error("?", ex);
} catch (EOFException e) {
/*
* This is probably just because the user clicked on a link before
* the page had finished downloading.
*/
if (log.isDebugEnabled())
log.debug("Received EOF in reverse proxy request [THIS IS PROBABLY NOT FATAL]", e);
} catch (IOException ex) {
if (connectionError) {
response.sendError(404,
"The proxied web server could not be contacted or did not respond correctly to the request! " + ex.getMessage());
}
log.error("?", ex);
} catch (AuthenticationCancelledException ex) {
log.error("?", ex);
}
return true;
}
HttpResponse doExecute(HttpClient client, ProxiedHttpMethod method) throws UnknownHostException, IOException,
HttpException, UnsupportedAuthenticationException, AuthenticationCancelledException {
if (log.isDebugEnabled()) {
log.debug("Connecting to " + client.getHost() + ":" + client.getPort() + " (Secure = " + client.isSecure() + ")");
}
return client.execute(method);
}
URL getTarget(LaunchSession launchSession, RequestHandlerRequest request) throws MalformedURLException {
ReverseProxyWebForward webForward = (ReverseProxyWebForward) launchSession.getResource();
VariableReplacement r = new VariableReplacement();
r.setRequest(request);
r.setSession(launchSession.getSession());
r.setPolicy(launchSession.getPolicy());
URL target = new URL(r.replace(webForward.getDestinationURL()));
if (log.isDebugEnabled()) {
log.debug("Reverse proxy target " + target.toExternalForm());
}
return target;
}
void checkProcessedContent(LaunchSession launchSession, ProxiedHttpMethod method, RequestHandlerRequest request)
throws IOException {
String contentType = request.getContentType();
int contentLength = request.getContentLength();
boolean hasProcessedContent = contentType != null && request.getMethod().equals("POST")
&& contentType.equals("application/x-www-form-urlencoded");
if (contentLength > 0 && !hasProcessedContent) {
if(log.isDebugEnabled())
log.debug("Setting request content of " + contentLength + " bytes with content type " + contentType + " available=" + request.getInputStream().available());
method.setContent(request.getInputStream(), contentLength, contentType);
}
}
private void processRequestHeaders(RequestHandlerRequest request, ProxiedHttpMethod method) {
String header;
for (Enumeration e = request.getFieldNames(); e.hasMoreElements();) {
header = (String) e.nextElement();
// Skip the connection header as our client maintains its own
// connections
if (header.equalsIgnoreCase(HttpConstants.HDR_CONNECTION) || header.equalsIgnoreCase(HttpConstants.HDR_KEEP_ALIVE)) {
continue;
}
for (Enumeration j = request.getFieldValues(header); j.hasMoreElements();) {
String val = (String) j.nextElement();
if (header.equalsIgnoreCase("cookie")) {
String[] cookieVals = val.split("\\;");
StringBuffer newVal = new StringBuffer();
for (int i = 0; i < cookieVals.length; i++) {
if (log.isDebugEnabled())
log.debug("Cookie = " + cookieVals[i]);
// Its possible cookies may be sent without values
int idx = cookieVals[i].indexOf('=');
String cn = idx == -1 ? cookieVals[i] : Util.trimBoth(cookieVals[i].substring(0, idx));
String cv = idx == -1 ? null : Util.trimBoth(cookieVals[i].substring(idx + 1));
// Ignore SSL-Exploer cookies
if (cn.equals(Constants.LOGON_TICKET) || cn.equals(Constants.DOMAIN_LOGON_TICKET)
|| cn.equals(SystemProperties.get("adito.cookie", "SSLX_SSESHID"))) {
if (log.isDebugEnabled())
log.debug(" Omiting cookie " + cn + "=" + cv);
} else {
if (newVal.length() > 0) {
newVal.append("; ");
}
newVal.append(cn);
if(cv != null) {
newVal.append("=");
newVal.append(cv);
}
}
}
if (newVal.length() > 0) {
method.getProxiedRequest().addHeaderField(header, newVal.toString());
if (log.isDebugEnabled())
log.debug("HEADER: " + header + " " + val);
}
} else {
method.getProxiedRequest().addHeaderField(header, val);
if (log.isDebugEnabled())
log.debug("HEADER: " + header + " " + val);
}
}
}
}
void processPortsAndXForwarding(ProxiedHttpMethod method, RequestHandlerRequest request) {
String thisHost = ContextHolder.getContext().getHostname();
int thisPort = ContextHolder.getContext().getPort();
// Check for a non default port (we dont care if we receive on 443
// but
// forward to 80 as this would not change the host header
if (thisPort != 443 && thisPort != 80) {
thisHost += ":" + thisPort;
}
method.getProxiedRequest().setHeaderField("X-Forwarded-Host", thisHost);
method.getProxiedRequest().setHeaderField("X-Forwarded-For", request.getRemoteHost());
method.getProxiedRequest().setHeaderField("X-Forwarded-Server", thisHost);
method.getProxiedRequest().setHeaderField("X-Forwarded-Port", String.valueOf(thisPort));
}
void processStatus(HttpResponse clientResponse, RequestHandlerResponse response) {
if (log.isDebugEnabled())
log.debug("HTTP response is " + clientResponse.getStartLine());
response.setStatus(clientResponse.getStatus());
response.setReason(clientResponse.getReason());
}
void processHeaders(HttpResponse clientResponse, RequestHandlerResponse response) {
String header;
for (Enumeration e = clientResponse.getHeaderFieldNames(); e.hasMoreElements();) {
header = (String) e.nextElement();
if (log.isDebugEnabled()) {
log.debug("Received header " + header);
}
String[] val = clientResponse.getHeaderFields(header);
if(val == null) {
log.debug("No value???");
}
if (ignoredHeaders.contains(header.toUpperCase())) {
if (log.isDebugEnabled())
log.debug("Ignoring header " + header);
continue;
}
for (int i = 0; i < val.length; i++) {
if (log.isDebugEnabled()) {
log.debug("Adding value " + val[i] + " for " + header);
}
if (i == 0)
response.setField(header, val[i]);
else
response.addField(header, val[i]);
}
}
}
void processRedirects(HttpResponse clientResponse, RequestHandlerRequest request, RequestHandlerResponse response) {
/**
* Process redirect Location headers, the location may be a HTTP
* resource which will require changing to HTTPS
*/
if (clientResponse.getStatus() >= 300 && clientResponse.getStatus() < 400) {
switch (clientResponse.getStatus()) {
case 300: // Multiple choices
case 301: // Moved permanentley
case 302: // Found
case 303: // See other
case 307: // Temporarily redirect
String[] locations = clientResponse.getHeaderFields(HttpConstants.HDR_LOCATION);
response.removeField(HttpConstants.HDR_LOCATION);
for (int i = 0; i < locations.length; i++) {
String originatingHost = clientResponse.getConnection().getHost();
String location = rebuildLocation(Util.urlDecode(locations[i]), request.getHost(), originatingHost);
response.addField("Location", location);
if (log.isDebugEnabled())
log.debug("Location is now '" + location + "'");
}
break;
case 304: // Not Modified
// Do nothing return as is
break;
case 305: // Use proxy
log.warn("Detected HTTP response 305 [Use proxy] this may break reverse proxy!");
break;
default:
log.error("Got unknown 3XX response code from server " + clientResponse.getStatus());
}
}
}
static final String rebuildLocation(String location, String host, String originatingHost) {
// Check against the requests Host value and change the Location if required
if (location.startsWith("http://" + host)) {
String protocolStripped = stripProtocol(location);
return encodeURL("https://" + protocolStripped);
} else if (location.startsWith("http://" + originatingHost) || location.startsWith("https://" + originatingHost)) {
String protocolStripped = stripProtocol(location);
int indexOf = protocolStripped.indexOf('/');
if (indexOf == -1) {
return encodeURL("https://" + host);
} else {
String remainingPath = protocolStripped.substring(indexOf);
return encodeURL("https://" + host + remainingPath);
}
} else {
if (log.isDebugEnabled()) {
log.debug("Redirect location may result in reverse proxy error " + location);
}
}
return encodeURL(location);
}
static final String stripProtocol(String url) {
if(url.startsWith("http://")) {
return url.substring(7);
} else if(url.startsWith("https://")) {
return url.substring(8);
}
return url;
}
void addCustomHeaders(ReverseProxyWebForward webForward, ProxiedHttpMethod method) {
/**
* Add any custom headers to the request
*/
Map customHeaders = webForward.getCustomHeaders();
String header;
for (Iterator it = customHeaders.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
header = (String) entry.getKey();
Vector v = (Vector) entry.getValue();
for (Iterator it2 = v.iterator(); it2.hasNext();) {
method.getProxiedRequest().addHeaderField(header, (String) it2.next());
}
}
}
void checkInsecureIIS(ReverseProxyWebForward webForward, HttpResponse clientResponse) {
/**
* Perform a check to see if we're connected to an IIS server and if the
* backend server is insecure. If it is set the customer
* Front-End-Https: on header.
*/
String server = clientResponse.getHeaderField("Server");
if (server != null && server.startsWith("Microsoft-IIS")) {
if (!webForward.containsCustomHeader("Front-End-Https"))
webForward.setCustomHeader("Front-End-Https", "on");
}
}
void filterUnsupportedAuthMethods(HttpResponse clientResponse) {
/**
* Filter out unsupported authentication methods because they dont work
* through the reverse proxy - the best way to enable these will be to
* allow credentials to be set on the reverse proxy web forward.
*/
String[] challenges = clientResponse.getHeaderFields("www-authenticate");
if (challenges != null) {
clientResponse.removeFields("www-authenticate");
for (int i = 0; i < challenges.length; i++) {
if (challenges[i].toLowerCase().startsWith("basic") || challenges[i].toLowerCase().startsWith("digest")
|| challenges[i].toLowerCase().startsWith("ntlm")) {
clientResponse.setHeaderField("WWW-Authenticate", challenges[i]);
}
}
}
}
ProxiedHttpMethod getMethod(HttpClient client, LaunchSession launchSession, RequestHandlerRequest request, URL target) {
ReverseProxyWebForward webForward = (ReverseProxyWebForward) launchSession.getResource();
ProxiedHttpMethod method;
VariableReplacement v = new VariableReplacement();
v.setRequest(request);
v.setSession(launchSession.getSession());
v.setPolicy(launchSession.getPolicy());
/**
* POST parameters are now not being
*/
if (!webForward.getFormType().equals(WebForwardTypes.FORM_SUBMIT_NONE)
&& !webForward.getFormType().equals(WebForwardTypes.FORM_SUBMIT_NONE)
&& !webForward.getFormType().equals("")
&& !webForward.getFormType().equals(WebForwardTypes.FORM_SUBMIT_JAVASCRIPT)
&& !Boolean.TRUE.equals(launchSession.getAttribute(LAUNCH_ATTR_AUTH_POSTED))) {
/**
* This code will automatically submit form parameters. If it is a post,
* then we ignore the parameters request and use the webforward target.
*/
method = new ProxiedHttpMethod(webForward.getFormType(),
target.getFile(),
webForward.getFormType().equals(WebForwardTypes.FORM_SUBMIT_POST) ? new MultiMap() : new MultiMap(request.getParameters()),
launchSession.getSession(),
webForward.getFormType().equals(WebForwardTypes.FORM_SUBMIT_POST));
if (webForward.getCharset() != null
&& !webForward.getCharset().equals("")
&& !webForward.getCharset().equals(WebForwardTypes.DEFAULT_ENCODING))
method.setCharsetEncoding(webForward.getCharset());
StringTokenizer tokens = new StringTokenizer(webForward.getFormParameters(), "\n");
int idx;
String param;
while (tokens.hasMoreTokens()) {
param = v.replace(tokens.nextToken().trim());
idx = param.indexOf('=');
if (idx > -1) {
method.addParameter(param.substring(0, idx), param.substring(idx + 1));
} else
method.addParameter(param, "");
}
launchSession.setAttribute(LAUNCH_ATTR_AUTH_POSTED, Boolean.TRUE);
processRequestHeaders(request, method);
// Do not send through any cookies on the authentication request
method.getProxiedRequest().removeFields(HttpConstants.HDR_COOKIE);
client.removeAllCookies();
} else {
method = new ProxiedHttpMethod(request.getMethod(),
request.getURIEncoded(),
new MultiMap(request.getParameters()),
launchSession.getSession(),
request.getContentType() != null && request.getContentType()
.toLowerCase()
.startsWith("application/x-www-form-urlencoded"));
if (webForward.getCharset() != null
&& !webForward.getCharset().equals("")
&& !webForward.getCharset().equals(WebForwardTypes.DEFAULT_ENCODING))
method.setCharsetEncoding(webForward.getCharset());
processRequestHeaders(request, method);
}
return method;
}
HttpClient getClient(LaunchSession launchSession, URL target) {
ReverseProxyWebForward webForward = (ReverseProxyWebForward) launchSession.getResource();
String hostname = target.getHost();
boolean isSecure = target.getProtocol().equalsIgnoreCase("https");
int connectPort = target.getPort() == -1 ? (isSecure ? 443 : 80) : target.getPort();
HttpClient client;
SessionClients clients = null;
// CookieMap cookieMap = null;
synchronized (launchSession.getSession().getHttpSession()) {
clients = (SessionClients) launchSession.getSession().getHttpSession().getAttribute(Constants.HTTP_CLIENTS);
if (clients == null) {
clients = new SessionClients();
launchSession.getSession().getHttpSession().setAttribute(Constants.HTTP_CLIENTS, clients);
}
}
synchronized (clients) {
String key = hostname + ":"
+ connectPort
+ ":"
+ isSecure
+ ":"
+ webForward.getResourceId()
+ ":"
+ Thread.currentThread().getName()
+ ":"
+ launchSession.getSession().getId();
client = (HttpClient) clients.get(key);
if (client == null) {
client = new HttpClient(hostname, connectPort, isSecure);
client.setIncludeCookies(false);
if (!webForward.getPreferredAuthenticationScheme().equals(HttpAuthenticatorFactory.NONE) && !webForward.getAuthenticationUsername()
.equals("")
&& !webForward.getAuthenticationPassword().equals("")) {
PasswordCredentials pwd = new PasswordCredentials();
pwd.setUsername(SessionInfoReplacer.replace(launchSession.getSession(), webForward.getAuthenticationUsername()));
pwd.setPassword(SessionInfoReplacer.replace(launchSession.getSession(), webForward.getAuthenticationPassword()));
client.setCredentials(pwd);
}
// Set the preferred scheme
client.setPreferredAuthentication(webForward.getPreferredAuthenticationScheme());
// If we're using basic authentication then preempt the 401
// response
client.setPreemtiveAuthentication(webForward.getPreferredAuthenticationScheme().equalsIgnoreCase("BASIC"));
clients.put(key, client);
}
}
return client;
}
void setRequestEncoding(LaunchSession launchSession, URL target, RequestHandlerRequest request) {
ReverseProxyWebForward webForward = (ReverseProxyWebForward) launchSession.getResource();
/**
* This code sets the character encoding of the request. This may be
* overridden because some servers assume the character set and there is
* no way for us work this.
*/
try {
if (webForward.getCharset() != null
&& !webForward.getCharset().equals("")
&& !webForward.getCharset().equals(WebForwardTypes.DEFAULT_ENCODING))
request.setCharacterEncoding(webForward.getCharset());
} catch (UnsupportedEncodingException ex) {
log.error("Java runtime does not support encoding", ex);
}
}
SessionInfo locateSession(String pathInContext, String pathParams, RequestHandlerRequest request,
RequestHandlerResponse response) {
/*
* When not authenticated, dont reverse proxy anything. We use the logon
* ticket to get the HttpSession in use
*/
SessionInfo session = null;
/**
* The launching of a reverse proxy will always be a GET. This change
* will allow us to set the character encoding of the request later so
* that POST parameters are not incorrectly encoded.
*/
if (request.getMethod().equals("GET") && request.getParameters().containsKey(LaunchSession.LONG_LAUNCH_ID)) {
String launchId = (String) request.getParameters().get(LaunchSession.LONG_LAUNCH_ID);
// Get the actual session for the reverse proxy
LaunchSession launchSession = LaunchSessionFactory.getInstance().getLaunchSession(launchId);
if (launchSession != null) {
// If the launch session is not for a reverse proxy web forward
// then ignore
if (launchSession.isTracked() && launchSession.getResource() instanceof ReverseProxyWebForward) {
session = launchSession.getSession();
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
if (cookies[i].getName().equalsIgnoreCase(sessionCookie)) {
LogonControllerFactory.getInstance().attachSession(cookies[i].getValue(), session);
break;
}
}
}
LogonControllerFactory.getInstance().addCookies(request, response, session.getLogonTicket(), session);
}
}
} else {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
if (cookies[i].getName().equalsIgnoreCase(sessionCookie)) {
session = LogonControllerFactory.getInstance().getSessionInfoBySessionId(cookies[i].getValue());
if (session != null) {
LogonControllerFactory.getInstance().addCookies(request, response, session.getLogonTicket(), session);
session.access();
break;
}
}
if (cookies[i].getName().equalsIgnoreCase(Constants.DOMAIN_LOGON_TICKET) || cookies[i].getName()
.equalsIgnoreCase(Constants.LOGON_TICKET)) {
session = LogonControllerFactory.getInstance().getSessionInfo(cookies[i].getValue());
if (session != null) {
LogonControllerFactory.getInstance().addCookies(request, response, session.getLogonTicket(), session);
session.access();
break;
}
}
}
}
}
if (session != null) {
session.access();
}
return session;
}
/**
* Takes an unencoded URL query string, and encodes it.
* @param query
* @return
*/
public static final String encodeQuery(String query) {
String encoded = "";
StringTokenizer pairs = new StringTokenizer(query, "&");
while(pairs.hasMoreTokens()) {
StringTokenizer pair = new StringTokenizer(pairs.nextToken(), "=");
if(pair.hasMoreTokens()) {
encoded += (encoded.length()==0 ? "" : "&") + URLUTF8Encoder.encode(pair.nextToken(), true);
if(pair.hasMoreTokens()) {
encoded += "=" + URLUTF8Encoder.encode(pair.nextToken(), true);
}
}
}
return encoded;
}
/**
* Encodes a URL
* @param location
* @return
*/
public static final String encodeURL(String location) {
try {
URL url = new URL(location);
StringBuffer buf = new StringBuffer();
buf.append(url.getProtocol());
buf.append("://");
if(!Util.isNullOrTrimmedBlank(url.getUserInfo())) {
buf.append(DAVUtilities.encodeURIUserInfo(url.getUserInfo()));
buf.append("@");
}
buf.append(url.getHost());
if(url.getPort() != -1) {
buf.append(":");
buf.append(url.getPort());
}
if(!Util.isNullOrTrimmedBlank(url.getPath())) {
buf.append(URLUTF8Encoder.encode(url.getPath(), false));
}
if(!Util.isNullOrTrimmedBlank(url.getQuery())) {
buf.append("?");
buf.append(encodeQuery(url.getQuery()));
}
return buf.toString();
} catch (MalformedURLException e) {
int idx = location.indexOf('?');
if(idx > -1 && idx < location.length()-1) {
return URLUTF8Encoder.encode(location.substring(0, idx), false) + "?" + encodeQuery(location.substring(idx+1));
} else
return URLUTF8Encoder.encode(location, false);
}
}
}