2008-06-19 03:02:49 +00:00
|
|
|
--[[
|
|
|
|
|
|
|
|
HTTP server implementation for LuCI - core
|
|
|
|
(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
$Id$
|
|
|
|
|
|
|
|
]]--
|
|
|
|
|
2008-06-23 18:17:02 +00:00
|
|
|
module("luci.httpd", package.seeall)
|
2008-06-19 03:02:49 +00:00
|
|
|
require("socket")
|
|
|
|
require("luci.util")
|
|
|
|
|
2008-06-23 18:17:02 +00:00
|
|
|
function Socket(ip, port)
|
|
|
|
local sock, err = socket.bind( ip, port )
|
|
|
|
|
|
|
|
if sock then
|
|
|
|
sock:settimeout( 0, "t" )
|
|
|
|
end
|
|
|
|
|
|
|
|
return sock, err
|
|
|
|
end
|
|
|
|
|
2008-06-25 16:38:48 +00:00
|
|
|
Thread = luci.util.class()
|
|
|
|
|
|
|
|
function Thread.__init__(self, socket, func)
|
|
|
|
self.socket = socket
|
|
|
|
self.routine = coroutine.create(func)
|
|
|
|
self.stamp = os.time()
|
|
|
|
self.waiting = false
|
|
|
|
end
|
|
|
|
|
2008-06-25 16:48:48 +00:00
|
|
|
function Thread.touched(self)
|
|
|
|
return self.stamp
|
2008-06-25 16:38:48 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function Thread.iswaiting(self)
|
|
|
|
return self.waiting
|
|
|
|
end
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
function Thread.resume(self, ...)
|
|
|
|
return coroutine.resume(self.routine, self, ...)
|
|
|
|
end
|
|
|
|
|
2008-06-25 18:09:53 +00:00
|
|
|
function Thread.isdead(self)
|
|
|
|
return coroutine.status(self.routine) == "dead"
|
2008-06-25 16:38:48 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function Thread.touch(self)
|
|
|
|
self.stamp = os.time()
|
|
|
|
end
|
2008-06-19 03:02:49 +00:00
|
|
|
|
2008-06-21 19:41:17 +00:00
|
|
|
Daemon = luci.util.class()
|
2008-06-19 03:02:49 +00:00
|
|
|
|
2008-06-25 16:48:48 +00:00
|
|
|
function Daemon.__init__(self, threadlimit, waittime, timeout)
|
2008-06-21 19:41:17 +00:00
|
|
|
self.reading = {}
|
2008-06-25 16:38:48 +00:00
|
|
|
self.threads = {}
|
2008-06-21 19:41:17 +00:00
|
|
|
self.handler = {}
|
2008-06-25 16:38:48 +00:00
|
|
|
self.waiting = {}
|
|
|
|
self.threadc = 0
|
|
|
|
|
|
|
|
setmetatable(self.waiting, {__mode = "v"})
|
|
|
|
|
2008-06-23 18:17:02 +00:00
|
|
|
self.debug = false
|
2008-06-21 19:41:17 +00:00
|
|
|
self.threadlimit = threadlimit
|
2008-06-25 16:48:48 +00:00
|
|
|
self.waittime = waittime or 0.1
|
|
|
|
self.timeout = timeout or 90
|
2008-06-21 19:41:17 +00:00
|
|
|
end
|
2008-06-19 03:02:49 +00:00
|
|
|
|
2008-06-25 18:09:53 +00:00
|
|
|
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()
|
|
|
|
|
2008-06-25 18:33:38 +00:00
|
|
|
for sock, thread in pairs(self.threads) do
|
2008-06-25 18:09:53 +00:00
|
|
|
if os.difftime(now, thread:touched()) > self.timeout then
|
|
|
|
self.threads[sock] = nil
|
|
|
|
self.threadc = self.threadc - 1
|
|
|
|
sock:close()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-06-23 18:17:02 +00:00
|
|
|
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 }
|
2008-06-21 19:41:17 +00:00
|
|
|
end
|
2008-06-19 03:02:49 +00:00
|
|
|
|
2008-06-21 19:41:17 +00:00
|
|
|
function Daemon.run(self)
|
|
|
|
while true do
|
|
|
|
self:step()
|
|
|
|
end
|
|
|
|
end
|
2008-06-19 03:02:49 +00:00
|
|
|
|
2008-06-21 19:41:17 +00:00
|
|
|
function Daemon.step(self)
|
2008-06-23 18:17:02 +00:00
|
|
|
local input, output, err = socket.select( self.reading, nil, 0 )
|
2008-06-25 16:38:48 +00:00
|
|
|
local working = false
|
2008-06-19 03:02:49 +00:00
|
|
|
|
|
|
|
-- accept new connections
|
|
|
|
for i, connection in ipairs(input) do
|
|
|
|
|
|
|
|
local sock = connection:accept()
|
2008-06-23 19:41:35 +00:00
|
|
|
|
|
|
|
if sock then
|
|
|
|
-- check capacity
|
2008-06-25 16:38:48 +00:00
|
|
|
if not self.threadlimit or self.threadc < self.threadlimit then
|
2008-06-23 19:41:35 +00:00
|
|
|
|
|
|
|
if self.debug then
|
|
|
|
self:dprint("Accepted incoming connection from " .. sock:getpeername())
|
|
|
|
end
|
2008-06-25 16:38:48 +00:00
|
|
|
|
|
|
|
local t = Thread(sock, self.handler[connection].clhandler)
|
|
|
|
self.threads[sock] = t
|
|
|
|
self.threadc = self.threadc + 1
|
2008-06-23 19:41:35 +00:00
|
|
|
|
|
|
|
if self.debug then
|
2008-06-25 16:38:48 +00:00
|
|
|
self:dprint("Created " .. tostring(t))
|
2008-06-23 19:41:35 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- reject client
|
|
|
|
else
|
2008-06-25 18:09:53 +00:00
|
|
|
self:kill_timedout()
|
|
|
|
|
2008-06-23 19:41:35 +00:00
|
|
|
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()
|
2008-06-21 19:41:17 +00:00
|
|
|
end
|
2008-06-19 03:02:49 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- create client handler
|
2008-06-25 18:09:53 +00:00
|
|
|
for sock, thread in pairs( self.threads ) do
|
2008-06-25 16:38:48 +00:00
|
|
|
-- resume working threads
|
2008-06-25 18:09:53 +00:00
|
|
|
if not thread:iswaiting() then
|
2008-06-23 19:41:35 +00:00
|
|
|
if self.debug then
|
2008-06-25 16:38:48 +00:00
|
|
|
self:dprint("Resuming " .. tostring(thread))
|
2008-06-23 19:41:35 +00:00
|
|
|
end
|
2008-06-23 18:17:02 +00:00
|
|
|
|
2008-06-25 16:38:48 +00:00
|
|
|
local stat, err = thread:resume()
|
2008-06-25 18:09:53 +00:00
|
|
|
if stat and not thread:isdead() then
|
2008-06-25 16:38:48 +00:00
|
|
|
thread:touch()
|
|
|
|
if not thread:iswaiting() then
|
|
|
|
working = true
|
|
|
|
else
|
|
|
|
table.insert(self.waiting, sock)
|
|
|
|
end
|
2008-06-25 18:09:53 +00:00
|
|
|
else
|
|
|
|
self:remove_dead(thread)
|
2008-06-25 16:38:48 +00:00
|
|
|
end
|
2008-06-23 19:41:35 +00:00
|
|
|
|
|
|
|
if self.debug then
|
2008-06-25 16:38:48 +00:00
|
|
|
self:dprint(tostring(thread) .. " returned")
|
2008-06-23 21:30:44 +00:00
|
|
|
if not stat then
|
2008-06-25 16:38:48 +00:00
|
|
|
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 )
|
|
|
|
|
2008-06-25 18:09:53 +00:00
|
|
|
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
|
2008-06-23 21:30:44 +00:00
|
|
|
end
|
2008-06-25 16:38:48 +00:00
|
|
|
end
|
2008-06-23 18:17:02 +00:00
|
|
|
end
|
2008-06-19 03:02:49 +00:00
|
|
|
end
|
2008-06-25 16:38:48 +00:00
|
|
|
|
|
|
|
if err == "timeout" and not working then
|
2008-06-25 18:09:53 +00:00
|
|
|
self:kill_timedout()
|
2008-06-25 16:48:48 +00:00
|
|
|
socket.sleep(self.waittime)
|
2008-06-25 16:38:48 +00:00
|
|
|
end
|
2008-06-19 03:02:49 +00:00
|
|
|
end
|