9f2dbcde12828ba68bc2908db2e12d406925e3f5
[project/luci.git] / libs / httpd / luasrc / httpd.lua
1 --[[
2
3 HTTP server implementation for LuCI - core
4 (c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
5
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
9
10         http://www.apache.org/licenses/LICENSE-2.0
11
12 $Id$
13
14 ]]--
15
16 module("luci.httpd", package.seeall)
17 require("socket")
18 require("luci.util")
19
20 function Socket(ip, port)
21         local sock, err = socket.bind( ip, port )
22
23         if sock then
24                 sock:settimeout( 0, "t" )
25         end
26
27         return sock, err
28 end
29
30 Thread = luci.util.class()
31
32 function Thread.__init__(self, socket, func)
33         self.socket  = socket
34         self.routine = coroutine.create(func)
35         self.stamp   = os.time()
36         self.waiting = false
37 end
38
39 function Thread.touched(self)
40         return self.stamp
41 end
42
43 function Thread.iswaiting(self)
44         return self.waiting
45 end
46
47 function Thread.receive(self, ...)
48         local chunk, err, part
49         self.waiting = true
50         
51         while true do
52                 chunk, err, part = self.socket:receive(...)
53                 
54                 if err ~= "timeout" then
55                         self.waiting = false
56                         return chunk, err, part
57                 end
58                 
59                 coroutine.yield()
60         end
61 end
62
63 function Thread.resume(self, ...)
64         return coroutine.resume(self.routine, self, ...)
65 end
66
67 function Thread.isdead(self)
68         return coroutine.status(self.routine) == "dead"
69 end
70
71 function Thread.touch(self)
72         self.stamp = os.time()
73 end
74
75 Daemon = luci.util.class()
76
77 function Daemon.__init__(self, threadlimit, waittime, timeout)
78         self.reading = {}
79         self.threads = {}
80         self.handler = {}
81         self.waiting = {}
82         self.threadc = 0
83         
84         setmetatable(self.waiting, {__mode = "v"})
85         
86         self.debug   = false
87         self.threadlimit = threadlimit
88         self.waittime = waittime or 0.1
89         self.timeout  = timeout or 90
90 end
91
92 function Daemon.remove_dead(self, thread)
93         if self.debug then
94                 self:dprint("Completed " .. tostring(thread))
95         end
96         thread.socket:close()
97         self.threadc = self.threadc - 1
98         self.threads[thread.socket] = nil
99 end
100
101 function Daemon.kill_timedout(self)
102         local now = os.time()
103         
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
108                         sock:close()
109                 end
110         end
111 end
112
113 function Daemon.dprint(self, msg)
114         if self.debug then
115                 io.stderr:write("[daemon] " .. msg .. "\n")
116         end
117 end
118
119 function Daemon.register(self, sock, clhandler, errhandler)
120         table.insert( self.reading, sock )
121         self.handler[sock] = { clhandler = clhandler, errhandler = errhandler }
122 end
123
124 function Daemon.run(self)
125         while true do
126                 self:step()
127         end
128 end
129
130 function Daemon.step(self)      
131         local input, output, err = socket.select( self.reading, nil, 0 )
132         local working = false
133
134         -- accept new connections
135         for i, connection in ipairs(input) do
136
137                 local sock = connection:accept()
138                 
139                 if sock then
140                         -- check capacity
141                         if not self.threadlimit or self.threadc < self.threadlimit then
142                                 
143                                 if self.debug then
144                                         self:dprint("Accepted incoming connection from " .. sock:getpeername())
145                                 end
146                                 
147                                 local t = Thread(sock, self.handler[connection].clhandler)
148                                 self.threads[sock] = t
149                                 self.threadc = self.threadc + 1
150         
151                                 if self.debug then
152                                         self:dprint("Created " .. tostring(t))
153                                 end
154         
155                         -- reject client
156                         else
157                                 self:kill_timedout()
158                         
159                                 if self.debug then
160                                         self:dprint("Rejected incoming connection from " .. sock:getpeername())
161                                 end
162         
163                                 if self.handler[connection].errhandler then
164                                         self.handler[connection].errhandler( sock )
165                                 end
166         
167                                 sock:close()
168                         end
169                 end
170         end
171
172         -- create client handler
173         for sock, thread in pairs( self.threads ) do            
174                 -- resume working threads
175                 if not thread:iswaiting() then
176                         if self.debug then
177                                 self:dprint("Resuming " .. tostring(thread))
178                         end
179
180                         local stat, err = thread:resume()
181                         if stat and not thread:isdead() then
182                                 thread:touch()
183                                 if not thread:iswaiting() then
184                                         working = true
185                                 else
186                                         table.insert(self.waiting, sock)
187                                 end
188                         else
189                                 self:remove_dead(thread)
190                         end
191                         
192                         if self.debug then
193                                 self:dprint(tostring(thread) .. " returned")
194                                 if not stat then
195                                         self:dprint("Error in " .. tostring(thread) .. " " .. err)
196                                 end
197                         end
198                 end
199         end
200         
201         -- check for data on waiting threads
202         input, output, err = socket.select( self.waiting, nil, 0 )
203         
204         for i, sock in ipairs(input) do 
205                 local thread = self.threads[sock]
206                 thread:resume()
207                 if thread:isdead() then
208                         self:remove_dead(thread)
209                 else
210                         thread:touch()
211                         
212                         if not thread:iswaiting() then
213                                 for i, s in ipairs(self.waiting) do
214                                         if s == sock then
215                                                 table.remove(self.waiting, i)
216                                                 break
217                                         end
218                                 end
219                                 if not working then
220                                         working = true
221                                 end
222                         end
223                 end
224         end
225         
226         if err == "timeout" and not working then
227                 self:kill_timedout()
228                 socket.sleep(self.waittime)
229         end
230 end