Extending The Current Request Timeout In ColdFusion / CFML
The request timeout determines how long the current request / CFThread
can execute before the request throws a timeout exception. For the most part, this value is either never set (which then uses the default timeout defined in the ColdFusion admin); or, it's set once at the top of the request. Sometimes, however, I run into situations where I need to dynamically update the timeout of the current page. Unfortunately, the CFSetting
tag doesn't support this; as such, I wanted to outline ways in which this can be hacked into the request in either Adobe ColdFusion or Lucee CFML.
ASIDE: Over on the Lucee Dev Forum, I put out a suggestion that the
CFSetting
tag add an "extend" attribute to perform this operation. I'm not married to the idea - it was just a thought.
When you use the CFSetting
tag to set the timeout for the current request, you're setting an absolute value, not a relative value. Which means, if your request has already been running for 5-seconds, and then you set the request timeout to be 10-seconds, the new timeout threshold for the request will be 10-seconds - not 15 (which would have been 5 + 10).
To see this absolute value in action, we can attempt to increment the timeout value while making CFHttp
requests that we know will exceed the defined threshold:
<cfscript>
requestSetTimeout( 5 );
blockForSeconds( 4 );
// CAUTION: !! DOES NOT WORK !!
requestSetTimeout( 5 );
blockForSeconds( 4 );
writeOutput( "Done" );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
/**
* I set the request timeout to the given seconds using the native CFSetting tag.
*/
public void function requestSetTimeout( required numeric timeoutInSeconds ) {
cfsetting( requestTimeout = timeoutInSeconds );
}
/**
* I block for the given duration in seconds by using HTTP requests that block for one
* second. By performing this blocking in 1-second increments (essentially), this gives
* the overall page request more opportunities to step in and throw a timeout.
*/
public void function blockForSeconds( required numeric durationInSeconds ) {
for ( var i = 1 ; i <= durationInSeconds ; i++ ) {
// Will block for 1-second on target page.
cfhttp(
result = "local.apiResponse",
method = "get",
url = "http://#cgi.server_name#:#cgi.server_port#/request-timeout/block-and-lock.cfm"
);
}
}
</cfscript>
This code might look like I'm increasing the timeout by 5-seconds before I make my long-running requests; but, again, that's not how the CFSetting
tag works. As such, when we run this ColdFusion code, we get the following errors (truncated):
Lucee CFML:
RequestTimeoutException
: requestindex.cfm
has run into a timeout (timeout: 5 seconds) and has been stopped. The thread started 5028ms ago.Adobe ColdFusion:
RequestTimedOutException
: The request has exceeded the allowable time limit Tag:cfhttp
.
That second call to set the request timeout was, essentially, a no-op since I was setting it from 5
to 5
.
In order to extend the current request timeout, our subsequent calls to the CFSetting
tag have to set an increasingly large absolute value. Unfortunately, this isn't obvious; and, there's no single way to do this cross-platform (that I know of). But, it can be finagled in both Lucee CFML and Adobe ColdFusion through the use of the getPageContext()
system function.
Here's an updated version of the above approach; but, instead of calling requestSetTimeout()
a second time, I'm calling requestExtendTimeout()
! This method attempts to encapsulate the cross-platform complexity.
<cfscript>
requestSetTimeout( 5 );
blockForSeconds( 4 );
// NOTE: I'm EXTENDING here, NOT setting.
requestExtendTimeout( 5 );
blockForSeconds( 4 );
writeOutput( "Done" );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
/**
* I EXTEND the request timeout by the given seconds using the native CFSettings tag.
*
* CAUTION: In Lucee CFML, this extends the existing request timeout. In Adobe
* ColdFusion, this extends the current execution time of the request.
*/
public void function requestExtendTimeout( required numeric timeoutInSeconds ) {
var pageContext = getPageContext();
var requestTimeout = pageContext?.getRequestTimeout();
// ADOBE COLDFUSION does NOT support request timeout functions in the page
// context. As such, we'll have to go a bit more low-level.
if ( isNull( requestTimeout ) ) {
var startedAt = getPageContext().getFusionContext().getStartTime();
var startedSecondsAgo = now().diff( "s", startedAt );
cfsetting(
requestTimeout = ( startedSecondsAgo + timeoutInSeconds )
);
// LUCEE CFML allows request timeouts to be configured on the page context.
} else {
cfsetting(
requestTimeout = ( ( requestTimeout / 1000 ) + timeoutInSeconds )
);
}
}
// ... truncated - other two methods are the same in first example ... //
</cfscript>
As you can see, I attempt to call pageContext?.getRequestTimeout()
which will fail gracefully on Adobe ColdFusion thanks to the safe navigation operator. I use the result of this call to determine which platform I'm on; and, then, branch into a platform-specific approach for calculating the new request timeout.
And, when we run this in both Lucee CFML and Adobe ColdFusion, we get the following output:
Done.
The request timeout was successfully extended to 10-seconds on both CFML platforms, allowing 8-seconds worth of CFHttp
calls to execute. I don't love having to reach into the Page Context in order to accomplish our goals here. But, to be fair, I don't have to extend the request timeout very often.
coldfusion.runtime.RequestMonitor
Class
UPDATE: Using the Right after I published this article, I came across another article that I wrote in 2007 dealing with the same exact topic. In that article, I use the internal ColdFusion class, coldfusion.runtime.RequestMonitor
to get the current request timeout. And, unlike the getPageContext()
approach that I used above, this RequestMonitor
class appears to work in both Lucee CFML and Adobe ColdFusion.
Here's an updated demo that gets the current request timeout from this Java class:
<cfscript>
requestSetTimeout( 5 );
blockForSeconds( 4 );
// NOTE: I'm EXTENDING here, NOT setting.
requestExtendTimeout( 5 );
blockForSeconds( 4 );
writeOutput( "Done" );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
/**
* I EXTEND the request timeout by the given seconds using the native CFSettings tag.
*/
public void function requestExtendTimeout( required numeric timeoutInSeconds ) {
// CAUTION: Your ColdFusion platform needs to have permissions to access Java
// objects in order to use this (I think).
var currentTimeout = createObject( "java", "coldfusion.runtime.RequestMonitor" )
.getRequestTimeout()
;
cfsetting(
requestTimeout = ( currentTimeout + timeoutInSeconds )
);
}
// ... truncated - other two methods are the same in first example ... //
</cfscript>
This approach is more akin to the approach that I used with Lucee CFML.
Want to use code from this post? Check out the license.
Reader Comments
Ok, now I feel like I struck gold here! Great stuff! 🙌💥💯
@Chris,
Thank you, good sir! Sometimes it's fun to get into the weeds like this.
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →