Created
June 17, 2022 12:54
Ask Ben: Extending A ColdFusion Session On A Long-Lived Page
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
component | |
output = false | |
hint = "I define the application settings and event handlers." | |
{ | |
// Configure application management. | |
this.name = "SessionHeartBeatDemo"; | |
this.applicationTimeout = createTimeSpan( 1, 0, 0, 0 ); | |
// Configure session management. | |
this.sessionManagement = true; | |
this.sessionTimeout = createTimeSpan( 0, 0, 30, 0 ); | |
this.setClientCookies = true; | |
// --- | |
// PUBLIC METHODS. | |
// --- | |
/** | |
* I get called once at the start of each session to initialize the session. | |
*/ | |
public void function onSessionStart() { | |
session.id = createUuid(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<cfscript> | |
// This pages doesn't actually have to do anything - just making the AJAX request to | |
// this page should extend the user's current session timeout (assuming that the | |
// session is still active). | |
// Logging for the demo. | |
writeDump( | |
var = "Session heartbeat for: #session.id#", | |
output = "console" | |
); | |
cfcontent( | |
type = "application/json; charset=utf-8", | |
variable = charsetDecode( serializeJson({ "sessionID": session.id }), "utf-8" ) | |
); | |
</cfscript> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<cfoutput> | |
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8" /> | |
<title> | |
Ask Ben: Extending A ColdFusion Session On A Long-Lived Page | |
</title> | |
</head> | |
<body style="min-height: 300vh ;"> | |
<h1> | |
Welcome to Our Portal | |
</h1> | |
<p> | |
Please enjoy all of the wondrous things we have to offer here. Put your feet | |
up, make yourself comfortable. And, maybe drop us a long note that will take | |
a really long time to type: | |
</p> | |
<textarea placeholder="Leave us a message..." cols="50" rows="5"></textarea> | |
<script type="text/javascript"> | |
// The events that will be used to drive the session heartbeat. These are the | |
// user-interaction events that indicate that the user is still here. | |
var heartbeatEvents = [ "scroll", "touchstart", "mousemove", "mousedown", "keydown" ]; | |
// How long should the page wait until it starts monitoring user-interactions. | |
// This delay acts both as a throttle to the network requests; but, also as a | |
// way for us to reduce the event-handler activity on the page as a whole. | |
// -- | |
// NOTE: For the demo, I'm keeping this rather short. In production, you might | |
// want to set it to something like half your session timeout. | |
var heartbeatDelayInMilliseconds = 10000; | |
startHeartbeatTimer(); | |
// ----------------------------------------------------------------------- // | |
// ----------------------------------------------------------------------- // | |
// Our heartbeat is going to be triggered by user-interaction events. However, | |
// there's no need to constantly be listening for events - we just want to | |
// start listening at some point in the future, before the session times-out, | |
// but with enough time for the user to interact with the page. | |
function startHeartbeatTimer() { | |
setTimeout( setupHeartbeatEvents, heartbeatDelayInMilliseconds ); | |
} | |
// I bind the heartbeat events to the page such that the next meaningful user- | |
// interaction triggers a single ping to the server in order to extend the | |
// life of the user's session. | |
function setupHeartbeatEvents() { | |
console.info( "Setting up heartbeat event-bindings." ); | |
// When binding events, we can tell the browser that our binding will | |
// be "passive". This means that we'll never invoke the preventDefault() | |
// method on the event object. This allows the browser to enable some | |
// performance enhancements that will reduce jank. This is especially true | |
// for "scroll" events. | |
var options = { | |
passive: true, | |
once: true | |
}; | |
for ( var eventType of heartbeatEvents ) { | |
window.addEventListener( eventType, handleHeartbeatTriggerEvent, options ); | |
} | |
} | |
// I unbind the heartbeat events from the page. We only need them in place | |
// when we start to listen for interaction events - there's no need to have | |
// them in place all the time. | |
function teardownHeartbeatEvents() { | |
for ( var eventType of heartbeatEvents ) { | |
window.removeEventListener( eventType, handleHeartbeatTriggerEvent ); | |
} | |
} | |
// I handle one of the user-interaction events relating to our heartbeat. | |
function handleHeartbeatTriggerEvent( event ) { | |
console.warn( "User-interaction detected [%s], triggering heartbeat.", event.type ); | |
// Now that we're about to ping the heartbeat end-point, let's reset the | |
// event-bindings and setup the timer that adds event-handlers back to the | |
// DOM in the future when we need to trigger another heartbeat. | |
teardownHeartbeatEvents(); | |
startHeartbeatTimer(); | |
// Hit the server. This should extend the user's current session. | |
fetch( "./heartbeat.cfm" ).then( | |
() => { | |
console.info( "Heartbeat pinged successfully." ); | |
} | |
// How you handle errors is going to depend on what kind of AJAX | |
// client you are using. For example, a retry option might be built | |
// into the client. Or, you might have to implement retry yourself. | |
// Or you could just ignore errors and set the timer-delay to be | |
// small, thereby allowing errors to be "absorbed" naturally. | |
); | |
} | |
</script> | |
</body> | |
</html> | |
</cfoutput> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment