ac6e29216b966c0403545cf09f5b249a079471e7
[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(threads, _meta)
35 setmetatable(threadm, _meta)
36 setmetatable(threadi, _meta)
37
38
39 function Socket(ip, port)
40         local sock, err = socket.bind( ip, port )
41
42         if sock then
43                 sock:settimeout( 0, "t" )
44         end
45
46         return sock, err
47 end
48
49 function corecv(socket, ...)
50         threadi[socket] = true
51
52         while true do
53                 local chunk, err, part = socket:receive(...)
54
55                 if err ~= "timeout" then
56                         threadi[socket] = false
57                         return chunk, err, part
58                 end
59  
60                 coroutine.yield()
61         end
62 end
63
64 function cosend(socket, chunk, i, ...)
65         threadi[socket] = true
66         i = i or 1
67
68         while true do
69                 local stat, err, sent = socket:send(chunk, i, ...)
70
71                 if err ~= "timeout" then
72                         threadi[socket] = false
73                         return stat, err, sent
74                 else
75                         i = sent and (sent + 1) or i
76                 end
77  
78                 coroutine.yield()
79         end
80 end
81
82 function register(socket, s_clhandler, s_errhandler)
83         table.insert(reading, socket)
84         clhandler[socket] = s_clhandler
85         erhandler[socket] = s_errhandler
86 end
87
88 function run()
89         while true do
90                 step()
91         end
92 end
93
94 function step()
95         print(collectgarbage("count"))
96         local idle = true
97         if not THREAD_LIMIT or threadc < THREAD_LIMIT then
98                 local now = os.time()
99                 for i, server in ipairs(reading) do
100                         local client = server:accept()
101                         if client then
102                                 threadm[client] = now
103                                 threadc = threadc + 1
104                                 threads[client] = coroutine.create(clhandler[server])
105                         end
106                 end
107         end
108         
109         for client, thread in pairs(threads) do
110                 coroutine.resume(thread, client)
111                 local now = os.time()
112                 if coroutine.status(thread) == "dead" then
113                         threadc = threadc - 1
114                 elseif threadm[client] and threadm[client] + THREAD_TIMEOUT < now then
115                         threads[client] = nil
116                         threadc = threadc - 1   
117                         client:close()
118                 elseif not threadi[client] then 
119                         threadm[client] = now
120                         idle = false
121                 end
122         end
123         
124         if idle then
125                 socket.sleep(THREAD_IDLEWAIT)
126         end
127 end