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?
https://redd.it/1k83lvu
@r_lua
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" thenif number == 1 thenprint("Tie!")elseif number == 2 thenprint ("You Lose!")else print ("You Win!") endendif move == "paper" thenif number == 1 thenprint("You Win!")elseif number == 2 thenprint ("Tie!")else print ("You Lose!") endendif move == "scissors" thenif number == 1 thenprint("You Lose!")elseif number == 2 thenprint ("You Win!")else print ("You Lose!") endendprint (table[number])https://redd.it/1k83lvu
@r_lua
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
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
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
https://redd.it/1k84z5n
@r_lua
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
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:
https://redd.it/1k8gezl
@r_lua
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
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
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
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
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
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
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
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
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
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
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
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
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
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
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
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
I'm thinking of developing a Lua by example. Is this something the community would like to see?
https://redd.it/1k9qek4
@r_lua
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
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
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
Reddit
From the lua community on Reddit
Explore this post and more from the lua community
The first stage of my indie game, made with my own engine, is ready
The game https://reprobate.carimbo.run
Made with Carimbo, a home made (by me) 2D engine.
https://github.com/willtobyte/carimbo
The game is written in Lua.
Pixel Art by Aline
https://linktr.ee/dandelion.pixelart
Any feedback is more than welcome.
https://redd.it/1kc9rw7
@r_lua
The game https://reprobate.carimbo.run
Made with Carimbo, a home made (by me) 2D engine.
https://github.com/willtobyte/carimbo
The game is written in Lua.
Pixel Art by Aline
https://linktr.ee/dandelion.pixelart
Any feedback is more than welcome.
https://redd.it/1kc9rw7
@r_lua
GitHub
GitHub - willtobyte/carimbo: A 2D game engine developed in modern C++ using SDL, an Entity-Component-System (ECS) architecture…
A 2D game engine developed in modern C++ using SDL, an Entity-Component-System (ECS) architecture, and Box2D. It features Lua noscripting and is designed for web portability via WebAssembly - willtob...
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
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({
noscriptfilename = ngx.var"document_root" .. ngx.var"cgi_noscript_name",
noscriptname = ngx.var["cginoscriptname"],
documentroot = ngx.var"document_root",
serverport = ngx.var["balancerport"],
pathinfo = ngx.var["fastcgipathinfo"],
querystring = 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",
cachevalid = 300,
keepalive = true,
keepalivetimeout = 120000,
keepalivepoolsize = 100,
hideheaders = { "X-Powered-By", "X-Page-Speed", "X-Application-Version", "X-Varnish", "Last-Modified", "Cache-Control", "Vary", "X-CF-Powered-By" },
intercepterrors = true,
readtimeout = 60000,
cacheMethods = { "GET" },
headerchunksize = 50 1024,
body_chunk_size = 30 1024
})
if not ok then
ngx.exit(ngx.HTTPBADGATEWAY)
end
}
include /etc/nginx/fastcgiparams;
accesslog 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.HTTPNOTFOUND
ngx.exit(ngx.HTTPNOTFOUND)
return
end
file:close()
ngx.var["cginoscriptname"] = ngx.var["uri"]
local res = ngx.location.capture("/@FastCGI", {
-- method = ngx.HTTPGET,
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 = {
HEADERLEN = 0x08,
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 workI 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({
noscriptfilename = ngx.var"document_root" .. ngx.var"cgi_noscript_name",
noscriptname = ngx.var["cginoscriptname"],
documentroot = ngx.var"document_root",
serverport = ngx.var["balancerport"],
pathinfo = ngx.var["fastcgipathinfo"],
querystring = 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",
cachevalid = 300,
keepalive = true,
keepalivetimeout = 120000,
keepalivepoolsize = 100,
hideheaders = { "X-Powered-By", "X-Page-Speed", "X-Application-Version", "X-Varnish", "Last-Modified", "Cache-Control", "Vary", "X-CF-Powered-By" },
intercepterrors = true,
readtimeout = 60000,
cacheMethods = { "GET" },
headerchunksize = 50 1024,
body_chunk_size = 30 1024
})
if not ok then
ngx.exit(ngx.HTTPBADGATEWAY)
end
}
include /etc/nginx/fastcgiparams;
accesslog 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.HTTPNOTFOUND
ngx.exit(ngx.HTTPNOTFOUND)
return
end
file:close()
ngx.var["cginoscriptname"] = ngx.var["uri"]
local res = ngx.location.capture("/@FastCGI", {
-- method = ngx.HTTPGET,
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 = {
HEADERLEN = 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,
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 >
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
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)
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) ..
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"))
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"))