util-soc-imp-socket.lua /size: 4905 b    last modification: 2020-07-01 14:35
1-- original file : socket.lua
2-- for more into : see util-soc.lua
3
4local type, tostring, setmetatable = type, tostring, setmetatable
5local min = math.min
6local format = string.format
7
8local socket      = socket or package.loaded.socket or require("socket.core")
9
10local connect     = socket.connect
11local tcp4        = socket.tcp4
12local tcp6        = socket.tcp6
13local getaddrinfo = socket.dns.getaddrinfo
14
15local defaulthost = "0.0.0.0"
16
17local function report(fmt,first,...)
18    if logs then
19        report = logs and logs.reporter("socket")
20        report(fmt,first,...)
21    elseif fmt then
22        fmt = "socket: " .. fmt
23        if first then
24            print(format(fmt,first,...))
25        else
26            print(fmt)
27        end
28    end
29end
30
31socket.report = report
32
33function socket.connect4(address, port, laddress, lport)
34    return connect(address, port, laddress, lport, "inet")
35end
36
37function socket.connect6(address, port, laddress, lport)
38    return connect(address, port, laddress, lport, "inet6")
39end
40
41function socket.bind(host, port, backlog)
42    if host == "*" or host == "" then
43        host = defaulthost
44    end
45    local addrinfo, err = getaddrinfo(host)
46    if not addrinfo then
47        return nil, err
48    end
49    for i=1,#addrinfo do
50        local alt = addrinfo[i]
51        local sock, err = (alt.family == "inet" and tcp4 or tcp6)()
52        if not sock then
53            return nil, err or "unknown error"
54        end
55        sock:setoption("reuseaddr", true)
56        local res, err = sock:bind(alt.addr, port)
57        if res then
58            res, err = sock:listen(backlog)
59            if res then
60                return sock
61            else
62                sock:close()
63            end
64        else
65            sock:close()
66        end
67    end
68    return nil, "invalid address"
69end
70
71socket.try = socket.newtry()
72
73function socket.choose(list)
74    return function(name, opt1, opt2)
75        if type(name) ~= "string" then
76            name, opt1, opt2 = "default", name, opt1
77        end
78        local f = list[name or "nil"]
79        if f then
80            return f(opt1, opt2)
81        else
82            report("error: unknown key '%s'",tostring(name))
83        end
84    end
85end
86
87local sourcet    = { }
88local sinkt      = { }
89
90socket.sourcet   = sourcet
91socket.sinkt     = sinkt
92
93socket.BLOCKSIZE = 2048
94
95sinkt["close-when-done"] = function(sock)
96    return setmetatable (
97        {
98            getfd = function() return sock:getfd() end,
99            dirty = function() return sock:dirty() end,
100        },
101        {
102            __call = function(self, chunk, err)
103                if chunk then
104                    return sock:send(chunk)
105                else
106                    sock:close()
107                    return 1 -- why 1
108                end
109            end
110        }
111    )
112end
113
114sinkt["keep-open"] = function(sock)
115    return setmetatable (
116        {
117            getfd = function() return sock:getfd() end,
118            dirty = function() return sock:dirty() end,
119        }, {
120            __call = function(self, chunk, err)
121                if chunk then
122                    return sock:send(chunk)
123                else
124                    return 1 -- why 1
125                end
126            end
127        }
128    )
129end
130
131sinkt["default"] = sinkt["keep-open"]
132
133socket.sink = socket.choose(sinkt)
134
135sourcet["by-length"] = function(sock, length)
136    local blocksize = socket.BLOCKSIZE
137    return setmetatable (
138        {
139            getfd = function() return sock:getfd() end,
140            dirty = function() return sock:dirty() end,
141        },
142        {
143            __call = function()
144                if length <= 0 then
145                    return nil
146                end
147                local chunk, err = sock:receive(min(blocksize,length))
148                if err then
149                    return nil, err
150                end
151                length = length - #chunk
152                return chunk
153            end
154        }
155    )
156end
157
158sourcet["until-closed"] = function(sock)
159    local blocksize = socket.BLOCKSIZE
160    local done      = false
161    return setmetatable (
162        {
163            getfd = function() return sock:getfd() end,
164            dirty = function() return sock:dirty() end,
165        }, {
166            __call = function()
167                if done then
168                    return nil
169                end
170                local chunk, status, partial = sock:receive(blocksize)
171                if not status then
172                    return chunk
173                elseif status == "closed" then
174                    sock:close()
175                    done = true
176                    return partial
177                else
178                    return nil, status
179                end
180            end
181        }
182    )
183end
184
185sourcet["default"] = sourcet["until-closed"]
186
187socket.source = socket.choose(sourcet)
188
189_G.socket = socket -- for now global
190
191package.loaded["socket"] = socket
192
193return socket
194