Skip to main content
Ben Nadel at Scotch On The Rocks (SOTR) 2011 (Edinburgh) with: Oliver Hilton
Ben Nadel at Scotch On The Rocks (SOTR) 2011 (Edinburgh) with: Oliver Hilton ( @ojhilt )

getHttpRequestData() May Break Your Request In ColdFusion But getHttpRequestData(False) May Not

By on
Tags:

Out of the box, ColdFusion will parse multipart/form-data requests for you (putting the data in the Form scope). But, if you want to accept any other kind of data, such as JavaScript Object Notation (JSON) or XML payloads, you will likely have to reach into the HTTP request data to access the request content yourself. Unfortunately, for many years, getHttpRequestData() has had some problematic behavior. As I was reading through this Adobe Bug Base item, however, I came across a comment by Rupesh Kumar that describes a hidden ColdFusion feature to help deal with getHttpRequestData() - invoking it with False will return the HTTP request data without the body content.

The conditions surrounding the problematic behavior of getHttpRequestData() are not well documented. But, it boils down to this: If you call the getHttpRequestData() in the Application's pseudo-constructor (the space in the Application.cfc that exists outside of any event handlers), it will render the request content unavailable in subsequent calls to getHttpRequestData(). This is only true when the first call is in the pseudo-constructor. If your first call is within an event handler (either implicitly or explicitly), you can call getHttpRequestData() as many times as you want without concern.

To demonstrate this problematic behavior, I've set up a simple application that provides an "echo" end-point that does nothing but CFDump-out the HTTP request content:

<cfscript>

	// Simply echo-back the HTTP request data.
	writeDump( var = getHttpRequestData(), label = "getHttpRequestData()" );

</cfscript>

Then, I created an Application.cfc ColdFusion framework component that will invoke the getHttpRequestData() function at different points in the ColdFusion application lifecycle. In the following code, notice that getHttpRequestData() is always being called in the onRequestStart() event handler; but, that it is only called in the pseudo-constructor if the relevant URL-flag is present:

component
	output = false
	hint = "I define the application settings and event handlers."
	{

	// Define the application settings.
	this.name = hash( getCurrentTemplatePath() );
	this.applicationTimeout = createTimeSpan( 0, 0, 5, 0 );


	// Check to see if we need to call getHttpRequestData() inside of the Application's
	// pseudo-constructor.
	if ( structKeyExists( url, "pseudo" ) ) {

		this.temp1 = getHttpRequestData();

	}


	/**
	* I initialize the incoming request.
	*
	* @scriptName I am the path to the script that is being requested.
	* @output false
	*/
	public boolean function onRequestStart( required string scriptName ) {

		// Always call the getHttpRequestData() function in the event handler.
		var temp2 = getHttpRequestData();

		// Return true so the page can load.
		return( true );

	}

}

Now, let's try to hit the echo endpoint with an HTTP POST from ColdFusion:

<cfscript>

	// Make a call to our "echo" end-point. This does nothing but echo back
	// the cfdump() of the getHttpRequestData() function call.
	apiCall = new Http(
		method = "post",
		url = (
			"http://#cgi.server_name#" &
			getDirectoryFromPath( cgi.script_name ) &
			"echo.cfm"
		),
		getAsBinary = "yes"
	);

	// I will tell the target request to call getHttpRequestData() inside of the
	// pseudo-constructor. That is the space in the Application.cfc that exists within
	// the bounds of the component, but outside of any event handlers.
	// --
	apiCall.addParam( type = "url", name = "pseudo", value = 1 );

	// Post some body so that we can see it echoed back to us.
	apiCall.addParam( type = "body", value = "This is the post body" );

	// Execute the HTTP request.
	result = apiCall.send().getPrefix();

	// Output the results.
	// --
	// NOTE: Since we are using getAsBinary=yes, we have to explicitly convert the file
	// content back into a string.
	writeOutput( charsetEncode( result.fileContent, "utf-8" ) );

</cfscript>

As you can see, along with the HTTP post data, I'm including the URL flag to trigger the call to the getHttpRequestData() function inside the Application's pseudo-constructor. When we run this page, the echo result confirms the problematic data - notice that the request body is empty (even though we posted the string, "This is the post body"):

getHttpRequestData() can render the request body content unavailable.

Ok, now, I'm going to try to run the same request, but exclude the "pseudo" query-string flag:

<cfscript>

	// ... same as before ....

	// I will tell the target request to call getHttpRequestData() inside of the
	// pseudo-constructor. That is the space in the Application.cfc that exists within
	// the bounds of the component, but outside of any event handlers.
	// --
	// apiCall.addParam( type = "url", name = "pseudo", value = 1 );

	// Post some body so that we can see it echoed back to us.
	apiCall.addParam( type = "body", value = "This is the post body" );

	// ... same as before ....

</cfscript>

This time, with the pseudo URL-flag omitted, the echo content correctly contains the HTTP post body:

getHttpRequestData() makes the request content available in ColdFusion.

Keep in mind that this request was still calling the getHttpRequestData() function multiple times - once in the onRequestStart() event handler and once in the echo end-point. The problem is not calling the getHttpRequestData() function multiple times; the problem is that calling it in the Application's pseudo-constructor breaks it for the rest of the application.

Which brings me back to the Adobe Bug Base item I mentioned above. In that thread, Rupesh mentioned that you can call getHttpRequestData(False) to get the request data, less the body content. And, that doing so, will not break subsequent calls to the function:

GetHttpRequestData by default retrieves the body of the request and once it is retrieved, it cannot be read again. If you need to retrieve anything other than body from the request, use GetHttpRequestData(false). The expected behavior mentioned in the bug was indeed true with JRun application server but it would not work for any other application server.

To test this, I updated my Application.cfc pseudo-constructor to pass in False when calling getHttpRequestData():

component
	output = false
	hint = "I define the application settings and event handlers."
	{

	// Define the application settings.
	this.name = hash( getCurrentTemplatePath() );
	this.applicationTimeout = createTimeSpan( 0, 0, 5, 0 );


	// Always call getHttpRequestData(); but, pass in False. This will return the
	// request data, but not the body content. The body content will be reported as
	// an empty string.
	// --
	// NOTE: This is an undocumented feature, but was suggested by Rupesh Kumar, a lead
	// engineer at Adobe.
	this.temp1 = getHttpRequestData( false );


	/**
	* I initialize the incoming request.
	*
	* @scriptName I am the path to the script that is being requested.
	* @output false
	*/
	public boolean function onRequestStart( required string scriptName ) {

		// Always call the getHttpRequestData() function in the event handler.
		var temp2 = getHttpRequestData();

		// Return true so the page can load.
		return( true );

	}

}

Now, when we call the echo end-point, regardless of any pseudo-flag, we can see that echo is able to access the request body even though getHttpRequestData() was called in the Application's pseudo-constructor:

getHttpRequestData(false) safely makes the request data available, less the body content.

I have confirmed that this problematic behavior both exists and can be fixed in ColdFusion 10 and ColdFusion 11. And, while this information is in the Adobe Bug Base, hopefully this blog post will help get the word out.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel