3 HTTP server implementation for LuCI - core
4 (c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
16 module("luci.httpd", package.seeall)
20 function Socket(ip, port)
21 local sock, err = socket.bind( ip, port )
24 sock:settimeout( 0, "t" )
30 Thread = luci.util.class()
32 function Thread.__init__(self, socket, func)
34 self.routine = coroutine.create(func)
35 self.stamp = os.time()
39 function Thread.touched(self)
43 function Thread.iswaiting(self)
47 function Thread.receive(self, ...)
48 local chunk, err, part
52 chunk, err, part = self.socket:receive(...)
54 if err ~= "timeout" then
56 return chunk, err, part
63 function Thread.resume(self, ...)
64 return coroutine.resume(self.routine, self, ...)
67 function Thread.isdead(self)
68 return coroutine.status(self.routine) == "dead"
71 function Thread.touch(self)
72 self.stamp = os.time()
75 Daemon = luci.util.class()
77 function Daemon.__init__(self, threadlimit, waittime, timeout)
84 setmetatable(self.waiting, {__mode = "v"})
87 self.threadlimit = threadlimit
88 self.waittime = waittime or 0.1
89 self.timeout = timeout or 90
92 function Daemon.remove_dead(self, thread)
94 self:dprint("Completed " .. tostring(thread))
97 self.threadc = self.threadc - 1
98 self.threads[thread.socket] = nil
101 function Daemon.kill_timedout(self)
102 local now = os.time()
104 for sock, thread in pairs(self.threads) do
105 if os.difftime(now, thread:touched()) > self.timeout then
106 self.threads[sock] = nil
107 self.threadc = self.threadc - 1
113 function Daemon.dprint(self, msg)
115 io.stderr:write("[daemon] " .. msg .. "\n")
119 function Daemon.register(self, sock, clhandler, errhandler)
120 table.insert( self.reading, sock )
121 self.handler[sock] = { clhandler = clhandler, errhandler = errhandler }
124 function Daemon.run(self)
130 function Daemon.step(self)
131 local input, output, err = socket.select( self.reading, nil, 0 )
132 local working = false
134 -- accept new connections
135 for i, connection in ipairs(input) do
137 local sock = connection:accept()
141 if not self.threadlimit or self.threadc < self.threadlimit then
144 self:dprint("Accepted incoming connection from " .. sock:getpeername())
147 local t = Thread(sock, self.handler[connection].clhandler)
148 self.threads[sock] = t
149 self.threadc = self.threadc + 1
152 self:dprint("Created " .. tostring(t))
160 self:dprint("Rejected incoming connection from " .. sock:getpeername())
163 if self.handler[connection].errhandler then
164 self.handler[connection].errhandler( sock )
172 -- create client handler
173 for sock, thread in pairs( self.threads ) do
174 -- resume working threads
175 if not thread:iswaiting() then
177 self:dprint("Resuming " .. tostring(thread))
180 local stat, err = thread:resume()
181 if stat and not thread:isdead() then
183 if not thread:iswaiting() then
186 table.insert(self.waiting, sock)
189 self:remove_dead(thread)
193 self:dprint(tostring(thread) .. " returned")
195 self:dprint("Error in " .. tostring(thread) .. " " .. err)
201 -- check for data on waiting threads
202 input, output, err = socket.select( self.waiting, nil, 0 )
204 for i, sock in ipairs(input) do
205 local thread = self.threads[sock]
207 if thread:isdead() then
208 self:remove_dead(thread)
212 if not thread:iswaiting() then
213 for i, s in ipairs(self.waiting) do
215 table.remove(self.waiting, i)
226 if err == "timeout" and not working then
228 socket.sleep(self.waittime)