Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion bonus/LogToScreen.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
<cffunction name="saveLog">
<cfargument name="exception" />
<cfcontent type="text/html" />
<cfheader statuscode="500" statustext="Unhandled API Error" />
<cfinclude template="../core/cfHeaderHelper.cfm" />
<cfset setTaffyStatusHeader(500, "Unhandled API Error") />
<cfdump var="#arguments#" />
<cfif isDefined('request.debugData')>
<cfdump var="#request.debugData#" label="debug data" />
Expand Down
28 changes: 23 additions & 5 deletions core/api.cfc
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
<cfcomponent hint="Base class for taffy REST application's Application.cfc">

<!--- Instance-level utility for backwards compatible cfheader handling --->
<cfset variables.headerUtils = "" />

<!--- Lazy-loaded getter for header utility --->
<cffunction name="getHeaderUtils" access="private" output="false" returntype="any" hint="Returns header utility instance with lazy loading">
<cfif variables.headerUtils eq "">
<cfset variables.headerUtils = createObject("component", "taffy.core.cfHeaderUtils").init() />
</cfif>
<cfreturn variables.headerUtils />
</cffunction>

<!--- Wrapper function to maintain API compatibility --->
<cffunction name="setStatusHeader" access="private" output="false" returntype="void" hint="Sets HTTP status header with backwards compatibility">
<cfargument name="statusCode" type="numeric" required="true" hint="HTTP status code to set" />
<cfargument name="statusText" type="string" required="false" default="" hint="HTTP status text (optional for CF 2025+)" />
<cfset getHeaderUtils().setStatusHeader(arguments.statusCode, arguments.statusText) />
</cffunction>

<!--- this method is meant to be (optionally) overrided in your application.cfc --->
<cffunction name="getEnvironment" output="false" hint="override this function to define the current API environment"><cfreturn "" /></cffunction>

Expand Down Expand Up @@ -146,7 +164,7 @@
<cfset logger.saveLog(exception) />

<!--- return 500 no matter what --->
<cfheader statuscode="500" statustext="Error" />
<cfset setStatusHeader(500, "Error") />
<cfcontent reset="true" />

<cfif structKeyExists(exception, "rootCause")>
Expand Down Expand Up @@ -177,7 +195,7 @@
</cfif>
<cfcatch>
<cfcontent reset="true" type="text/plain; charset=utf-8" />
<cfheader statuscode="500" statustext="Error" />
<cfset setStatusHeader(500, "Error") />
<cfoutput>An unhandled exception occurred: <cfif isStruct(root) and structKeyExists(root,"message")>#root.message#<cfelse>#root#</cfif> <cfif isStruct(root) and structKeyExists(root,"detail")>-- #root.detail#</cfif></cfoutput>
<cfdump var="#cfcatch#" format="text" label="ERROR WHEN LOGGING EXCEPTION" />
<cfdump var="#exception#" format="text" label="ORIGINAL EXCEPTION" />
Expand Down Expand Up @@ -422,7 +440,7 @@

<cfsetting enablecfoutputonly="true" />
<cfcontent reset="true" type="#getReturnMimeAsHeader(_taffyRequest.returnMimeExt)#; charset=utf-8" />
<cfheader statuscode="#_taffyRequest.statusArgs.statusCode#" statustext="#_taffyRequest.statusArgs.statusText#" />
<cfset setStatusHeader(_taffyRequest.statusArgs.statusCode, _taffyRequest.statusArgs.statusText) />

<!--- headers --->
<cfset addHeaders(_taffyRequest.resultHeaders) />
Expand Down Expand Up @@ -498,7 +516,7 @@
<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" />
<cfset setStatusHeader(304, "Not Modified") />
<cfcontent reset="true" type="#application._taffy.settings.mimeExtensions[_taffyRequest.returnMimeExt]#; charset=utf-8" />
<cfreturn true />
<cfelse>
Expand Down Expand Up @@ -1117,7 +1135,7 @@
<cfargument name="headers" type="struct" required="false" default="#structNew()#" />
<cfcontent reset="true" />
<cfset addHeaders(arguments.headers) />
<cfheader statuscode="#arguments.statusCode#" statustext="#arguments.msg#" />
<cfset setStatusHeader(arguments.statusCode, arguments.msg) />
<cfabort />
</cffunction>

Expand Down
17 changes: 14 additions & 3 deletions core/baseDeserializer.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,24 @@
<!--- Helpers --->
<!--- ============================ --->

<!--- Instance-level utility for backwards compatible cfheader handling --->
<cfset variables.headerUtils = "" />

<!--- Lazy-loaded getter for header utility --->
<cffunction name="getHeaderUtils" access="private" output="false" returntype="any" hint="Returns header utility instance with lazy loading">
<cfif variables.headerUtils eq "">
<cfset variables.headerUtils = createObject("component", "taffy.core.cfHeaderUtils").init() />
</cfif>
<cfreturn variables.headerUtils />
</cffunction>

<cffunction name="throwError" access="private" output="false" returntype="void">
<cfargument name="statusCode" type="numeric" default="500" />
<cfargument name="statusCode" type="numeric" default="500" hint="HTTP status code to return" />
<cfargument name="msg" type="string" required="true" hint="message to return to api consumer" />
<cfargument name="headers" type="struct" required="false" default="#structNew()#" />
<cfargument name="headers" type="struct" required="false" default="#structNew()#" hint="additional HTTP headers" />
<cfcontent reset="true" />
<cfset addHeaders(arguments.headers) />
<cfheader statuscode="#arguments.statusCode#" statustext="#arguments.msg#" />
<cfset getHeaderUtils().setStatusHeader(arguments.statusCode, arguments.msg) />
<cfabort />
</cffunction>

Expand Down
26 changes: 26 additions & 0 deletions core/cfHeaderHelper.cfm
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!---
Global utility function wrapper for backwards compatible cfheader handling.

This provides a simple function interface that wraps the cfHeaderUtils.cfc component.
The CFC is cached in application scope for performance. This helper allows code to call
setTaffyStatusHeader() directly without managing component instances.
--->

<cffunction name="setTaffyStatusHeader" output="false" returntype="void" hint="Sets HTTP status header with backwards compatibility for CF 2025">
<cfargument name="statusCode" type="numeric" required="true" hint="HTTP status code to set" />
<cfargument name="statusText" type="string" required="false" default="" hint="HTTP status text (optional for CF 2025+)" />

<cfscript>
// Ensure thread-safe initialization using an exclusive application-scoped lock
if (!structKeyExists(application, "_taffyHeaderUtils")) {
lock name="taffyHeaderUtilsInit" scope="application" type="exclusive" timeout="5" {
// Double-check inside the lock to prevent race conditions
if (!structKeyExists(application, "_taffyHeaderUtils")) {
application._taffyHeaderUtils = createObject("component", "taffy.core.cfHeaderUtils").init();
}
}
}

application._taffyHeaderUtils.setStatusHeader(arguments.statusCode, arguments.statusText);
</cfscript>
</cffunction>
69 changes: 69 additions & 0 deletions core/cfHeaderUtils.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<!---
Core component class for backwards compatible HTTP status header handling for ColdFusion 2025+
where the statustext attribute has been deprecated.

This CFC provides the structured, testable implementation with dependency injection support.
For a simple global function interface, see cfHeaderHelper.cfm which wraps this component.
--->
<cfcomponent hint="Utility component for backwards compatible cfheader handling">

<!--- Cache the CF version check since it won't change during execution --->
<cfset variables.isCF2025OrLater = "" />
<cfset variables.serverInfo = "" />

<!--- Constructor to inject server information --->
<cffunction name="init" access="public" output="false" returntype="any" hint="Constructor with optional server info injection">
<cfargument name="serverInfo" type="struct" required="false" default="#structNew()#" hint="Server info for testing/injection" />

<cfscript>
if (structIsEmpty(arguments.serverInfo)) {
// Only reference server scope if no injection provided
variables.serverInfo = server;
} else {
variables.serverInfo = arguments.serverInfo;
}
</cfscript>

<cfreturn this />
</cffunction>

<cffunction name="setStatusHeader" access="public" output="false" returntype="void" hint="Sets HTTP status header with backwards compatibility for CF 2025">
<cfargument name="statusCode" type="numeric" required="true" hint="HTTP status code to set" />
<cfargument name="statusText" type="string" required="false" default="" hint="HTTP status text (optional for CF 2025+)" />

<cfscript>
if (isColdFusion2025OrLater()) {
cfheader(statuscode=arguments.statusCode);
} else {
if (len(arguments.statusText)) {
cfheader(statuscode=arguments.statusCode, statustext=arguments.statusText);
} else {
cfheader(statuscode=arguments.statusCode);
}
}
</cfscript>
</cffunction>

<cffunction name="isColdFusion2025OrLater" access="public" output="false" returntype="boolean" hint="Detects if running ColdFusion 2025 or later">
<cfscript>
// Cache the result since CF version won't change during execution
if (variables.isCF2025OrLater == "") {
var cfVersion = 0;
if (structKeyExists(variables.serverInfo, "coldfusion") &&
structKeyExists(variables.serverInfo.coldfusion, "productname") &&
variables.serverInfo.coldfusion.productname contains "ColdFusion") {
// Extract the first number from the productversion string, regardless of format
var versionMatch = reMatch("\d+", variables.serverInfo.coldfusion.productversion);
if (arrayLen(versionMatch) > 0) {
cfVersion = val(versionMatch[1]);
} else {
cfVersion = 0;
}
}
variables.isCF2025OrLater = (cfVersion >= 2025);
}
return variables.isCF2025OrLater;
</cfscript>
</cffunction>

</cfcomponent>
3 changes: 2 additions & 1 deletion dashboard/asset.cfm
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
</cfcase>

<cfdefaultcase>
<cfheader statuscode="404" statustext="Not Found" />
<cfinclude template="../core/cfHeaderHelper.cfm" />
<cfset setTaffyStatusHeader(404, "Not Found") />
<cfcontent reset="true" /><cfabort />
</cfdefaultcase>

Expand Down
Loading