Dumping Out The CGI Scope Does Not List All Available CGI Values In ColdFusion
I had an interesting / unfortunate journey yesterday, concerning the CGI scope. I was trying to access a value that was being passed-through by the load balancer; and, since I had never dealt with this value before, I naturally tried to CDump-out the CGI scope to see what it would be. Unfortunately, the value was not present in the CGI dump. This lead to several support tickets, which turned out to be useless since, as I am just now learning now for the first time, the CGI scope doesn't "report" all of its available keys.
Unlike a normal Struct in ColdFusion, it looks like the CGI scope will only report on a known subset of key values. You can still ask the CGI scope for which ever key you want; but, as far as CFDump and key-loops are concerned, you may only be seeing a part of the whole picture.
To demonstrate, I'm going to try to "find" the CGI value, "document_root". I will do this though both direct key reference as well as key reporting:
<cfscript>
target = ucase( "document_root" );
// Get the document root directly from the CGI scope.
writeOutput( "#target#: #cgi[ target ]#" );
writeOutput( "<br />" );
writeOutput( "- - - - - - - - - - - - - - - - -" );
writeOutput( "<br />" );
// Now, try to get the value by looping of the CGI key list
// and checking to see if the key matches our target.
for ( key in cgi ) {
writeOutput( key );
if ( key == target ) {
writeOutput( ": #cig[ key ]# <br />" );
} else {
writeOutput( ": . . . <br />" );
}
}
</cfscript>
As you can see, we try to loop over the CGI scope, looking for the "document_root" key. And, when we run the above code, we get the following output:
DOCUMENT_ROOT: /Sites/bennadel.com
HTTP_USER_AGENT: . . .
WEB_SERVER_API: . . .
PATH_TRANSLATED: . . .
CONTENT_TYPE: . . .
HTTP_ACCEPT_LANGUAGE: . . .
HTTP_REFERER: . . .
HTTP_ACCEPT: . . .
CERT_SERVER_ISSUER: . . .
CERT_SERVER_SUBJECT: . . .
HTTP_ACCEPT_ENCODING: . . .
SERVER_SOFTWARE: . . .
SERVER_NAME: . . .
CF_TEMPLATE_PATH: . . .
CERT_FLAGS: . . .
HTTPS_SERVER_ISSUER: . . .
CONTEXT_PATH: . . .
HTTP_COOKIE: . . .
SERVER_PROTOCOL: . . .
CERT_SECRETKEYSIZE: . . .
REQUEST_METHOD: . . .
HTTPS_SECRETKEYSIZE: . . .
AUTH_PASSWORD: . . .
HTTPS: . . .
CERT_SERIALNUMBER: . . .
CERT_SUBJECT: . . .
SERVER_PORT: . . .
CERT_KEYSIZE: . . .
SCRIPT_NAME: . . .
REMOTE_ADDR: . . .
SERVER_PORT_SECURE: . . .
REMOTE_HOST: . . .
HTTPS_KEYSIZE: . . .
HTTP_HOST: . . .
HTTP_CONNECTION: . . .
AUTH_USER: . . .
REMOTE_USER: . . .
PATH_INFO: . . .
QUERY_STRING: . . .
CERT_ISSUER: . . .
CERT_COOKIE: . . .
HTTPS_SERVER_SUBJECT: . . .
GATEWAY_INTERFACE: . . .
AUTH_TYPE: . . .
CONTENT_LENGTH: . . .
As you can see, while we were able to directly reference the "document_root" key, the CGI scope does not "report" it as a known value. Unfortunately, this little quirk of the language cost me a few hours of head-scratching and support tickets. Hopefully this will prevent you from doing the same!
Want to use code from this post? Check out the license.
Reader Comments
This is also the reason that the CGI doesn't throw errors if you request variables that don't actually exist. If you ask for CGI["OMGTHISISBAD"] all you get is an empty string, not some kind of undefined error.
@Jon,
Word. And, I actually like that particular feature! I find that it makes easier to write conditionals without having to do things like:
if ( structKeyExists( cgi, "xyz" ) && len( cgi.xyz ) ) { .. }
... just simply go:
if ( len( cgi.xyz ) ) { .. }
Much easier :)
I found this issue a couple years ago when I noticed one of custom Apache CGI variables wouldn't show up in our CGI dumps.
In order to get the custom variable to show up, I used structCopy/structInsert and then dumped the new customCGI scope:
Now all CGI variables are dumped. :-)
BTW, I think you might have a typo on line 20. It has cig instead of cgi.
@David,
Thanks for the typo. I didn't catch it because the IF-statement never fires (hence no exception). I'll fix that after work.
This makes no sense to me.
A scope that behaves in a unique manner to other scopes. I'm find that scopes have some core differences, like whether they are persistent or not, but when I tell something to dump, you best give up the goods... and NOT be taking a cut off the top.
Gotta say, I'm not liking CGI acting in this manner. If the variables are there, show them. If you reference a variable that doesn't exist, throw an error (even to Ben's dismay) :) One of the core truths we try to hold onto when coding is that we stay consistent with our methodologies and don't invoke a haphazard logic that does Action A THIS way, but would in other instances perform Action A THAT way.
Consistency!
@Aaron,
I'm with you; I absolutely hate that CGI gives back empty strings. Its essentially a unique behavior that goes against all documentation and language expectations.
@Aaron, @Jon,
It would be cool if ColdFusion implemented the "Elvis Operator" that does a sort of conditional check:
I saw this in a presentation on Groovy. It's sort of a short-hand for the conditional check / ternary operator.
@Aaron @Jon
Agreed, it makes no sense. Has someone raised a bug with adobe/railo that I can vote on?
Really the only way to find out what is there right now is to do a brute force on the cgi scope to see what is hidden away. Not cool :(
@Ben,
CF9 & CF10 both support the "Elvis" syntax that you list -- I've got production code running it now. I was introduced to it by a front-end guy who asked if it was possible and I was like -- what is that?!?
Turns out it worked and I said goodbye to IIF and silly if else variable sets forever!
That syntax is what IIF dreams of at night...
@Ben,
The closest I use is:
<cfset x = iif( structKeyExists( CGI, 'foo' ), de( 'this' ), de( 'that' ) ) />
Seems to work well enough, but it's not as sunccinct. :)
Fyi you can get all those vars via any decent debugging tools that shows u all http headers such as chrome developer tools and several firefox plugins
@snake,
Absolutely. I've sent the developers of Firebug and the Web Developer Toolbar money because I freaking LOVE those tools, but it *is* still 1 more step you'd have to take to make up for an odd shortcoming with ColdFusion.
Maybe introduce a new application variable, if Adobe wants to keep it like it is so bad.
<cfset this.GimmeAllDaDangVarsInDaClientScopeWhenIAskFerDem = true />
@Aaron,
If you have those tools Installed which I think most devs do now, it's not really an extra step. And tbh I think anyone who is not a newb must be used to the deficiencies in cf by now or switched to Railo :)
@Jason The "Elvis operator" is the "?:" in Ben's second example. CF9 doesn't support this and I don't believe CF10 does either, but Railo 4.1 does.
I think you must mean the "ternery operator" in Ben's first example.
Coldfusion absolutely has the ternary operator with <boolean expression> ? <true op> : <false op>. See Ben's page on it at:
www.bennadel.com/blog/1643-Learning-ColdFusion-9-The-Ternary-Operator.htm
We use Smart Cards here to login in and the person's info is dumped into variables in the CGI scope with the single sign on. Unfortunatley not all the variables show up in Coldfusion. I wrote an ASP page to dump all of them out and they are all there in the ASP page. However in Coldfusion, 3 of them are missing such as the person's middle name and couple of others. I don't know if this is because of tomcat or Coldfusion.
@Jim. Yup, CF has the ternary operator, but not the Elvis operator.
I vaguely remember that structGet() does some sort of auto-object creation for paths that don't exist. But, I don't think I've experimenting with auto-magic path creation in like 7 years:
www.bennadel.com/blog/315-ColdFusion-s-Auto-Struct-Generation-Caveat.htm
That said, the Elvis operator would be cool :)
deja vu ..
i was really convinced you already posted something like this many moons ago, but since the collective memory of the human race (google) does not record it, it does not exist
@Nelle,
I have posted little tid-bits about the CGI scope, like:
www.bennadel.com/blog/362-CGI-hot_n_sexy-Does-Not-Throw-A-ColdFusion-Error.htm
... but *definitely* I have written blog posts in the past, only to do a quick Google Search and found that I had *already* written a similar post years earlier :D
Nope. I just wasted 3 hours until I found this. Thanks Ben.
@Daniel,
Ha ha, an oldie but goodie - glad this helped!