summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/util-soc-imp-socket.lua
blob: d1486f8f83898e0d42662bc5e811b23c482addf8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
-- original file : socket.lua
-- for more into : see util-soc.lua

local type, tostring, setmetatable = type, tostring, setmetatable
local min = math.min
local format = string.format

local socket      = socket or package.loaded.socket or require("socket.core")

local connect     = socket.connect
local tcp4        = socket.tcp4
local tcp6        = socket.tcp6
local getaddrinfo = socket.dns.getaddrinfo

local defaulthost = "0.0.0.0"

local function report(fmt,first,...)
    if logs then
        report = logs and logs.reporter("socket")
        report(fmt,first,...)
    elseif fmt then
        fmt = "socket: " .. fmt
        if first then
            print(format(fmt,first,...))
        else
            print(fmt)
        end
    end
end

socket.report = report

function socket.connect4(address, port, laddress, lport)
    return connect(address, port, laddress, lport, "inet")
end

function socket.connect6(address, port, laddress, lport)
    return connect(address, port, laddress, lport, "inet6")
end

function socket.bind(host, port, backlog)
    if host == "*" or host == "" then
        host = defaulthost
    end
    local addrinfo, err = getaddrinfo(host)
    if not addrinfo then
        return nil, err
    end
    for i=1,#addrinfo do
        local alt = addrinfo[i]
        local sock, err = (alt.family == "inet" and tcp4 or tcp6)()
        if not sock then
            return nil, err or "unknown error"
        end
        sock:setoption("reuseaddr", true)
        local res, err = sock:bind(alt.addr, port)
        if res then
            res, err = sock:listen(backlog)
            if res then
                return sock
            else
                sock:close()
            end
        else
            sock:close()
        end
    end
    return nil, "invalid address"
end

socket.try = socket.newtry()

function socket.choose(list)
    return function(name, opt1, opt2)
        if type(name) ~= "string" then
            name, opt1, opt2 = "default", name, opt1
        end
        local f = list[name or "nil"]
        if f then
            return f(opt1, opt2)
        else
            report("error: unknown key '%s'",tostring(name))
        end
    end
end

local sourcet    = { }
local sinkt      = { }

socket.sourcet   = sourcet
socket.sinkt     = sinkt

socket.BLOCKSIZE = 2048

sinkt["close-when-done"] = function(sock)
    return 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
                    sock:close()
                    return 1 -- why 1
                end
            end
        }
    )
end

sinkt["keep-open"] = function(sock)
    return 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 -- why 1
                end
            end
        }
    )
end

sinkt["default"] = sinkt["keep-open"]

socket.sink = socket.choose(sinkt)

sourcet["by-length"] = function(sock, length)
    local blocksize = socket.BLOCKSIZE
    return setmetatable (
        {
            getfd = function() return sock:getfd() end,
            dirty = function() return sock:dirty() end,
        },
        {
            __call = function()
                if length <= 0 then
                    return nil
                end
                local chunk, err = sock:receive(min(blocksize,length))
                if err then
                    return nil, err
                end
                length = length - #chunk
                return chunk
            end
        }
    )
end

sourcet["until-closed"] = function(sock)
    local blocksize = socket.BLOCKSIZE
    local done      = false
    return setmetatable (
        {
            getfd = function() return sock:getfd() end,
            dirty = function() return sock:dirty() end,
        }, {
            __call = function()
                if done then
                    return nil
                end
                local chunk, status, partial = sock:receive(blocksize)
                if not status then
                    return chunk
                elseif status == "closed" then
                    sock:close()
                    done = true
                    return partial
                else
                    return nil, status
                end
            end
        }
    )
end

sourcet["default"] = sourcet["until-closed"]

socket.source = socket.choose(sourcet)

_G.socket = socket -- for now global

package.loaded["socket"] = socket

return socket