Thursday, February 3, 2011

Checking Bytes of Content-Length Header against the Content Body in CFML / ColdFusion

Sometimes you need to check to see if the POST or PUT content body is contains the same number of bytes indicated in the content-length header.  Remember that the content-length value is the number of bytes not the length of the content body string.  Depending on the character set encoding, some characters in the content body may have more than 1-byte per character.  It's ultra important to remember that when you check the content body against the value in the content-length header.  Also, you should only check the number of bytes when the request is of HTTP method PUT or POST as there is not content body for GET and DELETE requests.

The sample below tries to decipher the charset from the content-type header (if available).  Most REST / SOAP APIs require that all POST and PUT requests have content-type header in which the charset is usually a component value.  If your API doesn't require one, you may consider making that a requirement for POST and PUT operations.

Example

<cffunction name="performContentLengthChecks" access="private" returntype="false" output="false"
   hint="Performs content-length header and body checks.">

   <cfset var content = GetHttpRequestData().content />
   <cfset var headers = GetHttpRequestData().headers />
   <cfset var contentType = "" />
   <cfset var charset = "ISO-8859-1" />
 
   <cfif StructKeyExists(headers, "Content-Type")>
       <cfset contentType = headers["ContentType"] />
     
       <!--- Find a charset in example "application/xml; charset=UTF-8"--->
       <cfif ListLen(contentType, ";") GTE 2>
           <cfset charset = Trim(ListGetAt(ListGetAt(contentType, 2, ";"), 2, "=")) />
       </cfif>
   </cfif>
 
   <!--- Check that the content-length header was sent --->
   <cfif NOT StructKeyExists(headers, "Content-Length")>
       <cfthrow type="MissingContentLength" />
   <!--- Check that the number of bytes in the content-length header of the raw content equals the header value --->
   <cfelseif headers["Content-Length"] NEQ Len(content.getBytes(charset))>
       <cfthrow type="IncompleteBody" />
   </cfif>      
</cffunction>