Daniel,

I had requested some patches a while back for your usage... it's a new option to append the server-id to the JSESSION_ID. Scott added it in 3.0.18, and I assume is included in the 3.1/3.2 branches:

* add session-config/cookie-append-server-index (#706, rep by Serge Knystautas)

While that feature was added, I went with a separate cookie to do the "sticking". This approach is analogous to how some hardware load balancers do plugins.

I have a cookie called "lbm" with a value "ps.<server>", which is configured in mod_proxy_balancer as my sticky cookie. I created a filter that takes the request and wraps it in a custom request wrapper. The request wrapper overrides getSession() and getSession(false)... only when you need the session since my app is heavy anonymous and I only want to stick you if you have a session. So if those methods are called, the request wrapper sees if you have the "lbm" cookie set. If not, it sets it. If set but to another server, it changes it (and logs it to a database). if it set and right, continue normal. Anyway, that's more complex than most people need, but that's the full details. I've attached my wrapper class since that's got most of what you'd need and doesn't have anything really proprietary.

The benefit to decoupling the load balancing routing cookie and the session_id is that I can change servers and keep my session. What I'll frequently do is sign in, pull up the cookie editor with the Firefox "web developer" plugin and change my cookie to a staging server that is enabled but marked in mod_proxy_balancer as standby. It's a nice way of testing a particular server as well.

--
Serge Knystautas
Lokitech >> software . strategy . design >> http://www.lokitech.com
p. 301.656.5501
e. [EMAIL PROTECTED]

Daniel López wrote:
Hi,

I am doing some research in order to set up various servers behind an Apacher server that is already working as a proxy. Apache now includes a mod_proxy_balance that I'm trying to use and it even includes some settings to be able to "stick" the sessions.

However, in order to decide which server to redirect the session it uses the cookie value instead of the name so a sticky cookie session *value* of whatever.xxx will go to xxx.

I know that Resin can change the *name* of the session cookie but AFAIK, there's no setting to append the server/cluster id to the session value, or add a different cookie with some specific value.

Is that possible? I mean, can I set a cookie that changes with the server name, even if I have to set it manually at the resin.conf file? Or am I able to append some value depeneding on the server to the JSESSION id value?

Thanks,
D.


_______________________________________________
resin-interest mailing list
resin-interest@caucho.com
http://maillist.caucho.com/mailman/listinfo/resin-interest

package com.prestosports.security;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.Principal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Date;

import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.context.support.XmlWebApplicationContext;

import com.prestosports.util.Constants;

/**
 * This handles several key issues that might otherwise be put into several
 * filters and wrappers.
 * <ol>
 * <li>Our web server is a reverse proxy server to our app tier and this will
 * allow our app tier to use the regular getRemoteHost() API call to determine
 * who the end client is, instead of just returning the reverse proxy info.</li>
 * <li>This code will set a special cookie ("lbm") that will allow our load
 * balancer to traffic from the same client to the same app tier (sticky
 * sessions).</li>
 * </ol>
 */
public class HttpProxyAddrRequestWrapper extends HttpServletRequestWrapper {

    private static String LOG_ROUTE_CHANGE = "insert delayed into route_change 
(change_time, cookie_server, new_server, username, url) values (?, ?, ?, ?, ?)";
    static final Logger log = Logger.getLogger(SiteFilter.class);
    private HttpServletResponse response;
    private ServletContext application;

    /**
     * Constructs a wrapper to implement our logic.
     */
    public HttpProxyAddrRequestWrapper(HttpServletRequest request,
            HttpServletResponse response, ServletContext application) {
        super(request);
        this.response = response;
        this.application = application;
    }

    /**
     * Uses the proxy added header "X-Forwarded-For" to determine this.
     */
    public String getRemoteAddr() {
        HttpServletRequest request = (HttpServletRequest) getRequest();
        String proxiedIp = request.getHeader("X-Forwarded-For");
        if (proxiedIp != null) {
            // Get the last of the comma delimited list
            String[] ips = StringUtils.split(proxiedIp, ", ");
            proxiedIp = ips[ips.length - 1];
            return proxiedIp;
        }
        return super.getRemoteAddr();
    }

    /**
     * Uses the proxy added header "X-Forwarded-For" to determine this.
     */
    public String getRemoteHost() {
        HttpServletRequest request = (HttpServletRequest) getRequest();
        String proxiedIp = request.getHeader("X-Forwarded-For");
        if (proxiedIp != null) {
            // Convert from IP to hostname
            try {
                return InetAddress.getByName(proxiedIp).getHostName();
            } catch (UnknownHostException uhe) {
                return proxiedIp;
            }
        }
        return super.getRemoteHost();
    }

    /**
     * Override the getSession() to set the load-balancing cookie.
     */
    public HttpSession getSession() {
        setupBalancerCookie();
        return super.getSession();
    }

    /**
     * Override the getSession(true) to set the load-balancing cookie.
     */
    public HttpSession getSession(boolean create) {
        HttpSession session = super.getSession(create);
        if (session != null) {
            setupBalancerCookie();
        }
        return session;
    }

    /**
     * Setup that cookie for the load balancer.
     */
    public void setupBalancerCookie() {
        if (!Constants.LBM_COOKIE_ENABLED) {
            return;
        }
        if (getAttribute("lbm_added") != null) {
            // Prevent us from setting this multiple times
            return;
        }
        setAttribute("lbm_added", "y");
        Cookie[] cookies = getCookies();
        String route = "ps."
                + (String) application.getAttribute("caucho.server-id");
        if (cookies != null) {
            for (int i = 0; i < cookies.length; i++) {
                if (cookies[i].getName().equals("lbm")) {
                    if (log.isDebugEnabled()) {
                        log.debug("We got a cookie: " + cookies[i].getValue());
                    }
                    // We already have this cookie set.
                    if (!route.equals(cookies[i].getValue())) {
                        // This cookie value needs to change
                        logRouteChange(cookies[i].getValue(), route);
                        break;
                    }
                    // We have the cookie set and are good to go
                    return;
                }
            }
        }
        log.debug("We are setting the cookie");
        // Set this cookie
        // We want to get the server-id
        Cookie balancerCookie = new Cookie("lbm", route);
        // Expire when the browser dies, so no max age
        // contentTypeCookie.setMaxAge(60 * 60 * 24 * 90);
        balancerCookie.setPath("/");
        response.addCookie(balancerCookie);
        response.setHeader("Cache-Control", "private,no-cache");
    }

    /**
     * Log the change in lbm to the new server
     */
    private void logRouteChange(String originalServer, String newServer) {
        XmlWebApplicationContext spring = (XmlWebApplicationContext) 
WebApplicationContextUtils
                .getWebApplicationContext(application);
        DataSource datasource = (DataSource) spring
                .getBean("datasourceSessions");
        Connection conn = null;
        try {
            conn = datasource.getConnection();
            PreparedStatement stmt = conn.prepareStatement(LOG_ROUTE_CHANGE);
            stmt.setObject(1, new Date());
            if (originalServer.startsWith("ps.")) {
                originalServer = originalServer.substring(3);
            }
            originalServer = StringUtils.substring(originalServer, 0, 4);
            stmt.setString(2, originalServer);
            if (newServer.startsWith("ps.")) {
                newServer = newServer.substring(3);
            }
            newServer = StringUtils.substring(newServer, 0, 4);
            stmt.setString(3, newServer);
            // username
            String username = null;
            HttpServletRequest request = (HttpServletRequest) getRequest();
            Principal admin = ThreadRequest.getUserPrincipal();
            if (admin != null) {
                username = admin.getName();
                username = StringUtils.substring(username, 0, 40);
            }
            stmt.setString(4, username);
            // url
            String url = request.getRequestURI();
            url = StringUtils.substring(url, 0, 200);
            stmt.setString(5, url);
            stmt.execute();
            stmt.close();
        } catch (Exception e) {
            log.fatal("Could not log route change", e);
        } finally {
            try {
                conn.close();
            } catch (Exception e) {
                log.fatal("Could not close connection", e);
            }
        }
        // Object obj = spring.getBean(bean);
    }
}
_______________________________________________
resin-interest mailing list
resin-interest@caucho.com
http://maillist.caucho.com/mailman/listinfo/resin-interest

Reply via email to