Lua - Reddit – Telegram
Lua - Reddit
30 subscribers
281 photos
31 videos
4.26K links
News and discussion for the Lua programming language.

Subreddit: https://www.reddit.com/r/lua

Powered by : @r_channels & @reddit2telegram
Download Telegram
is there a way i can make my noscript smaller?

i'm new to lua i just made a rock paper scissors game but i want to make the noscript smaller is there a way i can do that?
math.randomseed(os.time())



table = {"rock", "paper", "scissors"}



number = math.random(1, 3)



print ("Welcome to Rock Paper Scissors!\nChoose Your Move!")



move = string.lower(io.read("*l"))



if move == "rock" then

if number == 1 then

print("Tie!")

elseif number == 2 then

print ("You Lose!")

else

print ("You Win!")

end

end

if move == "paper" then

if number == 1 then

print("You Win!")

elseif number == 2 then

print ("Tie!")

else

print ("You Lose!")

end

end

if move == "scissors" then

if number == 1 then

print("You Lose!")

elseif number == 2 then

print ("You Win!")

else

print ("You Lose!")

end

end

print (table[number])

https://redd.it/1k83lvu
@r_lua
working on a laser targeting system. i am using a public lua radar that has the option to mark as a friendly(iff) or a target (lok) and i added a laser locking mode. it works except that it marks them as friendly's not target's. for stormworks

T={}F={}MT=10;MD=200;pi2=math.pi*2;p4=pi2/4;m=0;w,h=0,0;c=math.cos;r=table.remove;s=math.sin;srt=math.sqrt;tria=screen.drawTriangleF;sdc=screen.drawCircle;ot=output.setNumber;ip=input.getNumber;stc=screen.setColor;dL=screen.drawLine;function clr()for a,b in pairs(T)do b.lok=false end end;sb=output.setBool;function onTick()u,l=0,0;isP=input.getBool(2)tx=ip(23)ty=ip(24)cm=math.fmod((ip(22)+1.25)*pi2,pi2)Gx=ip(25)Gy=ip(26)rg=ip(28)alt=ip(29)ro=ip(30)*pi2;ptc=ip(31)*pi2;n=4;while n<12 do fx=ip(n)fy=ip(n+1)fz=ip(n+2)table.insert(F,{x=fx,y=fy,z=fz})n=n+3 end;if isP then t=idx(T,tx,ty)if t\~=nil then clr()sb(2,true)T[t\].lok=true else clr()sb(2,false)end end

\------------ laser mode start

select_x = input.getNumber(7)

select_y = input.getNumber(8)



if select_x \~= 0 and select_y \~= 0 then

local foundTarget = idx(T, select_x, select_y)

if foundTarget \~= nil then

clr()

T[foundTarget\].lok = true

sb(2, true)

end

end

\---------------laser mode end

;tg={tgt=input.getBool(1),d=ip(1),az=ip(2)*pi2,el=ip(3)*pi2,ttl=100,hd=0,spd=1,spdx=1,spdy=1,spdz=1,ti=m,br=0,lok=false,iff=false}h_dst=tg.d*c(tg.el)tg.tgx,tg.tgy,tg.tgz=cpT(Gx,Gy,alt,tg,ro,ptc,cm)if tg.tgt and h_dst>30 and h_dst<rg then e_t=nil;nrdst=math.huge;ni=nil;for e,f in ipairs(T)do px,py=nil,nil;dlt=(tg.ti-f.ti)/60;dist=srt((f.tgx-tg.tgx)\^2+(f.tgy-tg.tgy)\^2+(f.tgz-tg.tgz)\^2)if f.spd>50 then px=f.tgx+f.spdx*dlt;py=f.tgy+f.spdy*dlt;pz=f.tgz+f.spdz*dlt end;if px==nil or py==nil then if dist<=MD and dist<nrdst then nrdst=dist;e_t=f;ni=e end else sph=srt((tg.tgx-px)\^2+(tg.tgx-py)\^2+(tg.tgx-pz)\^2)if sph<=MD or dist<MD then e_t=f;ni=e end end end;if e_t then rz=tg.d/rg;tg.x=w/2+rz*w*2*math.cos(tg.az-p4)tg.y=h+rz*h*2*math.sin(tg.az-p4)dq=srt((tg.tgx-e_t.tgx)\^2+(tg.tgy-e_t.tgy)\^2+(tg.tgz-e_t.tgz)\^2)dlt=(tg.ti-e_t.ti)/60;spdx=(e_t.tgx-tg.tgx)/dlt;spdy=(e_t.tgy-tg.tgy)/dlt;spdz=(e_t.tgz-tg.tgz)/dlt;spd=srt(spdx\^2+spdy\^2)e_t.d=tg.d;e_t.az=tg.az;e_t.el=tg.el;e_t.x=tg.x;e_t.y=tg.y;e_t.ttl=100;e_t.iff=tg.iff;if dq>20 and math.abs(spd-e_t.spd)<200 then e_t.tgz=tg.tgz;e_t.tgy=tg.tgy;e_t.tgx=tg.tgx;e_t.spd=spd;e_t.spdx=spdx;e_t.spdy=spdy;e_t.spdz=spdz;hdg=cdH(e_t,tg)e_t.hd=hdg end;e_t.ti=tg.ti else if ni then r(T,ni)end;tg.id=#T+1;table.insert(T,tg)end end;if#T>MT then r(T,1)end;for a,tg in pairs(T)do for a,n in ipairs(F)do d=srt((tg.tgx-n.x)\^2+(tg.tgy-n.y)\^2+(tg.tgz-n.z)\^2)if d<50 then tg.iff=true end end;r_=tg.d/rg;h_dst=tg.d*c(tg.el)tg.x=w/2+r_*w*2*c(tg.az-p4)tg.y=h+r_*h*2*s(tg.az-p4)tg.ttl=tg.ttl-1;if tg.ttl<=0 then r(T,a)end;if h_dst>rg then r(T,a)end;if tg.lok then ot(7,tg.spd)ot(8,math.deg(tg.hd))ot(1,tg.tgx)ot(2,tg.tgy)ot(3,tg.tgz)ot(10,tg.az)ot(11,tg.el)end end;ot(5,#T)m=m+1 end;function onDraw()w=screen.getWidth()h=screen.getHeight()stc(0,255,0,255)for i,b in pairs(T)do dh(b,i)end end;function dh(tg,i)xd,xy=tg.x,tg.y;o=tg.hd;if tg.iff then stc(0,255,255,tg.ttl+150)else stc(0,255,0,tg.ttl+150)end;g=2;if tg.tgz>200 then g=3 elseif tg.tgz>400 then g=5 end;x1,y1=xd+g*c(o),xy+g*s(o)o=o+2*math.pi/3;x2,y2=xd+g*c(o),xy+g*s(o)o=o+2*math.pi/3;x3,y3=xd+g*c(o),xy+g*s(o)if tg.tgz<50 then if tg.lok then stc(255,150,0)screen.drawText(xd-4,xy,"[+\]")else screen.drawText(xd,xy,"+")end else if tg.lok then stc(255,150,0)tria(x1,y1,x2,y2,x3,y3)else screen.drawTriangle(x1,y1,x2,y2,x3,y3)end;dL(x1,y1,x1+4*s(tg.hd+p4),y1-4*c(tg.hd+p4))end end;function cdH(o,n)hd=cm-math.atan(n.spdy-o.spdy,n.spdx-o.spdx)-p4;return hd end;function idx(j,k,p)for e,b in pairs(j)do if k<=b.x+2 and k>=b.x-2 and p<=b.y+2 and p>=b.y-2 then return e end end;return nil end;function idM()return{{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}end;function mxM(j,q)ra=idM()for e=1,4 do for v=1,4 do sum=0;for i=1,4 do sum=sum+j[e\][i\]*q[i\][v\]end;ra[e\][v\]=sum end end;return ra end;function
mxapp(m,k,p,x)rx=m[1\][1\]*k+m[1\][2\]*p+m[1\][3\]*x+m[1\][4\]ry=m[2\][1\]*k+m[2\][2\]*p+m[2\][3\]*x+m[2\][4\]rz=m[3\][1\]*k+m[3\][2\]*p+m[3\][3\]*x+m[3\][4\]return rx,ry,rz end;function mRx(j)return{{1,0,0,0},{0,c(j),-s(j),0},{0,s(j),c(j),0},{0,0,0,1}}end;function mRy(j)return{{c(j),0,s(j),0},{0,1,0,0},{-s(j),0,c(j),0},{0,0,0,1}}end;function mRz(j)return{{c(j),-s(j),0,0},{s(j),c(j),0,0},{0,0,1,0},{0,0,0,1}}end;function mxT(k,p,x)return{{1,0,0,k},{0,1,0,p},{0,0,1,x},{0,0,0,1}}end;function cpT(y,z,A,tg,r,B,p)mxc=mxM(mxT(y,-z,A),mxM(mRz(-p),mxM(mRy(-B),mRx(-r))))Txr=tg.d*c(tg.az)*c(tg.el)tyr=tg.d*s(tg.az)*c(tg.el)tzt=tg.d*s(tg.el)Tx,Ty,Tz=mxapp(mxc,Txr,tyr,tzt)return Tx,-Ty,Tz end

https://redd.it/1k84z5n
@r_lua
I made a little rock paper scissors program inspired by a recent post

Idk why but seeing this post https://www.reddit.com/r/lua/s/eWJooyqrJZ
Just gave me an itch to remake it, anyway:

local moves = {
rock = {rock = "Draw", paper = "Loss", scissors = "Win!"},
paper = {rock = "Win!", paper = "Draw", scissors = "Loss"},
scissors = {rock = "Loss", paper = "Win!", scissors = "Draw"}
}

local function GetRandMove(randNum)
local n = 0
local randNum = math.random(1, 3)
for k, _ in pairs(moves) do
n = n + 1
if randNum == n then return k end
end
end

local function sleep(n)
local t = os.time() + n
while os.time() < t do end
end

print([[

Time to Play:

--------
rock
paper
scissors
--------
]])

while true do
print("\nYour Move: ")

local PlayerInput = io.read()
local BotMove = GetRandMove()

print(BotMove, "\n")

sleep(.2)

print(moves[PlayerInput][BotMove])

sleep(.4)

::playagain::

print("\ntype y to play again or x to quit: \n")

local playAgain = io.read()

if playAgain == "x" then
print("Goodbye!")
break
elseif playAgain == "y" then
print("\nAlright!\n")
else
goto playagain
end
end


https://redd.it/1k8gezl
@r_lua
Yo guys, I have a question about the book

Is the book still relevant? And do you think it's worth buying or should I focus on learning from the internet?

https://redd.it/1k8jpsx
@r_lua
I'm trying to make a nice TV weather app, to show the potential and validate my cross-platform framework.
https://redd.it/1k8t3z7
@r_lua
Is there ANY difference between these two Lua snippets?

Snippet 1:

local function f(x,y)
local y = y or 1 -- Note: "local" here.`
do_something(x,y)
end

Snippet 2:

local function f(x,y)
y = y or 1 -- Note: no "local" here.
do_something(x,y)
end

I know why this pattern is often used but is there any situation whatsoever where these two snippets do not behave identically? If Lua version matters, I am specifically interested in LuaJIT. Also, if it matters, let's assume x and y are simple Lua values. They are **not** objects and don't have metatables.

https://redd.it/1k93frj
@r_lua
Where can i learn luau?

I am a very early stage roblox game developer and I need to know where is the best option to learn luau. I have been on the same youtube, there are few lessons. I read the documentation - it is unclear. What do you suggest me to do? Maybe I'm writing to the wrong community, but I still need help.

https://redd.it/1k9bqjt
@r_lua
Can nested loop have different types of loops within?

for example

for initialization, min/max value, iteration --yes this is numeric for idc
do
--insert stuff for 1st loop
while (condition)
do
--insert stuff for 2nd loop
repeat
--insert stuff for 3rd loop
until (condition)
end
end

i was wondering if it's possible to do so, since in many instance, nested loops will use loops of the same type...

https://redd.it/1k9ci93
@r_lua
Lua by example?

I'm thinking of developing a Lua by example. Is this something the community would like to see?

https://redd.it/1k9qek4
@r_lua
Advice

Hi. New here. And to Lua as a whole. I am currently learning Lua to embed it into C++ and possibly use it together with the C++ inline assembler for game development. However Lua is not a walk in the park. The basics of Lua are. And I am stuck there. The basics. Actually, outside of the basic input output, data type conversions (Tostring/tonumber), io.open()/ file:read() / file:close(), os.execute() and maybe "require", i cant say i understand much else.. Trust me i tried but this isnt as easy as promised.. Lua has a very special complexity surrounding it that you only discover after starting..

What advice do you have for a freshman college student like me learning Lua. I should add this is not a part of the program I am doing. Its just a personal interest. How did you master Lua or at least know enough of it to produce a game in it either in pure Lua or Lua embedded in C.

https://redd.it/1kbpn57
@r_lua
trying to rewrite lua-resty-fastcgi but it fails to connects

I am trying to rewrite FastCGI extension which was written for Lua Resty module of NGINX by benagricola, but it keeps on failing to connect to any PHP-FastCGI server (throws fastCGI : recv header error: closed which means that FastCGI is not available) i tried adjusting the timeout but it didn't work

I am using the extension like this

set $cginoscriptname '';

location ~ ^/@FastCGI(/+)?(((a-zA-Z0-9\_\-+(/+))+)?(a-zA-Z0-9\-\_+\.a-zA-Z0-9+))? {
internal;
ifmodifiedsince off;
contentbyluablock {
local fastcgi = require "fastcgi"
local fcgi = setmetatable({}, fastcgi)

fcgi:connect("
127.0.0.1", 25680)

local ok, err = fcgi:request({
noscript
filename = ngx.var"document_root" .. ngx.var"cgi_noscript_name",
noscriptname = ngx.var["cginoscriptname"],
document
root = ngx.var"document_root",
serverport = ngx.var["balancerport"],
pathinfo = ngx.var["fastcgipathinfo"],
query
string = ngx.var"query_string",
requesturi = ngx.var["requesturi"],
documenturi = ngx.var["requesturi"],
serverprotocol = ngx.var["serverprotocol"],
requestmethod = ngx.var["requestmethod"],
geoip2datacountrycode = ngx.var["geoip2datacountrycode"],
geoip2datacountryname = ngx.var["geoip2datacountryname"],
geoip2datacityname = ngx.var["geoip2datacityname"]
}, {
cachedict = "fastcgiCache",
cache
valid = 300,
keepalive = true,
keepalivetimeout = 120000,
keepalive
poolsize = 100,
hide
headers = { "X-Powered-By", "X-Page-Speed", "X-Application-Version", "X-Varnish", "Last-Modified", "Cache-Control", "Vary", "X-CF-Powered-By" },
intercepterrors = true,
read
timeout = 60000,
cacheMethods = { "GET" },
headerchunksize = 50 1024,
body_chunk_size = 30
1024
})
if not ok then
ngx.exit(ngx.HTTPBADGATEWAY)
end
}
include /etc/nginx/fastcgiparams;
access
log on;
}

and in my PATH Resolver (off-topic, but I have to include it in my question)

local uri = ngx.var"request_uri" or "/"
if type(uri) ~= "string" then
ngx.log(ngx.ERR, "URI is not a string: ", type(uri))
uri = "/"
end
ngx.log(ngx.DEBUG, "Request URI: ", uri or "Unknown!")
ngx.log(ngx.DEBUG, "URI: ", ngx.var"uri" or "Unknown!")

local PATH = ngx.var"document_root" .. uri
local PATH = string.match(PATH, "^[^?]*")
if not
PATH or PATH == 1 then
PATH = PATH
end
local file, err = io.open(PATH, "rb")
if not file then
ngx.log(ngx.ERR, "Failed to open file: " .. err)
ngx.status = ngx.HTTP
NOTFOUND
ngx.exit(ngx.HTTP
NOTFOUND)
return
end
file:close()

ngx.var["cgi
noscriptname"] = ngx.var["uri"]
local res = ngx.location.capture("/
@FastCGI", {
-- method = ngx.HTTP
GET,
args = ngx.var"args",
})
ngx.status = res.status
for k, v in pairs(res.header) do
ngx.headerk = v
end
ngx.print(res.body)
ngx.log(ngx.DEBUG, "#1 : " .. uri)

and my extension fork

local ngx = require "ngx"
local bit = require "bit"
local binutil = require 'resty.binutil'

local M = {}
M.index = M

local FCGI = {

HEADER
LEN = 0x08,
trying to rewrite lua-resty-fastcgi but it fails to connects

I am trying to rewrite FastCGI extension which was written for Lua Resty module of NGINX by **benagricola**, but it keeps on failing to connect to any PHP-FastCGI server (throws `fastCGI : recv header error: closed` which means that FastCGI is not available) i tried adjusting the timeout but it didn't work

I am using the extension like this

set $cgi_noscript_name '';

location ~ ^/@FastCGI(/+)?((([a-zA-Z0-9\_\-]+(/+))+)?([a-zA-Z0-9\-\_]+\.[a-zA-Z0-9]+))? {
internal;
if_modified_since off;
content_by_lua_block {
local fastcgi = require "fastcgi"
local fcgi = setmetatable({}, fastcgi)

fcgi:connect("127.0.0.1", 25680)

local ok, err = fcgi:request({
noscript_filename = ngx.var["document_root"] .. ngx.var["cgi_noscript_name"],
noscript_name = ngx.var["cgi_noscript_name"],
document_root = ngx.var["document_root"],
server_port = ngx.var["balancer_port"],
path_info = ngx.var["fastcgi_path_info"],
query_string = ngx.var["query_string"],
request_uri = ngx.var["request_uri"],
document_uri = ngx.var["request_uri"],
server_protocol = ngx.var["server_protocol"],
request_method = ngx.var["request_method"],
geoip2_data_country_code = ngx.var["geoip2_data_country_code"],
geoip2_data_country_name = ngx.var["geoip2_data_country_name"],
geoip2_data_city_name = ngx.var["geoip2_data_city_name"]
}, {
cache_dict = "fastcgiCache",
cache_valid = 300,
keepalive = true,
keepalive_timeout = 120000,
keepalive_pool_size = 100,
hide_headers = { "X-Powered-By", "X-Page-Speed", "X-Application-Version", "X-Varnish", "Last-Modified", "Cache-Control", "Vary", "X-CF-Powered-By" },
intercept_errors = true,
read_timeout = 60000,
cacheMethods = { "GET" },
header_chunk_size = 50 * 1024,
body_chunk_size = 30 * 1024
})
if not ok then
ngx.exit(ngx.HTTP_BAD_GATEWAY)
end
}
include /etc/nginx/fastcgi_params;
access_log on;
}

and in my PATH Resolver (off-topic, but I have to include it in my question)

local uri = ngx.var["request_uri"] or "/"
if type(uri) ~= "string" then
ngx.log(ngx.ERR, "URI is not a string: ", type(uri))
uri = "/"
end
ngx.log(ngx.DEBUG, "Request URI: ", uri or "Unknown!")
ngx.log(ngx.DEBUG, "URI: ", ngx.var["uri"] or "Unknown!")

local ____PATH = ngx.var["document_root"] .. uri
local ___PATH = string.match(____PATH, "^[^?]*")
if not ___PATH or ___PATH == 1 then
___PATH = ____PATH
end
local file, err = io.open(___PATH, "rb")
if not file then
ngx.log(ngx.ERR, "Failed to open file: " .. err)
ngx.status = ngx.HTTP_NOT_FOUND
ngx.exit(ngx.HTTP_NOT_FOUND)
return
end
file:close()

ngx.var["cgi_noscript_name"] = ngx.var["uri"]
local res = ngx.location.capture("/@FastCGI", {
-- method = ngx.HTTP_GET,
args = ngx.var["args"],
})
ngx.status = res.status
for k, v in pairs(res.header) do
ngx.header[k] = v
end
ngx.print(res.body)
ngx.log(ngx.DEBUG, "#1 : " .. uri)

and my extension fork

local ngx = require "ngx"
local bit = require "bit"
local binutil = require 'resty.binutil'

local _M = {}
_M.__index = _M

local FCGI = {

HEADER_LEN = 0x08,
VERSION_1 = 0x01,
BEGIN_REQUEST = 0x01,
ABORT_REQUEST = 0x02,
END_REQUEST = 0x03,
PARAMS = 0x04,
STDIN = 0x05,
STDOUT = 0x06,
STDERR = 0x07,
DATA = 0x08,
GET_VALUES = 0x09,
GET_VALUES_RESULT = 0x10,
UNKNOWN_TYPE = 0x11,
MAXTYPE = 0x11,
BODY_MAX_LENGTH = 32768,
KEEP_CONN = 0x01,
NO_KEEP_CONN = 0x00,
NULL_REQUEST_ID = 0x00,
RESPONDER = 0x01,
AUTHORIZER = 0x02,
FILTER = 0x03
}

local FCGI_HEADER_FORMAT = {
{ "version", 1, FCGI.VERSION_1 },
{ "type", 1, nil },
{ "request_id", 2, 1 },
{ "content_length", 2, 0 },
{ "padding_length", 1, 0 },
{ "reserved", 1, 0 }
}

local function _pack(format, params)
local bytes = ""

for unused, field in ipairs(format) do
local fieldname = field[1]
local fieldlength = field[2]
local defaulval = field[3]

local value = params[fieldname] or defaulval
if value == nil then
ngx.log(ngx.ERR, "fastCGI : Missing value for field: " .. fieldname)
return nil
end
bytes = bytes .. binutil.ntob(value, fieldlength)
end

return bytes
end

local function _pack_header(params)
local align = 8
params.padding_length = bit.band(-(params.content_length or 0), align - 1)
return _pack(FCGI_HEADER_FORMAT, params), params.padding_length
end

local FCGI_BEGIN_REQ_FORMAT = {
{ "role", 2, FCGI.RESPONDER },
{ "flags", 1, 0 },
{ "reserved", 5, 0 }
}

local FCGI_PREPACKED = {
end_params = _pack_header({
type = FCGI.PARAMS,
}),
begin_request = _pack_header({
type = FCGI.BEGIN_REQUEST,
request_id = 1,
content_length = FCGI.HEADER_LEN,
}) .. _pack(FCGI_BEGIN_REQ_FORMAT, {
role = FCGI.RESPONDER,
flags = 1,
}),
abort_request = _pack_header({
type = FCGI.ABORT_REQUEST,
}),
empty_stdin = _pack_header({
type = FCGI.STDIN,
content_length = 0,
}),
}

local FCGI_END_REQ_FORMAT = {
{ "status", 4, nil },
{ "protocolStatus", 1, nil },
{ "reserved", 3, nil }
}


local FCGI_PADDING_BYTES = {
string.char(0),
string.char(0, 0),
string.char(0, 0, 0),
string.char(0, 0, 0, 0),
string.char(0, 0, 0, 0, 0),
string.char(0, 0, 0, 0, 0, 0),
string.char(0, 0, 0, 0, 0, 0, 0),
}

local function _pad(bytes)
if bytes == 0 then
return ""
else
return FCGI_PADDING_BYTES[bytes]
end
end

local function _unpack_hdr(format, str)
-- If we received nil, return nil
if not str then
return nil
end

local res, idx = {}, 1

-- Extract bytes based on format. Convert back to number and place in res rable
for _, field in ipairs(format) do
res[field[1]] = bton(str_sub(str, idx, idx + field[2] - 1))
idx = idx + field[2]
end

return res
end

local function _format_stdin(stdin)
local chunk_length
local to_send = {}
local stdin_chunk = { "", "", "" }
local header = ""
local padding, idx = 0, 1
local stdin_length = #stdin

-- We could potentially need to send more than one records' worth of data, so
-- loop to format.
repeat
-- While we still have stdin data, build up STDIN record in chunks
if stdin_length >
FCGI.BODY_MAX_LENGTH then
chunk_length = FCGI.BODY_MAX_LENGTH
else
chunk_length = stdin_length
end

header, padding = _pack_header({
type = FCGI.STDIN,
content_length = chunk_length,
})

stdin_chunk[1] = header
stdin_chunk[2] = string.sub(stdin, 1, chunk_length)
stdin_chunk[3] = _pad(padding)

to_send[idx] = table.concat(stdin_chunk)
stdin = string.sub(stdin, chunk_length + 1)
stdin_length = stdin_length - chunk_length
idx = idx + 1
until stdin_length == 0

return table.concat(to_send)
end


local function _send_stdin(sock, stdin)
local ok, bytes, err, chunk, partial

if type(stdin) == 'function' then
repeat
chunk, err, partial = stdin(FCGI.BODY_MAX_LENGTH)

-- If the iterator returns nil, then we have no more stdin
-- Send an empty stdin record to signify the end of the request
if chunk then
ngx.log(ngx.DEBUG, "Request body reader yielded ", #chunk, " bytes of data - sending")
ok, err = sock:send(_format_stdin(chunk))
if not ok then
ngx.log(ngx.DEBUG, "Unable to send ", #chunk, " bytes of stdin: ", err)
return nil, err
end
-- Otherwise iterator errored, return
elseif err ~= nil then
ngx.log(ngx.DEBUG, "Request body reader yielded an error: ", err)
return nil, err, partial
end
until chunk == nil
elseif stdin ~= nil then
ngx.log(ngx.DEBUG, "Sending ", #stdin, " bytes of read data")
bytes, err = sock:send(_format_stdin(stdin))

if not bytes then
return nil, err
end
elseif stdin == nil then
return
end

-- Send empty stdin record to signify end
bytes, err = sock:send(FCGI_PREPACKED.empty_stdin)

if not bytes then
return nil, err
end

return true, nil
end

local function build_header(record_type, content_len, padding_len, request_id)
return string.char(
FCGI.VERSION_1,
record_type,
bit.rshift(request_id, 8),
bit.band(request_id, 0xFF),
bit.rshift(content_len, 8),
bit.band(content_len, 0xFF),
padding_len,
0
)
end

local function encode_name_value(name, value)
local n, v = #name, #value
local parts = {}

if n < 128 then
parts[#parts + 1] = string.char(n)
else

parts[#parts + 1] = string.char(
bit.bor(bit.rshift(n, 24), 0x80),
bit.band(bit.rshift(n, 16), 0xFF),
bit.band(bit.rshift(n, 8), 0xFF),
bit.band(n, 0xFF)
)
end

if v < 128 then
parts[#parts + 1] = string.char(v)
else
parts[#parts + 1] = string.char(
bit.bor(bit.rshift(v, 24), 0x80),
bit.band(bit.rshift(v, 16), 0xFF),
bit.band(bit.rshift(v, 8), 0xFF),
bit.band(v, 0xFF)
)
end

parts[#parts + 1] = name
parts[#parts + 1] = value
return table.concat(parts)
end

function _M:connect(host, port)
self.fcgiSocket = ngx.socket.tcp()
if not self.fcgiSocket then
ngx.log(ngx.ERR, "fastCGI : failed to create TCP socket")
return nil, "fastCGI : failed to create TCP socket"
end
self.request_id = 0
self.fcgiSocket:settimeout(3000) -- tmp change
local ok, err = self.fcgiSocket:connect(host, port)
if not ok then
ngx.log(ngx.ERR, "fastCGI : connect error: " .. (err or "Unknown"))
ngx.exit(ngx.HTTP_BAD_GATEWAY)
return nil, "fastCGI : connect error: " .. (err or "Unknown")
else
self.fcgiSocket:settimeout(30000)
end
return true
end

function _M:close()
if not self.fcgiSocket then
ngx.log(ngx.ERR, "fastCGI : no socket")
return nil, "fastCGI : no socket"
end
local _, close_err = self.fcgiSocket:close()
self.fcgiSocket = nil
if close_err and close_err ~= "closed" then
ngx.log(ngx.ERR, "fastCGI : close failed: " .. (close_err or "Unknown"))
return nil, "fastCGI : close failed: " .. (close_err or "Unknown")
end
return true
end

function _M.get_reused_times()
if not self.fcgiSocket then
ngx.log(ngx.ERR, "fastCGI : no socket")
return nil, "fastCGI : no socket"
end

return self.fcgiSocket:getreusedtimes()
end

function _M.set_timeout(timeout)
if not self.fcgiSocket then
ngx.log(ngx.ERR, "fastCGI : no socket")
return nil, "fastCGI : no socket"
end

return self.fcgiSocket:settimeout(timeout)
end

function _M.set_keepalive(...)
if not self.fcgiSocket then
ngx.log(ngx.ERR, "fastCGI : no socket")
return nil, "fastCGI : no socket"
end

return self.fcgiSocket:setkeepalive(...)
end

function _M:request(params, opts, stdin)
opts = opts or {}
if not self.fcgiSocket then
ngx.log(ngx.ERR, "fastCGI : not connected")
return nil, "fastCGI : not connected"
end

self.request_id = (self.request_id % 65535) + 1
local request_id = self.request_id

local function cleanup(ok)
if not self.fcgiSocket then return end
if ok and opts.keepalive then
local ka_ok, ka_err = self.fcgiSocket:setkeepalive(
opts.keepalive_timeout or 60000,
opts.keepalive_pool_size or 10
)
if not ka_ok and ka_err ~= "closed" then
ngx.log(ngx.ERR, "fastCGI : keepalive failed: " .. (ka_err or "Unknown"))
return nil, "fastCGI : keepalive failed: " .. (ka_err or "Unknown")
end
else

local _, close_err = self.fcgiSocket:close()
self.fcgiSocket = nil
if close_err and close_err ~= "closed" then
ngx.log(ngx.ERR, "fastCGI : close failed: " .. (close_err or "Unknown"))
return nil, "fastCGI : close failed: " .. (close_err or "Unknown")
end
end
end

local ok, err = xpcall(function()
local cache = nil
local cache_key = nil
if not (opts.cache_bypass and opts.cache_bypass()) and not ngx.var["skip_cache"] then
cache = ngx.shared[opts.cache_dict or "fastcgiCache"]
cache_key = table.concat({
ngx.var.scheme,
ngx.var.host,
ngx.var.uri,
ngx.var.args or "",
params.noscript_filename
}, "|")
local cached = cache:get(cache_key)
if cached then
ngx.status = cached.status
for k, v in pairs(cached.headers) do
ngx.header[k] = v
end
ngx.say(cached.body)
return ngx.exit(ngx.HTTP_OK)
end
end

local flags = 0
if opts.keepalive then
flags = FCGI.KEEP_CONN
end
local begin_body = string.char(0, FCGI.RESPONDER, flags, 0, 0, 0, 0, 0)

local header = build_header(FCGI.BEGIN_REQUEST, #begin_body, 0, request_id)
local ok, err = self.fcgiSocket:send(header .. begin_body)
if not ok then
ngx.log(ngx.ERR, "fastCGI : failed to send begin request: " .. (err or "Unknown"))
return nil, "fastCGI : failed to send begin request: " .. (err or "Unknown")
end

local fcgi_params = {}

if params.noscript_filename then
fcgi_params["SCRIPT_FILENAME"] = params.noscript_filename

local noscript_name = params.noscript_name
local path_info = params.path_info
if not noscript_name or not path_info then

local _uri = params.request_uri or ngx.var["request_uri"] or ""

_uri = _uri:match("^[^?]+") or ""

local m, n = _uri:match("(.+%.php)(/.*)")
if m then
noscript_name = noscript_name or (m or _uri)
path_info = path_info or n
else
noscript_name = noscript_name or _uri
path_info = path_info or ""
end
end
fcgi_params["SCRIPT_NAME"] = noscript_name or ""
fcgi_params["PATH_INFO"] = path_info or ""
end

fcgi_params["REQUEST_METHOD"] = params.request_method or ngx.var["request_method"]
fcgi_params["QUERY_STRING"] = params.query_string or ngx.var["query_string"] or ""
fcgi_params["SERVER_PROTOCOL"] = params.server_protocol or ngx.var["server_protocol"]
fcgi_params["REMOTE_ADDR"] = ngx.var["remote_addr"] or ""
fcgi_params["REMOTE_PORT"] = ngx.var["remote_port"] or ""
fcgi_params["SERVER_ADDR"] = ngx.var["server_addr"] or ""
fcgi_params["SERVER_PORT"] = ngx.var["server_port"] or ""
fcgi_params["SERVER_NAME"] = ngx.var["server_name"] or ""
fcgi_params["DOCUMENT_ROOT"] = params.document_root or ngx.var["document_root"] or ""
fcgi_params["DOCUMENT_URI"] = params.document_uri or ngx.var["request_uri"] or ""
fcgi_params["COUNTRY_CODE"] = params.geoip2_data_country_code or ngx.var["geoip2_data_country_code"] or ""
fcgi_params["COUNTRY_NAME"] = params.geoip2_data_country_name or ngx.var["geoip2_data_country_name"] or ""
fcgi_params["CITY_NAME"] = params.geoip2_data_city_name or ngx.var["geoip2_data_city_name"] or ""
fcgi_params["HTTP_PROXY"] = params.http_proxy or ""

local headers = ngx.req.get_headers()
if headers["Content-Type"] then
fcgi_params["CONTENT_TYPE"] = headers["Content-Type"]
end
if ngx.var["content_length"] then
fcgi_params["CONTENT_LENGTH"] = ngx.var["content_length"]
end

if params.fastcgi_params then
for k, v in pairs(params.fastcgi_params) do
fcgi_params[k] = v
end
end

for k, v in pairs(headers) do
if type(k) == "string" and type(v) == "string" then
local hk = "HTTP_" .. k:upper():gsub("-", "_")

if hk ~= "HTTP_CONTENT_TYPE" and hk ~= "HTTP_CONTENT_LENGTH" then
fcgi_params[hk] = v
end
end
end

local all_params = {}
for k, v in pairs(fcgi_params) do
all_params[#all_params + 1] = encode_name_value(k, tostring(v))
end
local pstr = table.concat(all_params)
local pos, plen = 1, #pstr
local chunk
local clen, pad
local bytes, sendERR
while plen > 0 do
chunk = pstr:sub(pos, pos + 65535 - 1)
clen, pad = #chunk, (8 - (#chunk % 8)) % 8
bytes, sendERR = self.fcgiSocket:send(build_header(FCGI.PARAMS, clen, pad, request_id) ..
chunk .. string.rep("\0", pad))
if not bytes then
ngx.log(ngx.ERR, "fastCGI : Failed to send params: " .. (sendERR or "Unknown"))
return nil, "fastCGI : Failed to send params: " .. (sendERR or "Unknown")
end
pos = pos + clen
plen = plen - clen
end
self.fcgiSocket:send(build_header(FCGI.PARAMS, 0, 0, request_id))

self.fcgiSocket:settimeout(opts.read_timeout or 60000)
local method = fcgi_params.REQUEST_METHOD
if method == "POST" or method == "PUT" or method == "PATCH" then
ngx.req.read_body()
local body_sock = ngx.req.socket(true)
local sendOK
local chunk_
local data, recv_err, partial
if body_sock then
repeat
data, recv_err, partial = body_sock:receive(opts.body_chunk_size or 8192)
ngx.log(ngx.DEBUG, "Attempting to read end request")

if not data or partial then
ngx.log(ngx.ERR, "Unable to parse FCGI end request body : " .. (err or "Unknown"))
return nil, "Unable to parse FCGI end request body : " .. (err or "Unknown")
end
chunk_ = data or partial
if chunk_ then
pad = (8 - (#chunk_ % 8)) % 8
sendOK, sendERR = self.fcgiSocket:send(build_header(FCGI.STDIN, #chunk_, pad, request_id) ..
chunk_ .. (pad > 0 and string.rep("\0", pad) or ""))
if not sendOK then
ngx.log(ngx.ERR, "Failed to send stdin: " .. (sendERR or "Unknown"))
return nil, "Failed to send stdin: " .. (sendERR or "Unknown")
end
end
until not data or recv_err
end
end
self.fcgiSocket:send(build_header(FCGI.STDIN, 0, 0, request_id))

local stdout, stderr = "", {}
local parsed_headers = false
local read_bytes = ""
local partial = ""
local bytes_to_read, hdrByte, rcvERR
local hdr, typ, rcvClen, rcvPad
local sep, raw, rest
local hn, hv, hName
local cacheMethod
local read_data
while true do
hdrByte, rcvERR = self.fcgiSocket:receive(opts.header_chunk_size or 8)
if (rcvERR == "closed") then
rcvERR = "connection closed"
end
if not hdrByte then
ngx.log(ngx.ERR, "fastCGI : recv header error: " .. (rcvERR or "Unknown"))
return nil, "fastCGI : recv header error: " .. (rcvERR or "Unknown")
end
hdr = _unpack_hdr(FCGI.HEADER_FORMAT, hdrByte)
if not hdr then
ngx.log(ngx.ERR, "Unable to parse FCGI record header : " .. (rcvERR or "Unknown"))
return nil, "Unable to parse FCGI record header : " .. (rcvERR or "Unknown")
end
typ = hdr.type
rcvClen = hdr.content_length
rcvPad = hdr.padding_length

if hdr.version ~= FCGI.VERSION_1 then
ngx.log(ngx.ERR, "invalid protocol version: " .. hdr.version)
return nil, "invalid protocol version: " .. hdr.version
end

ngx.log(ngx.DEBUG, "New content length is " .. rcvClen .. " padding ", rcvPad)

if rcvClen > 0 then
read_bytes, rcvERR, partial = self.fcgiSocket:receive(rcvClen)
if not read_bytes or partial then
ngx.log(ngx.ERR, "fastCGI : recv content error: " .. (rcvERR or "Unknown"))