--- LuaSocket (and LuaSec) based connector. -- -- This connector works with the blocking LuaSocket sockets. This connector uses -- `LuaSec` for TLS connections. This is the connector used for the included -- `mqtt.ioloop` scheduler. -- -- When using TLS / MQTTS connections, the `secure` option passed to the `client` -- when creating it, can be the standard table of options as used by LuaSec -- for creating a context. When omitted the defaults will be; -- `{ mode="client", protocol="any", verify="none", -- options={ "all", "no_sslv2", "no_sslv3", "no_tlsv1" } }` -- -- Caveats: -- -- * since the client creates a long lived connection for reading, it returns -- upon receiving a packet, to call an event handler. The handler must return -- swiftly, since while the handler runs the socket will not be reading. -- Any task that might take longer than a few milliseconds should be off -- loaded to another task. -- -- @module mqtt.connector.luasocket local super = require "mqtt.connector.base.buffered_base" local luasocket = setmetatable({}, super) luasocket.__index = luasocket luasocket.super = super local socket = require("socket") local validate_luasec = require("mqtt.connector.base.luasec") -- table with error messages that indicate a read timeout luasocket.timeout_errors = { timeout = true, -- luasocket wantread = true, -- luasec wantwrite = true, -- luasec } -- validate connection options function luasocket:validate() if self.secure then validate_luasec(self) end end -- Open network connection to .host and .port in conn table -- Store opened socket to conn table -- Returns true on success, or false and error text on failure function luasocket:connect() self:validate() local ssl if self.secure then ssl = require(self.ssl_module) end self:buffer_clear() -- sanity local sock = socket.tcp() sock:settimeout(self.timeout) local ok, err = sock:connect(self.host, self.port) if not ok then return false, "socket.connect failed to connect to '"..tostring(self.host)..":"..tostring(self.port).."': "..err end if self.secure_params then -- Wrap socket in TLS one do local wrapped wrapped, err = ssl.wrap(sock, self.secure_params) if not wrapped then sock:close(self) -- close TCP level return false, "ssl.wrap() failed: "..tostring(err) end -- replace sock with wrapped secure socket sock = wrapped end -- do TLS/SSL initialization/handshake sock:settimeout(self.timeout) -- sanity; again since its now a luasec socket ok, err = sock:dohandshake() if not ok then sock:close() return false, "ssl dohandshake failed: "..tostring(err) end end self.sock = sock return true end -- Shutdown network connection function luasocket:shutdown() self.sock:close() end -- Send data to network connection function luasocket:send(data) local sock = self.sock local i = 0 local err sock:settimeout(self.timeout) while i < #data do i, err = sock:send(data, i + 1) if not i then return false, err end end return true end -- Receive given amount of data from network connection function luasocket:plain_receive(size) local sock = self.sock sock:settimeout(0) local data, err, partial = sock:receive(size) data = data or partial or "" if #data > 0 then return data end -- convert error to signal if required if self.timeout_errors[err or -1] then return false, self.signal_idle elseif err == "closed" then return false, self.signal_closed else return false, err end end -- export module table return luasocket -- vim: ts=4 sts=4 sw=4 noet ft=lua