I'm working with the ConstantContact API webservice, and ran into a slight problem. The API uses Digest Authentication, and ColdFusion does not support Digest Authentication in cfhttp. I found this out by reading in the live docs that:
The cfhttp tag does not support NTLM or Digest Authentication.
The ConstantContact forums pointed me to a custom tag cfx_http5. But I hate CFX tags. I like to place as few dependencies on the ColdFusion Administrator as possible when I code. CFX tags also tend not to make the trip the first time in server migrations or emergency moves. It might be my own prejudice but, no, there will be no custom tags.
Which leaves me to implementing some Java client, as luck would have it there was ample documentation on doing this. I just needed to use the open source Apache HTTP client. To work with them, I had to add them to the class path, or add an entry in the ColdFusion Administrator class path list. That would have bought me nothing, as I didn't want to go through the administrator.
This left me looking for a way to dynamically call jar files. If only there was some way to dynamically call jar files in ColdFusion... preferably written by someone with a marsupial pouch.... Ohh wait, Mark Mandel wrote the Open Source project JavaLoader, which allows just that. Hmm, and Mark is a mammal from Australia. Things are looking good.
So I was able to build my http client with Digest Authentication. I added some code that formatted the response like a cfhttp struct, so that if there were other advantages to this method I could just swap out this for cfhttp in a pinch. The code is below in the extended section of this blog post.
I was trying to do 3 things with this post:
- Explain how to use Digest Authentication with ColdFusion
- Show how with ColdFusion Open Source community and ColdFusion's ability to call Java, there isn't much you can't do with it.
- Further the rumor that Mark Mandel is a marsupial.
I hope I've done these three things for you.
var paths = arrayNew(1);
var rootPath = GetDirectoryFromPath(GetCurrentTemplatePath());
paths[1] = rootPath & "/jars/commons-httpclient-3.1.jar";
paths[2] = rootPath & "/jars/commons-codec-1.3.jar";
paths[3] = rootPath & "/jars/commons-logging-1.1.1.jar";
//create the loader
variables.loader = createObject("component", "javaloader.JavaLoader").init(paths);
var result = "";
var credentials = loader.create("org.apache.commons.httpclient.UsernamePasswordCredentials");
var HttpClient = loader.create("org.apache.commons.httpclient.HttpClient").init();
var httpGet = loader.create("org.apache.commons.httpclient.methods.GetMethod");
var AuthScope = loader.create("org.apache.commons.httpclient.auth.AuthScope");
var jURL = createObject("java", "java.net.URL").init(arguments.url);
if (len(arguments.username) and len(arguments.password) gt 0){
AuthScope.init(jURL.getHost(), arguments.port, arguments.realm);
credentials.init(arguments.Username, arguments.password);
httpClient.getState().setCredentials(authScope, credentials);
}
httpGet.init(arguments.url);
httpClient.executeMethod(httpGet);
result = convertHttpClientResponseToCFHTTPFormat(httpGet);
httpGet.releaseConnection();
return result;
var result = structNew();
var responseheader = structNew();
responseheader['Status_Code'] = httpGet.getStatusCode();
responseheader['Explanation'] = httpGet.getStatusText();
responseheader['Http_Version'] = httpGet.getEffectiveVersion().toString();
header = httpGet.getStatusLine().toString();
headers = httpGet.getResponseHeaders();
for (i=1; i lte ArrayLen(headers); i=i+1){
responseheader[getToken(headers[i], 1, ":")] = getToken(headers[i], 2, ":");
header = listAppend(header, headers[i], " ");
}
result['Charset'] = httpGet.getResponseCharSet();
result['ErrorDetail'] = "";
result['Filecontent'] = httpGet.getResponseBodyAsString();
result['Header'] = header;
if (structKeyExists(responseheader, 'Content-Type')){
result['Mimetype'] = GetToken(responseheader['Content-Type'], 1, ";");
}
result['Responseheader'] = responseheader;
result['Statuscode'] = responseheader['Status_Code'] & " " & responseheader['Explanation'];
if( not structKeyExists(responseheader, 'Content-Type') OR
FindNoCase("text", responseheader['Content-Type']) OR
FindNoCase("message", responseheader['Content-Type']) OR
FindNoCase("application/octet-stream", responseheader['Content-Type'])
){
result['Text'] = "YES";
}
else{
result['Text'] = "NO";
}
return result;
13 response s so far ↓
1 Rob Brooks-Bilson // Jan 8, 2009 at 11:31 AM
Thanks for sharing. I'll be filing this one away for later.
Oh, and you are truly a master at spreading rumors!
2 Chris Diller // Jan 8, 2009 at 12:10 PM
3 Dave Konopka // Jan 8, 2009 at 12:41 PM
4 Sami Hoda // Jan 8, 2009 at 2:25 PM
5 Terrence Ryan // Jan 8, 2009 at 2:30 PM
Anyone who wants to steal the code and release on Riaforge can be my guest.
6 pat branley // Jan 8, 2009 at 3:38 PM
you can do the same thing on windows with the XMLHTTP COM object.
you don't need to install anything because its part of the OS.
7 James // Feb 12, 2009 at 3:34 AM
Many thanks!
8 Ben Margolis // Mar 18, 2009 at 11:32 AM
9 Ben Margolis // Mar 18, 2009 at 3:08 PM
...
<cfargument name="url" type="string" required="TRUE" hint="The url to call." />
<cfargument name="body" type="string" required="TRUE" hint="What to send to the URL" />
....
var AuthScope = loader.create("org.apache.commons.httpclient.auth.AuthScope");
var myStringRequestEntity = loader.create("org.apache.commons.httpclient.methods.StringRequestEntity");
....
httpPost.init(arguments.url);
httpPost.setRequestEntity(myStringRequestEntity.init(arguments.body, "application/atom+xml", "UTF-8"));
....
NOTE: This is for posting an ATOM XML document as a string.
10 .jonah // Mar 19, 2009 at 4:48 AM
11 Ben Margolis // Jul 17, 2009 at 3:49 PM
12 Ben Margolis // Aug 4, 2009 at 11:02 AM
13 ColdFusion // May 27, 2010 at 5:49 AM
Thank for the fix BTW
Martin
Leave a Comment