view mod_muc_eventsource/mod_muc_eventsource.lua @ 4537:53ee391ca689

mod_smacks: Fix traceback due to session being destroyed in send() Sending something can cause the OS to notice that the connection is dead and then the connection can be dead at this point. More likely if opportunistic_writes is enabled.
author Kim Alvefur <zash@zash.se>
date Thu, 01 Apr 2021 11:35:26 +0200
parents 39485b9bbdd6
children
line wrap: on
line source

module:depends("http");
local nodeprep = require "util.encodings".stringprep.nodeprep;

local jid_split = require "util.jid".split;
local json = require "util.json";

local streams = {};

function client_closed(response)
	local node = response._eventsource_node;
	module:log("debug", "Destroying client for %q", node);
	streams[node][response] = nil;
	if next(streams[node]) == nil then
		streams[node] = nil;
	end
end

function serve_stream(event, node)
	local response = event.response;

	node = nodeprep(node);
	if node == nil then
		return 400;
	end

	module:log("debug", "Client subscribed to: %s", node);

	response.on_destroy = client_closed;
	response._eventsource_node = node;

	response.conn:write(table.concat({
		"HTTP/1.1 200 OK";
		"Content-Type: text/event-stream";
		"Access-Control-Allow-Origin: *";
		"Access-Control-Allow-Methods: GET";
		"Access-Control-Max-Age: 7200";
		"";
		"";
	}, "\r\n"));

	local clientlist = streams[node];
	if not clientlist then
		clientlist = {};
		streams[node] = clientlist;
	end
	clientlist[response] = response.conn;

	return true;
end

function handle_message(event)
	local room, stanza = event.room, event.stanza;
	local node = (jid_split(event.room.jid));
	local clientlist = streams[node];
	if not clientlist then module:log("debug", "No clients for %q", node); return; end

	-- Extract body from message
	local body = event.stanza:get_child_text("body");
	if not body then
		return;
	end
	local nick = select(3, jid_split(stanza.attr.from));
	-- Encode body and broadcast to eventsource subscribers
	local json_data = json.encode({
		nick = nick;
		body = body;
	});
	local data = "data: "..json_data:gsub("\n", "\ndata: \n").."\n\n";
	for response, conn in pairs(clientlist) do
		conn:write(data);
	end
end

module:provides("http", {
	name = "eventsource";
	route = {
		["GET /*"] = serve_stream;
	};
});


module:hook("muc-broadcast-message", handle_message);