ICEfaces
  1. ICEfaces
  2. ICE-6791

Cookieless mode causes inputFile to disappear on file upload

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.8.2-EE-GA_P02
    • Fix Version/s: 1.8.3, EE-1.8.2.GA_P03
    • Component/s: ICE-Components
    • Labels:
      None
    • Environment:
      Cookies disabled on server and browser. Tomcat: context.xml -> <Context cookies="false"/>
    • Workaround Exists:
      Yes
    • Workaround Description:
      When apps disable session cookies in their context.xml file, they should also set the com.icesoft.faces.forceLifecycleOnCallingThread context param to true. And possibly set their app to synchronous mode, since server push might depend on cookies.

      Description

      When cookies are disabled on the server and the browser the inputFile disappears when a file is uploaded. The file is saved properly and no errors are thrown. A refresh of the browser shows the component again.

      There is a difference in behavior depending on the browser and cookie settings.

      Firefox:
       - Cookies disabled on the server and browser: inputFile does not disappear on upload.
       - Cookies disabled on the severe but not on the browser: inputFile disappears on upload.

      IE:
       - Component disappears no matter what the browser cookies setting is.

        Issue Links

          Activity

          Hide
          Mark Collette added a comment -

          Using the test application and tomcat 6, I duplicated the issue. Sometimes the inputFile's iframe would disappear after several uploads and sometimes it would disappear after one upload. I investigated ICE-5871, and audited the inputFile code to confirm that it does indeed use ExternalContext.encodeResourceURL(String) to render both the URL for the iframe source in the larger page, and within the iframe page for its posting. Viewing the browser dom shows that the initial page GET includes the jsessionid part in the urls, but that after one or more upload POSTs the jsessionid is not there anymore, and the iframe disappears. Further investigation in the core shows that com.icesoft.faces.webapp.http.servlet.ServletExternalContext.encodeResourceURL(String) is responsible for delegating to the Response.encodeURL(String). Modification of that method showed that the Catalina classes were throwing an exception, and our code was silently trapping it and using the unencoded url, when we were losing the jsessionid part. The stack trace indicated that it was while doing a PersistentFacesState.renderLater() for pushing updates.

          Disabling delayed rendering, that occurs on a separate thread, in favour of immediate rendering that happens on the current thread, seems to fix the issue. The jsessionid part remains after many uploads. The easiest way to accomplish this is for cookie-less apps to set the com.icesoft.faces.forceLifecycleOnCallingThread context param to true. Support are testing to ensure this works for all browsers, as IE had given different results than Firefox.

          For future reference, in case something related to this pops up again, here's more analysis. At the end of the file upload, the iframe contents need to be written out to the response object, so we do a lifecycle to get the contents. Currently, that lifecycle can use immediate rendering or deferred rendering, just like all the progress pushes. It's probably best to use the same technique, so they'll be queued appropriately. We wouldn't want to make our last render be immediate, and then queued progress pushes come in later, if that could somehow show older progress. But if we did make the last lifecycle be immediate, and used that to stop any other async ones that hadn't come through yet, that might also solve the issue of UploadServer.getResultingIframeContent making long running actionListeners have the iframe disappear. We could also see why the 5 second timeout is so short. Perhaps browsers do a timeout without any response by then anyway. If we wrote out some headers, then did the lifecycle, and let it run for however long, and made it immediate, then perhapse we'd get around it. Also, it seems like there's a bit of an issue where the lifecycle should cause the page to update before we return the iframe contents. But most likely the dom diff keeps that from updating that part of the page anyway.

          Show
          Mark Collette added a comment - Using the test application and tomcat 6, I duplicated the issue. Sometimes the inputFile's iframe would disappear after several uploads and sometimes it would disappear after one upload. I investigated ICE-5871 , and audited the inputFile code to confirm that it does indeed use ExternalContext.encodeResourceURL(String) to render both the URL for the iframe source in the larger page, and within the iframe page for its posting. Viewing the browser dom shows that the initial page GET includes the jsessionid part in the urls, but that after one or more upload POSTs the jsessionid is not there anymore, and the iframe disappears. Further investigation in the core shows that com.icesoft.faces.webapp.http.servlet.ServletExternalContext.encodeResourceURL(String) is responsible for delegating to the Response.encodeURL(String). Modification of that method showed that the Catalina classes were throwing an exception, and our code was silently trapping it and using the unencoded url, when we were losing the jsessionid part. The stack trace indicated that it was while doing a PersistentFacesState.renderLater() for pushing updates. Disabling delayed rendering, that occurs on a separate thread, in favour of immediate rendering that happens on the current thread, seems to fix the issue. The jsessionid part remains after many uploads. The easiest way to accomplish this is for cookie-less apps to set the com.icesoft.faces.forceLifecycleOnCallingThread context param to true. Support are testing to ensure this works for all browsers, as IE had given different results than Firefox. For future reference, in case something related to this pops up again, here's more analysis. At the end of the file upload, the iframe contents need to be written out to the response object, so we do a lifecycle to get the contents. Currently, that lifecycle can use immediate rendering or deferred rendering, just like all the progress pushes. It's probably best to use the same technique, so they'll be queued appropriately. We wouldn't want to make our last render be immediate, and then queued progress pushes come in later, if that could somehow show older progress. But if we did make the last lifecycle be immediate, and used that to stop any other async ones that hadn't come through yet, that might also solve the issue of UploadServer.getResultingIframeContent making long running actionListeners have the iframe disappear. We could also see why the 5 second timeout is so short. Perhaps browsers do a timeout without any response by then anyway. If we wrote out some headers, then did the lifecycle, and let it run for however long, and made it immediate, then perhapse we'd get around it. Also, it seems like there's a bit of an issue where the lifecycle should cause the page to update before we return the iframe contents. But most likely the dom diff keeps that from updating that part of the page anyway.
          Hide
          Arran Mccullough added a comment -

          I've tested my test case with the context param but I still see the same issues as reported in the initial issue description. Was there anything else that was required?

          Show
          Arran Mccullough added a comment - I've tested my test case with the context param but I still see the same issues as reported in the initial issue description. Was there anything else that was required?
          Hide
          Mark Collette added a comment -

          With both com.icesoft.faces.forceLifecycleOnCalling=true and com.icesoft.faces.synchronousUpdate=true this is still happening.

          Show
          Mark Collette added a comment - With both com.icesoft.faces.forceLifecycleOnCalling=true and com.icesoft.faces.synchronousUpdate=true this is still happening.
          Hide
          Mark Collette added a comment -

          Related to ICE-6881. Tried a code patch given to me, that modifies ServletExternalContext.encodeResourceURL(String) to first try using the originalResponse before the response object, but that had the same problem in that it was getting the same exception in the tomcat Response object.
          Here's the relevant code, and the NPE is from scheme being null, as edirectURLCC is used earlier in the method with no problem :

          String scheme = request.getScheme(); // "http" / "https"
          ...
          redirectURLCC.append(scheme, 0, scheme.length());

          So it looks like maybe that Request object, that this Response object references, might have already been recycled.

          The whole point of these methods is to concatenate this onto the url: request.getSessionInternal().getIdInternal()

          Show
          Mark Collette added a comment - Related to ICE-6881 . Tried a code patch given to me, that modifies ServletExternalContext.encodeResourceURL(String) to first try using the originalResponse before the response object, but that had the same problem in that it was getting the same exception in the tomcat Response object. Here's the relevant code, and the NPE is from scheme being null, as edirectURLCC is used earlier in the method with no problem : String scheme = request.getScheme(); // "http" / "https" ... redirectURLCC.append(scheme, 0, scheme.length()); So it looks like maybe that Request object, that this Response object references, might have already been recycled. The whole point of these methods is to concatenate this onto the url: request.getSessionInternal().getIdInternal()
          Hide
          Ted Goddard added a comment -

          Using forceLifecycleOnCallingThread = true is likely best since Threads running asynchronous lifecycles cannot obtain the "currentResponse" so they will fail to use the response encoding methods correctly.

          Show
          Ted Goddard added a comment - Using forceLifecycleOnCallingThread = true is likely best since Threads running asynchronous lifecycles cannot obtain the "currentResponse" so they will fail to use the response encoding methods correctly.
          Hide
          Ted Goddard added a comment -

          Note that failure was typically transient for all settings: first upload would succeed, second would fail, and subsequent uploads would succeed.

          Show
          Ted Goddard added a comment - Note that failure was typically transient for all settings: first upload would succeed, second would fail, and subsequent uploads would succeed.
          Hide
          Ken Fyten added a comment -

          Resolved by fix for ICE-6881.

          Show
          Ken Fyten added a comment - Resolved by fix for ICE-6881 .
          Hide
          Ted Goddard added a comment -

          An experimental fix for ICE-6881 is not applicable here: for that fix, a FailSafeResponseWrapper allows encodeURL to be called without Exceptions. This is not suitable in this case because the underlying Response is actually needed to include the jsessionid parameter (FailSafeResponseWrapper just returns the URL when the underlying Response fails).

          Show
          Ted Goddard added a comment - An experimental fix for ICE-6881 is not applicable here: for that fix, a FailSafeResponseWrapper allows encodeURL to be called without Exceptions. This is not suitable in this case because the underlying Response is actually needed to include the jsessionid parameter (FailSafeResponseWrapper just returns the URL when the underlying Response fails).

            People

            • Assignee:
              Ted Goddard
              Reporter:
              Arran Mccullough
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: