2023-03-30 13:55:38 +00:00
|
|
|
-----------------------------------------------------------------------------
|
|
|
|
-- LuaSocket helper module
|
|
|
|
-- Author: Diego Nehab
|
|
|
|
-- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $
|
|
|
|
-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
|
|
-- Declare module and import dependencies
|
|
|
|
-----------------------------------------------------------------------------
|
|
|
|
local base = _G
|
|
|
|
local string = require("string")
|
|
|
|
local math = require("math")
|
2023-04-15 07:17:33 +00:00
|
|
|
|
|
|
|
function get_lua_version()
|
|
|
|
local major, minor = _VERSION:match("Lua (%d+)%.(%d+)")
|
|
|
|
assert(tonumber(major) == 5)
|
|
|
|
if tonumber(minor) >= 4 then
|
|
|
|
return "5-4"
|
|
|
|
end
|
|
|
|
return "5-1"
|
|
|
|
end
|
|
|
|
|
|
|
|
function get_os()
|
|
|
|
local the_os, ext, arch
|
|
|
|
if package.config:sub(1,1) == "\\" then
|
|
|
|
the_os, ext = "windows", "dll"
|
|
|
|
arch = os.getenv"PROCESSOR_ARCHITECTURE"
|
|
|
|
else
|
|
|
|
-- TODO: macos?
|
|
|
|
the_os, ext = "linux", "so"
|
|
|
|
arch = "x86_64" -- TODO: read ELF header from /proc/$PID/exe to get arch
|
|
|
|
end
|
|
|
|
|
|
|
|
if arch:find("64") ~= nil then
|
|
|
|
arch = "x64"
|
|
|
|
else
|
|
|
|
arch = "x86"
|
|
|
|
end
|
|
|
|
|
|
|
|
return the_os, ext, arch
|
|
|
|
end
|
|
|
|
|
|
|
|
function get_socket_path()
|
|
|
|
local the_os, ext, arch = get_os()
|
|
|
|
-- for some reason ./ isn't working, so use a horrible hack to get the pwd
|
|
|
|
local pwd = (io.popen and io.popen("cd"):read'*l') or "."
|
|
|
|
return pwd .. "/" .. arch .. "/socket-" .. the_os .. "-" .. get_lua_version() .. "." .. ext
|
|
|
|
end
|
2023-07-09 13:17:24 +00:00
|
|
|
local lua_version = get_lua_version()
|
2023-04-15 07:17:33 +00:00
|
|
|
local socket_path = get_socket_path()
|
|
|
|
local socket = assert(package.loadlib(socket_path, "luaopen_socket_core"))()
|
2023-07-09 13:17:24 +00:00
|
|
|
local event = event
|
2023-04-15 07:17:33 +00:00
|
|
|
-- http://lua-users.org/wiki/ModulesTutorial
|
|
|
|
local M = {}
|
|
|
|
if setfenv then
|
|
|
|
setfenv(1, M) -- for 5.1
|
|
|
|
else
|
|
|
|
_ENV = M -- for 5.2
|
|
|
|
end
|
2023-03-30 13:55:38 +00:00
|
|
|
|
2023-05-03 21:35:14 +00:00
|
|
|
M.socket = socket
|
2023-07-09 13:17:24 +00:00
|
|
|
-- Bizhawk <= 2.8 has an issue where resetting the lua doesn't close the socket
|
|
|
|
-- ...to get around this, we register an exit handler to close the socket first
|
|
|
|
if lua_version == '5-1' then
|
|
|
|
local old_udp = socket.udp
|
|
|
|
function udp(self)
|
|
|
|
s = old_udp(self)
|
|
|
|
function close_socket(self)
|
|
|
|
s:close()
|
|
|
|
end
|
|
|
|
event.onexit(close_socket)
|
|
|
|
return s
|
|
|
|
end
|
|
|
|
socket.udp = udp
|
|
|
|
end
|
2023-05-03 21:35:14 +00:00
|
|
|
|
2023-03-30 13:55:38 +00:00
|
|
|
-----------------------------------------------------------------------------
|
|
|
|
-- Exported auxiliar functions
|
|
|
|
-----------------------------------------------------------------------------
|
|
|
|
function connect(address, port, laddress, lport)
|
|
|
|
local sock, err = socket.tcp()
|
|
|
|
if not sock then return nil, err end
|
|
|
|
if laddress then
|
|
|
|
local res, err = sock:bind(laddress, lport, -1)
|
|
|
|
if not res then return nil, err end
|
|
|
|
end
|
|
|
|
local res, err = sock:connect(address, port)
|
|
|
|
if not res then return nil, err end
|
|
|
|
return sock
|
|
|
|
end
|
|
|
|
|
|
|
|
function bind(host, port, backlog)
|
|
|
|
local sock, err = socket.tcp()
|
|
|
|
if not sock then return nil, err end
|
|
|
|
sock:setoption("reuseaddr", true)
|
|
|
|
local res, err = sock:bind(host, port)
|
|
|
|
if not res then return nil, err end
|
|
|
|
res, err = sock:listen(backlog)
|
|
|
|
if not res then return nil, err end
|
|
|
|
return sock
|
|
|
|
end
|
|
|
|
|
2023-04-15 07:17:33 +00:00
|
|
|
try = socket.newtry()
|
2023-03-30 13:55:38 +00:00
|
|
|
|
|
|
|
function choose(table)
|
|
|
|
return function(name, opt1, opt2)
|
|
|
|
if base.type(name) ~= "string" then
|
|
|
|
name, opt1, opt2 = "default", name, opt1
|
|
|
|
end
|
|
|
|
local f = table[name or "nil"]
|
|
|
|
if not f then base.error("unknown key (".. base.tostring(name) ..")", 3)
|
|
|
|
else return f(opt1, opt2) end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
|
|
-- Socket sources and sinks, conforming to LTN12
|
|
|
|
-----------------------------------------------------------------------------
|
|
|
|
-- create namespaces inside LuaSocket namespace
|
|
|
|
sourcet = {}
|
|
|
|
sinkt = {}
|
|
|
|
|
|
|
|
BLOCKSIZE = 2048
|
|
|
|
|
|
|
|
sinkt["close-when-done"] = function(sock)
|
|
|
|
return base.setmetatable({
|
|
|
|
getfd = function() return sock:getfd() end,
|
|
|
|
dirty = function() return sock:dirty() end
|
|
|
|
}, {
|
|
|
|
__call = function(self, chunk, err)
|
|
|
|
if not chunk then
|
|
|
|
sock:close()
|
|
|
|
return 1
|
|
|
|
else return sock:send(chunk) end
|
|
|
|
end
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
sinkt["keep-open"] = function(sock)
|
|
|
|
return base.setmetatable({
|
|
|
|
getfd = function() return sock:getfd() end,
|
|
|
|
dirty = function() return sock:dirty() end
|
|
|
|
}, {
|
|
|
|
__call = function(self, chunk, err)
|
|
|
|
if chunk then return sock:send(chunk)
|
|
|
|
else return 1 end
|
|
|
|
end
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
sinkt["default"] = sinkt["keep-open"]
|
|
|
|
|
|
|
|
sink = choose(sinkt)
|
|
|
|
|
|
|
|
sourcet["by-length"] = function(sock, length)
|
|
|
|
return base.setmetatable({
|
|
|
|
getfd = function() return sock:getfd() end,
|
|
|
|
dirty = function() return sock:dirty() end
|
|
|
|
}, {
|
|
|
|
__call = function()
|
|
|
|
if length <= 0 then return nil end
|
|
|
|
local size = math.min(socket.BLOCKSIZE, length)
|
|
|
|
local chunk, err = sock:receive(size)
|
|
|
|
if err then return nil, err end
|
|
|
|
length = length - string.len(chunk)
|
|
|
|
return chunk
|
|
|
|
end
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
sourcet["until-closed"] = function(sock)
|
|
|
|
local done
|
|
|
|
return base.setmetatable({
|
|
|
|
getfd = function() return sock:getfd() end,
|
|
|
|
dirty = function() return sock:dirty() end
|
|
|
|
}, {
|
|
|
|
__call = function()
|
|
|
|
if done then return nil end
|
|
|
|
local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
|
|
|
|
if not err then return chunk
|
|
|
|
elseif err == "closed" then
|
|
|
|
sock:close()
|
|
|
|
done = 1
|
|
|
|
return partial
|
|
|
|
else return nil, err end
|
|
|
|
end
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
sourcet["default"] = sourcet["until-closed"]
|
|
|
|
|
|
|
|
source = choose(sourcet)
|
2023-04-15 07:17:33 +00:00
|
|
|
|
|
|
|
return M
|