scansion/objects/client.lua

Thu, 23 Mar 2023 15:15:01 +0000

author
Matthew Wild <mwild1@gmail.com>
date
Thu, 23 Mar 2023 15:15:01 +0000
changeset 176
7674fb1dcc41
parent 175
e48074386468
permissions
-rw-r--r--

client: Fix harmless error logged when calling 'done' too many times

local async = require "scansion.async";
local new_error = require "scansion.error".new_error;
local verse = require "verse".init("client");

local parse_xml = require "scansion.xml".parse;

local default_stanza_timeout = 3;

local stanzacmp = require "scansion.stanzacmp";

local helpers = require "scansion.helpers";

return {
	_validate = function (client)
		assert(client.jid, "No JID specified");
		client.stream = verse.new(verse.new_logger(client.name));
		client.stream.connect_host = client.connect_host
		client.stream.connect_port = client.connect_port
	
		function client.log(fmt, ...)
			return client.stream:info(fmt, ...);
		end

		-- This one prints all received data
		client.stream:hook("incoming-raw", function (s) client.log("Data in:  %s", s); end, 1000);
		client.stream:hook("outgoing-raw", function (s) client.log("Data out: %s", s); end, 1000);
		-- And incoming, parsed, stanzas
		client.stream:hook("stanza", function (s) client.log("Stanza: %s", s) end);
		-- Handle unexpected disconnects
		client.stream:hook("disconnected", function (s)
			if not (client.disconnect_expected or client.script.finished) or (s.reason and s.reason ~= "stream closed" and s.reason ~= "closed") then
				client.log("Unexpected disconnect!");
				error("Unexpected disconnect"..(s.reason and " ("..tostring(s.reason)..")" or ""));
			end
		end);
	end;

	_finish = function (client)
		if client.stream.connected then
			client.disconnect_expected = true;
			client.stream:close();
		end
	end;

	connects = function (client)
		local wait, done = async.waiter();
		client.stream:hook("ready", function ()
			client.stream.conn:pause()
			client.log"ready"
			done()
			client.log("ready done")
		end);
		client.stream:connect_client(client.jid, client.password);
		wait();
		client.full_jid = client.stream.jid;
		client.host = client.stream.host;
	end;

	sends = function (client, data)
		local stanza =  helpers.fill_vars(client.script, assert(parse_xml((table.concat(data):gsub("\t", "  ")))));
		local wait, done = async.waiter();
		local function handle_drained()
			client.stream:unhook("drained", handle_drained);
			done();
		end
		client.stream:hook("drained", handle_drained);
		client.stream:send(stanza);
		wait();
	end;

	receives = function (client, data)
		local wait, done = async.waiter();
		local expected_stanza = false;
		local have_received_stanza = false;
		data = table.concat(data):gsub("\t", "    "):gsub("^%s+", ""):gsub("%s+$", "");
		if data ~= "nothing" then
			expected_stanza = helpers.fill_vars(client.script, assert(parse_xml(data)));
		end
		local function stanza_handler(received_stanza)
			have_received_stanza = true;
			if not expected_stanza then
				error(new_error("unexpected-stanza", {
					text = "Received unexpected stanza";
					stanza = tostring(received_stanza);
				}));
			elseif not expected_stanza or not stanzacmp.stanzas_match(expected_stanza, received_stanza, client.script.captures) then
				if not expected_stanza then
					client.log("Received a stanza when none were expected: %s", received_stanza);
				else
					client.log("Expected: %s", expected_stanza);
					client.log("Received: %s", received_stanza);
				end
				error(new_error("unexpected-stanza", {
					text = "Received unexpected stanza";
					stanza = tostring(received_stanza);
					expected = expected_stanza and tostring(expected_stanza) or nil;
				}));
			else
				client.last_received_id = received_stanza.attr.id;
				client.log("YES! %s", expected_stanza)
			end
			expected_stanza = nil;
			client.stream:unhook("stanza", stanza_handler);
			client.stream.conn:pause();
			client.log("Calling done")
			done();
			return true;
		end
		client.stream:hook("stanza", stanza_handler, 100);
		local finished_waiting;
		verse.add_task(client.stanza_timeout or default_stanza_timeout, function ()
			if not finished_waiting then
				done();
			end
		end);
		client.stream.conn:resume();
		wait();
		finished_waiting = true;

		if not have_received_stanza then
			if expected_stanza then
				client.log("TIMEOUT waiting for %s", expected_stanza)
				local e = new_error("stanza-timeout", { text = "Timed out waiting for stanza" });
				error(e);
			end
			if expected_stanza == false then
				client.log("Good - no stanzas were received (expected)");
				done();
			end
		end
	end;

	disconnects = function (client)
		client.disconnect_expected = true;
		client.stream:close();
	end;
}

mercurial