Extending The Current Request Timeout In ColdFusion / CFML

Cyberdime
Published: February 11, 2023

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: request index.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.

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.

Source: www.bennadel.com