| 1 | -- Use LuaRocks if available |
| 2 | pcall(require, "luarocks.require"); |
| 3 | |
| 4 | local socket = require"socket"; |
| 5 | |
| 6 | -- Load LuaSec if available |
| 7 | pcall(require, "ssl"); |
| 8 | |
| 9 | local server = require "prosody.net.server"; |
| 10 | local events = require "prosody.util.events"; |
| 11 | local logger = require "prosody.util.logger"; |
| 12 | |
| 13 | local format = require "prosody.util.format".format; |
| 14 | |
| 15 | local verse = {}; |
| 16 | verse.server = server; |
| 17 | |
| 18 | local stream = {}; |
| 19 | stream.__index = stream; |
| 20 | verse.stream_mt = stream; |
| 21 | |
| 22 | verse.plugins = {}; |
| 23 | |
| 24 | function verse.init(...) |
| 25 | for i=1,select("#", ...) do |
| 26 | local ok, err = pcall(require, "verse."..select(i,...)); |
| 27 | if not ok then |
| 28 | error("Verse connection module not found: verse."..select(i,...)..err); |
| 29 | end |
| 30 | end |
| 31 | return verse; |
| 32 | end |
| 33 | |
| 34 | |
| 35 | local max_id = 0; |
| 36 | |
| 37 | function verse.new(logger, base) |
| 38 | local t = setmetatable(base or {}, stream); |
| 39 | max_id = max_id + 1; |
| 40 | t.id = tostring(max_id); |
| 41 | t.logger = logger or verse.new_logger("stream"..t.id); |
| 42 | t.events = events.new(); |
| 43 | t.plugins = {}; |
| 44 | t.verse = verse; |
| 45 | return t; |
| 46 | end |
| 47 | |
| 48 | verse.add_task = require "prosody.util.timer".add_task; |
| 49 | |
| 50 | verse.logger = logger.init; -- COMPAT: Deprecated |
| 51 | verse.new_logger = logger.init; |
| 52 | verse.log = verse.logger("verse"); |
| 53 | |
| 54 | function verse.set_log_handler(log_handler, levels) |
| 55 | levels = levels or { "debug", "info", "warn", "error" }; |
| 56 | logger.reset(); |
| 57 | if io.type(log_handler) == "file" then |
| 58 | local f = log_handler; |
| 59 | function log_handler(name, level, message) |
| 60 | f:write(name, "\t", level, "\t", message, "\n"); |
| 61 | end |
| 62 | end |
| 63 | if log_handler then |
| 64 | local function _log_handler(name, level, message, ...) |
| 65 | return log_handler(name, level, format(message, ...)); |
| 66 | end |
| 67 | for i, level in ipairs(levels) do |
| 68 | logger.add_level_sink(level, _log_handler); |
| 69 | end |
| 70 | end |
| 71 | end |
| 72 | |
| 73 | function verse._default_log_handler(name, level, message) |
| 74 | return io.stderr:write(name, "\t", level, "\t", message, "\n"); |
| 75 | end |
| 76 | verse.set_log_handler(verse._default_log_handler, { "error" }); |
| 77 | |
| 78 | local function error_handler(err) |
| 79 | verse.log("error", "Error: %s", err); |
| 80 | verse.log("error", "Traceback: %s", debug.traceback()); |
| 81 | end |
| 82 | |
| 83 | function verse.set_error_handler(new_error_handler) |
| 84 | error_handler = new_error_handler; |
| 85 | end |
| 86 | |
| 87 | function verse.loop() |
| 88 | return xpcall(server.loop, error_handler); |
| 89 | end |
| 90 | |
| 91 | function verse.step() |
| 92 | return xpcall(server.step, error_handler); |
| 93 | end |
| 94 | |
| 95 | function verse.quit() |
| 96 | return server.setquitting("once"); |
| 97 | end |
| 98 | |
| 99 | function verse.tls_builder(...) |
| 100 | return server.tls_builder(...); |
| 101 | end |
| 102 | |
| 103 | function stream:listen(host, port) |
| 104 | host = host or "localhost"; |
| 105 | port = port or 0; |
| 106 | local conn, err = server.addserver(host, port, verse.new_listener(self, "server"), "*a"); |
| 107 | if conn then |
| 108 | self:debug("Bound to %s:%s", host, port); |
| 109 | self.server = conn; |
| 110 | end |
| 111 | return conn, err; |
| 112 | end |
| 113 | |
| 114 | function stream:connect(connect_host, connect_port) |
| 115 | connect_host = connect_host or "localhost"; |
| 116 | connect_port = tonumber(connect_port) or 5222; |
| 117 | |
| 118 | -- Create and initiate connection |
| 119 | local conn = socket.tcp() |
| 120 | conn:settimeout(0); |
| 121 | conn:setoption("keepalive", true); |
| 122 | local success, err = conn:connect(connect_host, connect_port); |
| 123 | |
| 124 | if not success and err ~= "timeout" then |
| 125 | self:warn("connect() to %s:%d failed: %s", connect_host, connect_port, err); |
| 126 | return self:event("disconnected", { reason = err }) or false, err; |
| 127 | end |
| 128 | |
| 129 | local conn = server.wrapclient(conn, connect_host, connect_port, verse.new_listener(self), "*a"); |
| 130 | if not conn then |
| 131 | self:warn("connection initialisation failed: %s", err); |
| 132 | return self:event("disconnected", { reason = err }) or false, err; |
| 133 | end |
| 134 | self:set_conn(conn); |
| 135 | return true; |
| 136 | end |
| 137 | |
| 138 | function stream:set_conn(conn) |
| 139 | self.conn = conn; |
| 140 | self.send = function (stream, data) |
| 141 | self:event("outgoing", data); |
| 142 | data = tostring(data); |
| 143 | self:event("outgoing-raw", data); |
| 144 | return conn:write(data); |
| 145 | end; |
| 146 | end |
| 147 | |
| 148 | function stream:close(reason) |
| 149 | if not self.conn then |
| 150 | verse.log("error", "Attempt to close disconnected connection - possibly a bug"); |
| 151 | return; |
| 152 | end |
| 153 | local conn = self.conn; |
| 154 | self:event("shutdown"); |
| 155 | conn:close(); |
| 156 | end |
| 157 | |
| 158 | -- Logging functions |
| 159 | function stream:debug(...) |
| 160 | return self.logger("debug", ...); |
| 161 | end |
| 162 | |
| 163 | function stream:info(...) |
| 164 | return self.logger("info", ...); |
| 165 | end |
| 166 | |
| 167 | function stream:warn(...) |
| 168 | return self.logger("warn", ...); |
| 169 | end |
| 170 | |
| 171 | function stream:error(...) |
| 172 | return self.logger("error", ...); |
| 173 | end |
| 174 | |
| 175 | -- Event handling |
| 176 | function stream:event(name, ...) |
| 177 | self:debug("Firing event: "..tostring(name)); |
| 178 | return self.events.fire_event(name, ...); |
| 179 | end |
| 180 | |
| 181 | function stream:hook(name, ...) |
| 182 | return self.events.add_handler(name, ...); |
| 183 | end |
| 184 | |
| 185 | function stream:unhook(name, handler) |
| 186 | return self.events.remove_handler(name, handler); |
| 187 | end |
| 188 | |
| 189 | function verse.eventable(object) |
| 190 | object.events = events.new(); |
| 191 | object.hook, object.unhook = stream.hook, stream.unhook; |
| 192 | local fire_event = object.events.fire_event; |
| 193 | function object:event(name, ...) return fire_event(name, ...); end |
| 194 | return object; |
| 195 | end |
| 196 | |
| 197 | function stream:add_plugin(name) |
| 198 | if self.plugins[name] then return true; end |
| 199 | if require("verse.plugins."..name) then |
| 200 | local ok, err = verse.plugins[name](self); |
| 201 | if ok ~= false then |
| 202 | self:debug("Loaded %s plugin", name); |
| 203 | self.plugins[name] = true; |
| 204 | else |
| 205 | self:warn("Failed to load %s plugin: %s", name, err); |
| 206 | end |
| 207 | end |
| 208 | return self; |
| 209 | end |
| 210 | |
| 211 | -- Listener factory |
| 212 | function verse.new_listener(stream) |
| 213 | local conn_listener = {}; |
| 214 | |
| 215 | function conn_listener.onconnect(conn) |
| 216 | if stream.server then |
| 217 | local client = verse.new(); |
| 218 | conn:setlistener(verse.new_listener(client)); |
| 219 | client:set_conn(conn); |
| 220 | stream:event("connected", { client = client }); |
| 221 | else |
| 222 | stream.connected = true; |
| 223 | stream:event("connected"); |
| 224 | end |
| 225 | end |
| 226 | |
| 227 | function conn_listener.onincoming(conn, data) |
| 228 | stream:event("incoming-raw", data); |
| 229 | end |
| 230 | |
| 231 | function conn_listener.ondisconnect(conn, err) |
| 232 | if conn ~= stream.conn then return end |
| 233 | stream.connected = false; |
| 234 | stream:event("disconnected", { reason = err }); |
| 235 | end |
| 236 | |
| 237 | function conn_listener.ondrain(conn) |
| 238 | stream:event("drained"); |
| 239 | end |
| 240 | |
| 241 | function conn_listener.onstatus(conn, new_status) |
| 242 | stream:event("status", new_status); |
| 243 | end |
| 244 | |
| 245 | function conn_listener.onreadtimeout(conn) |
| 246 | return stream:event("read-timeout"); |
| 247 | end |
| 248 | return conn_listener; |
| 249 | end |
| 250 | |
| 251 | return verse; |