Skip to content

Commit 97f88e4

Browse files
committed
luaposix unix socket support
1 parent cd42b4a commit 97f88e4

File tree

8 files changed

+608
-3
lines changed

8 files changed

+608
-3
lines changed

pgmoon/init.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,9 @@ do
252252
if not (ok) then
253253
return nil, err
254254
end
255+
if self.config.ssl and self.sock_type == "luaposix" then
256+
return nil, "ssl is not supported when using luaposix sockets"
257+
end
255258
if self.sock:getreusedtimes() == 0 then
256259
if self.config.ssl then
257260
local success

pgmoon/init.moon

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ class Postgres
225225
-- password: the username to authenticate with
226226
-- database: database to connect to
227227
-- application_name: name assigned to connection to server
228-
-- socket_type: type of socket to use (nginx, luasocket, cqueues)
228+
-- socket_type: type of socket to use (nginx, luasocket, cqueues, luaposix)
229229
-- ssl: enable ssl connections
230230
-- ssl_verify: verify the certificate
231231
-- cqueues_openssl_context: manually created openssl.ssl.context for cqueues sockets
@@ -257,6 +257,9 @@ class Postgres
257257
ok, err = @sock\connect @config.host, @config.port, connect_opts
258258
return nil, err unless ok
259259

260+
if @config.ssl and @sock_type == "luaposix"
261+
return nil, "ssl is not supported when using luaposix sockets"
262+
260263
if @sock\getreusedtimes! == 0
261264
if @config.ssl
262265
success, err = @send_ssl_message!
@@ -1029,4 +1032,3 @@ class Postgres
10291032
"<Postgres socket: #{@sock}>"
10301033
10311034
{ :Postgres, new: Postgres, :VERSION }
1032-

