Ephemeral web chat in 50 lines of D

Posted 2023-12-04

I slapped together a very quick web chat thing to demo how the EventSourceServer can work in cgi.d. The source and link will be below.

Core D Development Statistics

In the community

Community announcements

See more at the announce forum.

Web chat in D

Here's a self-contained little chat program. It has no database; if you have the connection open, you see new messages. If you don't, they're just disappeared. Closing the tab means the history is gone.

Here it is live to play (but odds are nobody else will be looking at it when you play with it, but you could open it in two tabs and talk to yourself):

http://arsdnet.net/cgi-bin/chat/main

And here's the code:

import arsd.cgi;
import arsd.dom;

class ChatApi : WebObject {
        Element main() {
                auto element = Element.make("div");
                auto div = element.addChild("div");
                div.attrs.id = "chat";

                element.addChild("script", q{
                        var ess = new EventSource("event-source");
                        ess.addEventListener("message", function(msg) {
                                var item = document.createElement("p");
                                item.textContent = msg.data;
                                document.getElementById("chat").appendChild(item);
                        });
                });

                element.appendHtml(`
                        <iframe name="submitter" id="submitter" style="visibility:hidden"></iframe>
                        <form action="send-message" method="POST" target="submitter">
                                <input type="text" name="message" />
                                <button>Send</button>
                        </form>
                `);

                return element;
        }

        @(Cgi.RequestMethod.POST)
        Redirection sendMessage(string message) {
                EventSourceServer.sendEvent("/random/chat/thing", "message", message, 0);
                return Redirection("main");
        }

        void eventSource(Cgi cgi) {
                EventSourceServer.adoptConnection(cgi, "/random/chat/thing");
        }
}

mixin DispatcherMain!(
        "/".serveApi!ChatApi
);

Notice a few things about the code: a few lines of embedded javascript and html are all it needs to make the display. The javascript connects to the eventSource function, which delegates it to the built-in EventSourceServer.

When you post a message, it submits to an iframe, and the POST handler in there again just forwards the message to the EventSourceServer, which then distributes it to waiting listeners.

Not much to it, but this same facility is easy to use for other things like games, file listeners triggering browser refreshes, signaling updates on social media sites, etc. And it isn't too hard to use, as you can see.

This has been around since 2019, but I'm only recently declaring its api stablized. I still want to change the implementation - it right now only works on linux servers - but at least it has proven itself useful.