The other day, in a comment to my caveman-style asynchronous ColdFusion page execution, JAlpino told me to look into the use of the 204 status code to help the CFHttp call return faster. I haven't done a ton with different status codes (mostly 200 and 301/302), so I did a quick Google search for status code 204. According to this page, the 204 status code represents a page that has no content:
204 No Content. This code is used in cases where the request was successfully processed, but the response doesn't have a message body.
According to JAlpino, when a browser receives this status code in the header, it closes the request because it thinks the request will not render any content. In the context of a CFHttp call, the hope is that the CFHttp request will close out immediately and allow the parent ColdFusion page to continue executing with little delay.
To test this, I set up a small ColdFusion page with a 204 status code definition:
<!--- Tell the browser there is no content by using the status code, 204 - No content. ---> <cfheader statuscode="204" statustext="No Content" /> <!--- Set the content type and reset the output stream. ---> <cfcontent type="text/html" reset="true" /> <!--- Flush headers. ---> <cfflush /> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Status Code Test: 204 No Content</title> </head> <body> <h1> Status Code Test: 204 No Content </h1> <p> There is content on this page, but the header information should tell the browser that no content could be found. </p> <!--- To make sure that the page doesn't execute instantaneously, let's get the page to sleep for a few seconds. ---> <cfthread action="sleep" duration="#(5 * 1000)#" /> <p> Done building page. </p> </body> </html>
Here, you can see that I am setting the status code to 204 using ColdFusion's CFHeader tag. Then, I flush the header to make sure we don't build the whole page on the server before returning the header data. To make sure that this header status works, I am sleeping the page for 5 seconds, which would be a noticeable delay in the calling page.
Then, I set up a ColdFusion page to execute a CFHttp request to the 204.cfm page I built above:
<!--- Get the page that we want to call. ---> <cfset strURL = ( GetDirectoryFromPath( "http://#CGI.server_name#:8#CGI.script_name#" ) & "204.cfm" ) /> <!--- Mark the time that we are starting the request. ---> <cfset intStartTick = GetTickCount() /> <!--- Grab page that we know has a 204 no content header and a long delay. This will test to see how fast the CFHttp call returns based on header information. ---> <cfhttp url="#strURL#" method="get" result="objGET" /> <!--- Output time to wait. ---> <cfoutput> <p> Wait Duration: #DecimalFormat( ((GetTickCount() - intStartTick) / 1000) )# Seconds </p> <p> Content: [#objGET.FileContent#] </p> </cfoutput>
After the ColdFusion CFHttp quest returns, I am outputting the time of execution as well as the content that was returned. The execution time is obvious, but the content I am outputting to make sure that the CFHttp really does cut out of the request before the page is fully rendered.
When I run the calling page, I get the following output:
Wait Duration: 5.01 Seconds
Ok, so what does this tell us? The 5.01 second execution time tells us that the CFHttp call did have to wait for the CFThread call on the 204.cfm template. But, the empty content tells us that the CFHttp call did not bring back any content with it. In a way, these are two conflicting properties of the call. It should have either returned instantly with no content, or it should have returned after 5 seconds with the content of the 204.cfm template.
At first, I thought that JAlpino was simply wrong in his assessment of how a 204 status code would interact with a ColdFusion CFHttp call. But the fact that we have conflicting behaviors as defined above didn't sit well with me. I kept going over it in my mind, and then I realized what the problem was: CFFlush!
CFFlush, as amazing of a tag as it is, is a bit buggy when it comes to flushing minimal amounts of data. If you look at where we are calling CFFlush in our 204.cfm ColdFusion template, you will notice that it is before any HTML and just after we cleared the output stream (content buffer). As such, at that point, we'd be flushing a few spaces and maybe a line return at best. CFFlush, however, doesn't always like to flush so little data.
To experiment, I moved the CFFlush to right after the doc type declaration:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!--- Flush headers. ---> <cfflush /> <html>
Now, the hope is that the text characters in the doc type would provide enough data such that the ColdFusion server would be happy to fully execute the CFFlush command. And indeed, running the page again, we get the following output:
Wait Duration: 0.02 Seconds
Ok, now we're getting the results that we were hoping for. Looks like JAlpino dropped a great tip on me; using a status code of 204 will allow the ColdFusion CFHttp call to return much faster than the 1 second timeout hack that I was using. We just have to be very careful on how soon the Headers are flushed to the client.
And, on a related note, this just goes to show you how important it is to have a framework that allows CFFlush in the page rendering. Case in point, if you want to leverage header values before the page has fully rendered, you need to allow it.
Nice write up Ben! As usual, you go above an beyond in your research and gratefully share it with the rest of us. I've typically used 204 status codes instead of AJAX requests when it wasn't (absolutely) necessary to receive a response from the server, items like 'watch this item' or 'flag this site' kind of situations. - Justin
That sounds like a good technique. And sorry about calling you JAlpino - I didn't know what your first name was :) Thanks for the hot tip and glad that I could echo it back out there.
@ Ben, no problem about the name thing, I usually sign with JAlpino instead of my full name. Here is the article that I first learned about using 204 for async processing (http://cfdj.sys-con.com/read/46789.htm). This article piqued my interest in learning more about status codes and various request\response headers.