Remove Luasocket - obsoleted by nixio

Remove old luasocket based LuCI Httpd - obsoleted by lucittpd
Remove lpeg - not in use
Rewrite luci-splashd using nixio
This commit is contained in:
Steven Barth 2009-05-01 11:28:34 +00:00
parent 78d8ad784b
commit 67fbe1b33c
17 changed files with 11 additions and 1040 deletions

View file

@ -48,8 +48,7 @@ runboa: hostenv
runhttpd: hostenv
build/hostenv.sh $(realpath host) $(LUA_MODULEDIR) $(LUA_LIBRARYDIR) "$(realpath host/usr/bin/lucittpd) $(realpath host)/usr/lib/lucittpd/plugins"
runluci: luahost
build/hostenv.sh $(realpath host) $(LUA_MODULEDIR) $(LUA_LIBRARYDIR) "$(realpath libs/httpd/host/runluci) $(realpath host) $(HTDOCS)"
runluci: runhttpd
runlua: hostenv
build/hostenv.sh $(realpath host) $(LUA_MODULEDIR) $(LUA_LIBRARYDIR) lua

View file

@ -1,41 +1,20 @@
#!/usr/bin/lua
require("socket")
require("luci.ip")
require("luci.model.uci")
local nixio = require "nixio"
local server = nixio.bind(nil, arg[1] or 8082)
local stat = server:listen(32)
local uci = luci.model.uci.cursor_state()
uci:load("network")
local server = socket.bind("0.0.0.0", arg[1] or 8082)
server:settimeout(0, "t")
while true do
while stat do
local client = server:accept()
if client then
client:settimeout(1)
local srv
local ip = luci.ip.IPv4((client:getpeername()))
client:setopt("socket", "rcvtimeo", 1)
client:setopt("socket", "sndtimeo", 1)
local srv = client:getsockname()
local function find_srv(section)
if section.ipaddr then
local net = luci.ip.IPv4(section.ipaddr, section.netmask)
if ip and net and net:contains(ip) then
srv = section.ipaddr
return
end
end
end
uci:foreach("network", "interface", find_srv)
uci:foreach("network", "alias", find_srv)
client:receive()
client:send("HTTP/1.0 302 Found\r\nLocation: http://" .. srv ..
client:read(1024)
client:writeall("HTTP/1.0 302 Found\r\nLocation: http://" .. srv ..
(arg[2] or "/luci/splash") .. "\r\n\r\n")
client:close()
else
socket.sleep(0.1)
end
end

View file

@ -1 +0,0 @@
lpeg-*

View file

@ -1,33 +0,0 @@
include ../../build/config.mk
include ../../build/gccconfig.mk
LPEG_VERSION = 0.8.1
LPEG_SITE = http://www.inf.puc-rio.br/~roberto/lpeg
LPEG_DIR = lpeg-$(LPEG_VERSION)
LPEG_FILE = $(LPEG_DIR).tar.gz
LPEG_URL = $(LPEG_SITE)/$(LPEG_FILE)
all: compile
include ../../build/module.mk
$(LPEG_FILE):
wget -O $@ $(LPEG_URL) || rm -f $@
$(LPEG_DIR)/.prepared: $(LPEG_FILE)
rm -rf $(LPEG_DIR)
tar xvfz $(LPEG_FILE)
touch $@
compile: $(LPEG_DIR)/.prepared
$(MAKE) -C $(LPEG_DIR) CC=$(CC) COPT="$(CFLAGS) $(LUA_CFLAGS) -fpic"
mkdir -p dist$(LUA_LIBRARYDIR)
cp $(LPEG_DIR)/{lpeg.so,re.lua} dist$(LUA_LIBRARYDIR)
luasource:
luastrip:
luacompile:
compile-all: compile
clean:
rm -rf $(LPEG_DIR) $(LPEG_FILE)

View file

@ -1 +0,0 @@
luasocket-*

View file

@ -1,32 +0,0 @@
include ../../build/config.mk
include ../../build/gccconfig.mk
LUASOCKET_VERSION = 2.0.2
LUASOCKET_SITE = http://dev.luci.freifunk-halle.net/sources/
LUASOCKET_DIR = luasocket-$(LUASOCKET_VERSION)
LUASOCKET_FILE = $(LUASOCKET_DIR).tar.gz
LUASOCKET_URL = $(LUASOCKET_SITE)/$(LUASOCKET_FILE)
INSTALL_MODULEDIR = dist$(LUA_MODULEDIR)
INSTALL_LIBRARYDIR = dist$(LUA_LIBRARYDIR)
all: compile
include ../../build/module.mk
$(LUASOCKET_FILE):
wget -O $@ $(LUASOCKET_URL) || rm -f $@
$(LUASOCKET_DIR)/.prepared: $(LUASOCKET_FILE)
rm -rf $(LUASOCKET_DIR)
tar xvfz $(LUASOCKET_FILE)
touch $@
compile: $(LUASOCKET_DIR)/.prepared
$(MAKE) -C $(LUASOCKET_DIR) CC=$(CC) CFLAGS="$(CFLAGS) $(LUA_CFLAGS) $(FPIC)" LDFLAGS="$(LDFLAGS) $(LUA_SHLIBS) $(SHLIB_FLAGS)" OS="$(OS)"
mkdir -p $(INSTALL_LIBRARYDIR) $(INSTALL_MODULEDIR)
$(MAKE) -C $(LUASOCKET_DIR) install INSTALL_TOP_SHARE=../../$(INSTALL_MODULEDIR) INSTALL_TOP_LIB=../../$(INSTALL_LIBRARYDIR)
compile-all: compile
clean:
rm -rf $(LUASOCKET_DIR) $(LUASOCKET_FILE)

View file

@ -1,42 +0,0 @@
# Copyright (C) 2008 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
# $Id$
include $(TOPDIR)/rules.mk
PKG_NAME:=lpeg
PKG_VERSION:=0.8.1
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=http://www.inf.puc-rio.br/~roberto/lpeg
PKG_MD5SUM:=b6b172bbcdcba5c87b37eef9bb5ae199
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
PKG_INSTALL_DIR:=$(PKG_BUILD_DIR)/ipkg-install
include $(INCLUDE_DIR)/package.mk
define Package/lpeg
SECTION:=lang
CATEGORY:=Languages
SUBMENU:=LUA
TITLE:=LPEG - Parsing Expression Grammars
URL:=http://www.inf.puc-rio.br/~roberto/lpeg.html
DEPENDS:=+liblua
endef
MAKE_FLAGS += COPT="$(TARGET_CFLAGS) $(FPIC) -I$(STAGING_DIR)/usr/include"
define Build/Configure
endef
define Package/lpeg/install
$(INSTALL_DIR) $(1)/usr/lib/lua
$(CP) $(PKG_BUILD_DIR)/{lpeg.so,re.lua} $(1)/usr/lib/lua
endef
$(eval $(call BuildPackage,lpeg))

View file

@ -1,40 +0,0 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luasocket
PKG_VERSION:=2.0.2
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=http://dev.luci.freifunk-halle.net/sources/\
http://luaforge.net/frs/download.php/2664/
PKG_MD5SUM:=41445b138deb7bcfe97bff957503da8e
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
PKG_INSTALL_DIR:=$(PKG_BUILD_DIR)/ipkg-install
include $(INCLUDE_DIR)/package.mk
define Package/luasocket
SECTION:=lang
CATEGORY:=Languages
SUBMENU:=LUA
TITLE:=Socket support for Lua
URL:=http://luaforge.net/projects/luasockets/
DEPENDS:=+liblua
endef
define Build/Configure
endef
define Build/Compile
for i in $(PKG_BUILD_DIR)/src/*.lua; do luac -s -o $$$$i $$$$i; done
$(call Build/Compile/Default)
endef
MAKE_FLAGS += LD="$(TARGET_CC)" CFLAGS="$(TARGET_CFLAGS) -I$(STAGING_DIR)/usr/include -pedantic -Wall -fpic" LDFLAGS="$(TARGET_LDFLAGS) -shared -fpic"
define Package/luasocket/install
$(MAKE) -C$(PKG_BUILD_DIR) install INSTALL_TOP_SHARE=$(1)/usr/lib/lua INSTALL_TOP_LIB=$(1)/usr/lib/lua
endef
$(eval $(call BuildPackage,luasocket))

View file

@ -462,7 +462,7 @@ endef
define Package/luci-app-splash
$(call Package/luci/fftemplate)
DEPENDS+=+PACKAGE_luci-app-splash:luasocket
DEPENDS+=+PACKAGE_luci-app-splash:luci-nixio
TITLE:=Freifunk DHCP-Splash application
endef

View file

@ -1,2 +0,0 @@
include ../../build/config.mk
include ../../build/module.mk

View file

@ -1,48 +0,0 @@
#!/usr/bin/env lua
require("luci.httpd")
require("luci.httpd.server")
require("luci.httpd.handler.file")
require("luci.httpd.handler.luci")
SYSROOT = arg[1]
DOCROOT = SYSROOT .. arg[2]
PORT = 8080
collectgarbage("setpause", 150)
serversocket = luci.httpd.Socket("0.0.0.0", PORT)
server = luci.httpd.server.Server()
vhost = luci.httpd.server.VHost()
server:set_default_vhost(vhost)
pcall(function()
require "uci"
require "luci.model.uci".cursor = function(config, save)
return uci.cursor(config or SYSROOT .. "/etc/config", save or SYSROOT .. "/tmp/.uci")
end
local x = require "luci.uvl".UVL.__init__
require "luci.uvl".UVL.__init__ = function(self, schemedir)
x(self, schemedir or SYSROOT .. "/lib/uci/schema")
end
end)
require("luci.sys")
luci.sys.user.checkpasswd = function() return true end
filehandler = luci.httpd.handler.file.Simple(DOCROOT)
vhost:set_default_handler(filehandler)
lucihandler = luci.httpd.handler.luci.Luci()
vhost:set_handler("/luci", lucihandler)
io.stderr:write("Starting LuCI HTTPD on port " .. PORT .. "...\n")
io.stderr:write("Point your browser to http://localhost:" .. PORT .. "/luci\n")
--daemon = luci.httpd.Daemon()
--daemon.debug = true
luci.httpd.register(serversocket, server:create_daemon_handlers())
luci.httpd.run()

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="refresh" content="0; URL=/luci" />
</head>
<body style="background-color: black">
<a style="color: white; text-decoration: none" href="/luci">LuCI - Lua Configuration Interface</a>
</body>
</html>

View file

@ -1,126 +0,0 @@
--[[
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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
module("luci.httpd", package.seeall)
require("socket")
THREAD_IDLEWAIT = 0.01
THREAD_TIMEOUT = 90
THREAD_LIMIT = nil
local reading = {}
local clhandler = {}
local erhandler = {}
local threadc = 0
local threads = {}
local threadm = {}
local threadi = {}
local _meta = {__mode = "k"}
setmetatable(threadm, _meta)
setmetatable(threadi, _meta)
function Socket(ip, port)
local sock, err = socket.bind( ip, port )
if sock then
sock:settimeout( 0, "t" )
end
return sock, err
end
function corecv(socket, ...)
threadi[socket] = true
while true do
local chunk, err, part = socket:receive(...)
if err ~= "timeout" then
threadi[socket] = false
return chunk, err, part
end
coroutine.yield()
end
end
function cosend(socket, chunk, i, ...)
threadi[socket] = true
i = i or 1
while true do
local stat, err, sent = socket:send(chunk, i, ...)
if err ~= "timeout" then
threadi[socket] = false
return stat, err, sent
else
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
function run()
while true do
step()
end
end
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
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
socket.sleep(THREAD_IDLEWAIT)
end
end

View file

@ -1,199 +0,0 @@
--[[
HTTP server implementation for LuCI - file handler
(c) 2008 Steven Barth <steven@midlink.org>
(c) 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
module("luci.httpd.handler.file", package.seeall)
require("luci.httpd.module")
require("luci.http.protocol.date")
require("luci.http.protocol.mime")
require("luci.http.protocol.conditionals")
require("luci.fs")
local ltn12 = require("luci.ltn12")
Simple = luci.util.class(luci.httpd.module.Handler)
Response = luci.httpd.module.Response
function Simple.__init__(self, docroot, dirlist)
luci.httpd.module.Handler.__init__(self)
self.docroot = docroot
self.dirlist = dirlist and true or false
self.proto = luci.http.protocol
self.mime = luci.http.protocol.mime
self.date = luci.http.protocol.date
self.cond = luci.http.protocol.conditionals
end
function Simple.getfile(self, uri)
local file = self.docroot .. uri:gsub("%.%./+", "")
local stat = luci.fs.stat(file)
return file, stat
end
function Simple.handle_get(self, request, sourcein, sinkerr)
local file, stat = self:getfile( self.proto.urldecode( request.env.PATH_INFO, true ) )
if stat then
if stat.type == "regular" then
-- Generate Entity Tag
local etag = self.cond.mk_etag( stat )
-- Check conditionals
local ok, code, hdrs
ok, code, hdrs = self.cond.if_modified_since( request, stat )
if ok then
ok, code, hdrs = self.cond.if_match( request, stat )
if ok then
ok, code, hdrs = self.cond.if_unmodified_since( request, stat )
if ok then
ok, code, hdrs = self.cond.if_none_match( request, stat )
if ok then
local f, err = io.open(file)
if f then
-- Send Response
return Response(
200, {
["Date"] = self.date.to_http( os.time() );
["Last-Modified"] = self.date.to_http( stat.mtime );
["Content-Type"] = self.mime.to_mime( file );
["Content-Length"] = stat.size;
["ETag"] = etag;
}
), ltn12.source.file(f)
else
return self:failure( 403, err:gsub("^.+: ", "") )
end
else
return Response( code, hdrs or { } )
end
else
return Response( code, hdrs or { } )
end
else
return Response( code, hdrs or { } )
end
else
return Response( code, hdrs or { } )
end
elseif stat.type == "directory" then
local ruri = request.request_uri:gsub("/$","")
local duri = self.proto.urldecode( ruri, true )
local root = self.docroot:gsub("/$","")
-- check for index files
local index_candidates = {
"index.html", "index.htm", "default.html", "default.htm",
"index.txt", "default.txt"
}
-- try to find an index file and redirect to it
for i, candidate in ipairs( index_candidates ) do
local istat = luci.fs.stat(
root .. "/" .. duri .. "/" .. candidate
)
if istat ~= nil and istat.type == "regular" then
return Response( 301, {
["Location"] = ruri .. "/" .. candidate
} ), ltn12.source.empty()
end
end
local html = string.format(
'<?xml version="1.0" encoding="ISO-8859-15"?>\n' ..
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' ..
'"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n' ..
'<html xmlns="http://www.w3.org/1999/xhtml" ' ..
'xml:lang="en" lang="en">\n' ..
'<head>\n' ..
'<title>Index of %s/</title>\n' ..
'<style type="text/css"><!--\n' ..
'body { background-color:#FFFFFF; color:#000000 } ' ..
'li { border-bottom:1px dotted #CCCCCC; padding:3px } ' ..
'small { font-size:60%%; color:#999999 } ' ..
'p { margin:0 }' ..
'\n--></style></head><body><h1>Index of %s/</h1><hr /><ul>',
duri, duri
)
local entries = luci.fs.dir( file )
if type(entries) == "table" then
for i, e in luci.util.spairs(
entries, function(a,b)
if entries[a] == '..' then
return true
elseif entries[b] == '..' then
return false
else
return ( entries[a] < entries[b] )
end
end
) do
if e ~= '.' and ( e == '..' or e:sub(1,1) ~= '.' ) then
local estat = luci.fs.stat( file .. "/" .. e )
if estat.type == "directory" then
html = html .. string.format(
'<li><p><a href="%s/%s/">%s/</a> ' ..
'<small>(directory)</small><br />' ..
'<small>Changed: %s</small></li>',
ruri, self.proto.urlencode( e ), e,
self.date.to_http( estat.mtime )
)
else
html = html .. string.format(
'<li><p><a href="%s/%s">%s</a> ' ..
'<small>(%s)</small><br />' ..
'<small>Size: %i Bytes | ' ..
'Changed: %s</small></li>',
ruri, self.proto.urlencode( e ), e,
self.mime.to_mime( e ),
estat.size, self.date.to_http( estat.mtime )
)
end
end
end
html = html .. '</ul><hr /></body></html>'
return Response(
200, {
["Date"] = self.date.to_http( os.time() );
["Content-Type"] = "text/html; charset=ISO-8859-15";
}
), ltn12.source.string(html)
else
return self:failure(403, "Permission denied")
end
else
return self:failure(403, "Unable to transmit " .. stat.type .. " " .. file)
end
else
return self:failure(404, "No such file: " .. file)
end
end
function Simple.handle_head(self, ...)
local response, sourceout = self:handle_get(...)
return response
end

View file

@ -1,96 +0,0 @@
--[[
HTTP server implementation for LuCI - luci handler
(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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
module("luci.httpd.handler.luci", package.seeall)
require("luci.dispatcher")
require("luci.http")
require("luci.http.protocol.date")
local ltn12 = require("luci.ltn12")
Luci = luci.util.class(luci.httpd.module.Handler)
Response = luci.httpd.module.Response
function Luci.__init__(self, limit)
luci.httpd.module.Handler.__init__(self)
self.limit = limit or 5
self.running = {}
setmetatable(self.running, {__mode = "k"})
end
function Luci.handle_head(self, ...)
local response, sourceout = self:handle_get(...)
return response
end
function Luci.handle_post(self, ...)
return self:handle_get(...)
end
function Luci.handle_get(self, request, sourcein, sinkerr)
local r = luci.http.Request(
request.env,
sourcein,
sinkerr
)
local res, id, data1, data2 = true, 0, nil, nil
local headers = {}
local status = 200
local active = true
local x = coroutine.create(luci.dispatcher.httpdispatch)
while not id or id < 3 do
coroutine.yield()
res, id, data1, data2 = coroutine.resume(x, r)
if not res then
status = 500
headers["Content-Type"] = "text/plain"
local err = {id}
return Response( status, headers ), function() return table.remove(err) end
end
if id == 1 then
status = data1
elseif id == 2 then
headers[data1] = data2
end
end
local function iter()
local res, id, data = coroutine.resume(x)
if not res then
return nil, id
elseif not id or not active then
return true
elseif id == 5 then
active = false
while (coroutine.resume(x)) do
end
return nil
elseif id == 4 then
return data
end
if coroutine.status(x) == "dead" then
return nil
end
end
return Response(status, headers), iter
end

View file

@ -1,136 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
module("luci.httpd.module", package.seeall)
require("luci.util")
require("luci.http.protocol")
local ltn12 = require("luci.ltn12")
-- Server handler implementation
Handler = luci.util.class()
-- Constructor
function Handler.__init__(self)
self.filters = {}
self.handler = {}
end
-- Adds a filter to the filter chain
function Handler.addfilter(self, filter)
table.insert(self.filters, filter)
end
-- Creates a failure reply
function Handler.failure(self, code, message)
local response = Response(code, { ["Content-Type"] = "text/plain" })
local sourceout = ltn12.source.string(message)
return response, sourceout
end
-- Processes a request
function Handler.process(self, request, sourcein, sinkerr, ...)
local stat, response, sourceout
-- Process incoming filters
for i, f in ipairs(self.filters) do
local i = f:get("input")
if i then
sourcein = ltn12.source.chain(sourcein, i)
end
if f.request then
f:request(request)
end
end
-- Detect request Method
local hname = "handle_" .. request.request_method
if self[hname] then
-- Run the handler
stat, response, sourceout = luci.util.copcall(
self[hname], self, request, sourcein, sinkerr, ...
)
-- Check for any errors
if not stat then
response, sourceout = self:failure(500, response)
end
else
response, sourceout = self:failure(405, luci.http.protocol.statusmsg[405])
end
-- Check data
if not luci.util.instanceof(response, Response) then
response, sourceout = self:failure(500, "Core error: Invalid module response!")
end
-- Process outgoing filters
for i, f in ipairs(self.filters) do
local o = f:get("output")
if o then
sourceout = ltn12.source.chain(sourceout, o)
end
if f.response then
f:response(response)
end
end
return response, sourceout
end
-- Server Filter implementation
Filter = luci.util.class()
function Filter.get(self, name)
return self[name] and function(...) return self[name](self, ...) end
end
-- Filters the incoming body stream
-- abstract function Filter.input(chunk)
-- Filters the outgoing body stream
-- abstract function Filter.output(chunk)
-- Filters the request object
-- abstract function Filter.request(request)
-- Filters the response object
-- abstract function Filter.response(response)
-- Handler Response
Response = luci.util.class()
function Response.__init__(self, status, headers)
self.status = tonumber(status) or 200
self.headers = (type(headers) == "table") and headers or {}
end
function Response.addheader(self, key, value)
self.headers[key] = value
end
function Response.setstatus(self, status)
self.status = status
end

View file

@ -1,241 +0,0 @@
--[[
HTTP server implementation for LuCI - helper class
(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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
module("luci.httpd.server", package.seeall)
require("socket")
require("socket.http")
require("luci.util")
READ_BUFSIZE = 1024
VERSION = 0.2
VHost = luci.util.class()
function VHost.__init__(self, handler)
self.handler = handler
self.dhandler = {}
end
function VHost.process(self, request, sourcein, sinkerr, ...)
local handler = self.handler
local uri = request.env.REQUEST_URI:match("^([^?]*)")
-- SCRIPT_NAME
request.env.SCRIPT_NAME = ""
-- Call URI part
request.env.PATH_INFO = uri
for k, dhandler in pairs(self.dhandler) do
if k == uri or k.."/" == uri:sub(1, #k+1) then
handler = dhandler
request.env.SCRIPT_NAME = k
request.env.PATH_INFO = uri:sub(#k+1)
break;
end
end
if handler then
return handler:process(request, sourcein, sinkerr, ...)
end
end
function VHost.set_default_handler(self, handler)
self.handler = handler
end
function VHost.set_handler(self, match, handler)
self.dhandler[match] = handler
end
Server = luci.util.class()
function Server.__init__(self, host)
self.host = host
self.vhosts = {}
end
function Server.set_default_vhost(self, vhost)
self.host = vhost
end
-- Sets a vhost
function Server.set_vhost(self, name, vhost)
self.vhosts[name] = vhost
end
function Server.create_daemon_handlers(self)
return function(...) return self:process(...) end,
function(...) return self:error_overload(...) end
end
function Server.error(self, socket, code, msg)
hcode = tostring(code)
socket:send( "HTTP/1.0 " .. hcode .. " " ..
luci.http.protocol.statusmsg[code] .. "\r\n" )
socket:send( "Connection: close\r\n" )
socket:send( "Content-Type: text/plain\r\n\r\n" )
if msg then
socket:send( "HTTP-Error " .. code .. ": " .. msg .. "\r\n" )
end
end
function Server.error_overload(self, socket)
self:error(socket, 503, "Too many simultaneous connections")
end
function Server.process( self, client )
-- Setup sockets and sources
local thread = {
receive = function(self, ...) return luci.httpd.corecv(client, ...) end,
send = function(self, ...) return luci.httpd.cosend(client, ...) end,
close = function(self, ...) return client:close(...) end,
getfd = function(self, ...) return client:getfd(...) end,
dirty = function(self, ...) return client:dirty(...) end
}
client:settimeout( 0 )
local sourcein = ltn12.source.empty()
local sourcehdr = luci.http.protocol.header_source( thread )
local sinkerr = ltn12.sink.file( io.stderr )
local close = false
local reading = { client }
local message, err
repeat
-- parse headers
message, err = luci.http.protocol.parse_message_header( sourcehdr )
if not message then
self:error( thread, 400, err )
break
end
-- keep-alive
if message.http_version == 1.1 then
close = (message.env.HTTP_CONNECTION == "close")
else
close = not message.env.HTTP_CONNECTION or message.env.HTTP_CONNECTION == "close"
end
if message.request_method == "get" or message.request_method == "head" then
-- Be happy
elseif message.request_method == "post" then
-- If we have a HTTP/1.1 client and an Expect: 100-continue header then
-- respond with HTTP 100 Continue message
if message.http_version == 1.1 and message.headers['Expect'] and
message.headers['Expect'] == '100-continue'
then
thread:send("HTTP/1.1 100 Continue\r\n\r\n")
end
if message.headers['Transfer-Encoding'] and
message.headers['Transfer-Encoding'] ~= "identity" then
sourcein = socket.source("http-chunked", thread)
elseif message.env.CONTENT_LENGTH then
sourcein = socket.source("by-length", thread,
tonumber(message.env.CONTENT_LENGTH))
else
self:error( thread, 411, luci.http.protocol.statusmsg[411] )
break;
end
-- FIXME: Close for POST requests
close = true
else
self:error( thread, 405, luci.http.protocol.statusmsg[405] )
break;
end
local host = self.vhosts[message.env.HTTP_HOST] or self.host
if not host then
self:error( thread, 500, "Unable to find matching host" )
break;
end
local response, sourceout = host:process(
message, sourcein, sinkerr,
client, io.stderr
)
if not response then
self:error( thread, 500, "Error processing handler" )
end
-- Post process response
local sinkmode = close and "close-when-done" or "keep-open"
if sourceout then
if not response.headers["Content-Length"] then
if message.http_version == 1.1 then
response.headers["Transfer-Encoding"] = "chunked"
sinkmode = "http-chunked"
else
close = true
sinkmode = "close-when-done"
end
end
end
if close then
response.headers["Connection"] = "close"
end
local sinkout = socket.sink(sinkmode, thread)
local header =
message.env.SERVER_PROTOCOL .. " " ..
tostring(response.status) .. " " ..
luci.http.protocol.statusmsg[response.status] .. "\r\n"
header = header .. "Server: LuCI HTTPd/" .. tostring(VERSION) .. "\r\n"
for k,v in pairs(response.headers) do
header = header .. k .. ": " .. v .. "\r\n"
end
thread:send(header .. "\r\n")
if sourceout then
local eof = false
repeat
coroutine.yield()
eof = not ltn12.pump.step(sourceout, sinkout)
until eof
end
until close
client:close()
end