Using WebSocket to broadcast messages

The following procedures let you create a broadcast setup:

Step

Procedure

Description

1

Define communication channels in the Application.cfc

Client can subscribe or publish to a pre-defined set of channels. To listen to the channel, first register it at the application level.

2

Create WebSocket object using the cfwebsocket tag in your CFM template

Creates WebSocket connection with all required properties automatically, and wraps it in a JavaScript object that can invoke various JavaScript methods. The tag internally identifies the IP address and port of the server.

3

Implement the business logic using the JavaScript functions provided by the WebSocket object

On runtime, you can make use of the supported JavaScript functions for example, subscribe or publish to achieve your business goals.

Defining a communication channel

Specify the following settings in the Application.cfc to define the communication channel:

{
this.name="testapp";
this.wschannels=[{name = channelName,cfclistener= channel_listener_CFC}];
}
Description
  • Specify the channel name.

Note:

Though you can use any number of sub-channels, you do not specify them. They are dynamically created.

  • If necessary, specify the ColdFusion channel listener CFC, the component responsible for creating the channel. If not specified, the default ChannelListener.cfc, available in wwwroot\CFIDE\websocket directory is called.For further details, see Using channel listener functions below.

Creating WebSocket object using cfwebsocket

The new ColdFusion tag {{cfwebsocket }}lets you create the WebSocket object in your CFM template. The tag creates a reference to the WebSocket JavaScript object at the client-side.

Syntax
name="websocketName"
onMessage="JavaScript function name"
onOpen="JavaScript function name"
onClose="JavaScript function name"
onError="JavaScript function name"
useCfAuth=true|false
subscribeTo="channel_list">

Attribute

Attribute

Req/Opt

Descriptions

name

Required

The name of the WebSocket object. This is the reference to the JavaScript objects that are used to call WebSocket JavaScript functions.

onMessage

Required

The JavaScript function that is called when the WebSocket receives a message from the server. The function requires one argument.

onOpen

Optional

The JavaScript function that is called when the WebSocket establishes a connection.The function does not require any arguments.

onClose

Optional

The JavaScript function that is called when the WebSocket closes a connection.The function does not require any arguments.

onError

Optional

The JavaScript function that is called if there is an error while performing an action over the WebSocket connection. The function requires one argument.

usecfAuth

Optional

If set to true (default), authentication is not required for WebSocket connection (provided they have already logged in to the application). This is the default value.If false, to authenticate, you have to use the javaScript function authenticate.

subscribeTo

Optional

Comma-separated list of channels/subchannels to subscribe to. You can specify any or all channels set in the Application.cfc.

Usage

By specifying the attribute name, for example, mycfwebsocketobject (as in the following example), you create a JavaScript object. The JavaScript object can be used to call various JavaScript functions which you use for the WebSocket communication.

Example

In the following example,

  • The user is automatically subscribed to the channel stocks.
  • You have specified the channel name in the Application.cfc.
  • Uses the default channel listener (as you have not specified a custom channel listener).
  • In the Index.cfm, you specify the channels to which the user can automatically subscribe to (using the attribute subscribeTo) and also define the message handler.
    Application.cfc

{
this.name="websocketsampleapp1";
this.wschannels=[{name="stocks"}];
}

Index.cfm

<script type="text/javascript"> 
/* mymessangehandler function recieves all messages that comes via WebSocket. This function requires one argument. 
*/ 

function mymessagehandler(messageObject) 
{ 
//JavaScript messageobject is converted to a string. 
var message = ColdFusion.JSON.encode(messageObject); 
var txt=document.getElementById("myDiv"); 
txt.innerHTML +=message +"<br>"; 
} 
</script> 
<cfwebsocket name="mycfwebsocketobject" onmessage="mymessagehandler" subscribeto="stocks" > 
<cfdiv id="myDiv"></cfdiv>

The code creates a JavaScript WebSocket object named mycfwebsocketobject. If the server sends a message, it calls mymessagehandler function.Since you have specified subscribeTo in the cfwebsocket tag, the WebSocket object automatically subscribes you to the channel stocks.

Using the WebSocket JavaScript functions

After you create a JavaScript WebSocket object using the cfwebsocket tag, you can use it to call the following JavaScript functions:

Function

Description

Syntax

Parameters

authenticate

