050e03ec2b10b682cc2046bfe6ae10d065bf1cbf
[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 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.isdead(self)
65 return coroutine.status(self.routine) == "dead"
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, waittime, 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.waittime = waittime or 0.1
86 self.timeout = timeout or 90
87 end
88
89 function Daemon.remove_dead(self, thread)
90 if self.debug then
91 self:dprint("Completed " .. tostring(thread))
92 end
93 thread.socket:close()
94 self.threadc = self.threadc - 1
95 self.threads[thread.socket] = nil
96 end
97
98 function Daemon.kill_timedout(self)
99 local now = os.time()
100
101 for k, v in pairs(self.threads) do
102 if os.difftime(now, thread:touched()) > self.timeout then
103 self.threads[sock] = nil
104 self.threadc = self.threadc - 1
105 sock:close()
106 end
107 end
108 end
109
110 function Daemon.dprint(self, msg)
111 if self.debug then
112 io.stderr:write("[daemon] " .. msg .. "\n")
113 end
114 end
115
116 function Daemon.register(self, sock, clhandler, errhandler)
117 table.insert( self.reading, sock )
118 self.handler[sock] = { clhandler = clhandler, errhandler = errhandler }
119 end
120
121 function Daemon.run(self)
122 while true do
123 self:step()
124 end
125 end
126
127 function Daemon.step(self)
128 local input, output, err = socket.select( self.reading, nil, 0 )
129 local working = false
130
131 -- accept new connections
132 for i, connection in ipairs(input) do
133
134 local sock = connection:accept()
135
136 if sock then
137 -- check capacity
138 if not self.threadlimit or self.threadc < self.threadlimit then
139
140 if self.debug then
141 self:dprint("Accepted incoming connection from " .. sock:getpeername())
142 end
143
144 local t = Thread(sock, self.handler[connection].clhandler)
145 self.threads[sock] = t
146 self.threadc = self.threadc + 1
147
148 if self.debug then
149 self:dprint("Created " .. tostring(t))
150 end
151
152 -- reject client
153 else
154 self:kill_timedout()
155
156 if self.debug then
157 self:dprint("Rejected incoming connection from " .. sock:getpeername())
158 end
159
160 if self.handler[connection].errhandler then
161 self.handler[connection].errhandler( sock )
162 end
163
164 sock:close()
165 end
166 end
167 end
168
169 -- create client handler
170 for sock, thread in pairs( self.threads ) do
171 -- resume working threads
172 if not thread:iswaiting() then
173 if self.debug then
174 self:dprint("Resuming " .. tostring(thread))
175 end
176
177 local stat, err = thread:resume()
178 if stat and not thread:isdead() then
179 thread:touch()
180 if not thread:iswaiting() then
181 working = true
182 else
183 table.insert(self.waiting, sock)
184 end
185 else
186 self:remove_dead(thread)
187 end
188
189 if self.debug then
190 self:dprint(tostring(thread) .. " returned")
191 if not stat then
192 self:dprint("Error in " .. tostring(thread) .. " " .. err)
193 end
194 end
195 end
196 end
197
198 -- check for data on waiting threads
199 input, output, err = socket.select( self.waiting, nil, 0 )
200
201 for i, sock in ipairs(input) do
202 local thread = self.threads[sock]
203 thread:resume()
204 if thread:isdead() then
205 self:remove_dead(thread)
206 else
207 thread:touch()
208
209 if not thread:iswaiting() then
210 for i, s in ipairs(self.waiting) do
211 if s == sock then
212 table.remove(self.waiting, i)
213 break
214 end
215 end
216 if not working then
217 working = true
218 end
219 end
220 end
221 end
222
223 if err == "timeout" and not working then
224 self:kill_timedout()
225 socket.sleep(self.waittime)
226 end
227 end