Files
svc-api-x/v1/Application.cfc

753 lines
30 KiB
Plaintext

<cfcomponent extends="taffy.core.api">
<cfset this.mappings["/resources"] = expandPath("./resources")/>
<cfset this.mappings["/taffy"] = expandPath("./taffy")/>
<cfset this.mappings["/lib"] = expandPath("./lib")/>
<cflock scope="application" type="exclusive" timeout="3">
<cftry>
<cfinclude template="../../conf/prod.cfm"/>
<cfcatch type="ANY">
<cftry>
<cfinclude template="../../conf/stage.cfm"/>
<cfcatch type="ANY">
<cftry>
<cfinclude template="../../conf/dev.cfm"/>
<cfcatch type="ANY">
<cfset this.config.environment = "dev-default" />
<cfset this.config.datasource = "cmdb" />
<cfset this.config.is_dev_default = "true" />
</cfcatch>
</cftry>
</cfcatch>
</cftry>
</cfcatch>
</cftry>
<cfset this.datasource = this.config.datasource />
<cfset request.config = this.config />
</cflock>
<cfscript>
this.name = hash(getCurrentTemplatePath());
variables.framework.debugKey = "debug";
variables.framework.reloadKey = "reload";
variables.framework.reloadPassword = "true";
variables.framework.reloadOnEveryRequest = true;
variables.framework.serializer = "taffy.core.nativeJsonSerializer";
variables.framework.dashboardKey = "dashboard";
variables.framework.disableDashboard = false;
variables.framework.unhandledPaths = "/flex2gateway";
variables.framework.allowCrossDomain = true;
variables.framework.docs.APIName="svc-api";
variables.framework.docs.APIVersion="0.234";
variables.framework.globalHeaders = structNew();
variables.framework.globalHeaders["Access-Control-Expose-Headers"] = "Location";
variables.framework.globalHeaders["Access-Control-Allow-Credentials"] = "true";
variables.framework.globalHeaders["X-Application-Version"] = variables.framework.docs.APIVersion;
request.UNDEFINED_USR_ID=-1;
request.ANONYMOUS_USR_ID=2;
request.GUEST_USR_ID=3;
request.USER_AGENT="#variables.framework.docs.APIName# #variables.framework.docs.APIVersion# stand:#this.getStand()#";
request.ORCHESTRATOR_AUTH = createObject("java", "java.lang.System").getEnv("ORCHESTRATOR_AUTH");
if (isNull(request.ORCHESTRATOR_AUTH)) {
request.ORCHESTRATOR_AUTH = "Basic ...no data";
}
request.vault_login_url="https://vault.lk.adl.nubes.ru/v1/auth/approle/login";
request.vault_role_id=createObject("java", "java.lang.System").getEnv("VAULT_ROLE_ID");
request.vault_secret_id=createObject("java", "java.lang.System").getEnv("VAULT_SECRET_ID");
request.auth_header="";
</cfscript>
<cffunction name="rethrow" returntype="void">
<cftry>
<cfcatch>
<cfrethrow/>
</cfcatch>
</cftry>
<cfthrow type="Context validation error" message="RETHROW() called outside TRY-CATCH"/>
</cffunction>
<cffunction name="castToBool" returntype="any">
<cfargument name="x"/>
<cfif isNull(arguments.x) OR isEmpty(arguments.x)>
<cfreturn arguments.x/>
<cfelse>
<cfreturn (arguments.x NEQ 0)/>
</cfif>
</cffunction>
<cfset request.castToBool=#castToBool#/>
<cffunction name="castToBoolSimple" returntype="any">
<cfargument name="x"/>
<cfreturn (arguments.x NEQ 0)/>
</cffunction>
<cfset request.castToBoolSimple=#castToBoolSimple#/>
<cffunction
name="getStand"
access="private"
returntype="string"
output="true">
<cftry>
<cfquery name=qConfig>
select value as stand from config
where name='STAND'
</cfquery>
<cfreturn qConfig.stand/>
<cfcatch type="ANY">
</cfcatch>
</cftry>
<cfreturn ""/>
</cffunction>
<cffunction name="onApplicationStart">
<cfset this.iamServiceUrl=locateIamService()/>
<cfreturn super.onApplicationStart() />
</cffunction>
<cffunction name="locateIamService">
<cfset var stand=""/>
<cftry>
<cfquery name=qConfig>
select value as stand from config
where name='STAND'
</cfquery>
<cfset stand = qConfig.stand/>
<cfswitch expression=#stand#>
<cfcase value=",dev">
<cfreturn "https://auth-api-dev.ngcloud.ru/api/v1" />
</cfcase>
<cfcase value="test">
<cfreturn "https://auth-api-test.ngcloud.ru/api/v1" />
</cfcase>
<cfcase value="prod">
<cfreturn "https://auth-api.ngcloud.ru/api/v1" />
</cfcase>
<cfdefaultcase></cfdefaultcase>
</cfswitch>
<cfcatch type="ANY">
<cfthrow message="IAM service unavailable" detail="there is no IAM for stand #stand# defined"/>
</cfcatch>
</cftry>
</cffunction>
<cfscript>
function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers){
request.stand=this.getStand();
request.iam_service_url=this.iamServiceUrl;
if (uCase(arguments.verb) EQ 'OPTIONS') return true;
if (lCase(arguments.cfc) EQ 'err') return true;
if (lCase(arguments.cfc) EQ 'throw') return true;
if (structKeyExists(headers,"Authorization")) {
request.auth_header=headers.Authorization;
} else {
return representationOf("Authorization header expected").withStatus(401);
}
var result="";
try {
var authUrl = "#this.iamServiceUrl#/auth/user";
var httpService = new http(method = "GET", charset = "utf-8", url = #authUrl#, timeout="5");
httpService.addParam(type = "HEADER", name = "Accept", value = "application/json");
httpService.addParam(type = "HEADER", name = "Authorization", value = "#request.auth_header#");
var resp = httpService.send();
var prefix = resp.getPrefix();
if (prefix.status_code NEQ 200) {
var iamStatusCode = (isValid("integer", prefix.status_code)) ? val(prefix.status_code) : 500;
return representationOf( {"IAM URL"=#authUrl#, "idpResponse"=resp} ).withStatus(iamStatusCode);
}
result = prefix.filecontent;
} catch (e) {
return representationOf( {"exception"=e, "idpResponse"=result} ).withStatus(200);
}
try {
var idpUserData=deserializeJson(result);
"arguments.requestArguments.companyUid"=idpUserData.userInfo.companyId;
"arguments.requestArguments.usrUid"=idpUserData.userInfo.contactId;
var usrCustomerInfo=getUsrCustomerInfo(idpUserData.userInfo.contactId, idpUserData.userInfo.companyId);
} catch (e) {
return representationOf( {"exception.Message"=e.Message, "exception.Detail"=e.Detail, "idpResponse"=result} ).withStatus(422);
}
if (lCase(arguments.cfc) EQ 'user') {
if (structIsEmpty(usrCustomerInfo)) return representationOf("User information not found").withStatus(404);
}
if (lCase(arguments.cfc) EQ 'notification_ls') {
if (structIsEmpty(usrCustomerInfo)) {
"arguments.requestArguments.usrId"=-1;
"arguments.requestArguments.contragentId"=-1;
"arguments.requestArguments.contractId"=-1;
"arguments.requestArguments.specificationId"=-1;
"arguments.requestArguments.isImpersonated"=false;
"arguments.requestArguments.clientID"="";
"arguments.requestArguments.login"="";
return true;
}
}
if (structIsEmpty(usrCustomerInfo)) return representationOf("Cannot find default specification for current user #result#").withStatus(422);
"arguments.requestArguments.usrId"=usrCustomerInfo.usrId;
"arguments.requestArguments.contragentId"=usrCustomerInfo.contragentId;
"arguments.requestArguments.contractId"=usrCustomerInfo.contractId;
"arguments.requestArguments.specificationId"=usrCustomerInfo.specificationId;
try {
"arguments.requestArguments.isImpersonated"=idpUserData.impersonation.is_impersonated;
} catch (e) {
"arguments.requestArguments.isImpersonated"=false;
}
try {
"arguments.requestArguments.clientID"=idpUserData.userInfo.clientID;
} catch (e) {
"arguments.requestArguments.clientID"="";
}
try {
"arguments.requestArguments.login"=idpUserData.userInfo.login;
} catch (e) {
"arguments.requestArguments.login"="";
}
return true;
}
</cfscript>
<cffunction name="checkAuth">
<cfset var jwtHelper=CreateObject("component","lib.jwt").init()/>
<cfset var headers=#GetHttpRequestData().headers#/>
<cftry>
<cfset var rawToken=right(headers.Authorization,len(headers.Authorization)-len('bearer '))/>
<cfset var token=jwtHelper.decode(token=rawToken, key=request.config.IDP_certificate.keys[1], algorithms='RS256')/>
<cfset var login=token.preferred_username/>
<cfset login=ReplaceNoCase(login,"#request.config.auth_domain_suffix#","")/>
<cfquery name="local.qUsr">
select usr_id from usr where login=<cfqueryparam cfsqltype="cf_sql_varchar" value="#login#"/>
</cfquery>
<cfif #local.qUsr.recordCount# EQ 0>
<cfheader statuscode="401" statustext="User not found in local database"/>
<cfreturn false/>
</cfif>
<cfset request.usr_id=local.qUsr.usr_id/>
<cfcatch type="database">
<cfheader statuscode="500" statustext="Internal Server Error - Database Error"/>
<cfoutput>#cfcatch.message# : #cfcatch.detail#</cfoutput>
<cfreturn false/>
</cfcatch>
<cfcatch type="ANY">
<cfif !GetHttpRequestData().method EQ "OPTIONS">
<cfoutput>#cfcatch.message# : #cfcatch.detail#</cfoutput>
<cfreturn false/>
</cfif>
</cfcatch>
</cftry>
<cfreturn true/>
</cffunction>
<cffunction name="getUsrCustomerInfo">
при этом если имперсонируется компания, то спецификацию и контракт нужно брать по компании
Кстати, зачем нужен вообще юзер, если компания всегда доступна, а вся информация висит на ней
Для того, чтобы разрешить ключ в целочисленный ключ CMDB--->
<cfargument name="usrUid"/>
<cfargument name="contragentUid"/>
<cfif !isValid('guid',arguments.usrUid)>
</cfif>
<cfif !isValid('guid',arguments.contragentUid)>
<cfthrow message="Invalid contragent uuid" detail="uuid=(#arguments.contragentUid#) is not a valid UUID"/>
</cfif>
<cfset local={}/>
<cfquery name="local.qGetContragentInfo">
select z.contragent_id, c.contract_id, s.specification_id
from contragent z
join contract c on (z.contragent_id=c.contragent_id)
join specification s on (c.contract_id=s.contract_id)
where z.external_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.contragentUid#" null=#!isValid('guid',arguments.contragentUid)#/>
order by s.specification_id desc
limit 1;
</cfquery>
<cfif local.qGetContragentInfo.recordCount EQ 0>
<cfquery name="local.qGetContragent">
select z.contragent_id
from contragent z
where z.external_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.contragentUid#" null=#!isValid('guid',arguments.contragentUid)#/>
</cfquery>
<cfif local.qGetContragent.recordCount EQ 0>
<cfthrow message="Contragent Not Found in CMDB" detail="Contragent with uuid=(#arguments.contragentUid#) not found in CMDB"/>
</cfif>
<cfquery name="local.qGetContract">
select z.contragent_id, c.contract_id
from contragent z
join contract c on (z.contragent_id=c.contragent_id)
where z.external_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.contragentUid#" null=#!isValid('guid',arguments.contragentUid)#/>
</cfquery>
<cfif local.qGetContract.recordCount EQ 0>
<cfthrow message="Contragent does not have a contract in CMDB" detail="No contract for contragent with uuid=(#arguments.contragentUid#) found in CMDB (contragent_id=#local.qGetContragent.contragent_id#)"/>
</cfif>
<cfthrow message="Contragent does not have a specification in CMDB" detail="No specification for contragent with uuid=(#arguments.contragentUid#) found in CMDB (contragent_id=#local.qGetContragent.contragent_id#)"/>
</cfif>
<cfquery name="local.qGetUserInfo">
select u.usr_id, u.contragent_id, k.contragent_id as c_contragent_id
from usr u
left outer join contragent k on (u.contragent_id=k.contragent_id)
where u.idp_usr_uid=<cfqueryparam cfsqltype="cf_sql_other" value="#arguments.usrUid#" null=#!isValid('guid',arguments.usrUid)#/>
limit 1;
</cfquery>
<cfif local.qGetUserInfo.recordCount EQ 0>
<cfthrow message="User Not Found in CMDB" detail="User with uuid=(#arguments.usrUid#) not found in CMDB"/>
</cfif>
<cfif !len(local.qGetUserInfo.contragent_id)>
<cfthrow message="User is not mapped to Contragent in CMDB" detail="Contragent for user with uuid=(#arguments.usrUid#) not specified in CMDB"/>
</cfif>
<cfif !len(local.qGetUserInfo.c_contragent_id)>
<cfthrow message="User is not mapped to Contragent in CMDB" detail="Contragent for user with uuid=(#arguments.usrUid#) not found in CMDB (specified contragent_id=#local.qGetUserInfo.contragent_id#)"/>
</cfif>
<cfreturn {
"usrId"=#local.qGetUserInfo.usr_id#,
"contragentId"=#local.qGetContragentInfo.contragent_id#,
"contractId"=#local.qGetContragentInfo.contract_id#,
"specificationId"=#local.qGetContragentInfo.specification_id#
}/>
</cffunction>
<cffunction name="corsHeaders">
<cfset var _taffyRequest=request._taffyRequest/>
<cfset local={}/>
<cfset local.allowVerbs = uCase(structKeyList(_taffyRequest.matchDetails.methods)) />
<cfif true
AND listFindNoCase('PUT,PATCH,DELETE,OPTIONS',_taffyRequest.verb)
AND NOT listFind(local.allowVerbs,'OPTIONS')>
<cfset local.allowVerbs = listAppend(local.allowVerbs,'OPTIONS') />
</cfif>
<cfif structKeyExists(_taffyRequest.headers, "origin") >
<cfheader name="Access-Control-Allow-Origin" value="#_taffyRequest.headers.origin#" />
<cfheader name="Access-Control-Allow-Methods" value="#local.allowVerbs#" />
<cfif not structKeyExists(_taffyRequest.headers, "Access-Control-Request-Headers")>
<cfheader name="Access-Control-Allow-Headers" value="Origin, Authorization, X-CSRF-Token, X-Requested-With, Content-Type, X-HTTP-Method-Override, Accept, Referrer, User-Agent" />
<cfelse>
<cfset local.allowedHeaders = {} />
<cfloop list="Origin,Authorization,X-CSRF-Token,X-Requested-With,Content-Type,X-HTTP-Method-Override,Accept,Referrer,User-Agent" index="local.h">
<cfset local.allowedHeaders[local.h] = 1 />
</cfloop>
<cfset local.requestedHeaders = _taffyRequest.headers['Access-Control-Request-Headers'] />
<cfloop list="#local.requestedHeaders#" index="local.i">
<cfset local.allowedHeaders[ local.i ] = 1 />
</cfloop>
<cfheader name="Access-Control-Allow-Headers" value="#structKeyList(local.allowedHeaders)#" />
</cfif>
</cfif>
</cffunction>
<cffunction name="onRequest" output="true" returntype="boolean">
<cfargument name="targetPage" type="string" required="true" />
<cfset var _taffyRequest = {} />
<cfset var local = {} />
<cfset var m = '' />
<cfset request._taffyRequest = _taffyRequest />
<cfset local.debug = false />
<cfset _taffyRequest.metrics = {} />
<cfset m = _taffyRequest.metrics />
<cfset m.init = getTickCount() />
<cfif not structKeyExists(url, application._taffy.settings.debugKey)>
<cfsetting showdebugoutput="false" />
</cfif>
<cfif
NOT structKeyExists(url,application._taffy.settings.endpointURLParam)
AND NOT structKeyExists(form,application._taffy.settings.endpointURLParam)
AND len(cgi.path_info) lte 1
AND listFindNoCase(cgi.script_name, "index.cfm", "/") EQ listLen(cgi.script_name, "/")>
<cfif NOT application._taffy.settings.disableDashboard>
<cfif StructKeyExists( URL, "docs" )>
<cfinclude template="#application._taffy.settings.docsPath#" />
<cfelse>
<cfinclude template="../dashboard/dashboard.cfm" />
</cfif>
<cfabort />
<cfelse>
<cfif len(application._taffy.settings.disabledDashboardRedirect)>
<cflocation url="#application._taffy.settings.disabledDashboardRedirect#" addtoken="false" />
<cfabort />
<cfelseif application._taffy.settings.showDocsWhenDashboardDisabled>
<cfinclude template="#application._taffy.settings.docsPath#" />
<cfabort />
<cfelse>
<cfset throwError(403, "Forbidden") />
</cfif>
</cfif>
</cfif>
<cfset m.beforeParse = getTickCount() />
<cfset local.parsed = parseRequest() />
<cfset m.afterParse = getTickCount() />
<cfset structAppend(_taffyRequest, local.parsed) />
<cfset m.parseTime = m.afterParse - m.beforeParse />
<cfset local.allowVerbs = uCase(structKeyList(_taffyRequest.matchDetails.methods)) />
<cfif (application._taffy.settings.allowCrossDomain eq true or len(application._taffy.settings.allowCrossDomain) gt 0)
AND listFindNoCase('PUT,PATCH,DELETE,OPTIONS',_taffyRequest.verb)
AND NOT listFind(local.allowVerbs,'OPTIONS')>
<cfset local.allowVerbs = listAppend(local.allowVerbs,'OPTIONS') />
</cfif>
<cfif structKeyExists(_taffyRequest.headers, "origin") AND (application._taffy.settings.allowCrossDomain eq true or len(application._taffy.settings.allowCrossDomain) gt 0)>
<cfif application._taffy.settings.allowCrossDomain eq true>
<cfheader name="Access-Control-Allow-Origin" value="#_taffyRequest.headers.origin#" />
<cfelse>
<cfset local.domains = listToArray( application._taffy.settings.allowCrossDomain, ', ;' )>
<cfif structKeyExists(_taffyRequest.headers, "origin")>
<cfloop from="1" to="#arrayLen( local.domains )#" index="local.i">
<cfif lcase( rereplace( _taffyRequest.headers.origin, "(http|https):\/\/", "", "all" ) ) EQ lcase( rereplace( local.domains[ local.i ], "(http|https):\/\/", "", "all" ) ) >
<cfheader name="Access-Control-Allow-Origin" value="#_taffyRequest.headers.origin#" />
<cfheader name="Access-Control-Allow-Credentials" value="true" />
<cfbreak>
</cfif>
</cfloop>
</cfif>
</cfif>
<cfheader name="Access-Control-Allow-Methods" value="#local.allowVerbs#" />
<cfif not structKeyExists(_taffyRequest.headers, "Access-Control-Request-Headers")>
<cfheader name="Access-Control-Allow-Headers" value="Origin, Authorization, X-CSRF-Token, X-Requested-With, Content-Type, X-HTTP-Method-Override, Accept, Referrer, User-Agent" />
<cfelse>
<cfset local.allowedHeaders = {} />
<cfloop list="Origin,Authorization,X-CSRF-Token,X-Requested-With,Content-Type,X-HTTP-Method-Override,Accept,Referrer,User-Agent" index="local.h">
<cfset local.allowedHeaders[local.h] = 1 />
</cfloop>
<cfset local.requestedHeaders = _taffyRequest.headers['Access-Control-Request-Headers'] />
<cfloop list="#local.requestedHeaders#" index="local.i">
<cfset local.allowedHeaders[ local.i ] = 1 />
</cfloop>
<cfheader name="Access-Control-Allow-Headers" value="#structKeyList(local.allowedHeaders)#" />
</cfif>
</cfif>
<cfset addHeaders(getGlobalHeaders()) />
Now we know everything we need to know to service the request. let's service it!
--->
<cfset m.beforeOnTaffyRequest = getTickCount() />
<cfset _taffyRequest.continue = onTaffyRequest(
_taffyRequest.verb
,_taffyRequest.matchDetails.beanName
,_taffyRequest.requestArguments
,_taffyRequest.returnMimeExt
,_taffyRequest.headers
,_taffyRequest.methodMetadata
,local.parsed.matchDetails.srcUri
) />
<cfset m.afterOnTaffyRequest = getTickCount() />
<cfset m.otrTime = m.afterOnTaffyRequest - m.beforeOnTaffyRequest />
<cfif not structKeyExists(_taffyRequest, "continue")>
<cfthrow
message="Error in your onTaffyRequest method"
detail="Your onTaffyRequest method returned no value. Expected: Return TRUE or call noData()/representationOf()."
errorcode="400"
/>
</cfif>
<cfif isObject(_taffyRequest.continue)>
<cfset _taffyRequest.result = duplicate(_taffyRequest.continue) />
<cfset structDelete(_taffyRequest, "continue")/>
<cfset m.resourceTime = 0 />
<cfelse>
<cfif structKeyExists(_taffyRequest.requestArguments, application._taffy.settings.simulateKey) and _taffyRequest.requestArguments[application._taffy.settings.simulateKey] eq application._taffy.settings.simulatePassword>
<cfset sampler = 'sample#_taffyRequest.method#Response' />
<cfif structKeyExists(_taffyRequest.matchDetails.metadata, sampler)>
<cfinvoke
component="#application._taffy.factory.getBean(_taffyRequest.matchDetails.beanName)#"
method="#sampler#"
returnvariable="_taffyRequest.result"
/>
<cfset _taffyRequest.result = rep(_taffyRequest.result) />
<cfelse>
<cfset _taffyRequest.result = noData().withStatus(400, "No Sample Response Available") />
</cfif>
<cfelse>
<cfif structKeyExists(_taffyRequest.matchDetails.methods, _taffyRequest.verb)>
<cfset m.cacheCheckTime = getTickCount() />
<cfset local.cacheKey = getCacheKey(
_taffyRequest.matchDetails.beanName
,_taffyRequest.requestArguments
,local.parsed.matchDetails.srcUri
) />
<cfif ucase(_taffyRequest.verb) eq "GET" and validCacheExists(local.cacheKey)>
<cfset m.cacheCheckTime = getTickCount() - m.cacheCheckTime />
<cfset m.cacheGetTime = getTickCount() />
<cfset _taffyRequest.result = getCachedResponse(local.cacheKey) />
<cfset m.cacheGetTime = m.cacheGetTime - getTickCount() />
<cfelse>
<cfif ucase(_taffyRequest.verb) eq "GET">
<cfset m.cacheCheckTime = getTickCount() - m.cacheCheckTime />
<cfelse>
<cfset structDelete(m, "cacheCheckTime") />
</cfif>
<cfset m.beforeResource = getTickCount() />
<cfinvoke
component="#application._taffy.factory.getBean(_taffyRequest.matchDetails.beanName)#"
method="#_taffyRequest.method#"
argumentcollection="#_taffyRequest.requestArguments#"
returnvariable="_taffyRequest.result"
/>
<cfset m.afterResource = getTickCount() />
<cfset m.resourceTime = m.afterResource - m.beforeResource />
<cfif !isDefined("_taffyRequest.result")>
<cfthrow
message="Resource did not return a value"
detail="The resource is expected to return a call to rep()/representationOf() or noData(). It appears there was no return at all."
errorcode="taffy.resources.ResourceReturnsNothing"
/>
</cfif>
This way we can directly return the object instead of a serializer from resource actions. --->
<cfif !isInstanceOf(_taffyRequest.result, "taffy.core.baseSerializer")>
<cfset _taffyRequest.result = rep(_taffyRequest.result) />
</cfif>
<cfif ucase(_taffyRequest.verb) eq "GET" and structKeyExists(local, "cacheKey")>
<cfset m.cacheSaveStart = getTickCount() />
<cfset setCachedResponse(local.cacheKey, _taffyRequest.result) />
<cfset m.cacheSaveTime = getTickCount() - m.cacheSaveStart />
</cfif>
</cfif>
<cfelseif NOT listFind(local.allowVerbs,_taffyRequest.verb)>
<cfheader name="ALLOW" value="#local.allowVerbs#" />
<cfset throwError(405, "Method Not Allowed") />
<cfelse>
<cfset _taffyRequest.resultHeaders = structNew() />
<cfset _taffyRequest.statusArgs = structNew() />
<cfset _taffyRequest.statusArgs.statusCode = 200 />
<cfset _taffyRequest.statusArgs.statusText = 'OK' />
</cfif>
</cfif>
</cfif>
<cfif not mimeSupported(_taffyRequest.returnMimeExt)>
<cfset throwError(400, "Requested format not available (#_taffyRequest.returnMimeExt#)") />
</cfif>
<cfif structKeyExists(_taffyRequest,'result')>
<cfset _taffyRequest.statusArgs = structNew() />
<cfset _taffyRequest.statusArgs.statusCode = _taffyRequest.result.getStatus() />
<cfset _taffyRequest.statusArgs.statusText = _taffyRequest.result.getStatusText() />
<cfinvoke
component="#_taffyRequest.result#"
method="getHeaders"
returnvariable="_taffyRequest.resultHeaders"
/>
</cfif>
<cfsetting enablecfoutputonly="true" />
<cfcontent reset="true" type="#getReturnMimeAsHeader(_taffyRequest.returnMimeExt)#; charset=utf-8" />
<cfheader statuscode="#_taffyRequest.statusArgs.statusCode#" statustext="#_taffyRequest.statusArgs.statusText#" />
<cfset addHeaders(_taffyRequest.resultHeaders) />
<cfheader name="ALLOW" value="#local.allowVerbs#" />
<cfheader name="X-TIME-IN-PARSE" value="#m.parseTime#" />
<cfheader name="X-TIME-IN-ONTAFFYREQUEST" value="#m.otrTime#" />
<cfif structKeyExists(m, "resourceTime")>
<cfheader name="X-TIME-IN-RESOURCE" value="#m.resourceTime#" />
</cfif>
<cfif structKeyExists(m, "cacheCheckTime")>
<cfheader name="X-TIME-IN-CACHE-CHECK" value="#m.cacheCheckTime#" />
</cfif>
<cfif structKeyExists(m, "cacheGetTime")>
<cfheader name="X-TIME-IN-CACHE-GET" value="#m.cacheGetTime#" />
</cfif>
<cfif structKeyExists(m, "cacheSaveTime")>
<cfheader name="X-TIME-IN-CACHE-SAVE" value="#m.cacheSaveTime#" />
</cfif>
<cfif application._taffy.settings.exposeHeaders>
<cfset local.exposeHeaderList = structKeyList(_taffyRequest.resultHeaders) />
<cfset local.exposeHeaderValue = "" />
<cfif application._taffy.settings.useEtags and _taffyRequest.verb eq "GET" and _taffyRequest.result.getType() eq "textual">
<cfset local.exposeHeaderList = listAppend(local.exposeHeaderList, "Etag") />
</cfif>
<cfloop list="#local.exposeHeaderList#" index="local.exposeHeader">
<cfif not listFindNoCase("Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma", local.exposeHeader)>
<cfset local.exposeHeaderValue = listAppend(local.exposeHeaderValue, local.exposeHeader) />
</cfif>
</cfloop>
<cfif listLen(local.exposeHeaderValue) gt 0>
<cfheader name="Access-Control-Expose-Headers" value="#local.exposeHeaderValue#" />
</cfif>
</cfif>
<cfif structKeyExists(_taffyRequest,'result')>
<cfset _taffyRequest.resultType = _taffyRequest.result.getType() />
<cfset local.resultSerialized = '' />
<cfif _taffyRequest.resultType eq "textual">
<cfset _taffyRequest.metrics.beforeSerialize = getTickCount() />
<cfinvoke
component="#_taffyRequest.result#"
method="getAs#_taffyRequest.returnMimeExt#"
returnvariable="_taffyRequest.resultSerialized"
/>
<cfset _taffyRequest.metrics.afterSerialize = getTickCount() />
<cfset m.serializeTime = m.afterSerialize - m.beforeSerialize />
<cfheader name="X-TIME-IN-SERIALIZE" value="#m.serializeTime#" />
<cfif structKeyExists(_taffyRequest, "jsonpCallback")>
<cfset _taffyRequest.resultSerialized = _taffyRequest.jsonpCallback & "(" & _taffyRequest.resultSerialized & ");" />
</cfif>
<cfif application._taffy.settings.useEtags and _taffyRequest.verb eq "GET">
<cfif structKeyExists(server, "lucee")>
<cfset _taffyRequest.serverEtag = '"' & hash(_taffyRequest.resultSerialized) & '"' />
<cfelse>
<cfset _taffyRequest.serverEtag = '"' & _taffyRequest.result.getData().hashCode() & '"' />
</cfif>
<cfif structKeyExists(_taffyRequest.headers, "If-None-Match")>
<cfset _taffyRequest.clientEtag = _taffyRequest.headers['If-None-Match'] />
<cfif len(_taffyRequest.clientEtag) gt 0 and _taffyRequest.clientEtag eq _taffyRequest.serverEtag>
<cfheader statuscode="304" statustext="Not Modified" />
<cfcontent reset="true" type="#application._taffy.settings.mimeExtensions[_taffyRequest.returnMimeExt]#; charset=utf-8" />
<cfreturn true />
<cfelse>
<cfheader name="Etag" value="#_taffyRequest.serverEtag#" />
</cfif>
<cfelse>
<cfheader name="Etag" value="#_taffyRequest.serverEtag#" />
</cfif>
</cfif>
<cfset m.done = getTickCount() />
<cfset m.taffyTime = m.done - m.init - m.parseTime - m.otrTime - m.serializeTime />
<cfif structKeyExists(m, "resourceTime")>
<cfset m.taffyTime -= m.resourceTime />
</cfif>
<cfheader name="X-TIME-IN-TAFFY" value="#m.taffyTime#" />
<cfcontent reset="true" type="#application._taffy.settings.mimeExtensions[_taffyRequest.returnMimeExt]#; charset=utf-8" />
<cfif _taffyRequest.resultSerialized neq ('"' & '"')>
<cfset local.resultSerialized = _taffyRequest.resultSerialized />
</cfif>
<cfif structKeyExists(url, application._taffy.settings.debugKey)>
<cfset local.debug = true />
</cfif>
<cfelseif _taffyRequest.resultType eq "filename">
<cfset m.done = getTickCount() />
<cfset m.taffyTime = m.done - m.init - m.parseTime - m.otrTime - m.resourceTime />
<cfheader name="X-TIME-IN-TAFFY" value="#m.taffyTime#" />
<cfcontent reset="true" file="#_taffyRequest.result.getFileName()#" type="#_taffyRequest.result.getFileMime()#" deletefile="#_taffyRequest.result.getDeleteFile()#" />
<cfelseif _taffyRequest.resultType eq "filedata">
<cfset m.done = getTickCount() />
<cfset m.taffyTime = m.done - m.init - m.parseTime - m.otrTime - m.resourceTime />
<cfheader name="X-TIME-IN-TAFFY" value="#m.taffyTime#" />
<cfcontent reset="true" variable="#_taffyRequest.result.getFileData()#" type="#_taffyRequest.result.getFileMime()#" />
<cfelseif _taffyRequest.resultType eq "imagedata">
<cfset m.done = getTickCount() />
<cfset m.taffyTime = m.done - m.init - m.parseTime - m.otrTime - m.resourceTime />
<cfheader name="X-TIME-IN-TAFFY" value="#m.taffyTime#" />
<cfcontent reset="true" variable="#_taffyRequest.result.getImageData()#" type="#_taffyRequest.result.getFileMime()#" />
</cfif>
</cfif>
<cfset local.resultSerialized = "" />
<cfif structKeyExists( _taffyRequest, "resultSerialized" )>
<cfset local.resultSerialized = _taffyRequest.resultSerialized />
</cfif>
<cfset local.result = StructNew() />
<cfif structKeyExists( _taffyRequest, "result" )>
<cfset local.result = _taffyRequest.result.getData() />
</cfif>
<cfset m.beforeOnTaffyRequestEnd = getTickCount() />
<cfset onTaffyRequestEnd(
_taffyRequest.verb
,_taffyRequest.matchDetails.beanName
,_taffyRequest.requestArguments
,_taffyRequest.returnMimeExt
,_taffyRequest.headers
,_taffyRequest.methodMetadata
,local.parsed.matchDetails.srcUri
,local.resultSerialized
,local.result
,_taffyRequest.statusArgs.statusCode
) />
<cfset m.otreTime = getTickCount() - m.beforeOnTaffyRequestEnd />
<cfheader name="X-TIME-IN-ONTAFFYREQUESTEND" value="#m.otreTime#" />
<cfif len(trim(local.resultSerialized))>
<cfoutput>#local.resultSerialized#</cfoutput>
</cfif>
<cfif local.debug>
<cfoutput><h3>Request Details:</h3><cfdump var="#_taffyRequest#"></cfoutput>
</cfif>
<cfreturn true />
</cffunction>
</cfcomponent>