# HG changeset patch # User Matthew Wild # Date 1262411212 0 # Node ID 6e60da4625db83c203b06b471f3fda896e7e9b57 Initial commit diff -r 000000000000 -r 6e60da4625db lahttp.in.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lahttp.in.lua Sat Jan 02 05:46:52 2010 +0000 @@ -0,0 +1,4 @@ +require "net.connlisteners" +require "net.httpclient_listener" + +return require "net.http"; diff -r 000000000000 -r 6e60da4625db lahttp.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lahttp.lua Sat Jan 02 05:46:52 2010 +0000 @@ -0,0 +1,1183 @@ +package.preload['util.logger']=(function(...) +local t; +if os.getenv("LAHTTP_DEBUG")then +t=_G.print; +else +t=function()end; +end +local i,a=select,tostring; +module"logger" +local function e(t,...) +local e,o=0,#arg; +return(t:gsub("%%(.)",function(t)if t~="%"and e<=o then e=e+1;return a(arg[e]);end end)); +end +local function s(n,...) +local e,t=0,i('#',...); +local o={...}; +return(n:gsub("%%(.)",function(i)if e<=t then e=e+1;return a(o[e]);end end)); +end +function init(e) +return function(a,e,...) +t(a,s(e,...)); +end +end +return _M; +end) +package.preload['net.server']=(function(...) +local l=function(e) +return _G[e] +end +local J=function(e) +for t,a in pairs(e)do +e[t]=nil +end +end +local w,e=require("util.logger").init("socket"),table.concat; +local n=function(...)return w("debug",e{...});end +local H=function(...)return w("warn",e{...});end +local e=collectgarbage +local ie=1 +local A=l"type" +local T=l"pairs" +local he=l"ipairs" +local h=l"tostring" +local e=l"collectgarbage" +local o=l"os" +local a=l"table" +local t=l"string" +local e=l"coroutine" +local V=o.time +local R=o.difftime +local te=a.concat +local a=a.remove +local B=t.len +local de=t.sub +local ue=e.wrap +local le=e.yield +local I=select(2,pcall(require,"ssl")) +local L=require"socket" +local ee=(I and I.wrap) +local fe=L.bind +local me=L.sleep +local ce=L.select +local e=(I and I.newcontext) +local G +local X +local Z +local W +local Y +local ne +local re +local se +local ae +local oe +local P +local d +local Q +local t +local D +local K +local p +local s +local C +local r +local i +local k +local b +local f +local c +local a +local o +local v +local U +local S +local _ +local j +local M +local u +local z +local x +local O +local E +local q +local N +local F +local g +p={} +s={} +r={} +C={} +i={} +b={} +f={} +k={} +a=0 +o=0 +v=0 +U=0 +S=0 +_=1 +j=0 +z=51e3*1024 +x=25e3*1024 +O=12e5 +E=6e4 +q=6*60*60 +N=false +g=1e3 +_maxsslhandshake=30 +ae=function(y,l,k,u,b,t,p,v) +p=p or g +local f=0 +local g,c=y.onincoming,y.ondisconnect +local m +local c=false +if t then +c=true +if not e then +H"luasec not found" +c=false +end +if A(t)~="table"then +H"server.lua: wrong server sslctx" +c=false +end +local o; +o,m=e(t) +if not o then +m=m or"wrong sslctx parameters" +local a; +a=m:match("^error loading (.-) %("); +if a then +if a=="private key"then +a=t.key or"your private key"; +elseif a=="certificate"then +a=t.certificate or"your certificate file"; +end +local e=m:match("%((.+)%)$")or"some reason"; +if e=="Permission denied"then +e="Check that the permissions allow Prosody to read this file."; +elseif e=="No such file or directory"then +e="Check that the path is correct, and the file exists."; +elseif e=="system lib"then +e="Previous error (see logs), or other system error."; +else +e="Reason: "..h(e or"unknown"):lower(); +end +w("error","SSL/TLS: Failed to load %s: %s",a,e); +else +w("error","SSL/TLS: Error initialising for port %d: %s",u,m); +end +c=false +end +t=o; +end +if not c then +t=false; +if v then +w("error","Failed to listen on port %d due to SSL/TLS to SSL/TLS initialisation errors (see logs)",u) +return nil,"Cannot start ssl, see log for details" +end +end +local m=l.accept +local e={} +e.shutdown=function()end +e.ssl=function() +return c +end +e.sslctx=function() +return t +end +e.remove=function() +f=f-1 +end +e.close=function() +for t,e in T(i)do +if e.serverport==u then +e.disconnect(e,"server closed") +e:close(true) +end +end +l:close() +o=d(r,l,o) +a=d(s,l,a) +i[l]=nil +e=nil +l=nil +n"server.lua: closed server handler and removed sockets from list" +end +e.ip=function() +return k +end +e.serverport=function() +return u +end +e.socket=function() +return l +end +e.readbuffer=function() +if f>p then +n("server.lua: refused new client connection: server full") +return false +end +local a,s=m(l) +if a then +local i,o=a:getpeername() +a:settimeout(0) +local e,a,t=D(e,y,a,i,u,o,b,t,v) +if t then +return false +end +f=f+1 +n("server.lua: accepted new client connection from ",h(i),":",h(o)," to ",h(u)) +return g(e) +elseif s then +n("server.lua: error with new client connection: ",h(s)) +return false +end +end +return e +end +D=function(M,e,t,I,Y,L,_,p,P) +t:settimeout(0) +local w +local T +local q +local v +local O=e.onincoming +local F=e.status +local g=e.ondisconnect +local y={} +local m=0 +local C +local A +local D +local l=0 +local j=false +local E=false +local H,R=0,0 +local z=z +local x=x +local e=y +e.dispatch=function() +return O +end +e.disconnect=function() +return g +end +e.setlistener=function(a,t) +O=t.onincoming +g=t.ondisconnect +end +e.getstats=function() +return R,H +end +e.ssl=function() +return v +end +e.sslctx=function() +return p +end +e.send=function(n,i,o,a) +return w(t,i,o,a) +end +e.receive=function(a,o) +return T(t,a,o) +end +e.shutdown=function(a) +return q(t,a) +end +e.close=function(u,h) +if not e then return true;end +a=d(s,t,a) +b[e]=nil +if m~=0 then +if not(h or A)then +e.sendbuffer() +if m~=0 then +if e then +e.write=nil +end +C=true +return false +end +else +w(t,te(y,"",1,m),1,l) +end +end +if t then +c=q and q(t) +t:close() +o=d(r,t,o) +i[t]=nil +t=nil +else +n"server.lua: socket already closed" +end +if e then +f[e]=nil +k[e]=nil +e=nil +end +if M then +M.remove() +end +n"server.lua: closed client handler and removed socket from list" +return true +end +e.ip=function() +return I +end +e.serverport=function() +return Y +end +e.clientport=function() +return L +end +local k=function(i,a) +l=l+B(a) +if l>z then +k[e]="send buffer exceeded" +e.write=W +return false +elseif t and not r[t]then +o=addsocket(r,t,o) +end +m=m+1 +y[m]=a +if e then +f[e]=f[e]or u +end +return true +end +e.write=k +e.bufferqueue=function(t) +return y +end +e.socket=function(a) +return t +end +e.pattern=function(a,t) +_=t or _ +return _ +end +e.set_send=function(a,t) +w=t or w +return w +end +e.bufferlen=function(o,t,a) +z=a or z +x=t or x +return l,x,z +end +e.lock_read=function(i,o) +if o==true then +local o=a +a=d(s,t,a) +b[e]=nil +if a~=o then +j=true +end +elseif o==false then +if j then +j=false +a=addsocket(s,t,a) +b[e]=u +end +end +return j +end +e.lock=function(i,a) +e.lock_read(a) +if a==true then +e.write=W +local a=o +o=d(r,t,o) +f[e]=nil +if o~=a then +E=true +end +elseif a==false then +e.write=k +if E then +E=false +k("") +end +end +return j,E +end +local b=function() +local o,t,a=T(t,_) +if not t or(t=="wantread"or t=="timeout")or B(a)>0 then +local o=o or a or"" +local a=B(o) +if a>x then +g(e,"receive buffer exceeded") +e.close(true) +return false +end +local a=a*ie +R=R+a +S=S+a +b[e]=u +return O(e,o,t) +else +n("server.lua: client ",h(I),":",h(L)," read error: ",h(t)) +A=true +g(e,t) +c=e and e.close() +return false +end +end +local f=function() +local p,a,i,s,v; +local v; +if t then +s=te(y,"",1,m) +p,a,i=w(t,s,1,l) +v=(p or i or 0)*ie +H=H+v +U=U+v +c=N and J(y) +else +p,a,v=false,"closed",0; +end +if p then +m=0 +l=0 +o=d(r,t,o) +c=D and e:starttls(true) +f[e]=nil +c=C and e.close() +return true +elseif i and(a=="timeout"or a=="wantwrite")then +s=de(s,i+1,l) +y[1]=s +m=1 +l=l-i +f[e]=u +return true +else +n("server.lua: client ",h(I),":",h(L)," write error: ",h(a)) +A=true +g(e,a) +c=e and e.close() +return false +end +end +local l; +function e.set_sslctx(i,t) +v=true +p=t; +local u +local m +l=ue(function(t) +local i +for l=1,_maxsslhandshake do +o=(u and d(r,t,o))or o +a=(m and d(s,t,a))or a +m,u=nil,nil +c,i=t:dohandshake() +if not i then +n("server.lua: ssl handshake done") +e.readbuffer=b +e.sendbuffer=f +c=F and F(e,"ssl-handshake-complete") +a=addsocket(s,t,a) +return true +else +n("server.lua: error during ssl handshake: ",h(i)) +if i=="wantwrite"and not u then +o=addsocket(r,t,o) +u=true +elseif i=="wantread"and not m then +a=addsocket(s,t,a) +m=true +else +break; +end +le() +end +end +g(e,"ssl handshake failed") +c=e and e:close(true) +return false +end +) +end +if p then +e:set_sslctx(p); +if P then +local a +t,a=ee(t,p) +if a then +n("server.lua: ssl error: ",h(a)) +return nil,nil,a +end +t:settimeout(0) +e.readbuffer=l +e.sendbuffer=l +l(t) +if not t then +return nil,nil,"ssl handshake failed"; +end +else +v=false +e.starttls=function(c,u) +if not u then +D=true +return +end +local c,u=t +t,u=ee(t,p) +if u then +n("server.lua: error while starting tls on client: ",h(u)) +return nil,u +end +t:settimeout(0) +w=t.send +T=t.receive +q=G +i[t]=e +a=addsocket(s,t,a) +a=d(s,c,a) +o=d(r,c,o) +i[c]=nil +e.starttls=nil +D=nil +v=true +e.readbuffer=l +e.sendbuffer=l +l(t) +end +e.readbuffer=b +e.sendbuffer=f +end +else +v=false +e.readbuffer=b +e.sendbuffer=f +end +w=t.send +T=t.receive +q=(v and G)or t.shutdown +i[t]=e +a=addsocket(s,t,a) +return e,t +end +G=function() +end +W=function() +return false +end +addsocket=function(a,t,e) +if not a[t]then +e=e+1 +a[e]=t +a[t]=e +end +return e; +end +d=function(e,a,t) +local o=e[a] +if o then +e[a]=nil +local i=e[t] +e[t]=nil +if i~=a then +e[i]=o +e[o]=i +end +return t-1 +end +return t +end +P=function(e) +o=d(r,e,o) +a=d(s,e,a) +i[e]=nil +e:close() +end +re=function(o,e,d,u,r,l) +local t +if A(d)~="table"then +t="invalid listener table" +end +if not A(e)=="number"or not(e>=0 and e<=65535)then +t="invalid port" +elseif p[e]then +t="listeners on port '"..e.."' already exist" +elseif r and not I then +t="luasec not found" +end +if t then +H("server.lua, port ",e,": ",t) +return nil,t +end +o=o or"*" +local t,h=fe(o,e) +if h then +H("server.lua, port ",e,": ",h) +return nil,h +end +local h,r=ae(d,t,o,e,u,r,g,l) +if not h then +t:close() +return nil,r +end +t:settimeout(0) +a=addsocket(s,t,a) +p[e]=h +i[t]=h +n("server.lua: new server listener on '",o,":",e,"'") +return h +end +se=function(e) +return p[e]; +end +Q=function(e) +local t=p[e] +if not t then +return nil,"no server found on port '"..h(e).."'" +end +t:close() +p[e]=nil +return true +end +ne=function() +for t,e in T(i)do +e:close() +i[t]=nil +end +a=0 +o=0 +v=0 +p={} +s={} +r={} +C={} +i={} +end +oe=function() +return _,j,z,x,O,E,q,N,g,_maxsslhandshake +end +K=function(e) +if A(e)~="table"then +return nil,"invalid settings table" +end +_=tonumber(e.timeout)or _ +j=tonumber(e.sleeptime)or j +z=tonumber(e.maxsendlen)or z +x=tonumber(e.maxreadlen)or x +O=tonumber(e.checkinterval)or O +E=tonumber(e.sendtimeout)or E +q=tonumber(e.readtimeout)or q +N=e.cleanqueue +g=e._maxclientsperserver or g +_maxsslhandshake=e._maxsslhandshake or _maxsslhandshake +return true +end +Y=function(e) +if A(e)~="function"then +return nil,"invalid listener function" +end +v=v+1 +C[v]=e +return true +end +Z=function() +return S,U,a,o,v +end +local e=true; +setquitting=function(t) +e=not t; +return; +end +X=function() +while e do +local a,e,t=ce(s,r,_) +for e,t in he(e)do +local e=i[t] +if e then +e.sendbuffer() +else +P(t) +n"server.lua: found no handler and closed socket (writelist)" +end +end +for t,e in he(a)do +local t=i[e] +if t then +t.readbuffer() +else +P(e) +n"server.lua: found no handler and closed socket (readlist)" +end +end +for e,t in T(k)do +e.disconnect()(e,t) +e:close(true) +end +J(k) +u=V() +if R(u-F)>=1 then +for e=1,v do +C[e](u) +end +F=u +end +me(j) +end +return"quitting" +end +local function s() +return"select"; +end +local n=function(e,s,n,t,a,h,d) +local t=D(nil,t,e,s,n,"clientport",a,h,d) +i[e]=t +o=addsocket(r,e,o) +return t,e +end +local t=function(a,o,i,s,r,h) +local t,e=L.tcp() +if e then +return nil,e +end +t:settimeout(0) +c,e=t:connect(a,o) +if e then +local e=n(t,a,o,i) +else +D(nil,i,t,a,o,"clientport",s,r,h) +end +end +l"setmetatable"(i,{__mode="k"}) +l"setmetatable"(b,{__mode="k"}) +l"setmetatable"(f,{__mode="k"}) +F=V() +M=V() +Y(function() +local e=R(u-M) +if e>O then +M=u +for e,t in T(f)do +if R(u-t)>E then +e.disconnect()(e,"send timeout") +e:close(true) +end +end +for e,t in T(b)do +if R(u-t)>q then +e.disconnect()(e,"read timeout") +e:close() +end +end +end +end +) +return{ +addclient=t, +wrapclient=n, +loop=X, +stats=Z, +closeall=ne, +addtimer=Y, +addserver=re, +getserver=se, +getsettings=oe, +setquitting=setquitting, +removeserver=Q, +get_backend=s, +changesettings=K, +} +end) +package.preload['net.httpclient_listener']=(function(...) +local a=require"util.logger".init("httpclient_listener"); +local i=require"net.connlisteners".register; +local e={}; +local t={}; +local t={default_port=80,default_mode="*a"}; +function t.onincoming(t,o) +local e=e[t]; +if not e then +a("warn","Received response from connection %s with no request attached!",tostring(t)); +return; +end +if o and e.reader then +e:reader(o); +end +end +function t.ondisconnect(a,t) +local t=e[a]; +if t then +t:reader(nil); +end +e[a]=nil; +end +function t.register_request(o,t) +a("debug","Attaching request %s to connection %s",tostring(t.id or t),tostring(o)); +e[o]=t; +end +i("httpclient",t); +end) +package.preload['net.connlisteners']=(function(...) +local r=(CFG_SOURCEDIR or".").."/net/"; +local h=require"net.server"; +local o=require"util.logger".init("connlisteners"); +local i=tostring; +local s,n,a= +dofile,pcall,error +module"connlisteners" +local e={}; +function register(t,a) +if e[t]and e[t]~=a then +o("debug","Listener %s is already registered, not registering any more",t); +return false; +end +e[t]=a; +o("debug","Registered connection listener %s",t); +return true; +end +function deregister(t) +e[t]=nil; +end +function get(t) +local a=e[t]; +if not a then +local s,n=n(s,r..t:gsub("[^%w%-]","_").."_listener.lua"); +if not s then +o("error","Error while loading listener '%s': %s",i(t),i(n)); +return nil,n; +end +a=e[t]; +end +return a; +end +function start(o,e) +local t,n=get(o); +if not t then +a("No such connection module: "..o..(n and(" ("..n..")")or""),0); +end +if e then +if(e.type=="ssl"or e.type=="tls")and not e.ssl then +a("No SSL context supplied for a "..i(e.type):upper().." connection!",0); +elseif e.ssl and e.type=="tcp"then +a("SSL context supplied for a TCP connection!",0); +end +end +local i=(e and e.interface)or t.default_interface or"*"; +local o=(e and e.port)or t.default_port or a("Can't start listener "..o.." because no port was specified, and it has no default port",0); +local a=(e and e.mode)or t.default_mode or 1; +local n=(e and e.ssl)or nil; +local s=99999999; +local e=e and e.type=="ssl"; +return h.addserver(i,o,t,a,n,e); +end +return _M; +end) +package.preload['net.http']=(function(...) +local b=require"socket" +local v=require"mime" +local y=require"socket.url" +local p=require"net.server" +local e=require"net.connlisteners".get; +local d=e("httpclient")or error("No httpclient listener!"); +local r,i=table.insert,table.concat; +local s,u,c,m,f,l,t,a= +tonumber,tostring,pairs,xpcall,select,debug.traceback,string.char,string.format; +local n=require"util.logger".init("http"); +local o=function()end +module"http" +function urlencode(e)return e and(e:gsub("%W",function(e)return a("%%%02x",e:byte());end));end +function urldecode(e)return e and(e:gsub("%%(%x%x)",function(e)return t(s(e,16));end));end +local function w(t,e) +if t.method=="HEAD"then return nil end +if e==204 or e==304 or e==301 then return nil end +if e>=100 and e<200 then return nil end +return 1 +end +local function h(e,t,a) +if not t then +if e.body then +n("debug","Connection closed, but we have data, calling callback..."); +e.callback(i(e.body),e.code,e); +elseif e.state~="completed"then +e.callback("connection-closed",0,e); +end +destroy_request(e); +e.body=nil; +e.state="completed"; +return; +end +if e.state=="body"and e.state~="completed"then +o("Reading body...") +if not e.body then e.body={};e.havebodylength,e.bodylength=0,s(e.responseheaders["content-length"]);end +if a then +t=t:sub(a,-1) +end +r(e.body,t); +if e.bodylength then +e.havebodylength=e.havebodylength+#t; +if e.havebodylength>=e.bodylength then +n("debug","Have full body, calling callback"); +if e.callback then +e.callback(i(e.body),e.code,e); +end +e.body=nil; +e.state="completed"; +else +o("","Have "..e.havebodylength.." bytes out of "..e.bodylength); +end +end +elseif e.state=="headers"then +o("Reading headers...") +local i=a; +local n=e.responseheaders or{}; +for t in t:sub(a,-1):gmatch("(.-)\r\n")do +a=a+#t+2; +local a,i=t:match("(%S+): (.+)"); +if a and i then +n[a:lower()]=i; +o("Header: "..a:lower().." = "..i); +elseif#t==0 then +e.responseheaders=n; +break; +else +o("Unhandled header line: "..t); +end +end +e.state="body"; +if#t>a then +return h(e,t,a); +end +elseif e.state=="status"then +o("Reading status...") +local i,a,n,o=t:match("^HTTP/(%S+) (%d+) (.-)\r\n()",a); +a=s(a); +if not a then +return e.callback("invalid-status-line",0,e); +end +e.code,e.responseversion=a,i; +if e.onlystatus or not w(e,a)then +if e.callback then +e.callback(nil,a,e); +end +destroy_request(e); +return; +end +e.state="headers"; +if#t>o then +return h(e,t,o); +end +end +end +local function w(e)n("error","Traceback[http]: %s: %s",u(e),l());end +function request(e,a,s) +local e=y.parse(e); +if not(e and e.host)then +s(nil,0,e); +return nil,"invalid-url"; +end +if not e.path then +e.path="/"; +end +local l,o; +local t={["Host"]=e.host,["User-Agent"]="Prosody XMPP Server"} +if e.userinfo then +t["Authorization"]="Basic "..v.b64(e.userinfo); +end +if a then +l=a.headers; +e.onlystatus=a.onlystatus; +o=a.body; +if o then +e.method="POST "; +t["Content-Length"]=u(#o); +t["Content-Type"]="application/x-www-form-urlencoded"; +end +if a.method then e.method=a.method;end +end +e.handler,e.conn=p.wrapclient(b.tcp(),e.host,e.port or 80,d,"*a"); +e.write=function(...)return e.handler:write(...);end +e.conn:settimeout(0); +local u,a=e.conn:connect(e.host,e.port or 80); +if not u and a~="timeout"then +s(nil,0,e); +return nil,a; +end +local a={e.method or"GET"," ",e.path," HTTP/1.1\r\n"}; +if e.query then +r(a,4,"?"); +r(a,5,e.query); +end +e.write(i(a)); +local a={[2]=": ",[4]="\r\n"}; +if l then +for o,n in c(l)do +a[1],a[3]=o,n; +e.write(i(a)); +t[o]=nil; +end +end +for o,n in c(t)do +a[1],a[3]=o,n; +e.write(i(a)); +t[o]=nil; +end +e.write("\r\n"); +if o then +e.write(o); +end +e.callback=function(a,t,o)n("debug","Calling callback, status %s",t or"---");return f(2,m(function()return s(a,t,o)end,w));end +e.reader=h; +e.state="status"; +d.register_request(e.handler,e); +return e; +end +function destroy_request(e) +if e.conn then +e.handler.close() +d.ondisconnect(e.conn,"closed"); +end +end +_M.urlencode=urlencode; +return _M; +end) +package.preload['util.timer']=(function(...) +local h=require"net.server".addtimer; +local a=require"net.server".event; +local d=require"net.server".event_base; +local s=os.time; +local n=table.insert; +local e=table.remove; +local e,i=ipairs,pairs; +local r=type; +local o={}; +local t={}; +module"timer" +local e; +if not a then +function e(e,a) +local o=s(); +e=e+o; +if e>=o then +n(t,{e,a}); +else +a(); +end +end +h(function() +local s=s(); +if#t>0 then +for a,e in i(t)do +n(o,e); +end +t={}; +end +for i,t in i(o)do +local t,a=t[1],t[2]; +if t<=s then +o[i]=nil; +local t=a(s); +if r(t)=="number"then e(t,a);end +end +end +end); +else +local t=(a.core and a.core.LEAVE)or-1; +function e(a,e) +d:addevent(nil,0,function() +local e=e(); +if e then +return 0,e; +else +return t; +end +end +,a); +end +end +add_task=e; +return _M; +end) +package.preload['multihttp']=(function(...) +local s=require"net.http"; +local o=require"net.server"; +local i=require"util.timer"; +local n,h,r,d= +pairs,ipairs,type,setmetatable; +module"multihttp" +__index=_M; +function set_callback(t,e) +t.callback=e; +end +function set_progress_callback(e,a,t) +t=t or 1; +e.progress_callback=a; +i.add_task(t,function() +if e.progress_callback then +e.progress_callback(e); +if e.progress_callback then +return t; +end +end +end); +end +function add_url(t,e) +t.urls[e]=true; +end +function remove_url(t,e) +t.urls[e]=nil; +end +function download(e,i) +e.status="downloading"; +local t=0; +for a in n(e.urls)do +t=t+1; +e.urls[a]= +s.request(a,nil,function(n,t,s) +if e.callback then +e.downloading_count=e.downloading_count-1; +e.callback(a,t,n,s); +if e.downloading_count==0 and i then +o.setquitting(true); +end +end +end); +end +e.download_count=t; +e.downloading_count=t; +if i then +o.loop(); +o.setquitting(false); +end +return t; +end +function progress(e) +local a={total={}}; +for o,t in n(e.urls)do +local e={}; +e.bytes_downloaded=t.havebodylength; +e.bytes_total=t.bodylength; +if e.bytes_total then +e.percent=100/(e.bytes_total/e.bytes_downloaded); +end +a[o]=e; +end +return a; +end +function new(t,e) +local t={callback=t,urls={}}; +if r(e)=="table"then +for a,e in h(e)do +t.urls[e]=true; +end +end +return d(t,_M); +end +return _M; +end) +require"net.connlisteners" +require"net.httpclient_listener" +return require"net.http"; diff -r 000000000000 -r 6e60da4625db libs/httpclient_listener.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/httpclient_listener.lua Sat Jan 02 05:46:52 2010 +0000 @@ -0,0 +1,44 @@ +-- Prosody IM +-- Copyright (C) 2008-2009 Matthew Wild +-- Copyright (C) 2008-2009 Waqas Hussain +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local log = require "util.logger".init("httpclient_listener"); + +local connlisteners_register = require "net.connlisteners".register; + +local requests = {}; -- Open requests +local buffers = {}; -- Buffers of partial lines + +local httpclient = { default_port = 80, default_mode = "*a" }; + +function httpclient.onincoming(conn, data) + local request = requests[conn]; + + if not request then + log("warn", "Received response from connection %s with no request attached!", tostring(conn)); + return; + end + + if data and request.reader then + request:reader(data); + end +end + +function httpclient.ondisconnect(conn, err) + local request = requests[conn]; + if request then + request:reader(nil); + end + requests[conn] = nil; +end + +function httpclient.register_request(conn, req) + log("debug", "Attaching request %s to connection %s", tostring(req.id or req), tostring(conn)); + requests[conn] = req; +end + +connlisteners_register("httpclient", httpclient); diff -r 000000000000 -r 6e60da4625db libs/logger.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/logger.lua Sat Jan 02 05:46:52 2010 +0000 @@ -0,0 +1,28 @@ +local print; +if os.getenv("LAHTTP_DEBUG") then + print = _G.print; +else + print = function () end; +end + +local select, tostring = select, tostring; +module "logger" + +local function format(format, ...) + local n, maxn = 0, #arg; + return (format:gsub("%%(.)", function (c) if c ~= "%" and n <= maxn then n = n + 1; return tostring(arg[n]); end end)); +end + +local function format(format, ...) + local n, maxn = 0, select('#', ...); + local arg = { ... }; + return (format:gsub("%%(.)", function (c) if n <= maxn then n = n + 1; return tostring(arg[n]); end end)); +end + +function init(name) + return function (level, message, ...) + print(level, format(message, ...)); + end +end + +return _M; diff -r 000000000000 -r 6e60da4625db libs/multihttp.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/multihttp.lua Sat Jan 02 05:46:52 2010 +0000 @@ -0,0 +1,85 @@ +local http = require "net.http"; +local server = require "net.server"; +local timer = require "util.timer"; + +local pairs, ipairs, type, setmetatable = + pairs, ipairs, type, setmetatable; + +module "multihttp" +__index = _M; + +function set_callback(batch, callback) + batch.callback = callback; +end + +function set_progress_callback(batch, callback, delay) + delay = delay or 1; + batch.progress_callback = callback; + timer.add_task(delay, function () + if batch.progress_callback then + batch.progress_callback(batch); + if batch.progress_callback then + return delay; + end + end + end); +end + +function add_url(batch, url) + batch.urls[url] = true; +end + +function remove_url(batch, url) + batch.urls[url] = nil; +end + +function download(batch, block) + batch.status = "downloading"; + local count = 0; + for url in pairs(batch.urls) do + count = count + 1; + batch.urls[url] = + http.request(url, nil, function (data, status, request) + if batch.callback then + batch.downloading_count = batch.downloading_count - 1; + batch.callback(url, status, data, request); + if batch.downloading_count == 0 and block then + server.setquitting(true); + end + end + end); + end + batch.download_count = count; + batch.downloading_count = count; + if block then + server.loop(); + server.setquitting(false); + end + return count; +end + +function progress(batch) + local progress = {}; + for url, request in pairs(batch.urls) do + local p = {}; + p.bytes_downloaded = request.havebodylength; + p.bytes_total = request.bodylength; + if p.bytes_total then + p.percent = 100/(p.bytes_total/p.bytes_downloaded); + end + progress[url] = p; + end + return progress; +end + +function new(callback, urls) + local batch = { callback = callback, urls = {} }; + if type(urls) == "table" then + for _, url in ipairs(urls) do + batch.urls[url] = true; + end + end + return setmetatable(batch, _M); +end + +return _M; diff -r 000000000000 -r 6e60da4625db squishy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/squishy Sat Jan 02 05:46:52 2010 +0000 @@ -0,0 +1,21 @@ + +-- Dummy logger, set LAHTTP_DEBUG environment var to enable logging +Module "util.logger" "libs/logger.lua" + +-- Optional helper library for managing multiple downloads +Module "multihttp" "libs/multihttp.lua" + +AutoFetchURL "http://prosody.im/tip/?" + +Module "net.server" "net/server_select.lua" +Module "net.httpclient_listener" "net/httpclient_listener.lua" +Module "net.connlisteners" "net/connlisteners.lua" +Module "net.http" "net/http.lua" + +-- Required by multihttp, but otherwise not required (though nice to have) +Module "util.timer" "util/timer.lua" + + +Main "lahttp.in.lua" + +Output "lahttp.lua";