1
2
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
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
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
190
191package.loaded["socket"] = socket
192
193return socket
194 |