1

I've been working on implementing Punchout in my eCommerce Application. My implementation works as follows.

enter image description here

Everything was working fine till yesterday, then the session started to getting dropped when the redirection to store front took place.

My Observation:

The HttpServletRequest object is RequestFacade before the redirection takes place, but after I redirect, it becomes ApplicationHttpRequest. I can find the RequestFacade wrapped in ApplicationHttpRequest but I cannot find the object I put in the session. Below is the function I am using to put the object in session.

/**
 * Creates an object of {@link PunchoutSessionDTO} and puts it in
 * the session, making it a punchout session.
 * 
 * @param punchoutTransaction
 *          The {@link PunchoutTransaction} object passed from the 
 *          {@link PunchoutStoreEntryController}.
 * @param session
 *          The {@link HttpSession}.
 */
public void createPunchoutSession(PunchoutTransaction punchoutTransaction, HttpSession session) {

    // Create a PunchoutSessionDTO object.
    PunchoutSessionDTO state = new PunchoutSessionDTO();

    // Initialize it with the variables from the PunchoutTransaction
    // object passed to it.
    state.setBrowserFormPost(punchoutTransaction.getCallbackURL());
    state.setBuyerCookie(punchoutTransaction.getBuyerCookie());
    state.setFromId(punchoutTransaction.getFromId());
    state.setToId(punchoutTransaction.getToId());
    state.setPoTransId(punchoutTransaction.getTransactionId());
    state.setOciPunchout(punchoutTransaction.getTransactionType() == PunchoutTransaction.TYPE_OCI);

    // And put it in the session, so that the session could be
    // identified as a punchout session.
    session.setAttribute("PunchoutState", state);

    // Set the max inactive interval of the session to the value
    // provided in the store property. If such store property is
    // not found, a default of 5 minutes is used.
    /*String vid = punchoutTransaction.getVendorId();
    Integer timeout = PunchoutStorePropertyFactory.getTimeoutPeriod(vid);
    session.setMaxInactiveInterval( (timeout == null ? 5 : timeout) * 60); */

    logger.info("Punchout Session Created for " + punchoutTransaction.getBuyerCookie());
}

Everything was working fine till I decided that I should set a timeout value for the session. After this point, problem started to occur. At first, I thought that I am messing it up by passing the incorrect value for setMaxInactiveInterval(), so I commented it. To my surprise, the session was getting dropped anyway.

Please Note:

  • We are using Apache Tomcat on Windows 8.1.
Server version: Apache Tomcat/7.0.54
Server built:   May 19 2014 10:26:15
Server number:  7.0.54.0
OS Name:        Windows 8
OS Version:     6.2
Architecture:   amd64
JVM Version:    1.7.0_51-b13
JVM Vendor:     Oracle Corporation
  • We are using Spring 2.5. Yes! And we cannot migrate since this application is very huge (Over 10,000 source files).

  • The URL patterns *.po and *.html are mapping to the same servlet, so the redirection occurs within the same servlet.

Google Searches:

  1. Why does HttpServletRequest object changes.

  2. HttpServletRequest changes to ApplicationHttpRequest

  3. HttpServletRequest to ApplicationHttpRequest

  4. Spring ServletRequest object changing

  5. HttpServletRequest changes after redirection

This silly mistake is pissing us off since last 3 days. Any help would be appreciated! Please point out any silly mistake I have made, and some good tips related to session handling/management are the most welcome ones. If you think that I have not included enough information, please point it out as well. Thanks :)

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Jay
  • 1,089
  • 12
  • 29

2 Answers2

3

Yes, I am answering my own question. I wish to go in details so that future comers may find their answers.

Turns out I am an idiot who is redirecting to absolute path.

Redirection is the key.

No matter whether the servlet you are redirecting to resides on the same application server, is within the same application or it even shares the same servlet. Any request redirected to absolute paths has their own context! So the request object created for them are completely new and has their separate sessions. A session is only maintained if the redirection is being sent on the same port, host and webapp. If redirection is done within the same application, use relative paths using relative paths is the best practice.

Redirecting from https://localhost/servlet1.html to http://localhost/servlet2.html are very different, even though the URLs map to the same servlet (Please notice the difference).

Redirecting from https://localhost/servlet1.html to https://192.168.x.x/servlet2.html or https://127.0.0.1/servlet2.html would bear the same results.

The best practice here would be redirecting to paths relative to your application. This would share the session object in the most efficient way. Using response.sendRedirect("servlet2.html"); is best thing do here (in my opinion).

Understanding the Cookies

A session is identified by the JSESSIONID cookie. If the browser forwards this cookie, then the session is forwarded to the other servlet or controller (or whatever). HTTP and HTTPs are different protocols and hence, different cookies are used. Similarly, localhost and IP addresses are different hosts for a browser, hence different cookies are used. The JSESSIONID cookie is forwarded if the protocol, host and webapp context remains the same, which however, has the same meaning as using relative paths in the redirection. Redirecting to the relative addresses could be thought of as a safe way to redirect on the same host, protocol and application context. It is the browser that decides if it is going to forward your JSESSIONID cookie or not

Community
  • 1
  • 1
Jay
  • 1,089
  • 12
  • 29
  • Well... I don't know if this answer is useful. :/ – Paul Vargas Aug 25 '16 at 10:50
  • 1
    You're not understanding how cookies work and that is the key to the session. https to http is a different cookie. localhost to an IP is a different host according to the browser. You do not need to use relative paths. You need to use the same protocol and host. It is the browser that decides if it is going to forward your JSESSIONID cookie or not. – stdunbar Aug 25 '16 at 14:54
  • @stdunbar Thanks for your participation mate. Isn't it right that using the relative path will keep the redirection on same protocol and host? – Jay Aug 25 '16 at 16:45
  • Yes, from that perspective you are correct - your solution will work fine and I should done a better job indicating that. But it isn't _required_ - it just makes it easier so that you're not dealing with protocol and host changes. – stdunbar Aug 25 '16 at 16:56
  • Your point helped me identifying problem, In my case browser was sending empty cookie.. Still I couldn't catch why it is doing that – Nilesh Feb 07 '20 at 09:59
1

This is how it works, It first checks whether relative redirect available or not then it else construct absolute path.

  // Generate a temporary redirect to the specified location
    try {
        String locationUri;
        // Relative redirects require HTTP/1.1
        if (getRequest().getCoyoteRequest().getSupportsRelativeRedirects() &&
                getContext().getUseRelativeRedirects()) {
            locationUri = location;
        } else {
            locationUri = toAbsolute(location);
        }
        setStatus(status);
        setHeader("Location", locationUri);
        if (getContext().getSendRedirectBody()) {
            PrintWriter writer = getWriter();
            writer.print(sm.getString("coyoteResponse.sendRedirect.note",
                    Escape.htmlElementContent(locationUri)));
            flushBuffer();
        }
    } catch (IllegalArgumentException e) {
        log.warn(sm.getString("response.sendRedirectFail", location), e);
        setStatus(SC_NOT_FOUND);
    }
Nilesh
  • 237
  • 1
  • 3
  • 13