pgmoon/posix_socket.lua

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
local flatten
2+
flatten = require("pgmoon.util").flatten
3+
local posix_socket = require("posix.sys.socket")
4+
local posix_unistd = require("posix.unistd")
5+
local posix_errno = require("posix.errno")
6+
local posix_poll = require("posix.poll")
7+
local strerror
8+
strerror = function(code)
9+
if code then
10+
return posix_errno.strerror(code)
11+
else
12+
return posix_errno.strerror(posix_errno.errno())
13+
end
14+
end
15+
local should_retry
16+
should_retry = function(code)
17+
return code == posix_errno.EINTR or code == posix_errno.EAGAIN or code == posix_errno.EWOULDBLOCK
18+
end
19+
local poll_events = {
20+
read = posix_poll.POLLIN,
21+
write = posix_poll.POLLOUT
22+
}
23+
local socket_path_for
24+
socket_path_for = function(host, port)
25+
assert(host and host ~= "", "luaposix socket requires a host")
26+
if not (host:sub(1, 1) == "/") then
27+
return host
28+
end
29+
if host:match(".s%.PGSQL%.%d+$") then
30+
return host
31+
else
32+
port = tostring(port or "5432")
33+
local prefix
34+
if host:sub(-1) == "/" then
35+
prefix = host:sub(1, -2)
36+
else
37+
prefix = host
38+
end
39+
return tostring(prefix) .. "/.s.PGSQL." .. tostring(port)
40+
end
41+
end
42+
local PosixSocket
43+
do
44+
local _class_0
45+
local _base_0 = {
46+
connect = function(self, host, port)
47+
local path = socket_path_for(host, port)
48+
if not (path:sub(1, 1) == "/") then
49+
return nil, "luaposix socket requires an absolute unix socket path"
50+
end
51+
local fd, err, code = posix_socket.socket(posix_socket.AF_UNIX, posix_socket.SOCK_STREAM, 0)
52+
if not (fd) then
53+
return nil, err or strerror(code)
54+
end
55+
local addr = {
56+
family = posix_socket.AF_UNIX,
57+
path = path
58+
}
59+
local ok, connect_err, connect_code = posix_socket.connect(fd, addr)
60+
if not (ok) then
61+
posix_unistd.close(fd)
62+
return nil, connect_err or strerror(connect_code)
63+
end
64+
self.fd = fd
65+
return true
66+
end,
67+
wait_for = function(self, what)
68+
if not (self.timeout and self.timeout >= 0) then
69+
return true
70+
end
71+
local events = poll_events[what]
72+
assert(events, "unknown wait type " .. tostring(what))
73+
local fds = {
74+
{
75+
fd = self.fd,
76+
events = events
77+
}
78+
}
79+
while true do
80+
local _continue_0 = false
81+
repeat
82+
do
83+
local ready, err, code = posix_poll.poll(fds, self.timeout)
84+
if ready == 0 then
85+
return nil, "timeout"
86+
end
87+
if not ready then
88+
if should_retry(code) then
89+
_continue_0 = true
90+
break
91+
end
92+
return nil, err or strerror(code)
93+
end
94+
return true
95+
end
96+
_continue_0 = true
97+
until true
98+
if not _continue_0 then
99+
break
100+
end
101+
end
102+
end,
103+
send = function(self, ...)
104+
if not (self.fd) then
105+
return nil, "socket is not connected"
106+
end
107+
local data = flatten(...)
108+
local total = 0
109+
local len = #data
110+
while total < len do
111+
local _continue_0 = false
112+
repeat
113+
local ok, err = self:wait_for("write")
114+
if not (ok) then
115+
return nil, err
116+
end
117+
local written, write_err, code = posix_unistd.write(self.fd, data:sub(total + 1))
118+
if not (written) then
119+
if should_retry(code) then
120+
_continue_0 = true
121+
break
122+
end
123+
return nil, write_err or strerror(code)
124+
end
125+
total = total + written
126+
_continue_0 = true
127+
until true
128+
if not _continue_0 then
129+
break
130+
end
131+
end
132+
return total
133+
end,
134+
receive = function(self, len)
135+
if not (self.fd) then
136+
return nil, "socket is not connected"
137+
end
138+
if not (type(len) == "number") then
139+
return nil, "luaposix socket only supports length-based receives"
140+
end
141+
local remaining = len
142+
local chunks = { }
143+
while remaining > 0 do
144+
local _continue_0 = false
145+
repeat
146+
local ok, err = self:wait_for("read")
147+
if not (ok) then
148+
return nil, err
149+
end
150+
local chunk, read_err, code = posix_unistd.read(self.fd, remaining)
151+
if not (chunk) then
152+
if should_retry(code) then
153+
_continue_0 = true
154+
break
155+
end
156+
return nil, read_err or strerror(code)
157+
end
158+
if #chunk == 0 then
159+
return nil, "closed"
160+
end
161+
chunks[#chunks + 1] = chunk
162+
remaining = remaining - #chunk
163+
_continue_0 = true
164+
until true
165+
if not _continue_0 then
166+
break
167+
end
168+
end
169+
return table.concat(chunks)
170+
end,
171+
close = function(self)
172+
if not (self.fd) then
173+
return true
174+
end
175+
posix_unistd.close(self.fd)
176+
self.fd = nil
177+
return true
178+
end,
179+
settimeout = function(self, t)
180+
if t == nil then
181+
self.timeout = nil
182+
return
183+
end
184+
local timeout = assert(tonumber(t), "timeout must be numeric")
185+
if timeout < 0 then
186+
self.timeout = nil
187+
else
188+
self.timeout = math.floor(timeout)
189+
end
190+
end,
191+
getreusedtimes = function(self)
192+
return 0
193+
end,
194+
setkeepalive = function(self)
195+
return error("You attempted to call setkeepalive on a luaposix socket. This method is only available for the ngx cosocket API for releasing a socket back into the connection pool")
196+
end,
197+
sslhandshake = function(self)
198+
return nil, "luaposix sockets do not support SSL handshakes"
199+
end
200+
}
201+
_base_0.__index = _base_0
202+
_class_0 = setmetatable({
203+
__init = function(self)
204+
self.fd = nil
205+
self.timeout = nil
206+
end,
207+
__base = _base_0,
208+
__name = "PosixSocket"
209+
}, {
210+
__index = _base_0,
211+
__call = function(cls, ...)
212+
local _self_0 = setmetatable({}, _base_0)
213+
cls.__init(_self_0, ...)
214+
return _self_0
215+
end
216+
})
217+
_base_0.__class = _class_0
218+
PosixSocket = _class_0
219+
end
220+
return {
221+
PosixSocket = PosixSocket,
222+
socket_path_for = socket_path_for
223+
}

0 commit comments

Comments
 (0)