GSoC Commit #1: LuCId + HTTP-Server
This commit is contained in:
parent
0ad58e38b4
commit
8c4f847ea5
20 changed files with 1739 additions and 10 deletions
5
Makefile
5
Makefile
|
@ -52,10 +52,11 @@ runboa: hostenv
|
||||||
runhttpd: hostenv
|
runhttpd: hostenv
|
||||||
build/hostenv.sh $(realpath host) $(LUA_MODULEDIR) $(LUA_LIBRARYDIR) "$(realpath host/usr/bin/lucittpd) $(realpath host)/usr/lib/lucittpd/plugins"
|
build/hostenv.sh $(realpath host) $(LUA_MODULEDIR) $(LUA_LIBRARYDIR) "$(realpath host/usr/bin/lucittpd) $(realpath host)/usr/lib/lucittpd/plugins"
|
||||||
|
|
||||||
runluci: runhttpd
|
runluci: luahost
|
||||||
|
build/hostenv.sh $(realpath host) $(LUA_MODULEDIR) $(LUA_LIBRARYDIR) "$(realpath libs/httpd/host/runluci) $(realpath host) $(HTDOCS)"
|
||||||
|
|
||||||
runlua: hostenv
|
runlua: hostenv
|
||||||
build/hostenv.sh $(realpath host) $(LUA_MODULEDIR) $(LUA_LIBRARYDIR) lua
|
build/hostenv.sh $(realpath host) $(LUA_MODULEDIR) $(LUA_LIBRARYDIR) "lua -i build/setup.lua"
|
||||||
|
|
||||||
runshell: hostenv
|
runshell: hostenv
|
||||||
build/hostenv.sh $(realpath host) $(LUA_MODULEDIR) $(LUA_LIBRARYDIR) $$SHELL
|
build/hostenv.sh $(realpath host) $(LUA_MODULEDIR) $(LUA_LIBRARYDIR) $$SHELL
|
||||||
|
|
|
@ -38,9 +38,6 @@ luastrip: luasource
|
||||||
luacompile: luasource
|
luacompile: luasource
|
||||||
for i in $$(find dist -name *.lua -not -name debug.lua); do $(LUAC) $(LUAC_OPTIONS) -o $$i $$i; done
|
for i in $$(find dist -name *.lua -not -name debug.lua); do $(LUAC) $(LUAC_OPTIONS) -o $$i $$i; done
|
||||||
|
|
||||||
luagzip: luacompile
|
|
||||||
for i in $$(find dist -name *.lua -not -name debug.lua); do gzip -f9 $$i; done
|
|
||||||
|
|
||||||
luaclean:
|
luaclean:
|
||||||
rm -rf dist
|
rm -rf dist
|
||||||
|
|
||||||
|
|
11
build/setup.lua
Normal file
11
build/setup.lua
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
local SYSROOT = os.getenv("LUCI_SYSROOT")
|
||||||
|
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
|
||||||
|
|
2
libs/lucid-http/Makefile
Normal file
2
libs/lucid-http/Makefile
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
include ../../build/module.mk
|
||||||
|
include ../../build/config.mk
|
33
libs/lucid-http/luasrc/lucid/http.lua
Normal file
33
libs/lucid-http/luasrc/lucid/http.lua
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
--[[
|
||||||
|
LuCI - Lua Configuration Interface
|
||||||
|
|
||||||
|
Copyright 2009 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$
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local require, ipairs, pcall = require, ipairs, pcall
|
||||||
|
local srv = require "luci.lucid.http.server"
|
||||||
|
|
||||||
|
module "luci.lucid.http"
|
||||||
|
|
||||||
|
function factory(publisher)
|
||||||
|
local server = srv.Server()
|
||||||
|
for _, r in ipairs(publisher) do
|
||||||
|
local t = r[".type"]
|
||||||
|
local s, mod = pcall(require, "luci.lucid.http." .. (r[".type"] or ""))
|
||||||
|
if s and mod then
|
||||||
|
mod.factory(server, r)
|
||||||
|
else
|
||||||
|
return nil, mod
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return function(...) return server:process(...) end
|
||||||
|
end
|
47
libs/lucid-http/luasrc/lucid/http/DirectoryPublisher.lua
Normal file
47
libs/lucid-http/luasrc/lucid/http/DirectoryPublisher.lua
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
--[[
|
||||||
|
LuCId HTTP-Slave
|
||||||
|
(c) 2009 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$
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local ipairs, require, tostring, type = ipairs, require, tostring, type
|
||||||
|
local file = require "luci.lucid.http.handler.file"
|
||||||
|
local srv = require "luci.lucid.http.server"
|
||||||
|
|
||||||
|
module "luci.lucid.http.DirectoryPublisher"
|
||||||
|
|
||||||
|
|
||||||
|
function factory(server, config)
|
||||||
|
config.domain = config.domain or ""
|
||||||
|
local vhost = server:get_vhosts()[config.domain]
|
||||||
|
if not vhost then
|
||||||
|
vhost = srv.VHost()
|
||||||
|
server:set_vhost(config.domain, vhost)
|
||||||
|
end
|
||||||
|
|
||||||
|
local handler = file.Simple(config.name, config.physical, config)
|
||||||
|
if config.read then
|
||||||
|
for _, r in ipairs(config.read) do
|
||||||
|
if r:sub(1,1) == ":" then
|
||||||
|
handler:restrict({interface = r:sub(2)})
|
||||||
|
else
|
||||||
|
handler:restrict({user = r})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(config.virtual) == "table" then
|
||||||
|
for _, v in ipairs(config.virtual) do
|
||||||
|
vhost:set_handler(v, handler)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
vhost:set_handler(config.virtual, handler)
|
||||||
|
end
|
||||||
|
end
|
62
libs/lucid-http/luasrc/lucid/http/LuciWebPublisher.lua
Normal file
62
libs/lucid-http/luasrc/lucid/http/LuciWebPublisher.lua
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
--[[
|
||||||
|
LuCId HTTP-Slave
|
||||||
|
(c) 2009 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$
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local ipairs, pcall, type = ipairs, pcall, type
|
||||||
|
local luci = require "luci.lucid.http.handler.luci"
|
||||||
|
local srv = require "luci.lucid.http.server"
|
||||||
|
|
||||||
|
|
||||||
|
module "luci.lucid.http.LuciWebPublisher"
|
||||||
|
|
||||||
|
function factory(server, config)
|
||||||
|
pcall(function()
|
||||||
|
require "luci.dispatcher"
|
||||||
|
require "luci.cbi"
|
||||||
|
end)
|
||||||
|
|
||||||
|
config.domain = config.domain or ""
|
||||||
|
local vhost = server:get_vhosts()[config.domain]
|
||||||
|
if not vhost then
|
||||||
|
vhost = srv.VHost()
|
||||||
|
server:set_vhost(config.domain, vhost)
|
||||||
|
end
|
||||||
|
|
||||||
|
local prefix
|
||||||
|
if config.physical and #config.physical > 0 then
|
||||||
|
prefix = {}
|
||||||
|
for k in config.physical:gmatch("[^/]+") do
|
||||||
|
if #k > 0 then
|
||||||
|
prefix[#prefix+1] = k
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local handler = luci.Luci(config.name, prefix)
|
||||||
|
if config.exec then
|
||||||
|
for _, r in ipairs(config.exec) do
|
||||||
|
if r:sub(1,1) == ":" then
|
||||||
|
handler:restrict({interface = r:sub(2)})
|
||||||
|
else
|
||||||
|
handler:restrict({user = r})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(config.virtual) == "table" then
|
||||||
|
for _, v in ipairs(config.virtual) do
|
||||||
|
vhost:set_handler(v, handler)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
vhost:set_handler(config.virtual, handler)
|
||||||
|
end
|
||||||
|
end
|
31
libs/lucid-http/luasrc/lucid/http/Redirector.lua
Normal file
31
libs/lucid-http/luasrc/lucid/http/Redirector.lua
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
--[[
|
||||||
|
LuCId HTTP-Slave
|
||||||
|
(c) 2009 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$
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local ipairs = ipairs
|
||||||
|
local catchall = require "luci.lucid.http.handler.catchall"
|
||||||
|
local srv = require "luci.lucid.http.server"
|
||||||
|
|
||||||
|
module "luci.lucid.http.Redirector"
|
||||||
|
|
||||||
|
|
||||||
|
function factory(server, config)
|
||||||
|
config.domain = config.domain or ""
|
||||||
|
local vhost = server:get_vhosts()[config.domain]
|
||||||
|
if not vhost then
|
||||||
|
vhost = srv.VHost()
|
||||||
|
server:set_vhost(config.domain, vhost)
|
||||||
|
end
|
||||||
|
|
||||||
|
local handler = catchall.Redirect(config.name, config.physical)
|
||||||
|
vhost:set_handler(config.virtual, handler)
|
||||||
|
end
|
53
libs/lucid-http/luasrc/lucid/http/handler/catchall.lua
Normal file
53
libs/lucid-http/luasrc/lucid/http/handler/catchall.lua
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
--[[
|
||||||
|
LuCId HTTP-Slave
|
||||||
|
(c) 2009 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$
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local srv = require "luci.lucid.http.server"
|
||||||
|
local proto = require "luci.http.protocol"
|
||||||
|
|
||||||
|
module "luci.lucid.http.handler.catchall"
|
||||||
|
|
||||||
|
Redirect = util.class(srv.Handler)
|
||||||
|
|
||||||
|
function Redirect.__init__(self, name, target)
|
||||||
|
srv.Handler.__init__(self, name)
|
||||||
|
self.target = target
|
||||||
|
end
|
||||||
|
|
||||||
|
function Redirect.handle_GET(self, request)
|
||||||
|
local target = self.target
|
||||||
|
local protocol = request.env.HTTPS and "https://" or "http://"
|
||||||
|
local server = request.env.SERVER_ADDR
|
||||||
|
if server:find(":") then
|
||||||
|
server = "[" .. server .. "]"
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.target:sub(1,1) == ":" then
|
||||||
|
target = protocol .. server .. target
|
||||||
|
end
|
||||||
|
|
||||||
|
local s, e = target:find("%TARGET%", 1, true)
|
||||||
|
if s then
|
||||||
|
local req = protocol .. (request.env.HTTP_HOST or server)
|
||||||
|
.. request.env.REQUEST_URI
|
||||||
|
target = target:sub(1, s-1) .. req .. target:sub(e+1)
|
||||||
|
end
|
||||||
|
|
||||||
|
return 302, { Location = target }
|
||||||
|
end
|
||||||
|
|
||||||
|
Redirect.handle_POST = Redirect.handle_GET
|
||||||
|
|
||||||
|
function Redirect.handle_HEAD(self, request)
|
||||||
|
local stat, head = self:handle_GET(request)
|
||||||
|
return stat, head
|
||||||
|
end
|
250
libs/lucid-http/luasrc/lucid/http/handler/file.lua
Normal file
250
libs/lucid-http/luasrc/lucid/http/handler/file.lua
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
--[[
|
||||||
|
|
||||||
|
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$
|
||||||
|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local ipairs, type, tonumber = ipairs, type, tonumber
|
||||||
|
local os = require "os"
|
||||||
|
local nixio = require "nixio", require "nixio.util"
|
||||||
|
local fs = require "nixio.fs"
|
||||||
|
local util = require "luci.util"
|
||||||
|
local ltn12 = require "luci.ltn12"
|
||||||
|
local srv = require "luci.lucid.http.server"
|
||||||
|
local string = require "string"
|
||||||
|
|
||||||
|
local prot = require "luci.http.protocol"
|
||||||
|
local date = require "luci.http.protocol.date"
|
||||||
|
local mime = require "luci.http.protocol.mime"
|
||||||
|
local cond = require "luci.http.protocol.conditionals"
|
||||||
|
|
||||||
|
module "luci.lucid.http.handler.file"
|
||||||
|
|
||||||
|
Simple = util.class(srv.Handler)
|
||||||
|
|
||||||
|
function Simple.__init__(self, name, docroot, options)
|
||||||
|
srv.Handler.__init__(self, name)
|
||||||
|
self.docroot = docroot
|
||||||
|
self.realdocroot = fs.realpath(self.docroot)
|
||||||
|
|
||||||
|
options = options or {}
|
||||||
|
self.dirlist = not options.noindex
|
||||||
|
self.error404 = options.error404
|
||||||
|
end
|
||||||
|
|
||||||
|
function Simple.parse_range(self, request, size)
|
||||||
|
if not request.headers.Range then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local from, to = request.headers.Range:match("bytes=([0-9]*)-([0-9]*)")
|
||||||
|
if not (from or to) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
from, to = tonumber(from), tonumber(to)
|
||||||
|
if not (from or to) then
|
||||||
|
return true
|
||||||
|
elseif not from then
|
||||||
|
from, to = size - to, size - 1
|
||||||
|
elseif not to then
|
||||||
|
to = size - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Not satisfiable
|
||||||
|
if from >= size then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Normalize
|
||||||
|
if to >= size then
|
||||||
|
to = size - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local range = "bytes " .. from .. "-" .. to .. "/" .. size
|
||||||
|
return from, (1 + to - from), range
|
||||||
|
end
|
||||||
|
|
||||||
|
function Simple.getfile(self, uri)
|
||||||
|
if not self.realdocroot then
|
||||||
|
self.realdocroot = fs.realpath(self.docroot)
|
||||||
|
end
|
||||||
|
local file = fs.realpath(self.docroot .. uri)
|
||||||
|
if not file or file:sub(1, #self.realdocroot) ~= self.realdocroot then
|
||||||
|
return uri
|
||||||
|
end
|
||||||
|
return file, fs.stat(file)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Simple.handle_GET(self, request)
|
||||||
|
local file, stat = self:getfile(prot.urldecode(request.env.PATH_INFO, true))
|
||||||
|
|
||||||
|
if stat then
|
||||||
|
if stat.type == "reg" then
|
||||||
|
|
||||||
|
-- Generate Entity Tag
|
||||||
|
local etag = cond.mk_etag( stat )
|
||||||
|
|
||||||
|
-- Check conditionals
|
||||||
|
local ok, code, hdrs
|
||||||
|
|
||||||
|
ok, code, hdrs = cond.if_modified_since( request, stat )
|
||||||
|
if ok then
|
||||||
|
ok, code, hdrs = cond.if_match( request, stat )
|
||||||
|
if ok then
|
||||||
|
ok, code, hdrs = cond.if_unmodified_since( request, stat )
|
||||||
|
if ok then
|
||||||
|
ok, code, hdrs = cond.if_none_match( request, stat )
|
||||||
|
if ok then
|
||||||
|
local f, err = nixio.open(file)
|
||||||
|
|
||||||
|
if f then
|
||||||
|
local code = 200
|
||||||
|
local o, s, r = self:parse_range(request, stat.size)
|
||||||
|
|
||||||
|
if not o then
|
||||||
|
return self:failure(416, "Invalid Range")
|
||||||
|
end
|
||||||
|
|
||||||
|
local headers = {
|
||||||
|
["Last-Modified"] = date.to_http( stat.mtime ),
|
||||||
|
["Content-Type"] = mime.to_mime( file ),
|
||||||
|
["ETag"] = etag,
|
||||||
|
["Accept-Ranges"] = "bytes",
|
||||||
|
}
|
||||||
|
|
||||||
|
if o == true then
|
||||||
|
s = stat.size
|
||||||
|
else
|
||||||
|
code = 206
|
||||||
|
headers["Content-Range"] = r
|
||||||
|
f:seek(o)
|
||||||
|
end
|
||||||
|
|
||||||
|
headers["Content-Length"] = s
|
||||||
|
|
||||||
|
-- Send Response
|
||||||
|
return code, headers, srv.IOResource(f, s)
|
||||||
|
else
|
||||||
|
return self:failure( 403, err:gsub("^.+: ", "") )
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return code, hdrs
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return code, hdrs
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return code, hdrs
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return code, hdrs
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif stat.type == "dir" then
|
||||||
|
|
||||||
|
local ruri = request.env.REQUEST_URI:gsub("/$", "")
|
||||||
|
local duri = prot.urldecode( ruri, true )
|
||||||
|
local root = self.docroot
|
||||||
|
|
||||||
|
-- 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 = fs.stat(
|
||||||
|
root .. "/" .. duri .. "/" .. candidate
|
||||||
|
)
|
||||||
|
|
||||||
|
if istat ~= nil and istat.type == "reg" then
|
||||||
|
return 302, { Location = ruri .. "/" .. candidate }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local html = string.format(
|
||||||
|
'<?xml version="1.0" encoding="utf-8"?>\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 { color:#000000 } ' ..
|
||||||
|
'li { border-bottom:1px dotted #CCCCCC; padding:3px } ' ..
|
||||||
|
'small { font-size:60%%; color:#333333 } ' ..
|
||||||
|
'p { margin:0 }' ..
|
||||||
|
'\n</style></head><body><h1>Index of %s/</h1><hr /><ul>'..
|
||||||
|
'<li><p><a href="%s/../">../</a> ' ..
|
||||||
|
'<small>(parent directory)</small><br />' ..
|
||||||
|
'<small></small></li>',
|
||||||
|
duri, duri, ruri
|
||||||
|
)
|
||||||
|
|
||||||
|
local entries = fs.dir( file )
|
||||||
|
|
||||||
|
if type(entries) == "function" then
|
||||||
|
for i, e in util.vspairs(nixio.util.consume(entries)) do
|
||||||
|
local estat = fs.stat( file .. "/" .. e )
|
||||||
|
|
||||||
|
if estat.type == "dir" then
|
||||||
|
html = html .. string.format(
|
||||||
|
'<li><p><a href="%s/%s/">%s/</a> ' ..
|
||||||
|
'<small>(directory)</small><br />' ..
|
||||||
|
'<small>Changed: %s</small></li>',
|
||||||
|
ruri, prot.urlencode( e ), e,
|
||||||
|
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, prot.urlencode( e ), e,
|
||||||
|
mime.to_mime( e ),
|
||||||
|
estat.size, date.to_http( estat.mtime )
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
html = html .. '</ul><hr /><address>LuCId-HTTPd' ..
|
||||||
|
'</address></body></html>'
|
||||||
|
|
||||||
|
return 200, {
|
||||||
|
["Date"] = date.to_http( os.time() );
|
||||||
|
["Content-Type"] = "text/html; charset=utf-8";
|
||||||
|
}, 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
|
||||||
|
if self.error404 then
|
||||||
|
return 302, { Location = self.error404 }
|
||||||
|
else
|
||||||
|
return self:failure(404, "No such file: " .. file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Simple.handle_HEAD(self, ...)
|
||||||
|
local stat, head = self:handle_GET(...)
|
||||||
|
return stat, head
|
||||||
|
end
|
96
libs/lucid-http/luasrc/lucid/http/handler/luci.lua
Normal file
96
libs/lucid-http/luasrc/lucid/http/handler/luci.lua
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
--[[
|
||||||
|
LuCId HTTP-Slave
|
||||||
|
(c) 2009 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$
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local dsp = require "luci.dispatcher"
|
||||||
|
local util = require "luci.util"
|
||||||
|
local http = require "luci.http"
|
||||||
|
local ltn12 = require "luci.ltn12"
|
||||||
|
local srv = require "luci.lucid.http.server"
|
||||||
|
local coroutine = require "coroutine"
|
||||||
|
local type = type
|
||||||
|
|
||||||
|
module "luci.lucid.http.handler.luci"
|
||||||
|
|
||||||
|
Luci = util.class(srv.Handler)
|
||||||
|
|
||||||
|
function Luci.__init__(self, name, prefix)
|
||||||
|
srv.Handler.__init__(self, name)
|
||||||
|
self.prefix = prefix
|
||||||
|
end
|
||||||
|
|
||||||
|
function Luci.handle_HEAD(self, ...)
|
||||||
|
local stat, head = self:handle_GET(...)
|
||||||
|
return stat, head
|
||||||
|
end
|
||||||
|
|
||||||
|
function Luci.handle_POST(self, ...)
|
||||||
|
return self:handle_GET(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Luci.handle_GET(self, request, sourcein)
|
||||||
|
local r = http.Request(
|
||||||
|
request.env,
|
||||||
|
sourcein
|
||||||
|
)
|
||||||
|
|
||||||
|
local res, id, data1, data2 = true, 0, nil, nil
|
||||||
|
local headers = {}
|
||||||
|
local status = 200
|
||||||
|
local active = true
|
||||||
|
|
||||||
|
local x = coroutine.create(dsp.httpdispatch)
|
||||||
|
while not id or id < 3 do
|
||||||
|
res, id, data1, data2 = coroutine.resume(x, r, self.prefix)
|
||||||
|
|
||||||
|
if not res then
|
||||||
|
status = 500
|
||||||
|
headers["Content-Type"] = "text/plain"
|
||||||
|
return status, headers, ltn12.source.string(id)
|
||||||
|
end
|
||||||
|
|
||||||
|
if id == 1 then
|
||||||
|
status = data1
|
||||||
|
elseif id == 2 then
|
||||||
|
if not headers[data1] then
|
||||||
|
headers[data1] = data2
|
||||||
|
elseif type(headers[data1]) ~= "table" then
|
||||||
|
headers[data1] = {headers[data1], data2}
|
||||||
|
else
|
||||||
|
headers[data1][#headers[data1]+1] = data2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if id == 6 then
|
||||||
|
while (coroutine.resume(x)) do end
|
||||||
|
return status, headers, srv.IOResource(data1, data2)
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
return status, headers, iter
|
||||||
|
end
|
||||||
|
|
522
libs/lucid-http/luasrc/lucid/http/server.lua
Normal file
522
libs/lucid-http/luasrc/lucid/http/server.lua
Normal file
|
@ -0,0 +1,522 @@
|
||||||
|
--[[
|
||||||
|
LuCId HTTP-Slave
|
||||||
|
(c) 2009 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$
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local ipairs, pairs = ipairs, pairs
|
||||||
|
local tostring, tonumber = tostring, tonumber
|
||||||
|
local pcall, assert, type = pcall, assert, type
|
||||||
|
|
||||||
|
local os = require "os"
|
||||||
|
local nixio = require "nixio"
|
||||||
|
local util = require "luci.util"
|
||||||
|
local ltn12 = require "luci.ltn12"
|
||||||
|
local proto = require "luci.http.protocol"
|
||||||
|
local table = require "table"
|
||||||
|
local date = require "luci.http.protocol.date"
|
||||||
|
|
||||||
|
module "luci.lucid.http.server"
|
||||||
|
|
||||||
|
VERSION = "1.0"
|
||||||
|
|
||||||
|
statusmsg = {
|
||||||
|
[200] = "OK",
|
||||||
|
[206] = "Partial Content",
|
||||||
|
[301] = "Moved Permanently",
|
||||||
|
[302] = "Found",
|
||||||
|
[304] = "Not Modified",
|
||||||
|
[400] = "Bad Request",
|
||||||
|
[401] = "Unauthorized",
|
||||||
|
[403] = "Forbidden",
|
||||||
|
[404] = "Not Found",
|
||||||
|
[405] = "Method Not Allowed",
|
||||||
|
[408] = "Request Time-out",
|
||||||
|
[411] = "Length Required",
|
||||||
|
[412] = "Precondition Failed",
|
||||||
|
[416] = "Requested range not satisfiable",
|
||||||
|
[500] = "Internal Server Error",
|
||||||
|
[503] = "Server Unavailable",
|
||||||
|
}
|
||||||
|
|
||||||
|
-- File Resource
|
||||||
|
IOResource = util.class()
|
||||||
|
|
||||||
|
function IOResource.__init__(self, fd, len)
|
||||||
|
self.fd, self.len = fd, len
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Server handler implementation
|
||||||
|
Handler = util.class()
|
||||||
|
|
||||||
|
function Handler.__init__(self, name)
|
||||||
|
self.name = name or tostring(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Creates a failure reply
|
||||||
|
function Handler.failure(self, code, msg)
|
||||||
|
return code, { ["Content-Type"] = "text/plain" }, ltn12.source.string(msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Access Restrictions
|
||||||
|
function Handler.restrict(self, restriction)
|
||||||
|
if not self.restrictions then
|
||||||
|
self.restrictions = {restriction}
|
||||||
|
else
|
||||||
|
self.restrictions[#self.restrictions+1] = restriction
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check restrictions
|
||||||
|
function Handler.checkrestricted(self, request)
|
||||||
|
if not self.restrictions then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local localif, user, pass
|
||||||
|
|
||||||
|
for _, r in ipairs(self.restrictions) do
|
||||||
|
local stat = true
|
||||||
|
if stat and r.interface then -- Interface restriction
|
||||||
|
if not localif then
|
||||||
|
for _, v in ipairs(request.server.interfaces) do
|
||||||
|
if v.addr == request.env.SERVER_ADDR then
|
||||||
|
localif = v.name
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if r.interface ~= localif then
|
||||||
|
stat = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if stat and r.user then -- User restriction
|
||||||
|
local rh, pwe
|
||||||
|
if not user then
|
||||||
|
rh = (request.headers.Authorization or ""):match("Basic (.*)")
|
||||||
|
rh = rh and nixio.bin.b64decode(rh) or ""
|
||||||
|
user, pass = rh:match("(.*):(.*)")
|
||||||
|
pass = pass or ""
|
||||||
|
end
|
||||||
|
pwe = nixio.getsp and nixio.getsp(r.user) or nixio.getpw(r.user)
|
||||||
|
local pwh = (user == r.user) and pwe and (pwe.pwdp or pwe.passwd)
|
||||||
|
if not pwh or #pwh < 1 or nixio.crypt(pass, pwh) ~= pwh then
|
||||||
|
stat = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if stat then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return 401, {
|
||||||
|
["WWW-Authenticate"] = ('Basic realm=%q'):format(self.name),
|
||||||
|
["Content-Type"] = 'text/plain'
|
||||||
|
}, ltn12.source.string("Unauthorized")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Processes a request
|
||||||
|
function Handler.process(self, request, sourcein)
|
||||||
|
local stat, code, hdr, sourceout
|
||||||
|
|
||||||
|
local stat, code, msg = self:checkrestricted(request)
|
||||||
|
if stat then -- Access Denied
|
||||||
|
return stat, code, msg
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Detect request Method
|
||||||
|
local hname = "handle_" .. request.env.REQUEST_METHOD
|
||||||
|
if self[hname] then
|
||||||
|
-- Run the handler
|
||||||
|
stat, code, hdr, sourceout = pcall(self[hname], self, request, sourcein)
|
||||||
|
|
||||||
|
-- Check for any errors
|
||||||
|
if not stat then
|
||||||
|
return self:failure(500, code)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return self:failure(405, statusmsg[405])
|
||||||
|
end
|
||||||
|
|
||||||
|
return code, hdr, sourceout
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
VHost = util.class()
|
||||||
|
|
||||||
|
function VHost.__init__(self)
|
||||||
|
self.handlers = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function VHost.process(self, request, ...)
|
||||||
|
local handler
|
||||||
|
local hlen = -1
|
||||||
|
local uri = request.env.SCRIPT_NAME
|
||||||
|
local sc = ("/"):byte()
|
||||||
|
|
||||||
|
-- SCRIPT_NAME
|
||||||
|
request.env.SCRIPT_NAME = ""
|
||||||
|
|
||||||
|
-- Call URI part
|
||||||
|
request.env.PATH_INFO = uri
|
||||||
|
|
||||||
|
for k, h in pairs(self.handlers) do
|
||||||
|
if #k > hlen then
|
||||||
|
if uri == k or (uri:sub(1, #k) == k and uri:byte(#k+1) == sc) then
|
||||||
|
handler = h
|
||||||
|
hlen = #k
|
||||||
|
request.env.SCRIPT_NAME = k
|
||||||
|
request.env.PATH_INFO = uri:sub(#k+1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if handler then
|
||||||
|
return handler:process(request, ...)
|
||||||
|
else
|
||||||
|
return 404, nil, ltn12.source.string("No such handler")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function VHost.get_handlers(self)
|
||||||
|
return self.handlers
|
||||||
|
end
|
||||||
|
|
||||||
|
function VHost.set_handler(self, match, handler)
|
||||||
|
self.handlers[match] = handler
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function remapipv6(adr)
|
||||||
|
local map = "::ffff:"
|
||||||
|
if adr:sub(1, #map) == map then
|
||||||
|
return adr:sub(#map+1)
|
||||||
|
else
|
||||||
|
return adr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function chunksource(sock, buffer)
|
||||||
|
buffer = buffer or ""
|
||||||
|
return function()
|
||||||
|
local output
|
||||||
|
local _, endp, count = buffer:find("^([0-9a-fA-F]+);?.-\r\n")
|
||||||
|
while not count and #buffer <= 1024 do
|
||||||
|
local newblock, code = sock:recv(1024 - #buffer)
|
||||||
|
if not newblock then
|
||||||
|
return nil, code
|
||||||
|
end
|
||||||
|
buffer = buffer .. newblock
|
||||||
|
_, endp, count = buffer:find("^([0-9a-fA-F]+);?.-\r\n")
|
||||||
|
end
|
||||||
|
count = tonumber(count, 16)
|
||||||
|
if not count then
|
||||||
|
return nil, -1, "invalid encoding"
|
||||||
|
elseif count == 0 then
|
||||||
|
return nil
|
||||||
|
elseif count + 2 <= #buffer - endp then
|
||||||
|
output = buffer:sub(endp+1, endp+count)
|
||||||
|
buffer = buffer:sub(endp+count+3)
|
||||||
|
return output
|
||||||
|
else
|
||||||
|
output = buffer:sub(endp+1, endp+count)
|
||||||
|
buffer = ""
|
||||||
|
if count - #output > 0 then
|
||||||
|
local remain, code = sock:recvall(count-#output)
|
||||||
|
if not remain then
|
||||||
|
return nil, code
|
||||||
|
end
|
||||||
|
output = output .. remain
|
||||||
|
count, code = sock:recvall(2)
|
||||||
|
else
|
||||||
|
count, code = sock:recvall(count+2-#buffer+endp)
|
||||||
|
end
|
||||||
|
if not count then
|
||||||
|
return nil, code
|
||||||
|
end
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function chunksink(sock)
|
||||||
|
return function(chunk, err)
|
||||||
|
if not chunk then
|
||||||
|
return sock:writeall("0\r\n\r\n")
|
||||||
|
else
|
||||||
|
return sock:writeall(("%X\r\n%s\r\n"):format(#chunk, chunk))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Server = util.class()
|
||||||
|
|
||||||
|
function Server.__init__(self)
|
||||||
|
self.vhosts = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function Server.get_vhosts(self)
|
||||||
|
return self.vhosts
|
||||||
|
end
|
||||||
|
|
||||||
|
function Server.set_vhost(self, name, vhost)
|
||||||
|
self.vhosts[name] = vhost
|
||||||
|
end
|
||||||
|
|
||||||
|
function Server.error(self, client, code, msg)
|
||||||
|
hcode = tostring(code)
|
||||||
|
|
||||||
|
client:writeall( "HTTP/1.0 " .. hcode .. " " ..
|
||||||
|
statusmsg[code] .. "\r\n" )
|
||||||
|
client:writeall( "Connection: close\r\n" )
|
||||||
|
client:writeall( "Content-Type: text/plain\r\n\r\n" )
|
||||||
|
|
||||||
|
if msg then
|
||||||
|
client:writeall( "HTTP-Error " .. code .. ": " .. msg .. "\r\n" )
|
||||||
|
end
|
||||||
|
|
||||||
|
client:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local hdr2env = {
|
||||||
|
["Content-Length"] = "CONTENT_LENGTH",
|
||||||
|
["Content-Type"] = "CONTENT_TYPE",
|
||||||
|
["Content-type"] = "CONTENT_TYPE",
|
||||||
|
["Accept"] = "HTTP_ACCEPT",
|
||||||
|
["Accept-Charset"] = "HTTP_ACCEPT_CHARSET",
|
||||||
|
["Accept-Encoding"] = "HTTP_ACCEPT_ENCODING",
|
||||||
|
["Accept-Language"] = "HTTP_ACCEPT_LANGUAGE",
|
||||||
|
["Connection"] = "HTTP_CONNECTION",
|
||||||
|
["Cookie"] = "HTTP_COOKIE",
|
||||||
|
["Host"] = "HTTP_HOST",
|
||||||
|
["Referer"] = "HTTP_REFERER",
|
||||||
|
["User-Agent"] = "HTTP_USER_AGENT"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Server.parse_headers(self, source)
|
||||||
|
local env = {}
|
||||||
|
local req = {env = env, headers = {}}
|
||||||
|
local line, err
|
||||||
|
|
||||||
|
repeat -- Ignore empty lines
|
||||||
|
line, err = source()
|
||||||
|
if not line then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
until #line > 0
|
||||||
|
|
||||||
|
env.REQUEST_METHOD, env.REQUEST_URI, env.SERVER_PROTOCOL =
|
||||||
|
line:match("^([A-Z]+) ([^ ]+) (HTTP/1%.[01])$")
|
||||||
|
|
||||||
|
if not env.REQUEST_METHOD then
|
||||||
|
return nil, "invalid magic"
|
||||||
|
end
|
||||||
|
|
||||||
|
local key, envkey, val
|
||||||
|
repeat
|
||||||
|
line, err = source()
|
||||||
|
if not line then
|
||||||
|
return nil, err
|
||||||
|
elseif #line > 0 then
|
||||||
|
key, val = line:match("^([%w-]+)%s?:%s?(.*)")
|
||||||
|
if key then
|
||||||
|
req.headers[key] = val
|
||||||
|
envkey = hdr2env[key]
|
||||||
|
if envkey then
|
||||||
|
env[envkey] = val
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return nil, "invalid header line"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
until false
|
||||||
|
|
||||||
|
env.SCRIPT_NAME, env.QUERY_STRING = env.REQUEST_URI:match("(.*)%??(.*)")
|
||||||
|
return req
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Server.process(self, client, env)
|
||||||
|
local sourcein = function() end
|
||||||
|
local sourcehdr = client:linesource()
|
||||||
|
local sinkout
|
||||||
|
local buffer
|
||||||
|
|
||||||
|
local close = false
|
||||||
|
local stat, code, msg, message, err
|
||||||
|
|
||||||
|
client:setsockopt("socket", "rcvtimeo", 15)
|
||||||
|
client:setsockopt("socket", "sndtimeo", 15)
|
||||||
|
|
||||||
|
repeat
|
||||||
|
-- parse headers
|
||||||
|
message, err = self:parse_headers(sourcehdr)
|
||||||
|
|
||||||
|
-- any other error
|
||||||
|
if not message or err then
|
||||||
|
if err == 11 then -- EAGAIN
|
||||||
|
break
|
||||||
|
else
|
||||||
|
return self:error(client, 400, err)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Prepare sources and sinks
|
||||||
|
buffer = sourcehdr(true)
|
||||||
|
sinkout = client:sink()
|
||||||
|
message.server = env
|
||||||
|
|
||||||
|
if client:is_tls_socket() then
|
||||||
|
message.env.HTTPS = "on"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Addresses
|
||||||
|
message.env.REMOTE_ADDR = remapipv6(env.host)
|
||||||
|
message.env.REMOTE_PORT = env.port
|
||||||
|
|
||||||
|
local srvaddr, srvport = client:getsockname()
|
||||||
|
message.env.SERVER_ADDR = remapipv6(srvaddr)
|
||||||
|
message.env.SERVER_PORT = srvport
|
||||||
|
|
||||||
|
-- keep-alive
|
||||||
|
if message.env.SERVER_PROTOCOL == "HTTP/1.1" then
|
||||||
|
close = (message.env.HTTP_CONNECTION == "close")
|
||||||
|
else
|
||||||
|
close = not message.env.HTTP_CONNECTION
|
||||||
|
or message.env.HTTP_CONNECTION == "close"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Uncomment this to disable keep-alive
|
||||||
|
close = close or env.config.nokeepalive
|
||||||
|
|
||||||
|
if message.env.REQUEST_METHOD == "GET"
|
||||||
|
or message.env.REQUEST_METHOD == "HEAD" then
|
||||||
|
-- Be happy
|
||||||
|
|
||||||
|
elseif message.env.REQUEST_METHOD == "POST" then
|
||||||
|
-- If we have a HTTP/1.1 client and an Expect: 100-continue header
|
||||||
|
-- respond with HTTP 100 Continue message
|
||||||
|
if message.env.SERVER_PROTOCOL == "HTTP/1.1"
|
||||||
|
and message.headers.Expect == '100-continue' then
|
||||||
|
client:writeall("HTTP/1.1 100 Continue\r\n\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
if message.headers['Transfer-Encoding'] and
|
||||||
|
message.headers['Transfer-Encoding'] ~= "identity" then
|
||||||
|
sourcein = chunksource(client, buffer)
|
||||||
|
buffer = nil
|
||||||
|
elseif message.env.CONTENT_LENGTH then
|
||||||
|
local len = tonumber(message.env.CONTENT_LENGTH)
|
||||||
|
if #buffer >= len then
|
||||||
|
sourcein = ltn12.source.string(buffer:sub(1, len))
|
||||||
|
buffer = buffer:sub(len+1)
|
||||||
|
else
|
||||||
|
sourcein = ltn12.source.cat(
|
||||||
|
ltn12.source.string(buffer),
|
||||||
|
client:blocksource(nil, len - #buffer)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return self:error(client, 411, statusmsg[411])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return self:error(client, 405, statusmsg[405])
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local host = self.vhosts[message.env.HTTP_HOST] or self.vhosts[""]
|
||||||
|
if not host then
|
||||||
|
return self:error(client, 404, "No virtual host found")
|
||||||
|
end
|
||||||
|
|
||||||
|
local code, headers, sourceout = host:process(message, sourcein)
|
||||||
|
headers = headers or {}
|
||||||
|
|
||||||
|
-- Post process response
|
||||||
|
if sourceout then
|
||||||
|
if util.instanceof(sourceout, IOResource) then
|
||||||
|
if not headers["Content-Length"] then
|
||||||
|
headers["Content-Length"] = sourceout.len
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not headers["Content-Length"] then
|
||||||
|
if message.http_version == 1.1 then
|
||||||
|
headers["Transfer-Encoding"] = "chunked"
|
||||||
|
sinkout = chunksink(client)
|
||||||
|
else
|
||||||
|
close = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif message.request_method ~= "head" then
|
||||||
|
headers["Content-Length"] = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if close then
|
||||||
|
headers["Connection"] = "close"
|
||||||
|
elseif message.env.SERVER_PROTOCOL == "HTTP/1.0" then
|
||||||
|
headers["Connection"] = "Keep-Alive"
|
||||||
|
end
|
||||||
|
|
||||||
|
headers["Date"] = date.to_http(os.time())
|
||||||
|
local header = {
|
||||||
|
message.env.SERVER_PROTOCOL .. " " .. tostring(code) .. " "
|
||||||
|
.. statusmsg[code],
|
||||||
|
"Server: LuCId-HTTPd/" .. VERSION
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for k, v in pairs(headers) do
|
||||||
|
if type(v) == "table" then
|
||||||
|
for _, h in ipairs(v) do
|
||||||
|
header[#header+1] = k .. ": " .. h
|
||||||
|
end
|
||||||
|
else
|
||||||
|
header[#header+1] = k .. ": " .. v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
header[#header+1] = ""
|
||||||
|
header[#header+1] = ""
|
||||||
|
|
||||||
|
-- Output
|
||||||
|
stat, code, msg = client:writeall(table.concat(header, "\r\n"))
|
||||||
|
|
||||||
|
if sourceout and stat then
|
||||||
|
if util.instanceof(sourceout, IOResource) then
|
||||||
|
stat, code, msg = sourceout.fd:copyz(client, sourceout.len)
|
||||||
|
else
|
||||||
|
stat, msg = ltn12.pump.all(sourceout, sinkout)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Write errors
|
||||||
|
if not stat then
|
||||||
|
if msg then
|
||||||
|
nixio.syslog("err", "Error sending data to " .. env.host ..
|
||||||
|
": " .. msg .. "\n")
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if buffer then
|
||||||
|
sourcehdr(buffer)
|
||||||
|
end
|
||||||
|
until close
|
||||||
|
|
||||||
|
client:shutdown()
|
||||||
|
client:close()
|
||||||
|
end
|
2
libs/lucid/Makefile
Normal file
2
libs/lucid/Makefile
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
include ../../build/config.mk
|
||||||
|
include ../../build/module.mk
|
61
libs/lucid/hostfiles/etc/config/lucid
Normal file
61
libs/lucid/hostfiles/etc/config/lucid
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
config lucid main
|
||||||
|
option pollinterval 15000
|
||||||
|
option daemon 1
|
||||||
|
option debug 1
|
||||||
|
list supports tcpserver
|
||||||
|
list supports server
|
||||||
|
|
||||||
|
config DirectoryPublisher webroot
|
||||||
|
option name 'Webserver Share'
|
||||||
|
option physical host/www
|
||||||
|
option virtual ''
|
||||||
|
option domain ''
|
||||||
|
list read ':lo'
|
||||||
|
list read ':br-lan'
|
||||||
|
list read 'root'
|
||||||
|
|
||||||
|
config LuciWebPublisher luciweb
|
||||||
|
option name 'LuCI Webapplication'
|
||||||
|
option physical ''
|
||||||
|
list virtual /luci
|
||||||
|
option domain ''
|
||||||
|
list exec ':lo'
|
||||||
|
list exec ':br-lan'
|
||||||
|
list exec 'root'
|
||||||
|
|
||||||
|
config RPCPublisher mainrpc
|
||||||
|
option namespace 'luci.lucid.rpc'
|
||||||
|
list export system
|
||||||
|
list export ruci
|
||||||
|
list exec ':lo'
|
||||||
|
list exec 'root'
|
||||||
|
|
||||||
|
config tcpserver httpd
|
||||||
|
option entrypoint "luci.lucid.http"
|
||||||
|
list supports DirectoryPublisher
|
||||||
|
list supports LuciWebPublisher
|
||||||
|
|
||||||
|
config tcpserver rpcd
|
||||||
|
option entrypoint "luci.lucid.rpc"
|
||||||
|
list supports RPCPublisher
|
||||||
|
|
||||||
|
config daemon http
|
||||||
|
option slave httpd
|
||||||
|
list address 8080
|
||||||
|
list publisher webroot
|
||||||
|
list publisher luciweb
|
||||||
|
option enabled 1
|
||||||
|
|
||||||
|
config daemon https
|
||||||
|
option slave httpd
|
||||||
|
list address 4443
|
||||||
|
list publisher webroot
|
||||||
|
list publisher luciweb
|
||||||
|
option enabled 1
|
||||||
|
option encryption enable
|
||||||
|
|
||||||
|
config daemon rpc
|
||||||
|
option slave rpcd
|
||||||
|
list address 12900
|
||||||
|
list publisher mainrpc
|
||||||
|
option enabled 1
|
266
libs/lucid/luasrc/lucid.lua
Normal file
266
libs/lucid/luasrc/lucid.lua
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
--[[
|
||||||
|
LuCI - Lua Development Framework
|
||||||
|
|
||||||
|
Copyright 2009 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$
|
||||||
|
]]
|
||||||
|
|
||||||
|
local nixio = require "nixio"
|
||||||
|
local table = require "table"
|
||||||
|
local uci = require "luci.model.uci"
|
||||||
|
local os = require "os"
|
||||||
|
local io = require "io"
|
||||||
|
|
||||||
|
local pairs, require, pcall, assert, type = pairs, require, pcall, assert, type
|
||||||
|
local ipairs, tonumber, collectgarbage = ipairs, tonumber, collectgarbage
|
||||||
|
|
||||||
|
|
||||||
|
module "luci.lucid"
|
||||||
|
|
||||||
|
local slaves = {}
|
||||||
|
local pollt = {}
|
||||||
|
local tickt = {}
|
||||||
|
local tpids = {}
|
||||||
|
local tcount = 0
|
||||||
|
local ifaddrs = nixio.getifaddrs()
|
||||||
|
|
||||||
|
cursor = uci.cursor()
|
||||||
|
state = uci.cursor_state()
|
||||||
|
UCINAME = "lucid"
|
||||||
|
|
||||||
|
local cursor = cursor
|
||||||
|
local state = state
|
||||||
|
local UCINAME = UCINAME
|
||||||
|
local SSTATE = "/tmp/.lucid_store"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function start()
|
||||||
|
prepare()
|
||||||
|
|
||||||
|
local detach = cursor:get(UCINAME, "main", "daemonize")
|
||||||
|
if detach == "1" then
|
||||||
|
local stat, code, msg = daemonize()
|
||||||
|
if not stat then
|
||||||
|
nixio.syslog("crit", "Unable to detach process: " .. msg .. "\n")
|
||||||
|
ox.exit(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
run()
|
||||||
|
end
|
||||||
|
|
||||||
|
function prepare()
|
||||||
|
local debug = tonumber((cursor:get(UCINAME, "main", "debug")))
|
||||||
|
|
||||||
|
nixio.openlog("lucid", "pid", "perror")
|
||||||
|
if debug ~= 1 then
|
||||||
|
nixio.setlogmask("warning")
|
||||||
|
end
|
||||||
|
|
||||||
|
cursor:foreach(UCINAME, "daemon", function(config)
|
||||||
|
if config.enabled ~= "1" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local key = config[".name"]
|
||||||
|
if not config.slave then
|
||||||
|
nixio.syslog("crit", "Daemon "..key.." is missing a slave\n")
|
||||||
|
os.exit(1)
|
||||||
|
else
|
||||||
|
nixio.syslog("info", "Initializing daemon " .. key)
|
||||||
|
end
|
||||||
|
|
||||||
|
state:revert(UCINAME, key)
|
||||||
|
|
||||||
|
local daemon, code, err = prepare_daemon(config)
|
||||||
|
if daemon then
|
||||||
|
state:set(UCINAME, key, "status", "started")
|
||||||
|
nixio.syslog("info", "Prepared daemon " .. key)
|
||||||
|
else
|
||||||
|
state:set(UCINAME, key, "status", "error")
|
||||||
|
state:set(UCINAME, key, "error", err)
|
||||||
|
nixio.syslog("err", "Failed to initialize daemon "..key..": "..
|
||||||
|
err .. "\n")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function run()
|
||||||
|
local pollint = tonumber((cursor:get(UCINAME, "main", "pollinterval")))
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local stat, code = nixio.poll(pollt, pollint)
|
||||||
|
|
||||||
|
if stat and stat > 0 then
|
||||||
|
for _, polle in ipairs(pollt) do
|
||||||
|
if polle.revents ~= 0 and polle.handler then
|
||||||
|
polle.handler(polle)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif stat == 0 then
|
||||||
|
ifaddrs = nixio.getifaddrs()
|
||||||
|
collectgarbage("collect")
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, cb in ipairs(tickt) do
|
||||||
|
cb()
|
||||||
|
end
|
||||||
|
|
||||||
|
local pid, stat, code = nixio.wait(-1, "nohang")
|
||||||
|
while pid and pid > 0 do
|
||||||
|
tcount = tcount - 1
|
||||||
|
if tpids[pid] and tpids[pid] ~= true then
|
||||||
|
tpids[pid](pid, stat, code)
|
||||||
|
end
|
||||||
|
pid, stat, code = nixio.wait(-1, "nohang")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function register_pollfd(polle)
|
||||||
|
pollt[#pollt+1] = polle
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function unregister_pollfd(polle)
|
||||||
|
for k, v in ipairs(pollt) do
|
||||||
|
if v == polle then
|
||||||
|
table.remove(pollt, k)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function close_pollfds()
|
||||||
|
for k, v in ipairs(pollt) do
|
||||||
|
if v.fd and v.fd.close then
|
||||||
|
v.fd:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function register_tick(cb)
|
||||||
|
tickt[#tickt+1] = cb
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function unregister_tick(cb)
|
||||||
|
for k, v in ipairs(tickt) do
|
||||||
|
if v == cb then
|
||||||
|
table.remove(tickt, k)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function create_process(threadcb, waitcb)
|
||||||
|
local threadlimit = tonumber(cursor:get(UCINAME, "main", "threadlimit"))
|
||||||
|
if threadlimit and #tpids >= tcount then
|
||||||
|
nixio.syslog("warning", "Unable to create thread: process limit reached")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local pid, code, err = nixio.fork()
|
||||||
|
if pid and pid ~= 0 then
|
||||||
|
tpids[pid] = waitcb
|
||||||
|
tcount = tcount + 1
|
||||||
|
elseif pid == 0 then
|
||||||
|
local code = threadcb()
|
||||||
|
os.exit(code)
|
||||||
|
else
|
||||||
|
nixio.syslog("err", "Unable to fork(): " .. err)
|
||||||
|
end
|
||||||
|
return pid, code, err
|
||||||
|
end
|
||||||
|
|
||||||
|
function prepare_daemon(config)
|
||||||
|
nixio.syslog("info", "Preparing daemon " .. config[".name"])
|
||||||
|
local modname = cursor:get(UCINAME, config.slave)
|
||||||
|
if not modname then
|
||||||
|
return nil, -1, "invalid slave"
|
||||||
|
end
|
||||||
|
|
||||||
|
local stat, module = pcall(require, _NAME .. "." .. modname)
|
||||||
|
if not stat or not module.prepare_daemon then
|
||||||
|
return nil, -2, "slave type not supported"
|
||||||
|
end
|
||||||
|
|
||||||
|
config.slave = prepare_slave(config.slave)
|
||||||
|
|
||||||
|
return module.prepare_daemon(config, _M)
|
||||||
|
end
|
||||||
|
|
||||||
|
function prepare_slave(name)
|
||||||
|
local slave = slaves[name]
|
||||||
|
if not slave then
|
||||||
|
local config = cursor:get_all(UCINAME, name)
|
||||||
|
|
||||||
|
local stat, module = pcall(require, config and config.entrypoint)
|
||||||
|
if stat then
|
||||||
|
slave = {module = module, config = config}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if slave then
|
||||||
|
return slave
|
||||||
|
else
|
||||||
|
return nil, module
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_interfaces()
|
||||||
|
return ifaddrs
|
||||||
|
end
|
||||||
|
|
||||||
|
function revoke_privileges(user, group)
|
||||||
|
if nixio.getuid() == 0 then
|
||||||
|
return nixio.setgid(group) and nixio.setuid(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function securestate()
|
||||||
|
local stat = nixio.fs.stat(SSTATE) or {}
|
||||||
|
local uid = nixio.getuid()
|
||||||
|
if stat.type ~= "dir" or (stat.modedec % 100) ~= 0 or stat.uid ~= uid then
|
||||||
|
nixio.fs.remover(SSTATE)
|
||||||
|
if not nixio.fs.mkdir(SSTATE, 700) then
|
||||||
|
local errno = nixio.errno()
|
||||||
|
nixio.syslog("err", "Integrity check on secure state failed!")
|
||||||
|
return nil, errno, nixio.perror(errno)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return uci.cursor(nil, SSTATE)
|
||||||
|
end
|
||||||
|
|
||||||
|
function daemonize()
|
||||||
|
if nixio.getppid() == 1 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local pid, code, msg = nixio.fork()
|
||||||
|
if not pid then
|
||||||
|
return nil, code, msg
|
||||||
|
elseif pid > 0 then
|
||||||
|
os.exit(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
nixio.setsid()
|
||||||
|
nixio.chdir("/")
|
||||||
|
|
||||||
|
local devnull = nixio.open("/dev/null", nixio.open_flags("rdwr"))
|
||||||
|
nixio.dup(devnull, nixio.stdin)
|
||||||
|
nixio.dup(devnull, nixio.stdout)
|
||||||
|
nixio.dup(devnull, nixio.stderr)
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
192
libs/lucid/luasrc/lucid/tcpserver.lua
Normal file
192
libs/lucid/luasrc/lucid/tcpserver.lua
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
--[[
|
||||||
|
LuCI - Lua Development Framework
|
||||||
|
|
||||||
|
Copyright 2009 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$
|
||||||
|
]]
|
||||||
|
|
||||||
|
local os = require "os"
|
||||||
|
local nixio = require "nixio"
|
||||||
|
local lucid = require "luci.lucid"
|
||||||
|
|
||||||
|
local ipairs, type, require, setmetatable = ipairs, type, require, setmetatable
|
||||||
|
local pairs, print, tostring, unpack = pairs, print, tostring, unpack
|
||||||
|
|
||||||
|
module "luci.lucid.tcpserver"
|
||||||
|
|
||||||
|
local cursor = lucid.cursor
|
||||||
|
local UCINAME = lucid.UCINAME
|
||||||
|
|
||||||
|
local tcpsockets = {}
|
||||||
|
|
||||||
|
|
||||||
|
function prepare_daemon(config, server)
|
||||||
|
nixio.syslog("info", "Preparing TCP-Daemon " .. config[".name"])
|
||||||
|
if type(config.address) ~= "table" then
|
||||||
|
config.address = {config.address}
|
||||||
|
end
|
||||||
|
|
||||||
|
local sockets, socket, code, err = {}
|
||||||
|
local sopts = {reuseaddr = 1}
|
||||||
|
for _, addr in ipairs(config.address) do
|
||||||
|
local host, port = addr:match("(.-):?([^:]*)")
|
||||||
|
if not host then
|
||||||
|
nixio.syslog("err", "Invalid address: " .. addr)
|
||||||
|
return nil, -5, "invalid address format"
|
||||||
|
elseif #host == 0 then
|
||||||
|
host = nil
|
||||||
|
end
|
||||||
|
socket, code, err = prepare_socket(config.family, host, port, sopts)
|
||||||
|
if socket then
|
||||||
|
sockets[#sockets+1] = socket
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
nixio.syslog("info", "Sockets bound for " .. config[".name"])
|
||||||
|
|
||||||
|
if #sockets < 1 then
|
||||||
|
return nil, -6, "no sockets bound"
|
||||||
|
end
|
||||||
|
|
||||||
|
nixio.syslog("info", "Preparing publishers for " .. config[".name"])
|
||||||
|
|
||||||
|
local publisher = {}
|
||||||
|
for k, pname in ipairs(config.publisher) do
|
||||||
|
local pdata = cursor:get_all(UCINAME, pname)
|
||||||
|
if pdata then
|
||||||
|
publisher[#publisher+1] = pdata
|
||||||
|
else
|
||||||
|
nixio.syslog("err", "Publisher " .. pname .. " not found")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
nixio.syslog("info", "Preparing TLS for " .. config[".name"])
|
||||||
|
|
||||||
|
local tls = prepare_tls(config.tls)
|
||||||
|
if not tls and config.encryption == "enable" then
|
||||||
|
for _, s in ipairs(sockets) do
|
||||||
|
s:close()
|
||||||
|
end
|
||||||
|
return nil, -4, "Encryption requested, but no TLS context given"
|
||||||
|
end
|
||||||
|
|
||||||
|
nixio.syslog("info", "Invoking daemon factory for " .. config[".name"])
|
||||||
|
local handler, err = config.slave.module.factory(publisher, config)
|
||||||
|
if not handler then
|
||||||
|
for _, s in ipairs(sockets) do
|
||||||
|
s:close()
|
||||||
|
end
|
||||||
|
return nil, -3, err
|
||||||
|
else
|
||||||
|
local pollin = nixio.poll_flags("in")
|
||||||
|
for _, s in ipairs(sockets) do
|
||||||
|
server.register_pollfd({
|
||||||
|
fd = s,
|
||||||
|
events = pollin,
|
||||||
|
revents = 0,
|
||||||
|
handler = accept,
|
||||||
|
accept = handler,
|
||||||
|
config = config,
|
||||||
|
publisher = publisher,
|
||||||
|
tls = tls
|
||||||
|
})
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function accept(polle)
|
||||||
|
local socket, host, port = polle.fd:accept()
|
||||||
|
if not socket then
|
||||||
|
return nixio.syslog("warn", "accept() failed: " .. port)
|
||||||
|
end
|
||||||
|
|
||||||
|
socket:setblocking(true)
|
||||||
|
|
||||||
|
local function thread()
|
||||||
|
lucid.close_pollfds()
|
||||||
|
local inst = setmetatable({
|
||||||
|
host = host, port = port, interfaces = lucid.get_interfaces()
|
||||||
|
}, {__index = polle})
|
||||||
|
if polle.config.encryption then
|
||||||
|
socket = polle.tls:create(socket)
|
||||||
|
if not socket:accept() then
|
||||||
|
socket:close()
|
||||||
|
return nixio.syslog("warning", "TLS handshake failed: " .. host)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return polle.accept(socket, inst)
|
||||||
|
end
|
||||||
|
|
||||||
|
local stat = {lucid.create_process(thread)}
|
||||||
|
socket:close()
|
||||||
|
return unpack(stat)
|
||||||
|
end
|
||||||
|
|
||||||
|
function prepare_socket(family, host, port, opts, backlog)
|
||||||
|
nixio.syslog("info", "Preparing socket for port " .. port)
|
||||||
|
backlog = backlog or 1024
|
||||||
|
family = family or "inetany"
|
||||||
|
opts = opts or {}
|
||||||
|
|
||||||
|
local inetany = family == "inetany"
|
||||||
|
family = inetany and "inet6" or family
|
||||||
|
|
||||||
|
local socket, code, err = nixio.socket(family, "stream")
|
||||||
|
if not socket and inetany then
|
||||||
|
family = "inet"
|
||||||
|
socket, code, err = nixio.socket(family, "stream")
|
||||||
|
end
|
||||||
|
|
||||||
|
if not socket then
|
||||||
|
return nil, code, err
|
||||||
|
end
|
||||||
|
|
||||||
|
for k, v in pairs(opts) do
|
||||||
|
socket:setsockopt("socket", k, v)
|
||||||
|
end
|
||||||
|
|
||||||
|
local stat, code, err = socket:bind(host, port)
|
||||||
|
if not stat then
|
||||||
|
return nil, code, err
|
||||||
|
end
|
||||||
|
|
||||||
|
stat, code, err = socket:listen(backlog)
|
||||||
|
if not stat then
|
||||||
|
return nil, code, err
|
||||||
|
end
|
||||||
|
|
||||||
|
socket:setblocking(false)
|
||||||
|
|
||||||
|
return socket, family
|
||||||
|
end
|
||||||
|
|
||||||
|
function prepare_tls(tlskey)
|
||||||
|
local tls = nixio.tls()
|
||||||
|
if tlskey and cursor:get(UCINAME, tlskey) then
|
||||||
|
local cert = cursor:get(UCINAME, tlskey, "cert")
|
||||||
|
if cert then
|
||||||
|
tls:set_cert(cert)
|
||||||
|
end
|
||||||
|
local key = cursor:get(UCINAME, tlskey, "key")
|
||||||
|
if key then
|
||||||
|
tls:set_key(key)
|
||||||
|
end
|
||||||
|
local ciphers = cursor:get(UCINAME, tlskey, "ciphers")
|
||||||
|
if ciphers then
|
||||||
|
if type(ciphers) == "table" then
|
||||||
|
ciphers = table.concat(ciphers, ":")
|
||||||
|
end
|
||||||
|
tls:set_ciphers(ciphers)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return tls
|
||||||
|
end
|
61
libs/lucid/root/etc/config/lucid
Normal file
61
libs/lucid/root/etc/config/lucid
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
config lucid main
|
||||||
|
option pollinterval 15000
|
||||||
|
option threadlimit 25
|
||||||
|
option daemon 1
|
||||||
|
option debug 1
|
||||||
|
list supports tcpserver
|
||||||
|
list supports server
|
||||||
|
|
||||||
|
config DirectoryPublisher webroot
|
||||||
|
option name 'Webserver Share'
|
||||||
|
option physical /www
|
||||||
|
option virtual /
|
||||||
|
option domain ''
|
||||||
|
list read ':lo'
|
||||||
|
list read ':br-lan'
|
||||||
|
list read 'root'
|
||||||
|
|
||||||
|
config LuciWebPublisher luciweb
|
||||||
|
option name 'LuCI Webapplication'
|
||||||
|
option physical ''
|
||||||
|
list virtual /luci
|
||||||
|
option domain ''
|
||||||
|
list exec ':lo'
|
||||||
|
list exec ':br-lan'
|
||||||
|
list exec 'root'
|
||||||
|
|
||||||
|
config RPCPublisher mainrpc
|
||||||
|
option namespace 'luci.lucid.rpc'
|
||||||
|
list export system
|
||||||
|
list exec ':lo'
|
||||||
|
list exec 'root'
|
||||||
|
|
||||||
|
config tcpserver httpd
|
||||||
|
option entrypoint "luci.lucid.http"
|
||||||
|
list supports DirectoryPublisher
|
||||||
|
list supports LuciWebPublisher
|
||||||
|
|
||||||
|
config tcpserver rpcd
|
||||||
|
option entrypoint "luci.lucid.rpc"
|
||||||
|
list supports RPCPublisher
|
||||||
|
|
||||||
|
config daemon http
|
||||||
|
option slave httpd
|
||||||
|
list address 8080
|
||||||
|
list publisher webroot
|
||||||
|
list publisher luciweb
|
||||||
|
option enabled 1
|
||||||
|
|
||||||
|
config daemon https
|
||||||
|
option slave httpd
|
||||||
|
list address 4443
|
||||||
|
list publisher webroot
|
||||||
|
list publisher luciweb
|
||||||
|
option enabled 1
|
||||||
|
option encryption enable
|
||||||
|
|
||||||
|
config daemon rpc
|
||||||
|
option slave rpcd
|
||||||
|
list address 12900
|
||||||
|
list publisher mainrpc
|
||||||
|
option enabled 1
|
1
libs/nixio/.gitignore
vendored
1
libs/nixio/.gitignore
vendored
|
@ -8,3 +8,4 @@ lkc_defs.h
|
||||||
mconf
|
mconf
|
||||||
zconf.tab.h
|
zconf.tab.h
|
||||||
zconf.tab.c
|
zconf.tab.c
|
||||||
|
src/nixio.dll
|
||||||
|
|
|
@ -79,7 +79,7 @@ function copy(src, dest)
|
||||||
end
|
end
|
||||||
elseif stat.type == "lnk" then
|
elseif stat.type == "lnk" then
|
||||||
res, code, msg = nixio.symlink(nixio.readlink(src), dest)
|
res, code, msg = nixio.symlink(nixio.readlink(src), dest)
|
||||||
else
|
elseif stat.type == "reg" then
|
||||||
res, code, msg = datacopy(src, dest)
|
res, code, msg = datacopy(src, dest)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ $Id$
|
||||||
|
|
||||||
local table = require "table"
|
local table = require "table"
|
||||||
local nixio = require "nixio"
|
local nixio = require "nixio"
|
||||||
local getmetatable, assert, pairs = getmetatable, assert, pairs
|
local getmetatable, assert, pairs, type = getmetatable, assert, pairs, type
|
||||||
|
|
||||||
module "nixio.util"
|
module "nixio.util"
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ function meta.linesource(self, limit)
|
||||||
|
|
||||||
if flush then
|
if flush then
|
||||||
line = buffer:sub(bpos + 1)
|
line = buffer:sub(bpos + 1)
|
||||||
buffer = ""
|
buffer = type(flush) == "string" and flush or ""
|
||||||
bpos = 0
|
bpos = 0
|
||||||
return line
|
return line
|
||||||
end
|
end
|
||||||
|
@ -189,8 +189,11 @@ end
|
||||||
|
|
||||||
function meta.copyz(self, fd, size)
|
function meta.copyz(self, fd, size)
|
||||||
local sent, lsent, code, msg = 0
|
local sent, lsent, code, msg = 0
|
||||||
|
local splicable
|
||||||
|
|
||||||
if self:is_file() then
|
if self:is_file() then
|
||||||
if nixio.sendfile and fd:is_socket() and self:stat("type") == "reg" then
|
local ftype = self:stat("type")
|
||||||
|
if nixio.sendfile and fd:is_socket() and ftype == "reg" then
|
||||||
repeat
|
repeat
|
||||||
lsent, code, msg = nixio.sendfile(fd, self, size or ZIOBLKSIZE)
|
lsent, code, msg = nixio.sendfile(fd, self, size or ZIOBLKSIZE)
|
||||||
if lsent then
|
if lsent then
|
||||||
|
@ -202,6 +205,26 @@ function meta.copyz(self, fd, size)
|
||||||
code ~= nixio.const.ENOSYS and code ~= nixio.const.EINVAL) then
|
code ~= nixio.const.ENOSYS and code ~= nixio.const.EINVAL) then
|
||||||
return lsent and sent, code, msg, sent
|
return lsent and sent, code, msg, sent
|
||||||
end
|
end
|
||||||
|
elseif nixio.splice and not fd:is_tls_socket() and ftype == "fifo" then
|
||||||
|
splicable = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if nixio.splice and fd:is_file() and not splicable then
|
||||||
|
splicable = not self:is_tls_socket() and fd:stat("type") == "fifo"
|
||||||
|
end
|
||||||
|
|
||||||
|
if splicable then
|
||||||
|
repeat
|
||||||
|
lsent, code, msg = nixio.splice(self, fd, size or ZIOBLKSIZE)
|
||||||
|
if lsent then
|
||||||
|
sent = sent + lsent
|
||||||
|
size = size and (size - lsent)
|
||||||
|
end
|
||||||
|
until (not lsent or lsent == 0 or (size and size == 0))
|
||||||
|
if lsent or (not lsent and sent == 0 and
|
||||||
|
code ~= nixio.const.ENOSYS and code ~= nixio.const.EINVAL) then
|
||||||
|
return lsent and sent, code, msg, sent
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -212,6 +235,24 @@ function tls_socket.close(self)
|
||||||
return self.socket:close()
|
return self.socket:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function tls_socket.getsockname(self)
|
||||||
|
return self.socket:getsockname()
|
||||||
|
end
|
||||||
|
|
||||||
|
function tls_socket.getpeername(self)
|
||||||
|
return self.socket:getpeername()
|
||||||
|
end
|
||||||
|
|
||||||
|
function tls_socket.getsockopt(self, ...)
|
||||||
|
return self.socket:getsockopt(...)
|
||||||
|
end
|
||||||
|
tls_socket.getopt = tls_socket.getsockopt
|
||||||
|
|
||||||
|
function tls_socket.setsockopt(self, ...)
|
||||||
|
return self.socket:setsockopt(...)
|
||||||
|
end
|
||||||
|
tls_socket.setopt = tls_socket.setsockopt
|
||||||
|
|
||||||
for k, v in pairs(meta) do
|
for k, v in pairs(meta) do
|
||||||
file[k] = v
|
file[k] = v
|
||||||
socket[k] = v
|
socket[k] = v
|
||||||
|
|
Loading…
Reference in a new issue