tags:

views:

429

answers:

3

I have a function in my company's site that uses the cgi.remote_host variable to call an API provided by hostip.info and return the country of orgin. The way this is supposed to work is that the function finds the country from the API, runs a query against our local database to determine which site we are going to forward the user to, then redirect the user to the appropriate site. If for any reason, we can't associate a country with one of our regions, we redirect the user to a default site.

What actually happens is that occasionally our server cannot locate the API and we get a timeout error.

This is a proxy of the function we're calling to get the territory

<cffunction name="getGeoInfo" access="public" output="true" returntype="any" hint="call getGeoIP API and check country against DB">
    <cfargument name="IPAddress" required="yes" type="string" />

    <!--- Calling hostip API to get our Geo IP information --->
    <cfhttp url="http://api.hostip.info/?ip=#arguments.IPAddress#" method="get" result="geoIP" />

    <!--- Try to parse the file, if it can't parse, we don't create the variable --->
    <cftry>
        <cfset geoIPXML = xmlParse(geoIP.fileContent) />
    <cfcatch type="any" />
    </cftry>

    <!--- If variable was created, perform the function to get territory info --->
    <cfif isDefined("geoIPXML")>
  <cfset countryAbbrev = geoIPXML.HostipLookupResultSet['gml:featureMember']['hostip']['countryabbrev'].xmlText />

        <cfquery name="theTerritory" datasource="#request.dsource#">
            SELECT territory
            FROM countries
            WHERE countryCode = <cfqueryparam value="#countryAbbrev#" cfsqltype="cf_sql_varchar" />
        </cfquery>

        <!--- If no record was found, set locale to default --->
        <cfif theTerritory.recordcount EQ 0>
            <cfset returnLocale = "default" />
        <cfelse>
            <!--- Otherwise set it to territory found in DB --->
            <cfset returnLocale = theTerritory.territory />
        </cfif>
    <cfelse>
    <!--- if no XML file was returned, set locale to default --->
        <cfset returnLocale="default" />
    </cfif>

    <cfreturn returnLocale />
</cffunction>

What am I missing here? How should I change this function so that if the http request times out, that I still return my default?

+4  A: 

Wrap the <cfhttp> call in <cftry>.

Also, I would have the catch block just return the defaultLocale rather than do nothing and then check for the existence of geoIPXML.

<cftry>

    <cfhttp url="http://api.hostip.info/?ip=#arguments.IPAddress#" 
            method="get" 
            result="geoIP" />
    <cfset geoIPXML = xmlParse(geoIP.fileContent) />

<cfcatch type="any">
   <cfreturn "default">
</cfcatch>

</cftry>
Patrick McElhaney
That's obvious enough. I should have figured that would do it. Thanks.
KeRiCr
+2  A: 

I'm not quite sure if your timeout is for the http call only, or for the page as a whole. CFHTTP has a timeout attribute. You can use this to prevent your page from timing out if your http call times out.

Use that with try/catch (as Patrick indicated) to return your default result.

Ben Doom
The timeout was for the HTTP call, the error message that was being returned was "The request has exceeded the allowable time limit Tag: cfhttp" on the line with the http request.
KeRiCr
+1  A: 

Either approach would work, but instead of wrapping your call in a try catch block, you could set the

throwOnTimeout="false"

attribute on the <cfhttp> call and not have to make any other additional modifications to your code. The cfhttp structure is returned and will include the "fileContent" variable but will be populated with "Connection Timeout". Trying to xmlParse it will fail and then execution will hit your catch block. I would recommend as Patrick did to <cfreturn "default" /> in your catch block so that the rest of your procedure doesn't have to arbitrarily execute.

jalpino