Testing for service availability before you call…


If you have used tags like CFLDAP and CFHTTP you probably find the timeout attribute is frustratingly ineffectual.  If the target server is down or otherwise slow to respond, it can hold up the CFML engine response. Charlie Arehart has a great post called CF911: Lies, Damned Lies, and CF Request Timeouts…What You May Not Realize in which he provides all the details surrounding the issue. I was recently writing some code to authenticate against Active Directory using LDAP. The requirement was to use the master server in normal operation, but failover to a slave Active Directory server. If CFLDAP is used to attempt a connection to the master while it is offline, it will cause the CFML engine to hang waiting for a response for far longer than the number of milliseconds in timeout attribute.

I decided that before making the LDAP connection, I would check to see if a TCP socket could be created to the server. Using a bit of Java, it’s pretty simple to test connectivity:

	<cffunction 
		name="isTcpServiceAlive" 
		returntype="boolean" 
		access="public" 
		output="false">
		
			<cfargument name="host" type="string" required="true"/>
			<cfargument name="port" type="numeric" required="true"/>
			<cfargument name="timeout" type="numeric" required="true" 
				default="2000"/>
			
			<cfset var socket = createObject("java", "java.net.Socket").init()/>
			<cfset var address = createObject("java", 
				"java.net.InetSocketAddress").init(
				javaCast("string", arguments.host), 
				javaCast("int", arguments.port))/>
				
			<cftry>
				<cfset socket.connect(address, arguments.timeout)/>
				<cfset socket.close()/>
				<cfreturn true/>
				
				<cfcatch>
					<cfreturn false/>
				</cfcatch>
			</cftry>
	</cffunction>

If the respone from isTcpServiceAlive() is false, there’s not much reason to try CFLDAP using the master:389. The timeout is in milliseconds, and I think 2 seconds is a pretty reasonable amount of time to wait for a service on the same LAN. My team uses ActiveDirectory/LDAP for its intranet “authentication”, using a basic ldap query to varify someone’s authentication creditials. The arguments passed in are used to connect to the LDAP server.  If the credentials fail the system will log the LDAP results and set a result code which the application will use to provide the user with feedback.  Agruments.LDAPAddress is the addressed we checked using the method mentioned above.

	<cftry>
		<cfldap action="query"
			name="qLDAP"
			start="dc=vexeddeveloper,dc=com"
			filter = "sAMAccountName=#Arguments.UserName#"
			secure="CFSSL_BASIC"
			attributes="cn,o,pwdLastSet"
			server="#Agruments.LDAPAddress#"
			port="636"
			username="#Arguments.UserName#"
			password="#Arguments.Authentication#"/>
			
		<cfcatch>
			<cflog 
				application="yes" 
				file="app_#LCase(application.applicationname)#" 
				type="error"
				text="Version: #application.version# - DAO:73 - #cfcatch.Type#: #cfcatch.Message#: #cfcatch.Detail#" />
			<cfset sMyResults.ResultCode = 700 />
		</cfcatch>
	</cftry>
 

Leave a Reply