-
Notifications
You must be signed in to change notification settings - Fork 285
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of https://github.com/lucee/lucee-docs
- Loading branch information
Showing
6 changed files
with
270 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,250 @@ | ||
<!-- | ||
{ | ||
"title": "WebSocket Extension", | ||
"id": "extension-websocket", | ||
"categories": [ | ||
"websocket" | ||
], | ||
"description": "How to install, congigure and create WebSockets", | ||
"keywords": [ | ||
"Lucee", | ||
"Extension" | ||
] | ||
} | ||
--> | ||
# WebSocket Extension | ||
|
||
This extension adds a WebSocket Server to your Lucee Server that runs over `TCP` on port 80 for `WS:` and 443 for `WSS:` | ||
|
||
WebSocket Listeners are created with a CFML Component - one per channel. | ||
|
||
## Installation | ||
|
||
The Extension can be installed via Lucee Administor | ||
|
||
![Lucee Admin: Extensions - Application](../_images/extension/websocket/lucee-admin-extension.png) | ||
|
||
Or downloaded the LEX file from [https://download.lucee.org/](https://download.lucee.org/) and save to ` | ||
|
||
![Lucee Download LEX File](../_images/extension/websocket/websocket-lex.png) | ||
|
||
eg Dockerfile | ||
|
||
```Dockerfile | ||
ADD https://ext.lucee.org/org.lucee.websocket.extension-1.0.0.4-BETA.lex /lucee/lucee-server/deploy/ | ||
``` | ||
|
||
Or using Environment Variables | ||
|
||
eg docker-compose.yml | ||
|
||
```yml | ||
environment: | ||
- LUCEE_EXTENSIONS=07082C66-510A-4F0B-B5E63814E2FDF7BE;version=1.0.0.4-BETA | ||
``` | ||
## Configuration | ||
By default, Lucee Server will look in `{lucee-config}/websockets/` for WebSocket Components. | ||
|
||
Lucee Server will create a config file if one does not exists at `{lucee-config}websocket.json` with the following defaults | ||
|
||
*{lucee-config}: /lucee/lucee-server/context* | ||
|
||
```json | ||
{ | ||
"directory":"{lucee-config}/websockets/", | ||
"requestTimeout":50, | ||
"idleTimeout":300 | ||
} | ||
``` | ||
|
||
The WebSocket Extension comes with a helper function `websocketInfo()` that well show the current configurations settings. More on other details later ... | ||
|
||
![websocketInfo()](../_images/extension/websocket/websocketInfo.png) | ||
<em>TODO: update with new version</em> | ||
|
||
## Component | ||
|
||
```lucee | ||
component hint="used to test websocket client" { | ||
public static function onFirstOpen(wsclients) {} | ||
function onOpen(wsclient) {} | ||
function onOpenAsync(wsclient) {} | ||
function onMessage(wsclient, message) {} | ||
function onClose(wsclient, ReasonPhrase) {} | ||
function onError(wsclient,cfcatch) {} | ||
public static function onLastClose() {} | ||
} | ||
``` | ||
|
||
### Javascript Client | ||
|
||
Given that the Component was saved as `{lucee-config}/websockets/test.cfc`, here is native Javascript to open and use a connection to your Lucee WebSocket: | ||
|
||
```javascript | ||
socket = new WebSocket("ws://127.0.0.1:80/ws/test"); | ||
socket.onopen = function(evt) { | ||
console.log(['onopen()', evt]); | ||
}; | ||
socket.onmessage = (event) => { | ||
console.log(event.data); | ||
}; | ||
socket.onerror = function(error) { | ||
console.error(error); | ||
}; | ||
socket.send("Hello, Lucee Extension!"); | ||
socketclose(); | ||
``` | ||
|
||
### Broadcast Message to all Clients | ||
A broadcast is a message send to all connected clients | ||
|
||
To be able to do this, we need to know who is connected. The first time a connection is made, `onFirstOpen(wsclients)` is fired. `wsclients` is a Java class with the following methods | ||
|
||
```java | ||
size():number // the number of clients connected | ||
broadcast(any message):boolean // send message to all clients | ||
getClients():Client[] // return array of all clients currently connected | ||
close():void // closes all clients | ||
``` | ||
|
||
SO we can save that for furture use | ||
|
||
```lucee | ||
public static function onFirstOpen(wsclients) { | ||
static.wsclients = arguments.wsclients; | ||
} | ||
``` | ||
|
||
For example | ||
```lucee | ||
function onOpen(wsclient) { | ||
static.wsclients.broadcast("There are now ##static.wsclients.size()## connections"); | ||
} | ||
``` | ||
|
||
### Send Message to one Client | ||
|
||
When a connection is instantiated, `onOpen(wsclient)` is fired. `wsclient` is a Java class with the following methods | ||
|
||
```java | ||
client.broadcast(message):void // send message to all connected clients | ||
client.send(message):void // send message to the client | ||
client.isOpen():boolean // is the client still connected? | ||
client.isClose():boolean // is the client no longer connected? | ||
client.close():void // closes the connection of the client | ||
``` | ||
|
||
To send a message using wsclient | ||
|
||
```lucee | ||
function onOpen(wsclient) { | ||
arguments.wsclient.send("You are connected to Lucee WebSocket"); | ||
} | ||
``` | ||
|
||
You can also send a message from `onOpen()` by returning a string | ||
|
||
```lucee | ||
function onOpen(wsclient) { | ||
return "Welcome to the test websocket channel"; | ||
} | ||
``` | ||
|
||
You can add your own function to the WebSocket Componet | ||
|
||
```lucee | ||
public void function sendMessage( | ||
required string jsonData | ||
) { | ||
variables.wsclient.send(jsonData); | ||
} | ||
function onOpen(wsclient) { | ||
sendMessage("Hello, Lucee WebSocket!"); | ||
} | ||
``` | ||
|
||
## Using Lucee WebSocket to PUSH data to Client | ||
With webSocets being a bidirectional communication channel, your Lucee Server no longer limited to responding to a *request*, it can now *push* data to the client. | ||
|
||
This means the user no longer has to refresh a page to see if data is updated, or have a Javascript looping function that is continuosly calling a ReST API to get lasted data. | ||
|
||
When your application has data ready for the user, have the WebSocket push the data to the cient! | ||
|
||
### Make use of Static Function | ||
|
||
Add a thread to start a background process, and have it continuously looping for as long as there are clients connected | ||
|
||
```lucee | ||
public static function onFirstOpen(wsclients) { | ||
static.wsclients = arguments.wsclients; | ||
thread name="threadDataQueue" oClients=static.wsclients { | ||
while( attributes.oClients.size() > 0 ) { | ||
data = getDataFromSomewhere(); | ||
attributes.oClients.broadcastMessage(data); | ||
sleep(1000); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Function `getDataFromSomewhere()` is respoible for obtaining the data that needs to be sent to the client. RedisQueue is an example of where data can be stored. Your Lucee application can Push data to a Redis Queue, and `getDataFromSomewhere()` can Pop one record at a time. | ||
|
||
### Using websocketInfo() to Send Message to Client | ||
|
||
`websocketInfo()` also has an array of instances - one for each client call to a WebSocket Component. So looping through the array, gives you access to the Component, and then you can call any of it'sfunction | ||
|
||
For Example ( *excuding role management functions* ) | ||
|
||
```lucee | ||
component hint="Test WebSocket" { | ||
variables.roles = []; | ||
public boolean function hasRole( | ||
required string role | ||
) { | ||
return ( variables.roles.find(arguments.role) > 0 ); | ||
} | ||
public void function sendMessage( | ||
required string jsonData | ||
) { | ||
variables.wsclient.send(jsonData); | ||
} | ||
... | ||
} | ||
``` | ||
|
||
```lucee | ||
var wsInfo = websocketInfo(false); | ||
if ( !wsInfo.instances.len() ) | ||
return; | ||
var wsInstances = wsInfo.instances; | ||
var item = getRedisData(); | ||
var stItem = deserializeJSON(item); | ||
for ( var wsI in wsInstances) { | ||
if ( GetMetadata(wsI).name == 'test' && wsI.hasRole(stItem.data.role) ) { | ||
<b>wsI.sendMessage(item);</b> | ||
} | ||
} | ||
``` | ||
[Task Event Gateway](event-gateways-overview.md) is a good candidate for this script | ||
|
||
*TODO: link to recipe page* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters