* libs/httpd: Fixed typos
[project/luci.git] / libs / httpd / luasrc / httpd.lua
index d95de7d..f471e6b 100644 (file)
@@ -13,57 +13,215 @@ $Id$
 
 ]]--
 
-require("ltn12")
+module("luci.httpd", package.seeall)
 require("socket")
-
 require("luci.util")
-require("luci.http.protocol")
-require("luci.httpd.server")
 
+function Socket(ip, port)
+       local sock, err = socket.bind( ip, port )
 
-local srv  = luci.httpd.server
-local host = "0.0.0.0"
-local port = 50000
+       if sock then
+               sock:settimeout( 0, "t" )
+       end
 
+       return sock, err
+end
 
-server = socket.bind(host, port)
-server:settimeout( 0, "t" )
+Thread = luci.util.class()
 
-reading = { server }
-running = { }
+function Thread.__init__(self, socket, func)
+       self.socket  = socket
+       self.routine = coroutine.create(func)
+       self.stamp   = os.time()
+       self.waiting = false
+end
 
+function Thread.touched(self)
+       return self.stamp
+end
 
-while true do
+function Thread.iswaiting(self)
+       return self.waiting
+end
 
-       local input = socket.select( reading, nil, 0.1 )
+function Thread.receive(self, ...)
+       local chunk, err, part
+       self.waiting = true
+       
+       repeat
+               coroutine.yield()
+               chunk, err, part = self.socket:receive(...)
+       until err ~= "timeout"
+       
+       self.waiting = false
+       return chunk, err, part
+end
 
-       -- accept new connections
-       for i, connection in ipairs(input) do
+function Thread.resume(self, ...)
+       return coroutine.resume(self.routine, self, ...)
+end
 
-               local sock = connection:accept()
+function Thread.isdead(self)
+       return coroutine.status(self.routine) == "dead"
+end
 
-               -- check capacity
-               if #running < srv.MAX_CLIENTS then
+function Thread.touch(self)
+       self.stamp = os.time()
+end
 
-                       table.insert( running, {
-                               coroutine.create( srv.client_handler ),
-                               sock
-                       } )
+Daemon = luci.util.class()
+
+function Daemon.__init__(self, threadlimit, waittime, timeout)
+       self.reading = {}
+       self.threads = {}
+       self.handler = {}
+       self.waiting = {}
+       self.threadc = 0
+       
+       setmetatable(self.waiting, {__mode = "v"})
+       
+       self.debug   = false
+       self.threadlimit = threadlimit
+       self.waittime = waittime or 0.1
+       self.timeout  = timeout or 90
+end
 
-               -- reject client
-               else
-                       srv.error503( sock )
+function Daemon.remove_dead(self, thread)
+       if self.debug then
+               self:dprint("Completed " .. tostring(thread))
+       end
+       thread.socket:close()
+       self.threadc = self.threadc - 1
+       self.threads[thread.socket] = nil
+end
+
+function Daemon.kill_timedout(self)
+       local now = os.time()
+       
+       for sock, thread in pairs(self.threads) do
+               if os.difftime(now, thread:touched()) > self.timeout then
+                       self.threads[sock] = nil
+                       self.threadc = self.threadc - 1
+                       sock:close()
                end
        end
+end
 
-       -- create client handler
-       for i, client in ipairs( running ) do
+function Daemon.dprint(self, msg)
+       if self.debug then
+               io.stderr:write("[daemon] " .. msg .. "\n")
+       end
+end
+
+function Daemon.register(self, sock, clhandler, errhandler)
+       table.insert( self.reading, sock )
+       self.handler[sock] = { clhandler = clhandler, errhandler = errhandler }
+end
+
+function Daemon.run(self)
+       while true do
+               self:step()
+       end
+end
+
+function Daemon.step(self)     
+       local input, output, err = socket.select( self.reading, nil, 0 )
+       local working = false
 
-               -- reap dead clients
-               if coroutine.status( client[1] ) == "dead" then
-                       table.remove( running, i )
+       -- accept new connections
+       for i, connection in ipairs(input) do
+
+               local sock = connection:accept()
+               
+               if sock then
+                       -- check capacity
+                       if not self.threadlimit or self.threadc < self.threadlimit then
+                               
+                               if self.debug then
+                                       self:dprint("Accepted incoming connection from " .. sock:getpeername())
+                               end
+                               
+                               local t = Thread(sock, self.handler[connection].clhandler)
+                               self.threads[sock] = t
+                               self.threadc = self.threadc + 1
+       
+                               if self.debug then
+                                       self:dprint("Created " .. tostring(t))
+                               end
+       
+                       -- reject client
+                       else
+                               self:kill_timedout()
+                       
+                               if self.debug then
+                                       self:dprint("Rejected incoming connection from " .. sock:getpeername())
+                               end
+       
+                               if self.handler[connection].errhandler then
+                                       self.handler[connection].errhandler( sock )
+                               end
+       
+                               sock:close()
+                       end
                end
+       end
 
-               coroutine.resume( client[1], client[2] )
+       -- create client handler
+       for sock, thread in pairs( self.threads ) do            
+               -- resume working threads
+               if not thread:iswaiting() then
+                       if self.debug then
+                               self:dprint("Resuming " .. tostring(thread))
+                       end
+
+                       local stat, err = thread:resume()
+                       if stat and not thread:isdead() then
+                               thread:touch()
+                               if not thread:iswaiting() then
+                                       working = true
+                               else
+                                       table.insert(self.waiting, sock)
+                               end
+                       else
+                               self:remove_dead(thread)
+                       end
+                       
+                       if self.debug then
+                               self:dprint(tostring(thread) .. " returned")
+                               if not stat then
+                                       self:dprint("Error in " .. tostring(thread) .. " " .. err)
+                               end
+                       end
+               end
+       end
+       
+       -- check for data on waiting threads
+       input, output, err = socket.select( self.waiting, nil, 0 )
+       
+       for i, sock in ipairs(input) do 
+               local thread = self.threads[sock]
+               thread:resume()
+               if thread:isdead() then
+                       self:remove_dead(thread)
+               else
+                       thread:touch()
+                       
+                       if not thread:iswaiting() then
+                               for i, s in ipairs(self.waiting) do
+                                       if s == sock then
+                                               table.remove(self.waiting, i)
+                                               break
+                                       end
+                               end
+                               if not working then
+                                       working = true
+                               end
+                       end
+               end
+       end
+       
+       if err == "timeout" and not working then
+               self:kill_timedout()
+               socket.sleep(self.waittime)
        end
 end