Authenticates the user to a specific channel. Returns true if the connection is open.

authenticate(idpassword)
For example,

 

mycfwebsocketobject.authenticate("Ben", "password")

  • id: The user-ID against which a user is authenticated.
  • password

subscribe

Lets the client subscribe to a specific channel. You can send additional information such as age of the user (in the form of custom_header JavaScript object) to the server. This information can be used to take a decision on whether to let the client subscribe or not. Returns true if the connection is open.

subscribe(channel[, custom_header][, messageHandler])
For example,

 

mycfwebsocketobject.subscribe(channel)

  • channel: Name of the channel/sub-channel to which the client wants to subscribe.
  • custom_header: Optional. Passed as key-value
    pairs. You can also use ColdFusion keyword {{selector }}and
    specify a condition that can be used as a filter criteria, for example, mycfwebsocketobject.subscribe(channelname,{age : 25,selector:"symbol eq 'adbe' and value gt 20"}).
  • messageHandler: Optional. Specifies the channel-specific message handler. If a message handler is specified, data sent from the server (to the channel) is passed to the specific message handler rather than to tag's onMessage attribute (that you specified while creating the WebSocket object). For more details, see Using channel-specific message handler below.

publish

Publishes the message to the channel. If the message is not available use the function invokeAndPublish.Message can be passed in any JavaScript data type (such as array, string, int, or a key-value pair) as a JavaScript object.You can send additional information (in the form of custom_header). The information can be used to decide if to allow the client to publish, for example Only platinum members have rights to publish.  Returns true if the connection is open.

 

For example,

 

publish(channel, message [, custom_header])

mycfwebsocketobject.publish("stocks",msg)

  • channel: Name of the channel/sub-channel to which the message has to be published.
  • message: The message object that has to be published. Message is published to clients subscribed to the specific channel.
  • custom_header: Optional. Passed as key-value
    pairs. You can also use ColdFusion keyword {{selector }}and
    specify a condition that can be used as a filter criteria, for example, mycfwebsocketobject.publish("mychannel", message, {item:phone, selector:"itemcode eq HTC HD"});.

invokeAndPublish

Invokes a specified method in a CFC file to generate the message that has to be published. Used in scnearios where you have raw data (that needs to be processed) to create a message.  Returns true if the connection is open.

 

For example,

 

invokeAndPublish(channel, cfcName, functionName [, argumentsArray] [, custom_header])

mycfwebsocketobject.invokeAndPublish(ch, "employee", "getEmployeeDetails", ["eid_1"])

  • channel: The name of the channel/sub-channel to which a message has to be published.
  • cfcName: The CFC that contains the method that is invoked for the message. Users can pass the cfcName as fully qualified path or path relative to the application root.
  • functionName: The name of the function in CFC that has to be invoked for the message.
  • argumentsArray: A JavaScript array of arguments that has to be passed to invoke the function on the given CFC.
  • custom_header: Optional. Passed as key-value pairs. You can also use ColdFusion keyword {{selector }}and specify a condition that can be used as a filter criteria.

getSubscriberCount

Provides the number of subscribers for a specific channel.  Returns true if the connection is open.

 

For example,

 

getSubscriberCount(channel)

mycfwebsocketobject.getSubscriberCount("stocks")

  • {{channel: }}Name of the channel/sub-channel whose subscriber count is sought.

    Supports inline call back functionality to receive asynchronous response.

     

getSubscriptions

Provides all channels the client is subscribed to, as a JavaScript array.  Returns true if the connection is open.

getSubscription()

 

 

Supports inline call back functionality to receive asynchronous response.

openConnection

Opens a WebSocket connection (if not already open). Returns undefined on successful completion.

 

 

openConnection()

 

isConnectionOpen

Verifies whether the WebSocket connection is open. Returns true if the connection is open.

 

 

isConnectionOpen()

 

closeConnection

Closes a WebSocket connection. Returns undefined on successful completion. The boolean value represents the status of the call only for openConnection and closeConnection.

 

 

closeConnection()

 

unsubscribe

Unsubscribes a client from the specified channel. After the client unsubscribes, the messages in the channel are not published to the client.  Returns true if the connection is open.

 

For example,

 

unsubscribe(channel)

mycfwebsocketobject.unsubscribe(channel)

  • {{channel: }}Name of the channel from which to unsubscribe.

