* libs/httpd: Fixed garbage collection
[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 (c) 2008 Steven Barth <steven@midlink.org>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14
15 ]]--
16
17 module("luci.httpd", package.seeall)
18 require("socket")
19
20 THREAD_IDLEWAIT = 0.01
21 THREAD_TIMEOUT = 90
22 THREAD_LIMIT = nil
23
24 local reading = {}
25 local clhandler = {}
26 local erhandler = {}
27
28 local threadc = 0
29 local threads = {}
30 local threadm = {}
31 local threadi = {}
32
33 local _meta = {__mode = "k"}
34 setmetatable(threadm, _meta)
35 setmetatable(threadi, _meta)
36
37
38 function Socket(ip, port)
39 local sock, err = socket.bind( ip, port )
40
41 if sock then
42 sock:settimeout( 0, "t" )
43 end
44
45 return sock, err
46 end
47
48 function corecv(socket, ...)
49 threadi[socket] = true
50
51 while true do
52 local chunk, err, part = socket:receive(...)
53
54 if err ~= "timeout" then
55 threadi[socket] = false
56 return chunk, err, part
57 end
58
59 coroutine.yield()
60 end
61 end
62
63 function cosend(socket, chunk, i, ...)
64 threadi[socket] = true
65 i = i or 1
66
67 while true do
68 local stat, err, sent = socket:send(chunk, i, ...)
69
70 if err ~= "timeout" then
71 threadi[socket] = false
72 return stat, err, sent
73 else
74 i = sent and (sent + 1) or i
75 end
76
77 coroutine.yield()
78 end
79 end
80
81 function register(socket, s_clhandler, s_errhandler)
82 table.insert(reading, socket)
83 clhandler[socket] = s_clhandler
84 erhandler[socket] = s_errhandler
85 end
86
87 function run()
88 while true do
89 step()
90 end
91 end
92
93 function step()
94 local idle = true
95 if not THREAD_LIMIT or threadc < THREAD_LIMIT then
96 local now = os.time()
97 for i, server in ipairs(reading) do
98 local client = server:accept()
99 if client then
100 threadm[client] = now
101 threadc = threadc + 1
102 threads[client] = coroutine.create(clhandler[server])
103 end
104 end
105 end
106
107 for client, thread in pairs(threads) do
108 coroutine.resume(thread, client)
109 local now = os.time()
110 if coroutine.status(thread) == "dead" then
111 threadc = threadc - 1
112 threads[client] = nil
113 elseif threadm[client] and threadm[client] + THREAD_TIMEOUT < now then
114 threads[client] = nil
115 threadc = threadc - 1
116 client:close()
117 elseif not threadi[client] then
118 threadm[client] = now
119 idle = false
120 end
121 end
122
123 if idle then
124 collectgarbage()
125 socket.sleep(THREAD_IDLEWAIT)
126 end
127 end