* libs/httpd: Added 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
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
31 Daemon = luci.util.class()
32
33 function Daemon.__init__(self, threadlimit, timeout)
34         self.reading = {}
35         self.running = {}
36         self.handler = {}
37         self.debug   = false
38         self.threadlimit = threadlimit
39         self.timeout = timeout or 0.1
40 end
41
42 function Daemon.dprint(self, msg)
43         if self.debug then
44                 io.stderr:write("[daemon] " .. msg .. "\n")
45         end
46 end
47
48 function Daemon.register(self, sock, clhandler, errhandler)
49         table.insert( self.reading, sock )
50         self.handler[sock] = { clhandler = clhandler, errhandler = errhandler }
51 end
52
53 function Daemon.run(self)
54         while true do
55                 self:step()
56         end
57 end
58
59 function Daemon.step(self)      
60         local input, output, err = socket.select( self.reading, nil, 0 )
61
62         if err == "timeout" and #self.running == 0 then
63                 socket.sleep(self.timeout)
64         end
65
66         -- accept new connections
67         for i, connection in ipairs(input) do
68
69                 local sock = connection:accept()
70                 
71                 if sock then
72                         -- check capacity
73                         if not self.threadlimit or #self.running < self.threadlimit then
74                                 
75                                 if self.debug then
76                                         self:dprint("Accepted incoming connection from " .. sock:getpeername())
77                                 end
78         
79                                 table.insert( self.running, {
80                                         coroutine.create( self.handler[connection].clhandler ),
81                                         sock
82                                 } )
83         
84                                 if self.debug then
85                                         self:dprint("Created " .. tostring(self.running[#self.running][1]))
86                                 end
87         
88                         -- reject client
89                         else
90                                 if self.debug then
91                                         self:dprint("Rejected incoming connection from " .. sock:getpeername())
92                                 end
93         
94                                 if self.handler[connection].errhandler then
95                                         self.handler[connection].errhandler( sock )
96                                 end
97         
98                                 sock:close()
99                         end
100                 end
101         end
102
103         -- create client handler
104         for i, client in ipairs( self.running ) do
105
106                 -- reap dead clients
107                 if coroutine.status( client[1] ) == "dead" then
108                         if self.debug then
109                                 self:dprint("Completed " .. tostring(client[1]))
110                         end
111                         table.remove( self.running, i )
112                 else
113                         if self.debug then
114                                 self:dprint("Resuming " .. tostring(client[1]))
115                         end
116
117                         local stat, err = coroutine.resume( client[1], client[2] )
118                         
119                         if self.debug then
120                                 self:dprint(tostring(client[1]) .. " returned")
121                         end
122
123                         if not stat and self.debug then
124                                 self:dprint("Error in " .. tostring(client[1]) .. " " .. err)
125                         end
126                 end
127         end
128 end