When you use these functions, a boolean value indicating the status of the call or connection is returned. But the result of the call is acknowledged at the message handler side. You can categorize the responses at the message handler using the request type token as shown in the following example: if (token.reqType =="getSubscriberCount).

Example

The following example illustrates how to use various JavaScript functions.Application.cfc

component 
{ 
this.name="websocketsampleapp3"; 
this.wschannels=[{name="stocks"}]; 
}

Index.cfm

<script> 
//messagehandler recieves all the messages from websocket 
function mycbHandler( messageobj) 
{ 
var message = ColdFusion.JSON.encode(messageobj); 
var txt=document.getElementById("myDiv"); 
txt.innerHTML +=message +"<br>"; 
} 

//openhandler is invoked when socket connection is 
function openHandler() 
{ 
var txt=document.getElementById("myDiv"); 
txt.innerHTML +="open Handler invoked <br>"; 
} 

function subscribeMe() 
{ 
var channelname = document.getElementById("channelname").value; 
mywsobj.subscribe(channelname); 
} 
function getSubscribers() 
{ 
var channelname = document.getElementById("channelname").value; 
mywsobj.getSubscriberCount(channelname); 
} 
function unsubscribe_Me() 
{ 
var channelname = document.getElementById("channelname").value; 
mywsobj.unsubscribe(channelname); 
} 
function publishClient() 
{ 
var channelname = document.getElementById("channelname").value; 
var message = document.getElementById("msg").value; 
mywsobj.publish(channelname,message); 
} 
function get_Subscriptions() 
{ 
mywsobj.getSubscriptions(); 
} 
function invokenpublish() 
{ 
cfcname = document.getElementById("cfcname").value; 
fnname = document.getElementById("fnname").value; 
channelname = document.getElementById("channelname").value; 
mywsobj.invokeAndPublish(channelname, cfcname, fnname); 
} 
function invokefn() 
{ 
cfcname = document.getElementById("cfcname").value; 
fnname = document.getElementById("fnname").value; 
channelname = document.getElementById("channelname").value; 
mywsobj.invoke(cfcname, fnname); 
} 
function opensocket() 
{ 
var txt=document.getElementById("myDiv"); 
txt.innerHTML+="opening socket"+"<br >"; 
x=mywsobj.openConnection(); 
} 
function stopsocket() 
{ 
var txt=document.getElementById("myDiv"); 
txt.innerHTML+="closing socket"+"<br >"; 
x=mywsobj.closeConnection(); 
} 
function checksocket() 
{ 
var x=mywsobj.isConnectionOpen(); 
var txt=document.getElementById("myDiv"); 
txt.innerHTML+=x+"<br >"; 
} 
</script> 
<form name="f"> 
<!---Define JS websocket object name and messagehandler and openhandler ---> 
<cfwebsocket name="mywsobj" onMessage="mycbHandler" onOpen="openHandler"/> 
<br> Subscribe to: 
<input id="channelname" name="channelname" type="text" value="stocks" > 
<input id="stocksSubscribe" type="button" value="stocksSubscribe" onclick="subscribeMe();"> 
<input id="unsubscribeMe" type="button" value="unsubscribeMe" onclick="unsubscribe_Me();"> 
<input id="getSubscribersCF" type="button" value="getSubscribersCF" onclick="getSubscribers();"> 
<input id="getSubscriptions" type="button" value="getSubscriptions" onclick="get_Subscriptions();"> 
<br> 
Message :<input id="msg" type="text" > 
<input id="publishMe" type="button" value="publishMe" onclick="publishClient();"> 
<br> 
CFC Name: <input id="cfcname" name="cfcname" type="text" value="invokeandpublish" > 
Function Name: <input id="fnname" name="fnname" type="text" value="publishall" > 
<input id="invoke_publish" type="button" value="invoke_publish" onclick="invokenpublish();"> 
<input id="invoke" type="button" value="invoke" onclick="invokefn();"> 
<br> 
<input id="stop" name ="Close" type="button" value ="stop" onclick="stopsocket()" > 
<input id="open" name ="Open" type="button" value ="open" onclick="opensocket()" > 
<input id="check" name ="Check" type="button" value="check" onclick="checksocket()" > 
<br> 
<div id="myDiv"></div> 
</form>

invokeandpublish.cfc

component 


public function publishall() 

return "All Clients"; 

}

Scenarios: Subscribing and publishing

If the users subscribe to a parent channel, automatically they subscribe to all subchannels. If users subscribe to a child channel, they are not subscribed to its parent channel.

The following table elaborates the conditions.

The table shows four clients Client 1, Client 2, Client 3, and Client 4 subscribed to the following channels: Stocks, Stocks.Finance, Stocks.Banks, and Stocks.Finance.Adobe.

Client

Channel subscribed to

Receives messages when publishing to Stocks

Receives messages when publishing to Stocks.Finance

Receives messages when publishing to Stocks.Banks

Receives messages when publishing to Stocks. Finance.Adobe

Client 1

Stocks

Yes

Yes

Yes

Yes

Client 2

Stocks.Finance

No

Yes

No

Yes

Client 3

Stocks.Banks

No

No

Yes

No

Client 4

Stocks.Finance.Adobe

No

No

No

Yes

Scenarios: Getting subscriber list

The following table explains the conditions on which you receive the subscriber list when you use the function getSubscribers.

Assume that there are four clients. Each client is subscribed to only one of the following channels (and no client is subscribed to the same channel): Stocks, Stocks.Finance, Stocks.Banks, and Stocks.Finance.Adobe.

Function getSubscribers called with the channel

Returns the ID of clients subscribed to the following channels

Stocks

Stocks

Stocks.Finance

Stocks.Finance and Stocks

Stocks.Banks

Stocks.Banks and Stocks

Stocks.Finance.Adobe

Stocks.Finance.Adobe, Stocks.Finnance, and Stocks

Using channel listener functions

Channel listener is a set of functions that decide the folow of messages to a channel. When a WebSocket object's JavaScript function is called, in turn functions in the channel listener are called. For example, when you call the subscribe function, it calls the allowSubscribe function from the listener CFC.

A channel listener can be:
  • Default channel listener: Used if no channel listener is specified while defining the channel in the Application.cfc.
  • User-defined channel listener: Used if a channel listener CFC is specified while creating a channel. Update the methods in the channel listener file to implement a specific business logic. Specify a custom channel listener to restrict subscription to a channel or right to publish. You can also use custom channel listener to format messages and decide if the client is eligible to receive message.
Setting up user-defined channel listener
  • (You must) Extend the default listener present in webroot/cfide/websocket/ChannelListener as shown in the following code:component extends="CFIDE.websocket.ChannelListener"
  • A user-defined channel listener CFC must be in the same directory or subdirectory of the application.
  • You can only override the functions specified in the default listener CFC to implement the business logic. All custom functions are ignored.
How the listener functions receive/provide information

The functions in the listener receive or provide information in the following three structs. You can use these three structs to add custom information.

  • clientInfo: Information about the client. The following are the values:
    • channelName
    • clientId: A unique client ID.It can be used for setting additional properties, such as roles.
    • connectionTime: Connection time as date object.
  • subscriberInfo: A struct that contains custom options passed in the subscribe request. Also contains the object ConnectionInfo as subkey connectionInfo.
  • publisherInfo: A struct that contains custom options passed in the publish request. Also contains the object ConnectionInfo as subkey connectionInfo.

The following table lists the WebSocket JavaScript functions and the corresponding function in the channel listener CFC:

WebSocket object JavaScript function

Function(s) called in the channel listener CFC

subscribe

allowSubscribe

unsubscribe

afterUnsubscribe

publish

allowPublish

invokeAndPublish

allowPublish

getSubscribers

None

getSubscriptions

None

invoke

None

openConnection

None

isConnectionOpen

None

closeConnection

None

Channel listener functions

The following table lists the functions in the listener file:

Function

Descriptions

Syntax

Parameters

allowSubscribe

Invoked before subscribing to a channel/sub-channel. Used to check if the requested client can be allowed to subscribe to the given channel. If returns true, allows the requested client to subscribe.Properties defined in the object subscriberInfo can be used decide on authorization.

 

 

allowSubscribe(subscriberInfo)

  • subscriberInfo : Subscriber information struct. Contains information that is passed by the function subscribe in the custom_header struct. It has a connectionInfo object which can be accessed through ConnectionInfo key.

allowPublish

Called before publishing to a channel/sub-channel. Used to check whether the requested client can be allowed to publish to the given channel. Properties defined in the publisherInfo struct can be used to take the authorization decision. If returns true, allows the requested client to publish.

 

 

allowPublish(publisherInfo)

  • publisherInfo : Publisher information struct. Contains the information that is passed by the function publish in the custom_headerstruct. It has a connectionInfo object which can be accessed through ConnectionInfo key.

beforePublish

Invoked before publishing the message on requested channel/sub-channel. Used to execute a business logic if required and to format messages.

beforePublish(message, publisherInfo)

  • message : The message that has to be published to the client. This can be of type Any.
  • publisherInfo : Publisher information struct.

canSendMessage

Invoked before sending the message to a subscribed client.Used to decide whether the message should be sent to a specific client. Called for all clients subscribed to a channel separately. Properties defined in the object subscriberInfo and publisherInfo help you find client's interest in the message.

canSendMessage(message, subscriberInfo, publisherInfo)

  • message : The message that has to be published to the client. This can be of type Any.
  • subscriberInfo : Subscriber information struct.
  • publisherInfo : Publisher information struct.

beforeSendMessage

Invoked before sending the message to a subscribed client. Can be used to format the message as per client requirement before sending. This function is executed for each client.

beforeSendMessage(message, subscriberInfo)

  • message: The message that has to be published to the client. This can be of type Any.
  • subscriberInfo : Subscriber information struct.

afterUnsubscribe

Called after you unsubscribe from a channel/sub-channel. Used to clear the settings if necessary..

afterUnsubscribe (subscriberInfo)

  • subscriberInfo : Subscriber information struct.

Using channel-specific message handler

When you create a WebSocket object, you can define a message handler to manage responses from the server. The handler can manage messages from all the channels. Additionally, while subscribing to a channel, you can define channel-specific message handler.

Example

Application.cfccomponent

{ 
this.name = "channelspecifichandlerexample"; 
this.wschannels = [{name="stocks"}, {name="news"}, {name="products"}]; 
}

index.cfm

<script type="text/javascript"> 
function stockhandler(stocksmessageobj){ 
//write appropriate logic here to handle data coming on stock channel 
if (stocksmessageobj.data != null) { 
var message = stocksmessageobj.data; 
var datadiv = document.getElementById("myDiv"); 
datadiv.innerHTML += "Stock Handler: " + message + "<br />"; 
} 
} 
function newshandler(newsmessageobj){ 
//write appropriate logic here to handle data coming on news channel 
if (newsmessageobj.data != null) { 
var message = newsmessageobj.data; 
var datadiv = document.getElementById("myDiv"); 
datadiv.innerHTML += "News Handler: " + message + "<br />"; 
} 
} 
function productshandler(productsmessageobj){ 
//write appropriate logic here to handle data coming on products channel 
if (productsmessageobj.data != null) { 
var message = productsmessageobj.data; 
var datadiv = document.getElementById("myDiv"); 
datadiv.innerHTML += "Product Handler: " + message + "<br />"; 
} 
} 
function subscribeMe(){ 
var channel = document.getElementById("channeloption").value; 
switch (channel) { 
case "stocks": 
mysock.subscribe("stocks", {}, stockhandler); 
break; 
case "news": 
mysock.subscribe("news", {}, newshandler); 
break; 
case "products": 
mysock.subscribe("products", {}, productshandler); 
break; 
} 
} 
function mycbHandler(messageobj){ 
var message = ColdFusion.JSON.encode(messageobj); 
var datadiv = document.getElementById("myDiv"); 
datadiv.innerHTML += "Message Handler : " + message + "<br />"; 
} 
</script> 
<cfwebsocket name="mysock" onmessage="mycbHandler"/> 
<form> 
<select id="channeloption"> 
<option> 
stocks 
</option> 
<option> 
news 
</option> 
<option> 
products 
</option> 
</select> 
<input id="subscribe" name="subscribe" value="Subscribe" type="button" 
onclick="subscribeMe();"> 
</form> 
<div id="myDiv"> 
</div>

PublishMessage.cfm

