Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created March 25, 2014 11:22
Using ColdFusion With Pusher - A Notification Service Powered By HTML5 WebSockets
<!--- Param the form value. --->
<cfparam name="form.message" type="string" default="" />
<!---
Check to see if the message is available (if the user
submitted the form).
--->
<cfif len( form.message )>
<!---
Pusher information used to tie this push notification
to a given application and to a given client (listening
for a given channel).
--->
<cfset pusherAppID = "1306" />
<cfset pusherKey = "61b7e858d47e67683bd8" />
<cfset pusherSecret = "****************" />
<cfset pusherChannel = "helloWorld" />
<!---
Event we are triggering (this is what the client will
"bind" to on the given channel).
--->
<cfset pusherEvent = "hellowWorldEvent" />
<!---
Data we are actually "pushing". All data must be sent as
JSON (although it doesn't have to be simple string values).
--->
<cfset pusherMessage = serializeJSON( form.message ) />
<!--- Authentication information. --->
<cfset authVersion = "1.0" />
<cfset authMD5Body = lcase( hash( pusherMessage, "md5" ) ) />
<cfset authTimeStamp = fix( getTickCount() / 1000 ) />
<!--- Build the post resource (the RESTfule resource). --->
<cfset pusherResource = "/apps/#pusherAppID#/channels/#pusherChannel#/events" />
<!--- ------------------------------------------------- --->
<!--- ------------------------------------------------- --->
<!---
The following is the digital signing of the HTTP request.
Frankly, this stuff is pretty far above my understanding
of cryptology. I have adapted code from the PusherApp
ColdFusion component written by Bradley Lambert:
http://github.com/blambert/pusher-cfc
To be 100% honest with you, I find this step to be very
frustrating. Do we really need so much security around this?
--->
<!---
Create the list of query string values (alpha-sorted with no
URL encoding).
--->
<cfsavecontent variable="queryStringData">
<cfoutput>
auth_key=#pusherKey#
auth_timestamp=#authTimeStamp#
auth_version=#authVersion#
body_md5=#authMD5Body#
name=#pusherEvent#
</cfoutput>
</cfsavecontent>
<!---
Create the raw signature data. This is the HTTP
method, the resource, and the alpha-ordered query
string (non-URL-encoded values).
--->
<cfset signatureData = (
("POST" & chr( 10 )) &
(pusherResource & chr( 10 )) &
reReplace(
trim( queryStringData ),
"\s+",
"&",
"all"
)
) />
<!---
Create our secret key generator. This can create a secret
key from a given byte array. Initialize it with the byte
array version of our PushApp secret key and the algorithm
we want to use to generate the secret key.
--->
<cfset secretKeySpec = createObject(
"java",
"javax.crypto.spec.SecretKeySpec"
).init(
toBinary( toBase64( pusherSecret ) ),
"HmacSHA256"
)
/>
<!---
Create our MAC (Message Authentication Code) generator
to encrypt the message data using the PusherApp shared
secret key.
--->
<cfset mac = createObject( "java", "javax.crypto.Mac" )
.getInstance( "HmacSHA256" )
/>
<!--- Initialize the MAC instance using our secret key. --->
<cfset mac.init( secretKeySpec ) />
<!---
Complete the mac operation, encrypting the given secret
key (that we created above).
--->
<cfset encryptedBytes = mac.doFinal( signatureData.getBytes() ) />
<!---
Now that we have the encrypted data, we have to convert
that data to a HEX-encoded string. We will use the big
integer for this.
--->
<cfset bigInt = createObject( "java", "java.math.BigInteger" )
.init( 1, encryptedBytes )
/>
<!--- Convert the encrypted bytes to the HEX string. --->
<cfset secureSignature = bigInt.toString(16) />
<!---
Apparently, we need to make sure the signature is at least
32 characters long. As such, let's just left-pad with spaces
and then replace with zeroes.
--->
<cfset secureSignature = replace(
lJustify( secureSignature, 32 ),
" ",
"0",
"all"
) />
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!---
Now that we have all the values we want to post, including
our encrypted signature, we can post to the PusherApp REST
web service using CFHTTP.
--->
<cfhttp
result="post"
method="post"
url="http://api.pusherapp.com#pusherResource#">
<!---
Alert the post resource that the value is coming through
as JSON data.
--->
<cfhttpparam
type="header"
name="content-type"
value="application/json"
/>
<!--- Set the authorization parameters. --->
<cfhttpparam
type="url"
name="auth_version"
value="#authVersion#"
/>
<cfhttpparam
type="url"
name="auth_key"
value="#pusherKey#"
/>
<cfhttpparam
type="url"
name="auth_timestamp"
value="#authTimeStamp#"
/>
<cfhttpparam
type="url"
name="body_md5"
value="#authMD5Body#"
/>
<!--- Sent the name of the pusher event. --->
<cfhttpparam
type="url"
name="name"
value="#pusherEvent#"
/>
<!--- Send the actual message (JSON data). --->
<cfhttpparam
type="body"
value="#pusherMessage#"
/>
<!--- Digitally sign the HTTP request. --->
<cfhttpparam
type="url"
name="auth_signature"
value="#secureSignature#"
/>
</cfhttp>
</cfif>
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<cfoutput>
<!DOCTYPE HTML>
<html>
<head>
<title>PusherApp Hello World - ColdFusion Pusher</title>
</head>
<body>
<h1>
PusherApp Hello World - ColdFusion Pusher
</h1>
<form action="#cgi.script_name#" method="post">
<p>
Enter your message to push:
</p>
<p>
<input type="text" name="message" size="40" />
<input type="submit" value="Push It, Push It Real Good!" />
</p>
</form>
</body>
</html>
</cfoutput>
<!DOCTYPE HTML>
<html>
<head>
<title>PushApp Hello World With ColdFusion</title>
<script type="text/javascript" src="jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="http://js.pusherapp.com/1.4/pusher.min.js"></script>
<script type="text/javascript">
// This is for compatability with browsers that don't yet
// support Web Sockets, but DO support Flash.
//
// NOTE: This SWF can be downloaded from the PusherApp
// website. It is a Flash proxy to the standard Web
// Sockets interface.
WebSocket.__swfLocation = "./WebSocketMain.swf";
// When the DOM is ready, init the scripts. Technically, we
// can bind the PusherApp before DOM ready, but we can't
// respond to it, so no need to bind early.
$(function(){
// Get a reference to the message list.
var messages = $( "#messages" );
// Create a Pusher server object with your app's key and
// the channel you want to listen on. Channels are unique
// to your application.
var server = new Pusher(
"61b7e858d47e67683bd8",
"helloWorld"
);
// Now that we have the pusher connection for a given
// channel, we want to listen for certain events to come
// over that channel.
server.bind(
"hellowWorldEvent",
function( data ) {
// The data that comes through is the evaluated
// JSON data that was pushed to the client. In
// our case, it's just a simple string.
messages.prepend(
"<li>" + data + "</li>"
);
}
);
});
</script>
</head>
<body>
<h1>
PusherApp Hello World With ColdFusion
</h1>
<p>
This page will listen for PusherApp via HTML5 Socket connectsion
(or, if not supported, a Flash SWF fallback). Messages will be posted
below as they arrive.
</p>
<ol id="messages">
<!-- To be populated via PUSH notification. -->
</ol>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment