Skip to content

Commit c898715

Browse files
committed
feat: support openresty 1.15.8 and later
1 parent 24032fb commit c898715

File tree

2 files changed

+127
-41
lines changed

2 files changed

+127
-41
lines changed

README.md

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,13 @@ This library implemented a transparent port service multiplexer, which can be us
3030

3131
Note that nginx [stream module](https://nginx.org/en/docs/stream/ngx_stream_core_module.html) and [stream-lua-nginx-module](https://github.com/openresty/stream-lua-nginx-module) is required.
3232

33-
Also a customed [patch](patches/stream-lua-readpartial.patch) from [@fcicq](https://github.com/fcicq) is needed. The origin discussion can be found [here](https://github.com/fffonion/lua-resty-sniproxy/issues/1).
33+
Tested on Openresty >= 1.13.6.1.
3434

35-
Tested on Openresty 1.13.6.1.
35+
With OpenResty 1.13.6.1, a customed [patch](patches/stream-lua-readpartial.patch) from [@fcicq](https://github.com/fcicq) is needed. The origin discussion can be found [here](https://github.com/fffonion/lua-resty-sniproxy/issues/1). And native
36+
proxying is not supported as `reqsock:peek` is missing.
37+
38+
Starting OpenResty 1.15.8.1, only native proxying is supported and no patch is needed. Lua land proxying will be
39+
possible when stream-lua-nginx-module implemented `tcpsock:receiveany`.
3640

3741
[Back to TOC](#table-of-contents)
3842

@@ -70,13 +74,37 @@ stream {
7074

7175
resolver 8.8.8.8;
7276

77+
# for OpenResty >= 1.13.6.1, native Nginx proxying
78+
lua_add_variable $multiplexer_upstream;
7379
server {
74-
listen 80;
75-
content_by_lua_block {
76-
local mul = require("resty.multiplexer")
77-
local mp = mul:new()
78-
mp:run()
79-
}
80+
error_log /var/log/nginx/multiplexer-error.log error;
81+
listen 443;
82+
83+
resolver 8.8.8.8;
84+
85+
preread_by_lua_block {
86+
local mul = require("resty.multiplexer")
87+
local mp = mul:new()
88+
mp:preread_by()
89+
}
90+
proxy_pass $multiplexer_upstream;
91+
}
92+
93+
# for OpenResty < 1.13.6.1, Lua land proxying
94+
server {
95+
error_log /var/log/nginx/multiplexer-error.log error;
96+
listen 443;
97+
98+
resolver 8.8.8.8;
99+
100+
server {
101+
listen 80;
102+
content_by_lua_block {
103+
local mul = require("resty.multiplexer")
104+
local mp = mul:new()
105+
mp:content_by()
106+
}
107+
}
80108
}
81109
}
82110
```

lib/resty/multiplexer/init.lua

Lines changed: 91 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,16 @@ _M.matcher_config = setmetatable({}, {
103103
})
104104

105105
function _M.new(self, connect_timeout, send_timeout, read_timeout)
106+
if _M.rules == nil or _M.matchers == nil then
107+
return nil, "[multiplexer] no rule is defined"
108+
end
109+
106110
local srvsock, err = tcp()
107111
if not srvsock then
108112
return nil, err
109113
end
110114
srvsock:settimeouts(connect_timeout or 10000, send_timeout or 10000, read_timeout or 3600000)
111-
115+
112116
local reqsock, err = ngx.req.socket()
113117
if not reqsock then
114118
return nil, err
@@ -138,7 +142,7 @@ local function _cleanup(self)
138142
end
139143
end
140144
end
141-
145+
142146
if reqsock ~= nil then
143147
if reqsock.shutdown then
144148
reqsock:shutdown("send")
@@ -150,10 +154,27 @@ local function _cleanup(self)
150154
end
151155
end
152156
end
153-
157+
154158
end
155159

156-
local function probe(self)
160+
local function probe(sock, is_preread)
161+
local f
162+
if is_preread then
163+
local read = 0
164+
-- peek always start from beginning
165+
f = function(sock, len)
166+
local b, err = sock:peek(len + read)
167+
if err then
168+
return b, err
169+
end
170+
b = b:sub(read+1)
171+
read = read + len
172+
return b
173+
end
174+
else
175+
f = sock.receive
176+
end
177+
157178
if _M.protocols == nil then
158179
return 0, nil, ""
159180
end
@@ -162,7 +183,7 @@ local function probe(self)
162183
for _, v in pairs(_M.protocols) do
163184
ngx.log(ngx.INFO, "[multiplexer] waiting for ", v[1] - bytes_read, " more bytes")
164185
-- read more bytes
165-
local new_buf, err, partial = self.reqsock:receive(v[1] - bytes_read)
186+
local new_buf, err, partial = f(sock, v[1] - bytes_read)
166187
if err then
167188
return 0, nil, buf .. partial
168189
end
@@ -218,45 +239,67 @@ local function _dwn(self)
218239
elseif buf == nil then
219240
break
220241
end
221-
242+
222243
_, err = rsock:send(buf)
223244
if err then
224245
break
225246
end
226247
end
227248
end
228249

229-
function _M.run(self)
230-
while true do
231-
if _M.matchers == nil then
232-
ngx.log(ngx.ERR, "[multiplexer] no rule is defined")
250+
local function _select_upstream(protocol_name)
251+
local upstream, port
252+
for _, v in pairs(_M.rules) do
253+
local is_match = false
254+
-- stop before last to elements of rules, which is server addr and port
255+
for i = 1, #v - 2, 1 do
256+
local m = _M.matchers[v[i][1]]
257+
if not m then
258+
ngx.log(ngx.WARN, "[multiplexer] try to use a matcher '", v[i][1], "', which is not loaded ")
259+
elseif m.match(protocol_name, v[i][2]) then
260+
is_match = true
261+
end
262+
end
263+
if is_match then
264+
upstream = v[#v - 1]
265+
port = v[#v]
233266
break
234267
end
235-
local code, protocol, buffer = probe(self)
268+
end
269+
return upstream, port, nil
270+
end
271+
272+
function _M.preread_by(self)
273+
local code, protocol, _ = probe(self.reqsock, true)
274+
if code ~= 0 then
275+
ngx.log(ngx.INFO, "[multiplexer] cleaning up with an exit code ", code)
276+
return
277+
end
278+
ngx.log(ngx.NOTICE, format("[multiplexer] protocol:%s exit:%d", protocol, code))
279+
280+
local upstream, port, _ = _select_upstream(protocol)
281+
if upstream == nil or port == nil then
282+
ngx.log(ngx.NOTICE, "[multiplexer] no matches found for this request")
283+
return
284+
end
285+
286+
if upstream:sub(1, 5) ~= "unix:" then
287+
upstream = upstream .. ":" .. tostring(port)
288+
end
289+
ngx.log(ngx.INFO, "[multiplexer] selecting upstream: ", upstream)
290+
ngx.var.multiplexer_upstream = upstream
291+
end
292+
293+
function _M.content_by(self)
294+
while true do
295+
local code, protocol, buffer = probe(self.reqsock)
236296
if code ~= 0 then
237297
ngx.log(ngx.INFO, "[multiplexer] cleaning up with an exit code ", code)
238298
break
239299
end
240300
ngx.log(ngx.NOTICE, format("[multiplexer] protocol:%s exit:%d", protocol, code))
241-
local upstream, port
242-
243-
for _, v in pairs(_M.rules) do
244-
local is_match = false
245-
-- stop before last to elements of rules, which is server addr and port
246-
for i = 1, #v - 2, 1 do
247-
local m = _M.matchers[v[i][1]]
248-
if not m then
249-
ngx.log(ngx.WARN, "[multiplexer] try to use a matcher '", v[i][1], "', which is not loaded ")
250-
elseif m.match(protocol, v[i][2]) then
251-
is_match = true
252-
end
253-
end
254-
if is_match then
255-
upstream = v[#v - 1]
256-
port = v[#v]
257-
break
258-
end
259-
end
301+
local upstream, port = _select_upstream(protocol)
302+
260303
if upstream == nil or port == nil then
261304
ngx.log(ngx.NOTICE, "[multiplexer] no matches found for this request")
262305
break
@@ -269,16 +312,31 @@ function _M.run(self)
269312
end
270313
-- send buffer
271314
self.srvsock:send(buffer)
272-
315+
273316
local co_upl = spawn(_upl, self)
274317
local co_dwn = spawn(_dwn, self)
275318
wait(co_upl)
276319
wait(co_dwn)
277-
320+
278321
break
279322
end
280323
_cleanup(self)
281-
324+
325+
end
326+
327+
-- backward compatibility
328+
function _M.run(self)
329+
local phase = ngx.get_phase()
330+
if phase == 'content' then
331+
ngx.log(ngx.ERR, "content_by")
332+
self:content_by()
333+
elseif phase == 'preread' then
334+
ngx.log(ngx.ERR, "preread_by")
335+
self:preread_by()
336+
else
337+
ngx.log(ngx.ERR, "multiplexer doesn't support running in ", phase)
338+
ngx.exit(ngx.ERROR)
339+
end
282340
end
283341

284342

0 commit comments

Comments
 (0)