Last night, at the New York ColdFusion User Group, I gave a sneak preview of my Scotch on the Rocks (SOTR) presentation - Mastering the ColdFusion Application Framework. In this presentation I demonstrate some of the more interesting things you can do with the ColdFusion Framework's page request life cycle. In one of the examples, I made use of a very small session timeout (one second) as a means to "explicitly" end a user's active session. During this particular demo, Michael Dinowitz suggested that I try experimenting with an even smaller timeout. And so, we did a bit of live-coding and quickly found some very odd behaviors.
The first thing we tried was setting the session timeout to be 0.9 seconds. This immediately resulted in a session-scope reference problem in my onRequestStart() ColdFusion application event handler. Now, I have previously demonstrated that there are no negative affects created by a mid-page session expiration; so, clearly something funky was going on here. To get to the bottom of this, I set up a simple Application.cfc and put some logging in various places to see what was going on.
<cfcomponent output="false" hint="I provide the application settings and event handlers."> <!--- Define application settings. ---> <cfset this.name = hash( getCurrentTemplatePath() ) /> <cfset this.applicationTimeout = createTimeSpan( 0, 0, 2, 0 ) /> <cfset this.sessionManagement = true /> <!--- ------------------------------------------------- ---> <!--- ------------------------------------------------- ---> <!--- Set the session timeout - for this experiment, we are going to be dealing with a very short period of time (one second or less) to see how the ColdFusion application framework behaves. ---> <cfset this.sessionTimeout = createTimeSpan( 0, 0, 0, 0.9 ) /> <!--- ------------------------------------------------- ---> <!--- ------------------------------------------------- ---> <cffunction name="onSessionStart" access="public" returntype="void" output="false" hint="I initialize the session."> <!--- Log the session start. ---> <cfdump var="#session#" label="NEW Session" format="html" output="#getDirectoryFromPath( getCurrentTemplatePath() )#log.htm" /> <!--- Return out. ---> <cfreturn /> </cffunction> <cffunction name="onRequest" access="public" returntype="void" output="true" hint="I process the user's request."> <!--- Output the application settings. ---> <cfdump var="#this#" label="Application Settings" showudfs="false" /> <br /> <!--- Output the cookies collection. ---> <cfdump var="#cookie#" label="Cookies" /> <!--- Flush the output to make sure the previous dumps are displayed even if the following dump errors out. ---> <cfflush /> <br /> <!--- Output the session data. ---> <cfdump var="#session#" label="Session Data" /> <!--- Return out. ---> <cfreturn /> </cffunction> <cffunction name="onSessionEnd" access="public" returntype="void" output="false" hint="I teardown a session."> <!--- Define arguments. ---> <cfargument name="sessionScope" type="any" required="true" hint="I am the ending session scope." /> <!--- Log the session scope for the session that has expired. ---> <cfdump var="#arguments.sessionScope#" label="EXPIRED Session" format="html" output="#getDirectoryFromPath( getCurrentTemplatePath() )#log.htm" /> <!--- Return out. ---> <cfreturn /> </cffunction> </cfcomponent>
As you can see, I am setting the session timeout to be 0.9 seconds. Then, I am logging the start and end of the session activity using the onSessionStart() and onSessionEnd() event handlers respectively. During the page request, I am using the onRequest() event handler to output the application, cookie, and session settings. When I run a page through this ColdFusion application framework configuration, I get the following page output:
As you can see here, when the onRequest() event handler executes, the application configuration and cookie scope output just fine. It appears, however, that my session scope is not available. Now, if you look at the application configuration, you can see that session management is enabled; but, if you look at the sessionTimeout value, you'll see that it is zero - not the 0.9 seconds that we defined in the pseudo constructor. Furthermore, when I look at the log.htm file which gets updated in the onSessionStart() and onSessionEnd() event handlers, this is what I see:
The log is empty! In addition to the session scope not being available, it also appears that neither the onSessionStart() nor the onSessionEnd() event handler are ever triggered.
When you raise the session timeout value to one second or higher, everything works as expected - the session scope can be referenced and both the onSessionStart() and onSessionEnd() event handlers execute. So, from what I can gather, lowering the session timeout to below one second effectively turns off session management completely even if the sessionManagement application setting is defined as True. Going back to my previous post on how application settings get normalized into a second-based time-span schema, this starts to make more sense; if the session time-span gets converted from factional days to seconds, it figures that a factional second value would get rounded down to a whole second value (zero in our case).
Using such a low session timeout is not a typical use case, so this is probably not something that you'll ever need to know about. But, if you're ever going to use dynamic session timeouts to affect session execution, this is an important caveat to understand. When in doubt - never drop your session timeouts below one second.
Similar behavior in Railo (3.1.2.001) in terms of the sessionTimeout being rounded down to 0 seconds for a LT 1 value. That said the session is still created and it does log.
Best guess it's the underlying Java causing the round down and a CF specific dependency which prevents creation of a 0 length sessionTimeout.
Thanks for the Railo insight. It seems odd that ColdFusion creates the CFID/CFTOKEN values, but then fails to boot-up / tear-down a session. Maybe it's just an optimization thing; after all, the need for this kind of outlier use-case is pretty much nill.
I think the CFID cookies are created because you have client vars enabled. If you add:
<cfset this.clientManagement="No" />
<cfset this.setClientCookies = "No" />
to the Application.cfc (and clear your cookies) then they're not recreated.
Agreed that this is pretty much fringe usability but I personally feel it's always good to define the boundaries of any language before you run across them as part of a client project.
I just tried this and I can confirm that if you turn of setClientCookies, then the CFID and CFTOKEN are not available in the cookie scope. And I guess this makes sense (as the cookie setting affects the response headers).
But! If the session tokens are being created (and then set if the setClientCookies is flagged True) then, it would figure that the session would start up? Who knows :)