User:Csmith1991/Vibe.d Documentation/websocket

From D Wiki
Jump to: navigation, search

Vibe.d Websocket's Tutorial

This tutorial is extended from here. Instead of a simple counter, this provides a functioning web chat application.

WebScokets overview

Vibe API

Code

Files

   .
   ├── public/
   |   ├── index.html    
   |   └── scripts/
   |       └── websocket.js
   ├── source/
   |   └── app.d

index.html

<!DOCTYPE html>
<html>
    <head>
        <title>WebSockets Client</title>
        <style>
            #chatLog {
                height: 400px;
                padding-bottom: 20px;
                overflow-y: scroll;
            }
        </style>
    </head>
    <body>
        <h1>WebSockets Client</h1>
        <div id="chatLog"></div><!-- this is where we'll insert chat into -->

        <div id="login">
            <!-- {which: 13} is a hack to make the button function the same as onkeypress -->
            <input id="name" type="text" placeholder="Your Name" onkeypress="startConnection(event);" /> <button id="connect" onclick="startConnection({which: 13});">Connect</button>
        </div>
        <div id="chat" style="display:none;">
            <input id="text" type="text" placeholder="press enter to submit" onkeypress="chat(event);" /> <button id="send" onclick="chat({which: 13});">Send</button><br />
            <button id="disconnect" onclick="endConnection();">Disconnect</button>
        </div>
        <script src="/scripts/websocket.js"></script>
    </body>
</html>

websocket.js

// Keep socket variable at global level
var socket = {};

function connect(name) {
    try {

        // Test if websocket doesn't exist, and if not, create a new connection
        if (! ('readyState' in socket)) {
            socket = new WebSocket(getBaseURL() + '/ws');
        }

        socket.onopen = function() {
            try {
                socket.send(name); // Tell the server who you are, handle validation server side!

                // Swap the chat and login divs around
                document.getElementById('name').value = '';
                document.getElementById('login').style.display = 'none';
                document.getElementById('chat').style.display = 'block';
                document.getElementById('text').focus();
            } catch (exception) {
                alert(exception);
            }
        }
        socket.onmessage = function(msg) {
            var msgVal = JSON.parse(msg.data); // We're anticipating messages formatted as "{'name':'Csmith1991', 'text':'example'}"
            var chatLog = document.getElementById('chatLog');
            chatLog.innerHTML += '<p>' + msgVal.name + ': ' + msgVal.text + '</p>'; // Add to the chatLog
            chatLog.scrollTop = chatLog.scrollHeight; // Scroll chatLog to bottom

        }
        socket.onclose = function() {
            socket = {}; // Remove socket connection

            // Swap the chat and login divs around. Note we don't do this until the connection has closed.
            document.getElementById('chat').style.display = 'none';
            document.getElementById('login').style.display = 'block';
            document.getElementById('name').focus();
        }
    } catch (exception) {
        alert(exception);
    }
}

function startConnection(event) {
    // 13 = enter button
    if (event.which === 13 || event.keyCode === 13 ) {
        // Connect to WebSocket server
        connect(document.getElementById('name').value);
    }
}

function chat(event) {
    if (event.which === 13 || event.keyCode === 13 ) {
        var myObj = document.getElementById('text');
        socket.send(myObj.value);
        myObj.value = '';
        myObj.focus();
    }
}

function endConnection() {
    // We want to close the connection on the server, so we create a message that the server listens for to close on
    socket.send('/close');
}

function getBaseURL() {
    // Get the WebSocket server address e.g. ws://127.0.0.1:8080

    var href = window.location.href.substring(7); // strip "http://"
    var idx = href.indexOf('/');
    return 'ws://' + href.substring(0, idx);
}

app.d

module webSocketExample;

import vibe.d;
import vibe.utils.array;

private WebSocket[] sockets;

shared static this()
{
    /// Use /ws to identify websocket requests, serve files out of the public folder otherwise
    auto router = new URLRouter;
    router.get("/", staticRedirect("/index.html"));
    router.get("/ws", handleWebSockets(&handleWebSocketConnection));
    router.get("*", serveStaticFiles("public/"));

    auto settings = new HTTPServerSettings;
    settings.port = 8080;
    settings.bindAddresses = ["::1", "127.0.0.1"];
    listenHTTP(settings, router);
}

void handleWebSocketConnection(scope WebSocket socket)
{
    // Add socket to sockets list
    sockets ~= socket;

    // Get username
    socket.waitForData(1.seconds);
    string name = socket.receiveText;

    // Server-side validation of results
    if (name !is null)
    {
        logInfo("%s connected @ %s.", name, socket.request.peer);
        sendTextToOtherClients(null, "System", name ~ " connected to the chat.");
    }
    else
    {
        // Kick person out
        socket.send("{\"name\":\"System\", \"text\":\"Invalid name.\"}");
        
        socket.close;
        sockets.removeFromArray!WebSocket(socket);
        logInfo("%s disconnected.", name);
        return;
    }

    // message loop
    while (socket.waitForData)
    {
        if (!socket.connected) break;
        // we got something
        auto text = socket.receiveText;
        
        // Close if recieve "/close"
        if (text == "/close") break;

        logInfo("Received: \"%s\" from %s.", text, name);
        // Relay text to everyone else
        sendTextToOtherClients(socket, name, text);
    }

    // Remove socket from sockets list and close socket
    socket.close;
    sockets.removeFromArray!WebSocket(socket);
    logInfo("%s disconnected.", name);
    
    sendTextToOtherClients(null, "System", name ~ " disconnected to the chat.");
}

void sendTextToOtherClients(scope WebSocket src_socket, string name, string text)
{
    foreach (socket; sockets)
    {
        // Don't send it to people who won't get it.
        if (!socket.connected) continue;
        
        logInfo("Sending: \"%s\" to %s.", text, socket.request.peer);
        // JSON encoding for simplicity
        socket.send("{\"name\":\"" ~ name ~ "\", \"text\":\"" ~ text ~ "\"}");
    }
}