Tuesday, March 20, 2012

Custom cfhttp tag

Annoyed by the limitations of the default cfhttp tag i have written a custom cf_http tag that overcomes some of the limitations. Well any comments of improvements are very welcome.

Some extra capabilities:
*) This tag automatically unzips gzipped documents.
*) Better control over request headers
*) Return structure is very similar to the cfhttp tag but has some extra data:
  - request headers
  - url of redirection
*) Converts body to utf-8
 <cffunction name="cf_http">
  <cfargument type="string" name="in_url" required="yes">
  <cfargument type="array" name="in_getParameters" required="no" default="#arrayNew(2)#" hint="Get variables url not encoded">
  <cfargument type="array" name="in_postParameters" required="no" default="#arrayNew(2)#" hint="Post variables url not encoded">
  <cfargument type="array" name="in_Cookies" required="no" default="#arrayNew(2)#" hint="Cookies not encoded">
  <cfargument type="string" name="in_max_redirects" required="no" default="5">
  <cfargument type="string" name="in_requestMethod" required="no" default="GET">
  <cfargument type="string" name="in_Accept" required="no" default="*/*">
  <cfargument type="string" name="in_ContentType" required="no" default="application/x-www-form-urlencoded">
  <cfargument type="string" name="in_AcceptEncoding" required="no" default="gzip, x-gzip, identity, *;q=0">
  <cfargument type="string" name="in_AcceptCharset" required="no" default="ISO-8859-1,utf-8;q=0.7,*;q=0.7">
  <cfargument type="string" name="in_AcceptLanguage" required="no" default="nl,en-us;q=0.7,en;q=0.3">
  <cfargument type="string" name="in_UserAgent" required="no" default="Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 4.0">
  <cfargument type="string" name="in_CacheControl" required="no" default="no-cache">
  <cfargument type="string" name="in_ConnectTimeout" required="no" default="15000">
  <cfargument type="string" name="in_ReadTimeout" required="no" default="15000">
  <cfargument type="string" name="in_ProxyPort" required="no" default="">
  <cfargument type="string" name="in_ProxyHost" required="no" default="">
  <cfargument type="boolean" name="in_RememberRedirectedCookies" required="no" default="true">

  <!--- Set local variables --->
   <cfset var local_results = structNew()>
   <cfset var local_i = 0>
   <cfset var local_j = 0>
   <Cfset var local_current_redirect = 0>
   <Cfset var local_PostVariables = "">
   <Cfset var local_Cookies = "">
   <Cfset var local_JavaEncoder = createObject("java","java.net.URLEncoder")>
 
   <Cfset var local_RedirectCookiesAsString = "">
   <Cfset var local_RedirectCookiesAsArray = arrayNew(2)>
  <!--- End set local variables --->

  <!--- Set defaults  --->
   <cfif in_requestMethod eq ""><cfset in_requestMethod="GET"></cfif>
   <cfif in_Accept eq ""><cfset in_Accept="*/*"></cfif>
   <cfif in_ContentType eq ""><cfset in_ContentType="application/x-www-form-urlencoded"></cfif>
   <cfif in_AcceptEncoding eq ""><cfset in_AcceptEncoding="gzip, x-gzip, identity, *;q=0"></cfif>
   <cfif in_AcceptCharset eq ""><cfset in_AcceptCharset="ISO-8859-1,utf-8;q=0.7,*;q=0.7"></cfif>
   <cfif in_AcceptLanguage eq ""><cfset in_AcceptLanguage="nl,en-us;q=0.7,en;q=0.3"></cfif>
   <cfif in_UserAgent eq ""><cfset in_UserAgent="Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 4.0"></cfif>
   <cfif in_CacheControl eq ""><cfset in_CacheControl="no-cache"></cfif>
   <cfif in_ConnectTimeout eq ""><cfset in_ConnectTimeout="15000"></cfif>
   <cfif in_ReadTimeout eq ""><cfset in_ReadTimeout="15000"></cfif>
   <cfif in_ProxyPort eq ""><cfset in_ProxyPort=""></cfif>
   <cfif in_ProxyHost eq ""><cfset in_ProxyHost=""></cfif>
   <cfif arraylen(in_postParameters) neq 0><cfset in_requestMethod = "POST"></cfif>
  <!--- End set defaults  --->
  
  <!--- Set get variables --->
   <cfloop from=1 to="#arraylen(in_getParameters)#" index="i">
    <cfif i eq 1><cfset in_url = "#in_url#?"></cfif>
    <cfif i gt 1><cfset in_url = "#in_url#&"></cfif>
    <cfset in_url = "#in_url##in_getParameters[i][1]#=#trim(local_JavaEncoder.encode(in_getParameters[i][2],"utf-8"))#">
   </cfloop>
  <!--- End set get variables --->

  <!--- Set post variables --->
   <cfloop from=1 to="#arraylen(in_postParameters)#" index="i">
    <cfif i gt 1><cfset local_PostVariables = "#local_PostVariables#&"></cfif>
    <cfset local_PostVariables = "#local_PostVariables##in_postParameters[i][1]#=#trim(local_JavaEncoder.encode(in_postParameters[i][2],"utf-8"))#">
   </cfloop>
  <!--- End set post variables --->

  <!--- Set cookies --->
   <cfloop from=1 to="#arraylen(in_Cookies)#" index="i">
    <cfif i gt 1><cfset local_Cookies = "#local_Cookies#;"></cfif>
    <cfset local_Cookies = "#local_Cookies##in_Cookies[i][1]#=#trim(local_JavaEncoder.encode(in_Cookies[i][2],"utf-8"))#">
   </cfloop>
  <!--- End set cookies --->

  <cfset in_max_redirects = in_max_redirects -1>

  <!--- Set default output variables --->
   <cfset local_results.CHARSET = "UTF-8">
   <cfset local_results.HEADER = "">
   <cfset local_results.MIMETYPE = "">
   <cfset local_results.CONNECTION = structNew()>
   <cfset local_results.CONNECTION.REDIRECTED = false>
   <cfset local_results.CONNECTION.REDIRECT_TIMES = 0>
   <cfset local_results.CONNECTION.REDIRECT_URL = structNew()>
   <cfset local_results.CONNECTION.URL_START = in_url>
   <cfset local_results.CONNECTION.URL_END = in_url>

   <cfset local_results.REQUESTHEADER  = structNew()>
   <cfset local_results.RESPONSEHEADER = structNew()>
   <cfset local_results.STATUSCODE = "">
   <cfset local_results.FILECONTENT = "">
   <cfset local_results.Errordetail = "">
  <!--- End set default output variables --->

  <cftry>
  <!--- Build connection --->
   <cfset var local_Url = createObject("java", "java.net.URL")>
   <cfif in_ProxyPort neq '' or in_ProxyHost neq ''>
    <cfset var local_Proxy = createObject("java", "java.lang.System").getProperties()>
    <cfif in_ProxyHost neq ''><cfset local_Proxy.setProperty("http.proxyHost","#in_ProxyHost#")></cfif>
    <cfif in_ProxyPort neq ''><cfset local_Proxy.setProperty("http.proxyPort","#in_ProxyPort#")></cfif>
   </cfif>
   <cfset var local_Connection = createObject("java", "java.net.HttpURLConnection")>

   <cfloop condition="local_current_redirect lte in_max_redirects">
    <cfset local_Url.init(in_url)>
    <cfset local_Connection = local_Url.openConnection()>
    <cfset local_Connection.setConnectTimeout(JavaCast("int",in_ConnectTimeout))>
    <cfset local_Connection.setReadTimeout(JavaCast("int",in_ReadTimeout))>
    <cfset local_Connection.setRequestMethod(in_requestMethod)>
    <cfset local_Connection.setFollowRedirects(true)>
    <cfset local_Connection.setDoInput(true)>
    <cfif local_PostVariables neq ''><cfset local_Connection.setDoOutput(true)></cfif>
    <cfset local_Connection.setRequestProperty("Accept", in_Accept)>
    <cfset local_Connection.setRequestProperty("Content-Type", in_ContentType)>
    <cfset local_Connection.setRequestProperty("Accept-Encoding", in_AcceptEncoding)>
    <cfset local_Connection.setRequestProperty("Accept-Charset", in_AcceptCharset)>
    <cfset local_Connection.setRequestProperty("Accept-Language",in_AcceptLanguage)>
    <cfset local_Connection.setRequestProperty("User-Agent",in_UserAgent)>
    <cfset local_Connection.setRequestProperty("Cache-Control", in_CacheControl)>
    <cfset local_Connection.setRequestProperty("Cookie", local_Cookies)>

    <cfset local_Connection.setInstanceFollowRedirects(false)>

    <cfset local_results.url = local_Connection.getURL().toString()>

    <!--- Get request headers --->
    <cfset var local_RequestHeadersStruc = structNew()>
    <cfset var local_RequestHeadersArrayNew = arrayNew(2)>
    <cfset var local_RequestHeadersArrayOriginal = local_Connection.getRequestProperties().entrySet().toArray()>

    <cfloop from=1 to="#arraylen(local_RequestHeadersArrayOriginal)#" index="i">
     <Cfif not isnull(local_RequestHeadersArrayOriginal[i].getKey())>
      <cfset local_RequestHeadersArrayNew[i][1]  = local_RequestHeadersArrayOriginal[i].getKey()>
      <cfloop from=1 to="#arrayLen(local_RequestHeadersArrayOriginal[i].getValue())#" index="j"><cfset local_RequestHeadersArrayNew[i][j+1]  = local_RequestHeadersArrayOriginal[i].getValue()[j]></cfloop>
     <cfelse>
      <cfset local_RequestHeadersArrayNew[i][1]  = "">
      <cfloop from=1 to="#arrayLen(local_RequestHeadersArrayOriginal[i].getValue())#" index="j"><cfset local_RequestHeadersArrayNew[i][j+1]  = local_RequestHeadersArrayOriginal[i].getValue()[j]></cfloop>
     </cfif>
    </cfloop>
    <cfloop from=1 to="#arraylen(local_RequestHeadersArrayNew)#" index="i">
     <cfif arraylen(local_RequestHeadersArrayNew[i]) eq 2>
      <cfset local_RequestHeadersStruc[local_RequestHeadersArrayNew[i][1]] = local_RequestHeadersArrayNew[i][2]>
     <cfelse>
      <cfset local_ResponseHeadersStruc[local_RequestHeadersArrayNew[i][1]] = structNew()>
      <cfloop from=1 to="#arraylen(local_RequestHeadersArrayNew[i])#" index="j">
       <Cftry><cfset local_RequestHeadersStruc[local_RequestHeadersArrayNew[i][1]][""&j&""] = local_RequestHeadersArrayNew[i][j+1]><cfcatch type="any"></cfcatch></cftry>
      </cfloop>
     </cfif>
    </cfloop>
    <!--- <cfset results.requestHeader_asArray = local_RequestHeadersArrayNew> --->
    <!--- End get request headers --->

    <!--- Post post variables --->
    <cfif local_PostVariables neq ''>
     <cfset var local_uploadStream = local_Connection.getOutputStream() />
     <cfset var local_uploadWriter = createObject( "java", "java.io.OutputStreamWriter" ).init(local_uploadStream) />
     <cfset local_uploadWriter.write(javaCast( "string", (local_PostVariables) )) />
     <cfset local_uploadWriter.close() />
    </cfif>
    <!--- End post post variables --->

    <!--- Store get variables in structure to return --->
    <cfset local_RequestHeadersStruc.urlVariables = structNew()>
    <cfloop from=1 to="#arraylen(in_getParameters)#" index="i">
     <cfset local_RequestHeadersStruc.urlVariables[""&i&""] = structNew()>
     <cfset local_RequestHeadersStruc.urlVariables[""&i&""].name = "#in_getParameters[i][1]#">
     <cfset local_RequestHeadersStruc.urlVariables[""&i&""].value =  "#in_getParameters[i][2]#">
    </cfloop>
    <!--- End store get variables in structure to return --->

    <!--- Store post variables in structure to return --->
    <cfset local_RequestHeadersStruc.postVariables = structNew()>
    <cfloop from=1 to="#arraylen(in_postParameters)#" index="i">
     <cfset local_RequestHeadersStruc.postVariables[""&i&""] = structNew()>
     <cfset local_RequestHeadersStruc.postVariables[""&i&""].name = "#in_postParameters[i][1]#">
     <cfset local_RequestHeadersStruc.postVariables[""&i&""].value =  "#in_postParameters[i][2]#">
    </cfloop>
    <!--- End store post variables in structure to return --->
 
    <!--- Store cookies in structure to return --->
    <cfset local_RequestHeadersStruc.Cookies = structNew()>
    <cfloop from=1 to="#arraylen(in_Cookies)#" index="i">
     <cfset local_RequestHeadersStruc.Cookies[""&i&""] = structNew()>
     <cfset local_RequestHeadersStruc.Cookies[""&i&""].name = "#in_Cookies[i][1]#">
     <cfset local_RequestHeadersStruc.Cookies[""&i&""].value =  "#in_Cookies[i][2]#">
    </cfloop>
    <!--- Stop store cookies in structure to return --->
    <cfset local_RequestHeadersStruc.requestMethod = in_requestMethod>
    <cfset local_results.requestHeader = local_RequestHeadersStruc>

    <!--- Get response headers --->
    <cfset var local_ResponseHeadersStruc = structNew()>
    <cfset var local_ResponseHeadersString = "">
    <cfset var local_ResponseHeadersArrayNew= arrayNew(2)>
    <cfset var local_ResponseHeadersArrayOriginal = local_Connection.getHeaderFields().entrySet().toArray()>
 
    <cfloop from=1 to="#arraylen(local_ResponseHeadersArrayOriginal)#" index="i">
     <Cfif not isnull(local_ResponseHeadersArrayOriginal[i].getKey())>
      <cfset local_ResponseHeadersString = "#local_ResponseHeadersString##local_ResponseHeadersArrayOriginal[i].getKey()#: ">
      <cfset local_ResponseHeadersArrayNew[i][1]  = local_ResponseHeadersArrayOriginal[i].getKey()>
      <cfloop from=1 to="#arrayLen(local_ResponseHeadersArrayOriginal[i].getValue())#" index="j"><cfset local_ResponseHeadersString = "#local_ResponseHeadersString##local_ResponseHeadersArrayOriginal[i].getValue()[j]# "><cfset local_ResponseHeadersArrayNew[i][j+1]  = local_ResponseHeadersArrayOriginal[i].getValue()[j]></cfloop>
     <cfelse>
      <cfset local_ResponseHeadersArrayNew[i][1]  = "">
      <cfloop from=1 to="#arrayLen(local_ResponseHeadersArrayOriginal[i].getValue())#" index="j"><cfset local_ResponseHeadersString = "#local_ResponseHeadersString##local_ResponseHeadersArrayOriginal[i].getValue()[j]# "><cfset local_ResponseHeadersArrayNew[i][j+1]  = local_ResponseHeadersArrayOriginal[i].getValue()[j]></cfloop>
     </cfif>
    </cfloop>

    <cfloop from=1 to="#arraylen(local_ResponseHeadersArrayNew)#" index="i">
     <cfif arraylen(local_ResponseHeadersArrayNew[i]) eq 2>
      <cfset local_ResponseHeadersStruc[local_ResponseHeadersArrayNew[i][1]] = local_ResponseHeadersArrayNew[i][2]>
     <cfelse>
      <cfset local_ResponseHeadersStruc[local_ResponseHeadersArrayNew[i][1]] = structNew()>
      <cfloop from=1 to="#arraylen(local_ResponseHeadersArrayNew[i])#" index="j">
       <Cftry><cfset local_ResponseHeadersStruc[local_ResponseHeadersArrayNew[i][1]][""&j&""] = local_ResponseHeadersArrayNew[i][j+1]><cfcatch type="any"></cfcatch></cftry>
      </cfloop>
     </cfif>
    </cfloop>
 
    <cfset local_results.header = local_ResponseHeadersString>
    <cfset local_results.responseHeader = local_ResponseHeadersStruc>
    <!--- <cfset results.responseHeader_asArray = local_ResponseHeadersArrayNew> --->
    <!--- End get response headers --->
 
    <!--- Get CHARSET AND MIMETYPE --->
    <Cfset var mimetype = local_Connection.getContentType()>
    <cftry><cfif find('charset=',mimetype) gt 0><cfset local_results.Charset = rereplace(mimetype,'.*?charset=(.*)','\1','one')></cfif><cfcatch type="any"></cfcatch></cftry>
    <cftry><cfset local_results.Mimetype = rereplace(mimetype,'(.*?);.*','\1','one')><cfcatch type="any"></cfcatch></cftry>
    <!--- End Get CHARSET AND MIMETYPE --->

    <!--- Get response body --->
    <cfset var local_ResponseBody_inputStream = local_Connection.getInputStream()>
    <cfset var local_ResponseBody = "">
     <cfif local_Connection.getContentEncoding() neq "" && local_Connection.getContentEncoding().equalsIgnoreCase("gzip")>
     <cfset var local_BufferedReader = createObject("java","java.io.BufferedReader").init(CreateObject( "JAVA", "java.io.InputStreamReader" ).init( createObject("java", "java.util.zip.GZIPInputStream").init(local_Connection.getInputStream()), "#local_results.Charset#"))>
    <!---
     <cfelseif local_Connection.getContentEncoding() neq "" && local_Connection.getContentEncoding().equalsIgnoreCase("deflate")>
     <cfset local_BufferedReader = createObject("java","java.io.BufferedReader").init(CreateObject( "JAVA", "java.util.zip.InflaterInputStream" ).init(local_Connection.getInputStream()))>
    --->
    <cfelse>
     <cfset var local_BufferedReader = createObject("java","java.io.BufferedReader").init(CreateObject( "JAVA", "java.io.InputStreamReader" ).init( local_Connection.getInputStream() , "#local_results.Charset#"))>
    </cfif>
  
    <cfset var local_line="">
    <cfset var local_lineCheck = false>
 
    <cfset local_line = local_BufferedReader.readLine()>
     <cfset local_lineCheck = isDefined("local_line")>

    <cfloop condition="#local_lineCheck#">
     <cfset local_ResponseBody = "#local_ResponseBody##local_line##chr(13)##chr(10)#">
     <cfset local_line = local_BufferedReader.readLine()>
      <cfset local_lineCheck = isDefined("local_line")>
    </cfloop> 
    <Cfset local_BufferedReader.close()>
    <cfset local_results.Filecontent = local_ResponseBody>
    <!--- End get response body --->

    <!--- Check if redirection is necessary --->
    <cfif (local_Connection.getResponseCode() eq local_Connection.HTTP_MOVED_PERM OR local_Connection.getResponseCode() eq local_Connection.HTTP_MOVED_TEMP OR local_Connection.getResponseCode() EQ local_Connection.HTTP_SEE_OTHER)>
    <cfif refind('^.{2,5}://',local_Connection.getHeaderField("Location")) eq 0><cfset in_url = local_Url.getProtocol() & "://" & local_Url.getHost() & local_Connection.getHeaderField("Location")>
    <Cfelse><cfset in_url = local_Connection.getHeaderField("Location")></cfif>

    <cfset local_results.Statuscode = "#local_Connection.getResponseCode()# #local_Connection.getResponseMessage()#">
    <cfset local_results.URL = in_url>
    <cfset local_results.CONNECTION.REDIRECTED = true>
    <cfset local_results.CONNECTION.REDIRECT_TIMES = local_current_redirect+1>
    <cfset local_results.CONNECTION.REDIRECT_URL[""&local_current_redirect+1&""] = in_url>
    <cfset local_results.CONNECTION.URL_END = in_url>
    <cfif StructKeyExists(local_results.RESPONSEHEADER, "Set-Cookie")>
     <cfloop collection="#local_results.RESPONSEHEADER["Set-Cookie"]#" item="key">
      <cfset var local_cookie_name = "#rereplace(local_results.RESPONSEHEADER["Set-Cookie"][key],'^(.*?)=.*','\1','one')#">
      <cfset var local_cookie_value = "#rereplace(local_results.RESPONSEHEADER["Set-Cookie"][key],'^.*?=(.*?)(;.*|$)','\1','one')#">
      <cfset var local_cookie_found = false>

      <cfloop from=1 to=#arrayLen(in_Cookies)# index="j">
       <cfif in_Cookies[j][1] eq local_cookie_name>
        <cfset in_Cookies[j][2] = local_cookie_value>
        <cfset local_cookie_found = true>
        <cfbreak>
       </cfif>
      </cfloop>

      <cfif not local_cookie_found>
       <cfset in_Cookies[#evaluate(arrayLen(in_Cookies)+1)#][1] = local_cookie_name>
       <cfset in_Cookies[#arrayLen(in_Cookies)#][2] = local_cookie_value>
      </cfif>

      <cfloop from=1 to=#arrayLen(local_RedirectCookiesAsArray)# index="j">
       <cfif in_Cookies[j][1] eq local_cookie_name>
        <cfset local_RedirectCookiesAsArray[j][1] = local_cookie_name>
        <cfset local_RedirectCookiesAsArray[j][2] = local_results.RESPONSEHEADER["Set-Cookie"][key]>
       <cfelseif j eq arrayLen(local_RedirectCookiesAsArray)> 
        <cfset local_RedirectCookiesAsArray[j+1][1] = local_cookie_name>
        <cfset local_RedirectCookiesAsArray[j+1][2] = local_results.RESPONSEHEADER["Set-Cookie"][key]>
       </cfif>
      </cfloop>
      <cfif arrayLen(local_RedirectCookiesAsArray) eq 0>
       <cfset local_RedirectCookiesAsArray[1][1] = local_cookie_name>
       <cfset local_RedirectCookiesAsArray[1][2] = local_results.RESPONSEHEADER["Set-Cookie"][key]>
      </cfif>

     </cfloop>
     <!--- Set cookies --->
      <cfloop from=1 to="#arraylen(in_Cookies)#" index="i">
       <cfif i gt 1><cfset local_Cookies = "#local_Cookies#;"></cfif>
       <cfset local_Cookies = "#local_Cookies##in_Cookies[i][1]#=#trim(local_JavaEncoder.encode(in_Cookies[i][2],"utf-8"))#">
      </cfloop>
     <!--- End set cookies --->
     </cfif>

    <cfelse>
     <cfset local_results.Statuscode = "#local_Connection.getResponseCode()# #local_Connection.getResponseMessage()#">
     <cfset local_results.URL = in_url>
      <cfset local_results.CONNECTION.REDIRECT_TIMES = local_current_redirect>
     <Cfbreak>
    </cfif>
    <!--- End check if redirection is necessary --->
 
 
    <cfset local_Connection.disconnect()> 
    <Cfset local_current_redirect = local_current_redirect + 1>
   </cfloop>
  <!--- End build connection --->
   <cfcatch type="any">
    <cfset local_results.errordetail= cfcatch.message>
    <cftry><cfset local_results.Statuscode = "#local_Connection.getResponseCode()# #local_Connection.getResponseMessage()#"><cfcatch type="any"></cfcatch></cftry>
    <cftry><cfset local_Connection.disconnect()> <cfcatch type="any"></cfcatch></cftry>
   </cfcatch>
  </cftry>
 
  <cfif in_ProxyPort neq '' or in_ProxyHost neq ''>
   <cfset var local_system = createObject("java", "java.lang.System")>
       <cfif in_ProxyHost neq ''><cfset local_system.clearProperty("http.proxyHost")></cfif>
      <cfif in_ProxyPort neq ''><cfset local_system.clearProperty("http.proxyPort")></cfif>
  </cfif>

  <cfif in_RememberRedirectedCookies>
   <cfif not StructKeyExists(local_results.RESPONSEHEADER, "Set-Cookie")>
    <cfset local_results.RESPONSEHEADER["Set-Cookie"] = structNew()>
   </cfif>
   <Cfset var local_count_keys = 0>
   <cftry><cfloop collection="#local_results.RESPONSEHEADER["Set-Cookie"]#" item="key"><cfset local_count_keys = local_count_keys+1></cfloop>
    <cfcatch type="any">
     <cfif not IsStruct(local_results.RESPONSEHEADER["Set-Cookie"])>
      <cfif local_results.RESPONSEHEADER["Set-Cookie"] neq ''><cfset local_count_keys = local_count_keys+1></cfif>
     </cfif>
    </cfcatch>
   </cftry>

   <cfloop from=1 to="#arrayLen(local_RedirectCookiesAsArray)#" index="i">
    <cfset local_count_keys = local_count_keys+1>
    <cfset local_results.RESPONSEHEADER["Set-Cookie"][""&local_count_keys&""] = local_RedirectCookiesAsArray[i][2]>
   </cfloop>
  </cfif>

  <cfreturn local_results>
 </cffunction>

Sunday, January 29, 2012

How to replace last occurrence of substring in coldfusion

I get a lot of questions lately on how to extract a substring from a string. Below you can find 4 examples on how to extract a specific substring from a string. It is not to difficult if you know how to write regular expressions. If you have no experience with regex writting these rules can give you a major headache.
<!--- Replace the last occurence of test in the string --->
<cfset example_string = "test abc test 123 test def test 456">
<cfoutput>
#rereplace(example_string,'(.*)(test )(.*)','\1\3','one')#
</cfoutput>
<!--- Output: test abc test 123 test def 456 --->
Below some other examples of how to replace a specific substring from a string.

Replace second occurrence of substring in a string
<!--- Replace the second occurence of test in the string --->
<cfset example_string = "test abc test 123 test def test 456">
<cfoutput>
#rereplace(example_string,'(.*?)(test )(.*?)(test )(.*)','\1\2\3\5','one')#
</cfoutput>
<!--- Output: test abc 123 test def test 456 --->
Replace the first occurrence of substring in a string
<!--- Replace the second occurence of test in the string --->
<cfset example_string = "test abc test 123 test def test 456">
<cfoutput>
#rereplace(example_string,'(.*?)(test )(.*)','\1\3','one')#
</cfoutput>
<!--- Output: abc test 123 test def test 456 --->
Replace the penultimate occurrence of substring in a string
<!--- Replace the penultimate occurence of test in the string --->
<cfset example_string = "test abc test 123 test def test 456">
<cfoutput>
#rereplace(example_string,'(.*)(test )(.*?)(test )(.*)','\1\3\4\5','one')#
</cfoutput>
<!--- Output: test abc test 123 def test 456 --->