Subject: Resin 4.0.19 Pro: url-rewriting & JSP code see different sessions?!
From: Jeffrey Keays <elided>
To: [email-masked]
Date: Thu, 22 Sep 2011 19:28:59 +0900

Hello Caucho folks,

We have a large struts webapp and we are having a problem (that we never had on resin 3.x series previously) of the current session as of the return of the Action not being accessible to the JSP templates, causing them to render incorrectly, _BUT_ mysteriously still being available to url rewriting. (so if the user navigates back to the page a second time via a more circuitous route, it will render correctly the second time).  This is for the first page after login.

This requires 

          <enable-cookies>false</enable-cookies>

and url rewriting left at default (i.e. on) in the session-config.

also, because this needs to support ancient cellphones that may not support redirects, doing a redirect may not be acceptable to the customer.  (BTW, doing a redirect works around it)

the action, login() invalidates the old session and starts a new one, copying all the attributes over to the new one (to prevent attacks where a bad guy has access to the pre-authentication session id) like this:

	// copy all the attributes out of the old session into local variables.
	session.invalidate(); 
	session = request.getSession(); 
	// copy all the attributes from the local variables back into the new session

However, even with that process turned off (we have a setting), and continuing to use the same session we still have the same problem.

I have put logging statements at the beginning and the end (first thing before returning the struts "SUCCESS" ActionForward) of the login() action, as well as into the JSP template to verify what is visible there.

For a sample invocation: here is the form that gets submitted to login()

> <form action="/do/mdologin?l=ja-JP" method="post">
> <input name="sessionId" value="aaa2XS2Lz-QCM99PrJqkt" type="hidden">
> <div>
> <input name="variant" value="spinnet-standard-Blue" type="hidden">
> <input name="locale" value="ja-JP" type="hidden">
> <input name="directMessageView" type="hidden">
> <input name="uid" type="hidden">
> <input name="folder" type="hidden">
> <input name="remoteAccountUID" type="hidden">
> <input name="client" value="mobile-html-c" type="hidden">
> <input name="login" value="1" type="hidden">
> </div>
> <div>
> ﾒｰﾙｱﾄﾞﾚｽ
> <input istyle="3" name="account" size="14" type="text">
> <br>
> ﾊﾟｽﾜｰﾄﾞ
> <input istyle="3" name="password" size="14" type="password">
> </div>
> <div>
> <input value="ﾛｸﾞｲﾝ" type="submit">
> </div>
> </form>

This is on initial view, we have a form tag that inserts "sessionId" automatically to forms to prevent a type of XSS attack.  On subsequent views, the "/do/mdologin" action interestingly gets url-rewritten to have a jsessionid that _differs_ from the one in the sessionId input (i.e. the one visible to our code using request.getSession())

posting that form, here are the log statements on entering login()

> on entry to login()
> request is HttpServletRequestImpl[HttpRequest[a, 2]]
> request.getSession() -> SessionImpl[aaaHwRSerRnffaE41Jqkt,]
> org.apache.struts.action.LOCALE == en_US

and at the end of login(), immediately prior to returning the "success" actionforward:

> before bottom return mapping.findForward(SUCCESS)
> request is HttpServletRequestImpl[HttpRequest[a, 2]]
> request.getSession() -> SessionImpl[aaaHwRSerRnffaE41Jqkt,]
> LOGIN_ID == [email-masked]
> accountStatusCheckedTime == 1316685307000
> com.openwave.paf.rswitch.client == mobile-html-c
> com.openwave.paf.rswitch.locale == ja-JP
> com.openwave.paf.rswitch.variant == x-standard-DarkRed
> org.apache.struts.action.LOCALE == en_US
> user == Subject:
>   	Principal: test01
>   	Private Credential: {maillogin=test01}                     
> we_account == {<elided>}
> we_cos == {<elided>}
> we_mail == {<elided>}

However in the JSP template I put this code:

