HTTP server implementation for LuCI - core
(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
+(c) 2008 Steven Barth <steven@midlink.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
]]--
-require("ltn12")
+module("luci.httpd", package.seeall)
require("socket")
-require("luci.util")
-require("luci.http.protocol")
-require("luci.httpd.server")
+THREAD_IDLEWAIT = 0.01
+THREAD_TIMEOUT = 90
+THREAD_LIMIT = nil
+local reading = {}
+local clhandler = {}
+local erhandler = {}
-local srv = luci.httpd.server
-local host = "0.0.0.0"
-local port = 50000
+local threadc = 0
+local threads = {}
+local threadm = {}
+local threadi = {}
+local _meta = {__mode = "k"}
+setmetatable(threadm, _meta)
+setmetatable(threadi, _meta)
-server = socket.bind(host, port)
-server:settimeout( 0, "t" )
-reading = { server }
-running = { }
+function Socket(ip, port)
+ local sock, err = socket.bind( ip, port )
+ if sock then
+ sock:settimeout( 0, "t" )
+ end
-while true do
+ return sock, err
+end
- local input = socket.select( reading, nil, 0.1 )
+function corecv(socket, ...)
+ threadi[socket] = true
- -- accept new connections
- for i, connection in ipairs(input) do
+ while true do
+ local chunk, err, part = socket:receive(...)
- local sock = connection:accept()
+ if err ~= "timeout" then
+ threadi[socket] = false
+ return chunk, err, part
+ end
+
+ coroutine.yield()
+ end
+end
- -- check capacity
- if #running < srv.MAX_CLIENTS then
+function cosend(socket, chunk, i, ...)
+ threadi[socket] = true
+ i = i or 1
- table.insert( running, {
- coroutine.create( srv.client_handler ),
- sock
- } )
+ while true do
+ local stat, err, sent = socket:send(chunk, i, ...)
- -- reject client
+ if err ~= "timeout" then
+ threadi[socket] = false
+ return stat, err, sent
else
- srv.error503( sock )
+ i = sent and (sent + 1) or i
end
+
+ coroutine.yield()
end
+end
+
+function register(socket, s_clhandler, s_errhandler)
+ table.insert(reading, socket)
+ clhandler[socket] = s_clhandler
+ erhandler[socket] = s_errhandler
+end
- -- create client handler
- for i, client in ipairs( running ) do
+function run()
+ while true do
+ step()
+ end
+end
- -- reap dead clients
- if coroutine.status( client[1] ) == "dead" then
- table.remove( running, i )
+function step()
+ local idle = true
+ if not THREAD_LIMIT or threadc < THREAD_LIMIT then
+ local now = os.time()
+ for i, server in ipairs(reading) do
+ local client = server:accept()
+ if client then
+ threadm[client] = now
+ threadc = threadc + 1
+ threads[client] = coroutine.create(clhandler[server])
+ end
end
-
- coroutine.resume( client[1], client[2] )
+ end
+
+ for client, thread in pairs(threads) do
+ coroutine.resume(thread, client)
+ local now = os.time()
+ if coroutine.status(thread) == "dead" then
+ threadc = threadc - 1
+ threads[client] = nil
+ elseif threadm[client] and threadm[client] + THREAD_TIMEOUT < now then
+ threads[client] = nil
+ threadc = threadc - 1
+ client:close()
+ elseif not threadi[client] then
+ threadm[client] = now
+ idle = false
+ end
+ end
+
+ if idle then
+ collectgarbage()
+ socket.sleep(THREAD_IDLEWAIT)
end
end