mirror of
https://github.com/lxsang/silk.git
synced 2025-07-26 18:59:46 +02:00
Add C based module + lua based API code (WIP)
This commit is contained in:
40
silkmvc/core/OOP.lua
Normal file
40
silkmvc/core/OOP.lua
Normal file
@ -0,0 +1,40 @@
|
||||
Object = {}
|
||||
|
||||
function Object:prototype(o)
|
||||
o = o or {} -- create table if user does not provide one
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function Object:new(o)
|
||||
local obj = self:prototype(o)
|
||||
obj:initialize()
|
||||
return obj
|
||||
end
|
||||
|
||||
function Object:print()
|
||||
print('an Object')
|
||||
end
|
||||
|
||||
function Object:initialize()
|
||||
end
|
||||
|
||||
function Object:asJSON()
|
||||
return '{}'
|
||||
end
|
||||
|
||||
function Object:inherit(o)
|
||||
return self:prototype(o)
|
||||
end
|
||||
|
||||
|
||||
function Object:extends(o)
|
||||
return self:inherit(o)
|
||||
end
|
||||
|
||||
Test = Object:inherit{dummy = 0}
|
||||
|
||||
function Test:toWeb()
|
||||
wio.t(self.dummy)
|
||||
end
|
260
silkmvc/core/api.lua
Normal file
260
silkmvc/core/api.lua
Normal file
@ -0,0 +1,260 @@
|
||||
math.randomseed(os.clock())
|
||||
package.cpath = __api__.apiroot..'/?.so'
|
||||
require("antd")
|
||||
std = modules.std()
|
||||
local read_header =function()
|
||||
local l
|
||||
repeat
|
||||
l = std.antd_recv(HTTP_REQUEST.id)
|
||||
if l and l ~= '\r' then
|
||||
if l == "HTTP_REQUEST" or l == "request" or l == "COOKIE" or l == "REQUEST_HEADER" or l == "REQUEST_DATA" then
|
||||
coroutine.yield(l, "LUA_TABLE")
|
||||
else
|
||||
local l1 = std.antd_recv(HTTP_REQUEST.id)
|
||||
if l1 ~= '\r' then
|
||||
coroutine.yield(l, l1)
|
||||
end
|
||||
l = l1
|
||||
end
|
||||
end
|
||||
until not l or l == '\r'
|
||||
end
|
||||
|
||||
|
||||
local read_headers = function()
|
||||
local co = coroutine.create(function () read_header() end)
|
||||
return function () -- iterator
|
||||
local code, k, v = coroutine.resume(co)
|
||||
return k,v
|
||||
end
|
||||
end
|
||||
|
||||
local parse_headers =function()
|
||||
local lut = {
|
||||
HTTP_REQUEST = HTTP_REQUEST
|
||||
}
|
||||
local curr_tbl = "HTTP_REQUEST"
|
||||
for k,v in read_headers() do
|
||||
if v == "LUA_TABLE" then
|
||||
if not lut[k] then
|
||||
lut[k] = {}
|
||||
end
|
||||
curr_tbl = k
|
||||
else
|
||||
lut[curr_tbl][k] = v
|
||||
end
|
||||
end
|
||||
HTTP_REQUEST.request = lut.request
|
||||
HTTP_REQUEST.request.COOKIE = lut.COOKIE
|
||||
HTTP_REQUEST.request.REQUEST_HEADER = lut.REQUEST_HEADER
|
||||
HTTP_REQUEST.request.REQUEST_DATA = lut.REQUEST_DATA
|
||||
end
|
||||
|
||||
-- parsing the header
|
||||
parse_headers()
|
||||
-- root dir
|
||||
__ROOT__ = HTTP_REQUEST.request.SERVER_WWW_ROOT
|
||||
-- set require path
|
||||
package.path = __ROOT__ .. '/?.lua;'..__api__.apiroot..'/?.lua'
|
||||
require("std")
|
||||
require("utils")
|
||||
require("extra_mime")
|
||||
ulib = require("ulib")
|
||||
-- set session
|
||||
SESSION = {}
|
||||
|
||||
REQUEST = HTTP_REQUEST.request.REQUEST_DATA
|
||||
REQUEST.method = HTTP_REQUEST.request.METHOD
|
||||
if HTTP_REQUEST.request.COOKIE then
|
||||
SESSION = HTTP_REQUEST.request.COOKIE
|
||||
end
|
||||
HEADER = HTTP_REQUEST.request.REQUEST_HEADER
|
||||
HEADER.mobile = false
|
||||
|
||||
if HEADER["User-Agent"] and HEADER["User-Agent"]:match("Mobi") then
|
||||
HEADER.mobile = true
|
||||
end
|
||||
|
||||
function LOG_INFO(fmt,...)
|
||||
ulib.syslog(5,string.format(fmt or "LOG",...))
|
||||
end
|
||||
|
||||
function LOG_ERROR(fmt,...)
|
||||
ulib.syslog(3,string.format(fmt or "ERROR",...))
|
||||
end
|
||||
|
||||
function has_module(m)
|
||||
if utils.file_exists(__ROOT__..'/'..m) then
|
||||
if m:find("%.ls$") then
|
||||
return true, true, __ROOT__..'/'..m
|
||||
else
|
||||
return true, false, m:gsub(".lua$","")
|
||||
end
|
||||
elseif utils.file_exists(__ROOT__..'/'..string.gsub(m,'%.','/')..'.lua') then
|
||||
return true, false, m
|
||||
elseif utils.file_exists(__ROOT__..'/'..string.gsub(m,'%.','/')..'.ls') then
|
||||
return true, true, __ROOT__..'/'..string.gsub(m,'%.','/')..'.ls'
|
||||
end
|
||||
return false, false, nil
|
||||
end
|
||||
|
||||
function echo(m)
|
||||
if m then std.t(m) else std.t("Undefined value") end
|
||||
end
|
||||
|
||||
function loadscript(file, args)
|
||||
local f = io.open(file, "rb")
|
||||
local content = ""
|
||||
if f then
|
||||
local html = ""
|
||||
local pro = "local fn = function(...)"
|
||||
local s,e, mt
|
||||
local mtbegin = true -- find begin of scrit, 0 end of scrit
|
||||
local i = 1
|
||||
if args then
|
||||
pro = "local fn = function("..table.concat( args, ",")..")"
|
||||
end
|
||||
for line in io.lines(file) do
|
||||
line = std.trim(line, " ")
|
||||
if(line ~= "") then
|
||||
if(mtbegin) then
|
||||
mt = "^%s*<%?lua"
|
||||
else
|
||||
mt = "%?>%s*$"
|
||||
end
|
||||
s,e = line:find(mt)
|
||||
if(s) then
|
||||
if mtbegin then
|
||||
if html ~= "" then
|
||||
pro= pro.."echo(\""..utils.escape(html).."\")\n"
|
||||
html = ""
|
||||
end
|
||||
local b,f = line:find("%?>%s*$")
|
||||
if b then
|
||||
pro = pro..line:sub(e+1,b-1).."\n"
|
||||
else
|
||||
pro = pro..line:sub(e+1).."\n"
|
||||
mtbegin = not mtbegin
|
||||
end
|
||||
else
|
||||
pro = pro..line:sub(0,s-1).."\n"
|
||||
mtbegin = not mtbegin
|
||||
end
|
||||
else -- no match
|
||||
if mtbegin then
|
||||
-- detect if we have inline lua with format <?=..?>
|
||||
local b,f = line:find("<%?=")
|
||||
if b then
|
||||
local tmp = line
|
||||
pro= pro.."echo("
|
||||
while(b) do
|
||||
-- find the close
|
||||
local x,y = tmp:find("%?>")
|
||||
if x then
|
||||
pro = pro.."\""..utils.escape(html..tmp:sub(0,b-1):gsub("%%","%%%%")).."\".."
|
||||
pro = pro..tmp:sub(f+1,x-1)..".."
|
||||
html = ""
|
||||
tmp = tmp:sub(y+1)
|
||||
b,f = tmp:find("<%?=")
|
||||
else
|
||||
error("Syntax error near line "..i)
|
||||
end
|
||||
end
|
||||
pro = pro.."\""..utils.escape(tmp:gsub("%%","%%%%")).."\")\n"
|
||||
else
|
||||
html = html..std.trim(line," "):gsub("%%","%%%%").."\n"
|
||||
end
|
||||
else
|
||||
if line ~= "" then pro = pro..line.."\n" end
|
||||
end
|
||||
end
|
||||
end
|
||||
i = i+ 1
|
||||
end
|
||||
f:close()
|
||||
if(html ~= "") then
|
||||
pro = pro.."echo(\""..utils.escape(html).."\")\n"
|
||||
end
|
||||
pro = pro.."\nend \n return fn"
|
||||
local r,e = load(pro)
|
||||
if r then return r(), e else return nil,e end
|
||||
end
|
||||
end
|
||||
|
||||
-- decode post data if any
|
||||
local decode_request_data = function()
|
||||
if (not REQUEST.method)
|
||||
or (REQUEST.method ~= "POST"
|
||||
and REQUEST.method ~= "PUT"
|
||||
and REQUEST.method ~= "PATCH")
|
||||
or (not REQUEST.HAS_RAW_BODY) then
|
||||
return 0
|
||||
end
|
||||
local ctype = HEADER['Content-Type']
|
||||
local clen = HEADER['Content-Length'] or -1
|
||||
if clen then
|
||||
clen = tonumber(clen)
|
||||
end
|
||||
if not ctype or clen == -1 then
|
||||
LOG_ERROR("Invalid content type %s or content length %d", ctype, clen)
|
||||
return 400, "Bad Request, missing content description"
|
||||
end
|
||||
local raw_data, len = std.antd_recv(HTTP_REQUEST.id, clen)
|
||||
if len ~= clen then
|
||||
LOG_ERROR("Unable to read all data: read %d expected %d", len, clen)
|
||||
return 400, "Bad Request, missing content data"
|
||||
end
|
||||
if ctype:find("application/json") then
|
||||
REQUEST.json = bytes.__tostring(raw_data)
|
||||
else
|
||||
REQUEST[ctype] = raw_data
|
||||
end
|
||||
REQUEST.HAS_RAW_BODY = nil
|
||||
return 0
|
||||
end
|
||||
|
||||
-- set compression level
|
||||
local accept_encoding = HEADER["Accept-Encoding"]
|
||||
if accept_encoding then
|
||||
if accept_encoding:find("gzip") then
|
||||
std.antd_set_zlevel(HTTP_REQUEST.id, "gzip")
|
||||
elseif accept_encoding:find("deflate") then
|
||||
std.antd_set_zlevel(HTTP_REQUEST.id, "deflate")
|
||||
end
|
||||
end
|
||||
|
||||
local code, error = decode_request_data()
|
||||
|
||||
if code ~= 0 then
|
||||
LOG_ERROR(error)
|
||||
std.error(code, error)
|
||||
return
|
||||
end
|
||||
|
||||
-- LOG_INFO(JSON.encode(REQUEST))
|
||||
|
||||
-- OOP support
|
||||
--require("OOP")
|
||||
-- load sqlite helper
|
||||
--require("sqlite")
|
||||
-- enable extra mime
|
||||
|
||||
-- run the file
|
||||
|
||||
|
||||
local m, s, p = has_module(HTTP_REQUEST.request.RESOURCE_PATH)
|
||||
if m then
|
||||
-- run the correct module
|
||||
if s then
|
||||
local r,e = loadscript(p)
|
||||
if r then r() else unknow(e) end
|
||||
else
|
||||
LOG_INFO("RUNNING MODULE %s", p)
|
||||
require(p)
|
||||
end
|
||||
else
|
||||
unknow("Resource not found for request "..HTTP_REQUEST.request.RESOURCE_PATH)
|
||||
end
|
||||
|
||||
|
||||
--require('router')
|
63
silkmvc/core/cif.lua
Normal file
63
silkmvc/core/cif.lua
Normal file
@ -0,0 +1,63 @@
|
||||
FFI = require("ffi")
|
||||
FFI.type = {}
|
||||
FFI.type.VOID = 0
|
||||
FFI.type.UINT8 = 1
|
||||
FFI.type.SINT8 = 2
|
||||
FFI.type.UINT16 = 3
|
||||
FFI.type.SINT16 = 4
|
||||
FFI.type.UINT32 = 5
|
||||
FFI.type.SINT32 = 6
|
||||
FFI.type.UINT64 = 7
|
||||
FFI.type.SINT64 = 8
|
||||
FFI.type.FLOAT = 9
|
||||
FFI.type.DOUBLE = 10
|
||||
FFI.type.UCHAR = 11
|
||||
FFI.type.SCHAR = 12
|
||||
FFI.type.USHORT = 13
|
||||
FFI.type.SSHORT = 14
|
||||
FFI.type.UINT = 15
|
||||
FFI.type.SINT = 16
|
||||
FFI.type.ULONG = 17
|
||||
FFI.type.SLONG = 18
|
||||
FFI.type.LONGDOUBLE = 19
|
||||
FFI.type.POINTER = 20
|
||||
FFI.cache = {}
|
||||
|
||||
FFI.load = function(path)
|
||||
if FFI.cache[path] then
|
||||
return FFI.cache[path]
|
||||
else
|
||||
print("Loading: "..path)
|
||||
local lib = FFI.dlopen(path)
|
||||
if lib then
|
||||
FFI.cache[path] = {ref = lib, fn= {}}
|
||||
end
|
||||
return FFI.cache[path]
|
||||
end
|
||||
end
|
||||
|
||||
FFI.unload = function(path)
|
||||
local lib = FFI.cache[path]
|
||||
if lib then
|
||||
FFI.dlclose(lib.ref)
|
||||
FFI.cache[path] = false
|
||||
end
|
||||
end
|
||||
|
||||
FFI.unloadAll = function()
|
||||
for k,v in pairs(FFI.cache) do
|
||||
FFI.dlclose(v.ref)
|
||||
end
|
||||
FFI.cache = {}
|
||||
end
|
||||
|
||||
FFI.lookup = function(lib, name)
|
||||
local fn = lib.fn[name]
|
||||
if fn then return fn end
|
||||
fn = FFI.dlsym(lib.ref, name)
|
||||
if fn then
|
||||
lib.fn[name] = fn
|
||||
return fn
|
||||
end
|
||||
return nil
|
||||
end
|
79
silkmvc/core/extra_mime.lua
Normal file
79
silkmvc/core/extra_mime.lua
Normal file
@ -0,0 +1,79 @@
|
||||
function std.extra_mime(name)
|
||||
local ext = std.ext(name)
|
||||
local mpath = __ROOT__.."/".."mimes.json"
|
||||
local xmimes = {}
|
||||
if utils.file_exists(mpath) then
|
||||
local f = io.open(mpath, "r")
|
||||
if f then
|
||||
xmimes = JSON.decodeString(f:read("*all"))
|
||||
f:close()
|
||||
end
|
||||
end
|
||||
if(name:find("Makefile$")) then return "text/makefile",false
|
||||
elseif ext == "php" then return "text/php",false
|
||||
elseif ext == "c" or ext == "h" then return "text/c",false
|
||||
elseif ext == "cpp" or ext == "hpp" then return "text/cpp",false
|
||||
elseif ext == "md" then return "text/markdown",false
|
||||
elseif ext == "lua" then return "text/lua",false
|
||||
elseif ext == "yml" then return "application/x-yaml", false
|
||||
elseif xmimes[ext] then return xmimes[ext].mime, xmimes[ext].binary
|
||||
--elseif ext == "pgm" then return "image/x-portable-graymap", true
|
||||
else
|
||||
return "application/octet-stream",true
|
||||
end
|
||||
end
|
||||
|
||||
function std.mimeOf(name)
|
||||
local mime = std.mime(name)
|
||||
if mime ~= "application/octet-stream" then
|
||||
return mime
|
||||
else
|
||||
return std.extra_mime(name)
|
||||
end
|
||||
end
|
||||
|
||||
--[[ function std.isBinary(name)
|
||||
local mime = std.mime(name)
|
||||
if mime ~= "application/octet-stream" then
|
||||
return std.is_bin(name)
|
||||
else
|
||||
local xmime,bin = std.extra_mime(name)
|
||||
return bin
|
||||
end
|
||||
end ]]
|
||||
|
||||
function std.sendFile(m)
|
||||
local mime = std.mimeOf(m)
|
||||
local finfo = ulib.file_stat(m)
|
||||
local len = tostring(math.floor(finfo.size))
|
||||
local len1 = tostring(math.floor(finfo.size - 1))
|
||||
if mime == "audio/mpeg" then
|
||||
std.status(200)
|
||||
std.header("Pragma", "public")
|
||||
std.header("Expires", "0")
|
||||
std.header("Content-Type", mime)
|
||||
std.header("Content-Length", len)
|
||||
std.header("Content-Disposition", "inline; filename=" .. std.basename(m))
|
||||
std.header("Content-Range:", "bytes 0-" .. len1 .. "/" .. len)
|
||||
std.header("Accept-Ranges", "bytes")
|
||||
std.header("X-Pad", "avoid browser bug")
|
||||
std.header("Content-Transfer-Encoding", "binary")
|
||||
std.header("Cache-Control", "no-cache, no-store")
|
||||
std.header("Connection", "Keep-Alive")
|
||||
std.header_flush()
|
||||
std.f(m)
|
||||
else
|
||||
if HEADER['If-Modified-Since'] and HEADER['If-Modified-Since'] == finfo.ctime then
|
||||
std.status(304)
|
||||
std.header_flush()
|
||||
else
|
||||
std.status(200)
|
||||
std.header("Content-Type", mime)
|
||||
--std.header("Content-Length", len)
|
||||
std.header("Cache-Control", "no-cache")
|
||||
std.header("Last-Modified", finfo.ctime)
|
||||
std.header_flush()
|
||||
std.f(m)
|
||||
end
|
||||
end
|
||||
end
|
157
silkmvc/core/sqlite.lua
Normal file
157
silkmvc/core/sqlite.lua
Normal file
@ -0,0 +1,157 @@
|
||||
sqlite = modules.sqlite()
|
||||
|
||||
if sqlite == nil then return 0 end
|
||||
require("OOP")
|
||||
-- create class
|
||||
DBModel = Object:inherit{db=nil, name=''}
|
||||
|
||||
function DBModel:createTable(m)
|
||||
if self:available() then return true end
|
||||
local sql = "CREATE TABLE "..self.name.."(id INTEGER PRIMARY KEY"
|
||||
for k, v in pairs(m) do
|
||||
if k ~= "id" then
|
||||
sql = sql..","..k.." "..v
|
||||
end
|
||||
end
|
||||
sql = sql..");"
|
||||
return sqlite.query(self.db,sql) == 1
|
||||
end
|
||||
|
||||
function DBModel:insert(m)
|
||||
local keys = {}
|
||||
local values = {}
|
||||
for k,v in pairs(m) do
|
||||
if k ~= "id" then
|
||||
table.insert(keys,k)
|
||||
if type(v) == "number" then
|
||||
table.insert(values, v)
|
||||
elseif type(v) == "boolean" then
|
||||
table.insert( values, v and 1 or 0 )
|
||||
else
|
||||
local t = "\""..v:gsub('"', '""').."\""
|
||||
table.insert(values,t)
|
||||
end
|
||||
end
|
||||
end
|
||||
local sql = "INSERT INTO "..self.name.." ("..table.concat(keys,',')..') VALUES ('
|
||||
sql = sql..table.concat(values,',')..');'
|
||||
return sqlite.query(self.db, sql) == 1
|
||||
end
|
||||
|
||||
function DBModel:get(id)
|
||||
return sqlite.select(self.db, self.name, "*","id="..id)[1]
|
||||
end
|
||||
|
||||
function DBModel:getAll()
|
||||
--local sql = "SELECT * FROM "..self.name
|
||||
--return sqlite.select(self.db, self.name, "1=1")
|
||||
local data = sqlite.select(self.db, self.name, "*", "1=1")
|
||||
if data == nil then return nil end
|
||||
local a = {}
|
||||
for n in pairs(data) do table.insert(a, n) end
|
||||
table.sort(a)
|
||||
return data, a
|
||||
end
|
||||
|
||||
function DBModel:find(cond)
|
||||
local cnd = "1=1"
|
||||
local sel = "*"
|
||||
if cond.exp then
|
||||
cnd = self:gencond(cond.exp)
|
||||
end
|
||||
if cond.order then
|
||||
cnd = cnd.." ORDER BY "
|
||||
local l = {}
|
||||
local i = 1
|
||||
for k,v in pairs(cond.order) do
|
||||
l[i] = k.." "..v
|
||||
i = i+1
|
||||
end
|
||||
cnd = cnd..table.concat(l, ",")
|
||||
end
|
||||
if cond.limit then
|
||||
cnd = cnd.." LIMIT "..cond.limit
|
||||
end
|
||||
if cond.fields then
|
||||
sel = table.concat(cond.fields, ",")
|
||||
--print(sel)
|
||||
end
|
||||
--print(cnd)
|
||||
local data = sqlite.select(self.db, self.name, sel, cnd)
|
||||
if data == nil then return nil end
|
||||
local a = {}
|
||||
for n in pairs(data) do table.insert(a, n) end
|
||||
table.sort(a)
|
||||
return data, a
|
||||
end
|
||||
|
||||
function DBModel:query(sql)
|
||||
return sqlite.query(self.db, sql) == 1
|
||||
end
|
||||
|
||||
function DBModel:update(m)
|
||||
local id = m['id']
|
||||
if id ~= nil then
|
||||
local lst = {}
|
||||
for k,v in pairs(m) do
|
||||
if(type(v)== "number") then
|
||||
table.insert(lst,k.."="..v)
|
||||
elseif type(v) == "boolean" then
|
||||
table.insert( lst, k.."="..(v and 1 or 0) )
|
||||
else
|
||||
table.insert(lst,k.."=\""..v:gsub('"', '""').."\"")
|
||||
end
|
||||
end
|
||||
local sql = "UPDATE "..self.name.." SET "..table.concat(lst,",").." WHERE id="..id..";"
|
||||
return sqlite.query(self.db, sql) == 1
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function DBModel:available()
|
||||
return sqlite.hasTable(self.db, self.name) == 1
|
||||
end
|
||||
function DBModel:deleteByID(id)
|
||||
local sql = "DELETE FROM "..self.name.." WHERE id="..id..";"
|
||||
return sqlite.query(self.db, sql) == 1
|
||||
end
|
||||
function DBModel:gencond(o)
|
||||
for k,v in pairs(o) do
|
||||
if k == "and" or k == "or" then
|
||||
local cnd = {}
|
||||
local i = 1
|
||||
for k1,v1 in pairs(v) do
|
||||
cnd[i] = self:gencond(v1)
|
||||
i = i + 1
|
||||
end
|
||||
return " ("..table.concat(cnd, " "..k.." ")..") "
|
||||
else
|
||||
for k1,v1 in pairs(v) do
|
||||
local t = type(v1)
|
||||
if(t == "string") then
|
||||
return " ("..k1.." "..k..' "'..v1:gsub('"','""')..'") '
|
||||
end
|
||||
return " ("..k1.." "..k.." "..v1..") "
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function DBModel:delete(cond)
|
||||
local sql = "DELETE FROM "..self.name.." WHERE "..self:gencond(cond)..";"
|
||||
return sqlite.query(self.db, sql) == 1
|
||||
end
|
||||
|
||||
function DBModel:lastInsertID()
|
||||
return sqlite.lastInsertID(self.db)
|
||||
end
|
||||
|
||||
function DBModel:close()
|
||||
if self.db then
|
||||
sqlite.dbclose(self.db)
|
||||
end
|
||||
end
|
||||
function DBModel:open()
|
||||
if self.db ~= nil then
|
||||
self.db = sqlite.getdb(self.db)
|
||||
end
|
||||
end
|
171
silkmvc/core/std.lua
Normal file
171
silkmvc/core/std.lua
Normal file
@ -0,0 +1,171 @@
|
||||
bytes = modules.bytes()
|
||||
array = modules.array()
|
||||
|
||||
modules.sqlite = function()
|
||||
if not sqlite then
|
||||
sqlite = require("sqlitedb")
|
||||
sqlite.getdb = function(name)
|
||||
if name:find("%.db$") then
|
||||
return sqlite._getdb(name)
|
||||
elseif name:find("/") then
|
||||
LOG_ERROR("Invalid database name %s", name)
|
||||
return nil
|
||||
else
|
||||
return sqlite._getdb(__api__.dbpath.."/"..name..".db")
|
||||
end
|
||||
end
|
||||
end
|
||||
return sqlite
|
||||
end
|
||||
|
||||
RESPONSE_HEADER = {
|
||||
status = 200,
|
||||
header = {},
|
||||
cookie = {},
|
||||
sent = false
|
||||
}
|
||||
|
||||
function std.status(code)
|
||||
RESPONSE_HEADER.status=code
|
||||
end
|
||||
function std.custom_header(k,v)
|
||||
std.header(k,v)
|
||||
end
|
||||
function std.header_flush()
|
||||
std._send_header(HTTP_REQUEST.id,RESPONSE_HEADER.status, RESPONSE_HEADER.header, RESPONSE_HEADER.cookie)
|
||||
RESPONSE_HEADER.sent = true
|
||||
end
|
||||
|
||||
function std.header(k,v)
|
||||
RESPONSE_HEADER.header[k] = v
|
||||
end
|
||||
|
||||
function std.cjson(ck)
|
||||
for k,v in pairs(ck) do
|
||||
std.setCookie(k.."="..v.."; Path=/")
|
||||
end
|
||||
std.header("Content-Type","application/json; charset=utf-8")
|
||||
std.header_flush()
|
||||
end
|
||||
function std.chtml(ck)
|
||||
for k,v in pairs(ck) do
|
||||
std.setCookie(k.."="..v.."; Path=/")
|
||||
end
|
||||
std.header("Content-Type","text/html; charset=utf-8")
|
||||
std.header_flush()
|
||||
end
|
||||
function std.t(s)
|
||||
if RESPONSE_HEADER.sent == false then
|
||||
std.header_flush()
|
||||
end
|
||||
std._t(HTTP_REQUEST.id,s)
|
||||
end
|
||||
function std.b(s)
|
||||
if RESPONSE_HEADER.sent == false then
|
||||
std.header_flush()
|
||||
end
|
||||
std._b(HTTP_REQUEST.id,s)
|
||||
end
|
||||
function std.f(v)
|
||||
std._f(HTTP_REQUEST.id,v)
|
||||
--ulib.send_file(v, HTTP_REQUEST.socket)
|
||||
end
|
||||
|
||||
function std.setCookie(v)
|
||||
RESPONSE_HEADER.cookie[#RESPONSE_HEADER.cookie] = v
|
||||
end
|
||||
|
||||
function std.error(status, msg)
|
||||
std._error(HTTP_REQUEST.id, status, msg)
|
||||
end
|
||||
--_upload
|
||||
--_route
|
||||
function std.unknow(s)
|
||||
std.error(404, "Unknown request")
|
||||
end
|
||||
|
||||
--_redirect
|
||||
--[[ function std.redirect(s)
|
||||
std._redirect(HTTP_REQUEST.id,s)
|
||||
end ]]
|
||||
|
||||
function std.html()
|
||||
std.header("Content-Type","text/html; charset=utf-8")
|
||||
std.header_flush()
|
||||
end
|
||||
function std.text()
|
||||
std.header("Content-Type","text/plain; charset=utf-8")
|
||||
std.header_flush()
|
||||
end
|
||||
|
||||
function std.json()
|
||||
std.header("Content-Type","application/json; charset=utf-8")
|
||||
std.header_flush()
|
||||
end
|
||||
function std.jpeg()
|
||||
std.header("Content-Type","image/jpeg")
|
||||
std.header_flush()
|
||||
end
|
||||
function std.octstream(s)
|
||||
std.header("Content-Type","application/octet-stream")
|
||||
std.header("Content-Disposition",'attachment; filename="'..s..'"')
|
||||
std.header_flush()
|
||||
end
|
||||
--[[ function std.textstream()
|
||||
std._textstream(HTTP_REQUEST.id)
|
||||
end ]]
|
||||
|
||||
|
||||
function std.readOnly(t) -- bugging
|
||||
local proxy = {}
|
||||
local mt = { -- create metatable
|
||||
__index = t,
|
||||
__newindex = function (t,k,v)
|
||||
error("attempt to update a read-only table", 2)
|
||||
end
|
||||
}
|
||||
setmetatable(proxy, mt)
|
||||
return proxy
|
||||
end
|
||||
|
||||
|
||||
-- web socket
|
||||
std.ws = {}
|
||||
function std.ws.header()
|
||||
local h = std.ws_header(HTTP_REQUEST.id)
|
||||
if(h) then
|
||||
return h --std.readOnly(h)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function std.ws.read(h)
|
||||
return std.ws_read(HTTP_REQUEST.id,h)
|
||||
end
|
||||
function std.ws.swrite(s)
|
||||
std.ws_t(HTTP_REQUEST.id,s)
|
||||
end
|
||||
function std.ws.fwrite(s)
|
||||
std.ws_f(HTTP_REQUEST.id,s)
|
||||
end
|
||||
function std.ws.write_bytes(arr)
|
||||
std.ws_b(HTTP_REQUEST.id,arr)
|
||||
end
|
||||
function std.ws.enable()
|
||||
return HTTP_REQUEST ~= nil and HTTP_REQUEST.request["__web_socket__"] == "1"
|
||||
end
|
||||
function std.ws.close(code)
|
||||
std.ws_close(HTTP_REQUEST.id,code)
|
||||
end
|
||||
function std.basename(str)
|
||||
local name = string.gsub(std.trim(str,"/"), "(.*/)(.*)", "%2")
|
||||
return name
|
||||
end
|
||||
function std.is_file(f)
|
||||
return std.is_dir(f) == false
|
||||
end
|
||||
|
||||
std.ws.TEXT = 1
|
||||
std.ws.BIN = 2
|
||||
std.ws.CLOSE = 8
|
161
silkmvc/core/utils.lua
Normal file
161
silkmvc/core/utils.lua
Normal file
@ -0,0 +1,161 @@
|
||||
utils = {}
|
||||
|
||||
function utils.is_array(table)
|
||||
local max = 0
|
||||
local count = 0
|
||||
for k, v in pairs(table) do
|
||||
if type(k) == "number" then
|
||||
if k > max then max = k end
|
||||
count = count + 1
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
if max > count * 2 then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function utils.escape(s)
|
||||
local replacements = {
|
||||
["\\"] = "\\\\" ,
|
||||
['"'] = '\\"',
|
||||
["\n"] = "\\n",
|
||||
["\t"] = "\\t",
|
||||
["\b"] = "\\b",
|
||||
["\f"] = "\\f",
|
||||
["\r"] = "\\r",
|
||||
["%"] = "%%"
|
||||
}
|
||||
return (s:gsub( "[\\'\"\n\t\b\f\r%%]", replacements ))
|
||||
end
|
||||
|
||||
function utils.escape_pattern(s)
|
||||
return s:gsub("[%(%)%.%+%-%*%?%[%]%^%$%%]", "%%%1")
|
||||
end
|
||||
|
||||
function utils.unescape_pattern(s)
|
||||
return s:gsub( "[%%]", "%%%%")
|
||||
end
|
||||
|
||||
function utils.hex_to_char(x)
|
||||
return string.char(tonumber(x, 16))
|
||||
end
|
||||
|
||||
function utils.decodeURI(url)
|
||||
return url:gsub("%%(%x%x)", utils.hex_to_char)
|
||||
end
|
||||
|
||||
function utils.unescape(s)
|
||||
local str = ""
|
||||
local escape = false
|
||||
local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'}
|
||||
for c in s:gmatch"." do
|
||||
if c ~= '\\' then
|
||||
if escape then
|
||||
if esc_map[c] then
|
||||
str = str..esc_map[c]
|
||||
else
|
||||
str = str..c
|
||||
end
|
||||
else
|
||||
str = str..c
|
||||
end
|
||||
escape = false
|
||||
else
|
||||
if escape then
|
||||
str = str..c
|
||||
escape = false
|
||||
else
|
||||
escape = true
|
||||
end
|
||||
end
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
function utils.file_exists(name)
|
||||
local f=io.open(name,"r")
|
||||
if f~=nil then io.close(f) return true else return false end
|
||||
end
|
||||
|
||||
function utils.url_parser(uri)
|
||||
local pattern = "^(https?)://([%.%w]+):?(%d*)(/?[^#]*)#?.*$"
|
||||
local obj = {}
|
||||
obj.protocol = uri:gsub(pattern, "%1")
|
||||
obj.hostname = uri:gsub(pattern, "%2")
|
||||
obj.port = uri:gsub(pattern, "%3")
|
||||
obj.query = uri:gsub(pattern, "%4")
|
||||
|
||||
if obj.port == "" then obj.port = 80 else obj.port = tonumber(obj.port) end
|
||||
if obj.query == "" then obj.query="/" end
|
||||
return obj
|
||||
end
|
||||
|
||||
JSON = require("json")
|
||||
|
||||
function JSON.encode(obj)
|
||||
local t = type(obj)
|
||||
if t == 'table' then
|
||||
-- encode object
|
||||
if utils.is_array(obj) == false then
|
||||
local lst = {}
|
||||
for k,v in pairs(obj) do
|
||||
table.insert(lst,'"'..k..'":'..JSON.encode(v))
|
||||
end
|
||||
return "{"..table.concat(lst,",").."}"
|
||||
else
|
||||
local lst = {}
|
||||
local a = {}
|
||||
for n in pairs(obj) do table.insert(a, n) end
|
||||
table.sort(a)
|
||||
for i,v in pairs(a) do
|
||||
table.insert(lst,JSON.encode(obj[v]))
|
||||
end
|
||||
return "["..table.concat(lst,",").."]"
|
||||
end
|
||||
elseif t == 'string' then
|
||||
--print('"'..utils.escape(obj)..'"')
|
||||
return '"'..utils.escape(obj)..'"'
|
||||
elseif t == 'boolean' or t == 'number' then
|
||||
return tostring(obj)
|
||||
elseif obj == nil then
|
||||
return "null"
|
||||
else
|
||||
return '"'..tostring(obj)..'"'
|
||||
end
|
||||
end
|
||||
|
||||
function explode(str, div) -- credit: http://richard.warburton.it
|
||||
if (div=='') then return false end
|
||||
local pos,arr = 0,{}
|
||||
-- for each divider found
|
||||
for st,sp in function() return string.find(str,div,pos,true) end do
|
||||
table.insert(arr,string.sub(str,pos,st-1)) -- Attach chars left of current divider
|
||||
pos = sp + 1 -- Jump past current divider
|
||||
end
|
||||
table.insert(arr,string.sub(str,pos)) -- Attach chars right of last divider
|
||||
return arr
|
||||
end
|
||||
function implode(arr, div)
|
||||
return table.concat(arr,div)
|
||||
end
|
||||
|
||||
function firstToUpper(str)
|
||||
return (str:gsub("^%l", string.upper))
|
||||
end
|
||||
|
||||
|
||||
local charset = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"
|
||||
|
||||
function utils.generate_salt(length)
|
||||
local ret = {}
|
||||
local r
|
||||
for i = 1, length do
|
||||
r = math.random(1, #charset)
|
||||
table.insert(ret, charset:sub(r, r))
|
||||
end
|
||||
return table.concat(ret)
|
||||
end
|
Reference in New Issue
Block a user