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