Initial commit

Sat, 02 Jan 2010 05:46:52 +0000

author
Matthew Wild <mwild1@gmail.com>
date
Sat, 02 Jan 2010 05:46:52 +0000
changeset 0
6e60da4625db
child 1
7ef4b95f77b5

Initial commit

lahttp.in.lua file | annotate | diff | comparison | revisions
lahttp.lua file | annotate | diff | comparison | revisions
libs/httpclient_listener.lua file | annotate | diff | comparison | revisions
libs/logger.lua file | annotate | diff | comparison | revisions
libs/multihttp.lua file | annotate | diff | comparison | revisions
squishy file | annotate | diff | comparison | revisions
--- /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";
--- /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";
--- /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);
--- /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;
--- /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;
--- /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";

mercurial