a9b1ccbb4cb18bd1771c77e7a99dea2027b6abdc
[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.getidletime(self)
40         return os.difftime(os.time(), 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         repeat
52                 coroutine.yield()
53                 chunk, err, part = self.socket:receive(...)
54         until err ~= "timeout"
55         
56         self.waiting = false
57         return chunk, err, part
58 end
59
60 function Thread.resume(self, ...)
61         return coroutine.resume(self.routine, self, ...)
62 end
63
64 function Thread.status(self)
65         return coroutine.status(self.routine)
66 end
67
68 function Thread.touch(self)
69         self.stamp = os.time()
70 end
71
72 Daemon = luci.util.class()
73
74 function Daemon.__init__(self, threadlimit, timeout)
75         self.reading = {}
76         self.threads = {}
77         self.handler = {}
78         self.waiting = {}
79         self.threadc = 0
80         
81         setmetatable(self.waiting, {__mode = "v"})
82         
83         self.debug   = false
84         self.threadlimit = threadlimit
85         self.timeout = timeout or 0.1
86 end
87
88 function Daemon.dprint(self, msg)
89         if self.debug then
90                 io.stderr:write("[daemon] " .. msg .. "\n")
91         end
92 end
93
94 function Daemon.register(self, sock, clhandler, errhandler)
95         table.insert( self.reading, sock )
96         self.handler[sock] = { clhandler = clhandler, errhandler = errhandler }
97 end
98
99 function Daemon.run(self)
100         while true do
101                 self:step()
102         end
103 end
104
105 function Daemon.step(self)      
106         local input, output, err = socket.select( self.reading, nil, 0 )
107         local working = false
108
109         -- accept new connections
110         for i, connection in ipairs(input) do
111
112                 local sock = connection:accept()
113                 
114                 if sock then
115                         -- check capacity
116                         if not self.threadlimit or self.threadc < self.threadlimit then
117                                 
118                                 if self.debug then
119                                         self:dprint("Accepted incoming connection from " .. sock:getpeername())
120                                 end
121                                 
122                                 local t = Thread(sock, self.handler[connection].clhandler)
123                                 self.threads[sock] = t
124                                 self.threadc = self.threadc + 1
125         
126                                 if self.debug then
127                                         self:dprint("Created " .. tostring(t))
128                                 end
129         
130                         -- reject client
131                         else
132                                 if self.debug then
133                                         self:dprint("Rejected incoming connection from " .. sock:getpeername())
134                                 end
135         
136                                 if self.handler[connection].errhandler then
137                                         self.handler[connection].errhandler( sock )
138                                 end
139         
140                                 sock:close()
141                         end
142                 end
143         end
144
145         -- create client handler
146         for sock, thread in pairs( self.threads ) do
147
148                 -- reap dead clients
149                 if thread:status() == "dead" then
150                         if self.debug then
151                                 self:dprint("Completed " .. tostring(thread))
152                         end
153                         sock:close()
154                         self.threadc = self.threadc - 1
155                         self.threads[sock] = nil
156                 -- resume working threads
157                 elseif not thread:iswaiting() then
158                         if self.debug then
159                                 self:dprint("Resuming " .. tostring(thread))
160                         end
161
162                         local stat, err = thread:resume()
163                         if stat then
164                                 thread:touch()
165                                 if not thread:iswaiting() then
166                                         working = true
167                                 else
168                                         table.insert(self.waiting, sock)
169                                 end
170                         end
171                         
172                         if self.debug then
173                                 self:dprint(tostring(thread) .. " returned")
174                                 if not stat then
175                                         self:dprint("Error in " .. tostring(thread) .. " " .. err)
176                                 end
177                         end
178                 end
179         end
180         
181         -- check for data on waiting threads
182         input, output, err = socket.select( self.waiting, nil, 0 )
183         
184         for i, sock in ipairs(input) do         
185                 self.threads[sock]:resume()
186                 self.threads[sock]:touch()
187                 
188                 if not self.threads[sock]:iswaiting() then
189                         for i, s in ipairs(self.waiting) do
190                                 if s == sock then
191                                         table.remove(self.waiting, i)
192                                         break
193                                 end
194                         end
195                         if not working then
196                                 working = true
197                         end
198                 end
199         end
200         
201         if err == "timeout" and not working then
202                 socket.sleep(self.timeout)
203         end
204 end