* libs/httpd: Rewrote daemon controller to increase performance
[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 function Socket(ip, port)
34         local sock, err = socket.bind( ip, port )
35
36         if sock then
37                 sock:settimeout( 0, "t" )
38         end
39
40         return sock, err
41 end
42
43 function corecv(socket, ...)
44         threadi[socket] = true
45
46         while true do
47                 local chunk, err, part = socket:receive(...)
48
49                 if err ~= "timeout" then
50                         threadi[socket] = false
51                         return chunk, err, part
52                 end
53  
54                 coroutine.yield()
55         end
56 end
57
58 function h(sock)
59         local sink = socket.sink("close-when-done", sock)
60         local f = ltn12.source.file(io.open("/home/steven/workspace/ffluci/host/www/luci-static/openwrt.org/cascade.css"))
61         local s = luci.fs.stat("/home/steven/workspace/ffluci/host/www/luci-static/openwrt.org/cascade.css", "size")
62         sink("HTTP/1.1 200 OK\r\nContent-Length: " ..s.."\r\nConnection: close\r\n\r\n")
63         repeat
64                 coroutine.yield()
65                 eof = not ltn12.pump.step(f, sink)
66         until eof
67 end
68
69
70 function register(socket, s_clhandler, s_errhandler)
71         table.insert(reading, socket)
72         clhandler[socket] = s_clhandler
73         erhandler[socket] = s_errhandler
74 end
75
76 function run()
77         while true do
78                 step()
79         end
80 end
81
82 function step()
83         local idle = true
84                 
85         if not THREAD_LIMIT or threadc < THREAD_LIMIT then
86                 local now = os.time()
87                 for i, server in ipairs(reading) do
88                         local client = server:accept()
89                         if client then
90                                 threadm[client] = now
91                                 threadc = threadc + 1
92                                 threads[client] = coroutine.create(clhandler[server])
93                         end
94                 end
95         end
96         
97         for client, thread in pairs(threads) do
98                 coroutine.resume(thread, client)
99                 local now = os.time()
100                 if coroutine.status(thread) == "dead" then
101                         threads[client] = nil
102                         threadc = threadc - 1
103                 elseif threadm[client] and threadm[client] + THREAD_TIMEOUT < now then
104                         threads[client] = nil
105                         threadc = threadc - 1   
106                         client:close()
107                 elseif not threadi[client] then 
108                         threadm[client] = now
109                         idle = false
110                 end
111         end
112         
113         if idle then
114                 socket.sleep(THREAD_IDLEWAIT)
115         end
116 end