<cfscript> 
if(isdefined("form.publish")) 
WsPublish(#form.channel#, #form.message#); 
</cfscript> 
<cfform method="post"> 
<cfselect name="channel"> 
<option> 
stocks 
</option> 
<option> 
news 
</option> 
<option> 
products 
</option> 
</cfselect> 
Message: 
<input id="message" name="message" type="text"> 
<cfinput id="publish" name="publish" value="publish" type="submit"> 
</cfform>

Specifying custom information

The following JavaScript functions subscribe, publish, invokeandpublish and the serverside function WSPublish let you pass custom information (for example age or status) in the form of key-value pairs.

You can use these custom data in the subscriberInfo in the channel listener.

Example 1: Custom information using subscribe

In the following example,

  1. In the subscribe function, specify the age of the client.
  2. In the channel listener, specify the age restriction in the allowSubscribe method.

Application.cfc

component 
{ 
this.name = "customoptionexample"; 
this.wschannels = [{name="Testchannel", cfclistener="TestListener"}]; 
}

Index.cfm

<script type = "text/javascript"> 
function msgHandler(messageobj) 
{ 
var ouputdiv=document.getElementById("myDiv"); 
var message = ColdFusion.JSON.encode(messageobj); 
ouputdiv.innerHTML+=message +"<br >" +"<br>"; 
} 
function subscribeMe() 
{ 
var clientAge = document.getElementById("age").value; 
TestSocket.subscribe("Testchannel", {age: clientAge}); }</script> 
<cfwebsocket name="TestSocket" onMessage="msgHandler"/> 
<br> 
Age <input id="age" name="age" type="text"> 
<br> 
<input id="stocksSubscribe" type="button" value="Subscribe" onclick="subscribeMe();"> 
<div id="myDiv"></div>

TestListener.cfc

component extends="CFIDE.websocket.ChannelListener" 
{ 

public boolean function allowSubscribe(Struct subscriberInfo) 
{ 
if(structKeyExists(subscriberInfo, "age")) 
if((subscriberInfo.age gt 18)) { 
return true; 
} 
else 
{ 
return false; 
} 
else 
return false; 
} 

}
Example 2: Custom information using publish

The following example illustrates how you communicate bid value data.

  1. In the function publish, specify the bid value as custom information.
  2. In the channel listener, specify a condition to validate the bid by comparing it with the current bid value. The message is broadcasted only if the bid is valid.

Application.cfc

component 
{ 
this.name = "customoptionexample1"; 
this.wschannels = [{name="bidchannel", cfclistener="bidListener"}]; 

boolean function onApplicationStart() 
{ 
application.bidvalue = 1; 
} 

}

Index.cfm

<script type="text/javascript"> 
function msgHandler(messageobj){ 
if (messageobj.data != null) { 
var ouputdiv = document.getElementById("myDiv"); 
var message = messageobj.data; 
ouputdiv.innerHTML += message + "<br >" + "<br>"; 
} 
if (messageobj.code == -1 && messageobj.reqType == "publish") { 
var ouputdiv = document.getElementById("myDiv"); 
var message = "Bid amount is less than current bid value"; 
ouputdiv.innerHTML += message + "<br >" + "<br>"; 
} 
} 
function publishme(){ 
var bidvalue = document.getElementById("amount").value; 
var clname = document.getElementById("name").value; 
var message = "Bid placed by " + clname + " Amount " + bidvalue; 
TestSocket.publish("bidchannel", message, { 
value: bidvalue 
}); 
} 
</script> 
<cfwebsocket name="TestSocket" onmessage="msgHandler" subscribeto="bidchannel"/><br> 
Bid Amount: 
<input id="amount" name="amount" type="text"> 
Name: 
<input id="name" type="text"> 
<input id="publishmessage" type="button" value="Publish" onclick="publishme();"> 
<div id="myDiv"> 
</div>

bidListener.cfc

component extends="CFIDE.websocket.ChannelListener"{ public boolean function 
allowPublish(Struct publisherInfo) 
{ 
if(structKeyExists(publisherInfo, "value")) 
if((publisherInfo.value gt application.bidvalue)) 
{ 
application.bidvalue = publisherInfo.value; 
return true; 
} 
else 
{ return false; 
} 
else 
return false; 
} 
}

Using selectors

Selectors provide filtering logic when you subscribe to a channel or publish a message.

While subscribing to a channel, a selector is provided as part of the subscriber information struct. For example, a client is subscribed to a channel with a selector product eq abc. Only the messages that contain the publisher information product:abc are published to the subscriber.

Similarly, you can include a selector as part of the publisher information struct while publishing a message. The messages are published to only the subscribers who satisfy the criteria specified in the selector.

For a custom channel listener CFC, you cannot have the canSendMessage method (for the selector to work).

The following example shows how to subscribe to a channel with a selector:

  1. Create a channel selectorchannel in the Application.cfc.

    {
    this.name = "websocketApp1";
    this.wschannels = [{name="selectorchannel"}];
    }
  2. Create a CFM that allows the user to subscribe to the channel, with the selector condition value greater than the specified value, for example you want to see only the stock values above a particular threshold value.

    <script type="text/javascript"> 
    function msgHandler(messageobj) 
    { 
    if (messageobj.data != null) { 
    var ouputdiv = document.getElementById("myDiv"); 
    var message = messageobj.data; 
    ouputdiv.innerHTML += message + "<br >" + "<br>"; 
    } 
    } 
    function subscribeMe() 
    { 
    var amt = document.getElementById("amount").value; 
    var selectstring="value gt "+ amt; 
    TestSocket.subscribe("selectorchannel", {selector : selectstring}); 
    document.getElementById("stocksSubscribe").style.display = 'none'; 
    document.getElementById("l1").style.display = 'none'; 
    document.getElementById("l2").style.display = 'block'; 
    document.getElementById("publisme").style.display = 'block'; 
    } 
    function publishme() 
    { 
    var amt = document.getElementById("amount").value; 
    TestSocket.publish("selectorchannel","Value is " +amt,{value: amt}) 
    } 
    </script> 
    <cfwebsocket name="TestSocket" onMessage="msgHandler"/> 
    <br /> 
    <lablel id="l1">Stock value above which you want to recieve message</lablel> 
    <label id="l2" style="display:none;">Value</label> 
    <input id="amount" name="amount" type="text"> 
    <br> 
    <input id="stocksSubscribe" type="button" value="Subscribe" onclick="subscribeMe();"> 
    <input id="publisme" type="button" value="Publish" onclick="publishme();" style="display:none;"> 
    <div id="myDiv"></div>

Specifying WebSocket authentication details

You can specify the authentication details for WebSocket at the application level. An application method OnWSAuthenticate has been added to support application level authentication.

  1. In the Application.cfc, define the function onWSAuthenticate.

  2. Call the JavaScript function authenticate. This function calls the onWSAuthenticate function.

Note:

You cannot restrict authentication to a specific channel. If you do not want to authorize the client to any channels, it can be done using the allowSubscribe function of channel listener.

onWSAuthenticate

Description

Authenticates the user

Syntax

OnWSAuthenticate(username, password, connectionInfo)

Parameters

Parameter

Description

username

Name of the user that has to be authenticated.

password

Password for the user.

connectionInfo

A struct that contains the following keys:

  • Authenticated: YES|NO
  • ConnectionTime: Connection time stamp.
  • clientID: Unique ID of the client.

Custom keys are also supported. For example, you can specify the user's role, status, or age.The connectionInfo is shared across all the channels for a given WebSocket client. Also, modifications are persisted across all the subscriptions for that client.|

Example

The following example uses the function onWSAuthenticate, validates the user, and associates a user role.

Note:

 For this example to work, ensure that you implement the user-defined functions.

{
this.name="websocketsampleapp23";
this.wschannels=[{name="stocks",cfclistener="stocksListener"}];

function onWSAuthenticate(username, password, connectionInfo)
{
//write appropriate logic to fetch user password in funtion checkPassword

If(checkPassword(username) eq password)
{
connectionInfo.authenticated="YES";
//Role is the custom information that you provide
connectionInfo.role= "admin";

return true;
}
else{
connectionInfo.authenticated="NO";
return false;
}
writedump("#connectionInfo#","console");

}

}

Enabling Single sign-on in WebSocket

If already authenticated to the ColdFusion server, you need not enter the credentials while subscribing to a WebSocket channel. You need only specify useCFAuth = "true" in the tag cfwebsocket.

In the following example, a login page is created using the tag cflogin. After authentication, the client is subscribed to a channel. That is, the client does not pass the login credentials while subscribing. Here, the function onWSAuthenticate need not be set up or called using the JavaScript function authenticate.

Example
  1. Create a login page using the tag cflogin.

    User: <input name="username" type="text" id="username" value="admin"><br>
    Pass: <input name="password" type="text" id="password"><br>
    <input type="submit" name="Submit" value="Submit">
    </form>
    <cfif structKeyExists(form,"username")>
    <cflogout>
    <cfset r = "user">
    <cfif FORM.username is "admin">
    <cfset r = "admin">
    </cfif>
    <cflogin idletimeout="1800">
    <cfloginuser
    name = "#FORM.username#"
    password ="#FORM.password#"
    roles = #r#>
    </cflogin>
    <cfoutput>Authorized user: #getAuthUser()#</cfoutput><br>
    <cfoutput>Authorized role: #GetUserRoles()#</cfoutput><br>
    <cflocation url="index.cfm">
    </cfif>
  2. Create the Application.cfc with a channel shares, and listener channelListener.

    {
    this.name = "myssoexample";
    this.wschannels = [ {name = "sso", cfclistener="ChannelListener"}];
    }
  3. Create a CFM that allows the user to subscribe to a channel and publish the message.

    var mycbHandler = function(msg)
    {
    if(msg.data)
    {
    var messageObject = msg.data;
    var txt=document.getElementById("myDiv");
    if((messageObject.indexOf("{")!=-1)){
    var jsonValue = (new Function( "return( " + messageObject + " );" ))();
    if(jsonValue){
    txt.innerHTML+="<br>Authenticated : " + jsonValue.authenticated;
    txt.innerHTML+="<br>UserName : " + jsonValue.username;
    txt.innerHTML+="<br>Role : " + jsonValue.roles;
    }
    }
    }
    }
    var openHandler = function()
    {
    mysock.publish("sso","hii");
    }
    </script>
    <form name="f">
    <cfwebsocket name="mysock" onMessage="mycbHandler" subscribeto="sso" onOpen="openHandler" useCFAuth="yes"/>
    <div id="myDiv"></div>
    </form>
  4. Create a ColdFusion listener file that returns the subscriber information while publishing.

    {
    public any function beforeSendMessage(any message, Struct subscriberInfo)
    {
    writedump(var=subscriberInfo.connectionInfo, output="console");
    return subscriberInfo.connectionInfo;
    }
    
    }

Server- side APIs

WebSocket server-side APIs allow a CFC to communicate with a channel or a specific client. For example, when a message is received from the client, the server sends a specific message.

The following table details the server-side functions:

Function Name

Syntax

Description

WSgetSubscribers

WSgetSubscribers (channel)

Returns an array of struct with clientID and subcriberInfo as the keys.

WSPublish

WSPublish(channel, message [, filterCriteria])

Sends messages to a specific channel. Optionally, you can specify a filter criteria struct.

WSGetAllChannels

WSGetAllChannels ()

Provides all the channels defined in the Application.cfc as an array.

Interpreting the Server response

The following is a sample response that you receive from the server:

{"clientid":2077108630,"ns":"coldfusion.websocket.channels","reqType":"subscribe","code":0,"type":"response","msg":"ok"}

The table explains the response keys:

Key

Description

clientid

Unique ID assigned to a client.

ns

ColdFusion WebSocket namespace

reqType

The type of request represented by the JavaScript functions (for example, publish, or invokeandpublish).

code

See <code section>

type

Either response or data.

  • Response: Conveys if the request is successful.
  • Data: The data that the channel recieves.

msg

Applies to responses; ok if successful. On failure, returns the cause of failure.

subscriberCount

An integer that stands for the subscriber count.

channels

Applies to getSubscriptions, list of all subscribed channels.

data

Message is conveyed through data.

publisherID

Client ID of the publisher. If the message is from WSPublish, the ID is 0.

channelname

Name of the channel.

The following is a sample subscribeto response sent to the message handler:

{"clientid":2077108664,"ns":"coldfusion.websocket.channels","channelsnotsubscribedto":"stocks.a","reqType":"subscribeTo","code":0,"type":"response","msg":"Subscription failed for channel(s) 'stocks.a'.","channelssubscribedto":"stocks"}

In this case, you have the key channelssubscribedto and channelsnotsubscribedto which applies to scenarios related to the attribute subsscribeTo in the tag cfwebsocket.

Get help faster and easier

New user?