> <%
>   StringWriter sw = new StringWriter();
>   PrintWriter pw = new PrintWriter(sw);
>   
>   pw.println("pageContext is " + pageContext);
>   pw.println("request is " + request);
>   pw.println("session is " + session);
>   pw.println("request.getSession() -> " + request.getSession());
>   pw.println("session.isNew() -> " + session.isNew());
>   pw.println("session contains: ");
>   Enumeration e = session.getAttributeNames();
>   while (e.hasMoreElements()) {
>       String name = (String) e.nextElement();
>       pw.println("\t." + name + " == " + session.getAttribute(name));
>   }
> %>
> 
> <wap:pre>
>   <%= sw.toString() %>
> </wap:pre>

which rendered as:

> <pre>
> pageContext is com.caucho.jsp.PageContextImpl@233aa44
> request is IncludeRequest[/variants/spinnet/lcommon/mobile/mainmenu/content.jsp,ForwardRequest[/variants/spinnet/lcommon/mobile/layouts/layout.jsp,ForwardRequest[/do/mmainmenu,HttpServletRequestImpl[HttpRequest[a, 2]]]]]
> session is SessionImpl[aaaqgA1FT9DTo6YE2Jqkt,]
> request.getSession() -> SessionImpl[aaaqgA1FT9DTo6YE2Jqkt,]
> session.isNew() -> true
> session contains:
> </pre>

which explains why the JSP template content did not render correctly, however, links that _did_ render  got url-rewritten ;jsessionid= additions like so:

> <li>
> <a href="/do/addresses/mfilters;jsessionid=aaaHwRSerRnffaE41Jqkt" accesskey="2">
> ｱﾄﾞﾚｽ帳
> </a>
> </li>
> <li>
> <a href="/do/logout;jsessionid=aaaHwRSerRnffaE41Jqkt" accesskey="3">
> ﾛｸﾞｱｳﾄ
> </a>
> </li>

i.e. the "real" session.  Unsurprisingly clicking on these links leads to working pages including the first one that rendered wrongly the first time.

This weirdness of the same session not being available from JSP pageContexts as is available to URL rewriting seems to be new in Resin 4, as this app worked fine in Resin 3.x.  Does this look like a Bug to you?  Or is it a changed spec?

	--Jeffrey Keays
	Senior Consultant
	Nihon Openwave Systems K.K.
	Shinjuku, Tokyo
	Japan

P.S. A coworker looked through in a debugger to see where request.getSession() became null, and he was able to come up with this:

> Daemon Thread [http://127.0.0.1:18143-4] (Suspended)	
> 	com.caucho.server.http.HttpServletResponseImpl.encodeURL(java.lang.String) line: 1330	
> 		-> request.getSession(false) == nulll            
> 		
> 	com.caucho.server.webapp.ForwardResponse(com.caucho.server.http.CauchoResponseWrapper).encodeURL(java.lang.String) line: 281	
> 	com.caucho.server.webapp.ForwardResponse(com.caucho.server.http.CauchoResponseWrapper).encodeURL(java.lang.String) line: 281	
> 	com.caucho.server.webapp.ForwardResponse(com.caucho.server.http.CauchoResponseWrapper).encodeURL(java.lang.String) line: 281	
> 	com.openwave.webedge.servlets.ContentLengthFilter$BufferedResponse(javax.servlet.http.HttpServletResponseWrapper).encodeURL(java.lang.String) line: 207	
> 	com.caucho.server.http.ToCharResponseAdapter(com.caucho.server.http.ResponseWrapper).encodeURL(java.lang.String) line: 417	
> 	com.caucho.server.webapp.IncludeResponse(com.caucho.server.http.CauchoResponseWrapper).encodeURL(java.lang.String) line: 281	
> 	org.apache.struts.util.RequestUtils.computeURL(javax.servlet.jsp.PageContext, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.util.Map, java.lang.String, boolean, boolean) line: 644	
> 	    -> ((javax.servlet.http.HttpServletRequest)pageContext.getRequest()).getSession(false) != null
> 
> 	org.apache.struts.util.RequestUtils.computeURL(javax.servlet.jsp.PageContext, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.util.Map, java.lang.String, boolean) line: 436	
> 	com.openwave.webedge.taglibs.FormTagWithSessionId(com.openwave.paf.taglibs.mobile.struts.FormTag).getAction() line: 92	
> 	com.openwave.webedge.taglibs.FormTagWithSessionId(com.openwave.paf.taglibs.mobile.FormTag).doStartTag() line: 77	
> 	com.openwave.webedge.taglibs.FormTagWithSessionId(com.openwave.paf.taglibs.mobile.struts.FormTag).doStartTag() line: 65	
> 	com.openwave.webedge.taglibs.FormTagWithSessionId.doStartTag() line: 45	
> 	docroot/lcommon/mobile/login/content.jsp line: 23	
> 	docroot/lcommon/mobile/login/content.jsp line: not available	
> 	_jsp._docroot._lcommon._mobile._login._content__jsp(com.caucho.jsp.JavaPage).service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 64	
> 	_jsp._docroot._lcommon._mobile._login._content__jsp(com.caucho.jsp.Page).pageservice(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 542	
> 	com.caucho.server.dispatch.PageFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 194	
> 	com.caucho.server.webapp.DispatchFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 126	
> 	com.caucho.server.dispatch.SubInvocation(com.caucho.server.dispatch.ServletInvocation).service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 287	
> 	com.caucho.server.webapp.RequestDispatcherImpl.include(javax.servlet.ServletRequest, javax.servlet.ServletResponse, java.lang.String) line: 440	
> 	com.caucho.server.webapp.RequestDispatcherImpl.include(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 359	
> 	com.caucho.jsp.PageContextImpl.include(java.lang.String, boolean) line: 1081	
> 	com.caucho.jsp.PageContextImpl.include(java.lang.String) line: 957	
> 	com.openwave.paf.taglibs.struts.GetTag(com.openwave.paf.taglibs.struts.InsertTag).doInclude(java.lang.String) line: 59	
> 	org.apache.struts.taglib.tiles.InsertTag$InsertHandler.doEndTag() line: 881	
> 	com.openwave.paf.taglibs.struts.GetTag(org.apache.struts.taglib.tiles.InsertTag).doEndTag() line: 473	
> 	docroot/lcommon/mobile/layouts/layout.jsp line: 43	
> 	docroot/lcommon/mobile/layouts/layout.jsp line: not available	
> 	_jsp._docroot._lcommon._mobile._layouts._layout__jsp(com.caucho.jsp.JavaPage).service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 64	
> 	_jsp._docroot._lcommon._mobile._layouts._layout__jsp(com.caucho.jsp.Page).pageservice(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 551	
> 	com.caucho.server.dispatch.PageFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 194	
> 	com.caucho.server.webapp.DispatchFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 126	
> 	com.caucho.server.dispatch.SubInvocation(com.caucho.server.dispatch.ServletInvocation).service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 287	
> 	com.caucho.server.webapp.RequestDispatcherImpl.forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse, java.lang.String, com.caucho.server.dispatch.Invocation, javax.servlet.DispatcherType) line: 298	
> 	com.caucho.server.webapp.RequestDispatcherImpl.forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 116	
> 	com.openwave.paf.struts.ResourceTilesRequestProcessor(org.apache.struts.action.RequestProcessor).doForward(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 1069	
> 	com.openwave.paf.struts.ResourceTilesRequestProcessor(org.apache.struts.tiles.TilesRequestProcessor).doForward(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 274	
> 	com.openwave.paf.struts.ResourceTilesRequestProcessor(com.openwave.paf.struts.TilesRequestProcessor).doForward(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 141	
> 	com.openwave.paf.struts.ResourceTilesRequestProcessor(org.apache.struts.tiles.TilesRequestProcessor).processTilesDefinition(java.lang.String, boolean, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 254	
> 	com.openwave.paf.struts.ResourceTilesRequestProcessor(org.apache.struts.tiles.TilesRequestProcessor).processForwardConfig(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.apache.struts.config.ForwardConfig) line: 309	
> 	com.openwave.paf.struts.ResourceTilesRequestProcessor(org.apache.struts.action.RequestProcessor).process(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 279	
> 	com.openwave.paf.struts.ResourceTilesRequestProcessor(com.openwave.paf.struts.TilesRequestProcessor).process(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 109	
> 	com.openwave.paf.struts.ActionServlet.process(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 106	
> 	com.openwave.paf.struts.ActionServlet(org.apache.struts.action.ActionServlet).doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 507	
> 	com.openwave.paf.struts.ActionServlet(javax.servlet.http.HttpServlet).service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 119	
> 	com.openwave.paf.struts.ActionServlet(javax.servlet.http.HttpServlet).service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 96	
> 	com.caucho.server.dispatch.ServletFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 109	
> 	com.caucho.server.webapp.DispatchFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 126	
> 	com.caucho.server.dispatch.SubInvocation(com.caucho.server.dispatch.ServletInvocation).service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 287	
> 	com.caucho.server.webapp.RequestDispatcherImpl.forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse, java.lang.String, com.caucho.server.dispatch.Invocation, javax.servlet.DispatcherType) line: 298	
> 	com.caucho.server.webapp.RequestDispatcherImpl.forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 116	
> 	com.openwave.webedge.servlets.AuthFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 376	
> 	com.caucho.server.dispatch.FilterFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 89	
> 	com.openwave.webedge.servlets.LoggingDataFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 79	
> 	com.caucho.server.dispatch.FilterFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 89	
> 	com.openwave.paf.filters.CloseFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 75	
> 	com.caucho.server.dispatch.FilterFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 89	
> 	com.openwave.webedge.servlets.MailboxLockFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 50	
> 	com.caucho.server.dispatch.FilterFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 89	
> 	com.openwave.paf.filters.AvailabilityFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 140	
> 	com.caucho.server.dispatch.FilterFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 89	
> 	com.openwave.webedge.servlets.ContentLengthFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 166	
> 	com.caucho.server.dispatch.FilterFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 89	
> 	com.openwave.paf.filters.BadParameterFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 161	
> 	com.caucho.server.dispatch.FilterFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 89	
> 	com.openwave.paf.filters.RefererCheckFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 239	
> 	com.caucho.server.dispatch.FilterFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 89	
> 	com.openwave.webedge.servlets.EncodingFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 133	
> 	com.caucho.server.dispatch.FilterFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 89	
> 	com.caucho.server.webapp.DispatchFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 126	
> 	com.caucho.server.dispatch.SubInvocation(com.caucho.server.dispatch.ServletInvocation).service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 287	
> 	com.caucho.server.webapp.RequestDispatcherImpl.forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse, java.lang.String, com.caucho.server.dispatch.Invocation, javax.servlet.DispatcherType) line: 298	
> 	com.caucho.server.webapp.RequestDispatcherImpl.dispatch(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 141	
> 	com.caucho.server.dispatch.RewriteDispatchFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 91	
> 	com.caucho.server.webapp.WebAppFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 156	
> 	com.caucho.server.webapp.AccessLogFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 95	
> 	com.caucho.server.dispatch.Invocation(com.caucho.server.dispatch.ServletInvocation).service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 287	
> 	com.caucho.server.http.HttpRequest.handleRequest() line: 802	
> 	com.caucho.network.listen.TcpSocketLink.dispatchRequest() line: 731	
> 	com.caucho.network.listen.TcpSocketLink.handleRequest(boolean) line: 693	
> 	com.caucho.network.listen.TcpSocketLink.handleRequestsImpl(boolean) line: 677	
> 	com.caucho.network.listen.TcpSocketLink.handleRequests(boolean) line: 623	
> 	com.caucho.network.listen.AcceptTask.doTask() line: 107	
> 	com.caucho.network.listen.AcceptTask(com.caucho.network.listen.ConnectionReadTask).runThread() line: 98	
> 	com.caucho.network.listen.AcceptTask(com.caucho.network.listen.ConnectionReadTask).run() line: 81	
> 	com.caucho.network.listen.AcceptTask.run() line: 65	
> 	com.caucho.env.thread.ResinThread.runTasks() line: 164	
> 	com.caucho.env.thread.ResinThread.run() line: 130