import ballerina/log;
import ballerina/http;@final string NAME = "NAME";
@final string AGE = "AGE";
endpoint http:WebSocketListener ep {
    port:9090
};@http:ServiceConfig {
    basePath: "/chat"
}
service<http:Service> ChatAppUpgrader bind ep {    @http:ResourceConfig {
        webSocketUpgrade: {
            upgradePath: "/{name}",
            upgradeService: typeof chatApp
        }
    }
    upgrader(endpoint ep, http:Request req, string name) {
        endpoint http:WebSocketListener wsEp;
        map<string> headers;
        wsEp = ep -> acceptWebSocketUpgrade(headers);
        wsEp.attributes[NAME] = name;
        wsEp.attributes[AGE] = req.getQueryParams()["age"];
        string msg = "Hi " + name + "! You have succesfully connected to the chat";
        wsEp -> pushText(msg) but {error e => log:printErrorCause("Error sending message", e)};
    }}
map<http:WebSocketListener> consMap;service<http:WebSocketService> chatApp {    onOpen (endpoint conn) {
        string msg = string `{{getAttributeStr(conn, NAME)}} with age {{getAttributeStr(conn, AGE)}} connected to chat`;
        broadcast(consMap, msg);
        consMap[conn.id] = conn;
    }    onText (endpoint conn, string text) {
        string msg = string `{{getAttributeStr(conn, NAME)}}: {{text}}`;
        log:printInfo(msg);
        broadcast(consMap, msg);
    }    onClose (endpoint conn, int statusCode, string reason) {
        _ = consMap.remove(conn.id);
        string msg = string `{{getAttributeStr(conn, NAME)}} left the chat`;
        broadcast(consMap, msg);
    }
}function broadcast (map<http:WebSocketListener> consMap, string text) {
    endpoint http:WebSocketListener ep;
    foreach id, con in consMap {
        ep = con;
        ep -> pushText(text) but {error e => log:printErrorCause("Error sending message", e)};
    }
}function getAttributeStr(http:WebSocketListener ep, string key) returns (string) {
    var name = <string> ep.attributes[key];
    return name;
}

WebSocket Chat Application

This example explains how to write a simple chat application using Ballerina.

import ballerina/log;
import ballerina/http;
@final string NAME = "NAME";
@final string AGE = "AGE";
endpoint http:WebSocketListener ep {
    port:9090
};
@http:ServiceConfig {
    basePath: "/chat"
}
service<http:Service> ChatAppUpgrader bind ep {
    @http:ResourceConfig {
        webSocketUpgrade: {
            upgradePath: "/{name}",
            upgradeService: typeof chatApp
        }
    }
    upgrader(endpoint ep, http:Request req, string name) {
        endpoint http:WebSocketListener wsEp;
        map<string> headers;
        wsEp = ep -> acceptWebSocketUpgrade(headers);
        wsEp.attributes[NAME] = name;
        wsEp.attributes[AGE] = req.getQueryParams()["age"];
        string msg = "Hi " + name + "! You have succesfully connected to the chat";
        wsEp -> pushText(msg) but {error e => log:printErrorCause("Error sending message", e)};
    }
}
map<http:WebSocketListener> consMap;

TODO: This map should go to service level after null pointer issue is fixed.

service<http:WebSocketService> chatApp {
    onOpen (endpoint conn) {
        string msg = string `{{getAttributeStr(conn, NAME)}} with age {{getAttributeStr(conn, AGE)}} connected to chat`;
        broadcast(consMap, msg);
        consMap[conn.id] = conn;
    }
    onText (endpoint conn, string text) {
        string msg = string `{{getAttributeStr(conn, NAME)}}: {{text}}`;
        log:printInfo(msg);
        broadcast(consMap, msg);
    }
    onClose (endpoint conn, int statusCode, string reason) {
        _ = consMap.remove(conn.id);
        string msg = string `{{getAttributeStr(conn, NAME)}} left the chat`;
        broadcast(consMap, msg);
    }
}
function broadcast (map<http:WebSocketListener> consMap, string text) {
    endpoint http:WebSocketListener ep;
    foreach id, con in consMap {
        ep = con;
        ep -> pushText(text) but {error e => log:printErrorCause("Error sending message", e)};
    }
}
function getAttributeStr(http:WebSocketListener ep, string key) returns (string) {
    var name = <string> ep.attributes[key];
    return name;
}
$ ballerina run websocket-chat-application.bal

To run the program, put the code in websocket-basic-application.bal and use ballerina run websocket-basic-application.bal command.

$ var ws = new WebSocket("ws://localhost:9090/chat/bruce?age=30");
$ ws.onmessage = function(frame) {console.log(frame.data)};
$ ws.onclose = function(frame) {console.log(frame)};

To check the sample, use Chrome or Firefox JavaScript console and run the following commands
Run the first 3 lines of the below code in two or more consoles and see how the messages are received by sending messages To check the capability of change the names the URI such that /chat/fistName+LastName/age with multiple clients

$ ws.send("hello world");

To send messages