Added plugins and boot.lua as startup script [Miodrag Milanovic]

This commit is contained in:
Miodrag Milanovic 2016-02-14 10:58:18 +01:00
parent 2db4908814
commit ccae0382bb
55 changed files with 4789 additions and 1 deletions

1
.gitignore vendored
View File

@ -9,6 +9,7 @@
!/hlsl/
!/keymaps/
!/nl_examples/
!/plugins/
!/regtests/
!/samples/
!/scripts/

26
plugins/boot.lua Normal file
View File

@ -0,0 +1,26 @@
local uv = require('luv')
local cwd = uv.cwd()
package.path = cwd .. "/plugins/?.lua;" .. cwd .. "/plugins/?/init.lua"
require('weblit/app')
.bind({
host = "0.0.0.0",
port = 8080
})
.use(require('weblit/logger'))
.use(require('weblit/auto-headers'))
.use(require('weblit/etag-cache'))
.route({
method = "GET",
path = "/",
}, function (req, res, go)
res.code = 200
res.headers["Content-Type"] = "text/html"
res.body = "<h1>Hello!</h1>\n"
end)
.start()

View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Tim Caswell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,2 @@
# luv-coro-channel
A luv port of creationix/coro-channel from lit.luvit.io

View File

@ -0,0 +1,128 @@
local exports = {}
exports.name = "creationix/coro-channel"
exports.version = "1.2.0"
exports.homepage = "https://github.com/luvit/lit/blob/master/deps/coro-channel.lua"
exports.description = "An adapter for wrapping uv streams as coro-streams and chaining filters."
exports.tags = {"coro", "adapter"}
exports.license = "MIT"
exports.author = { name = "Tim Caswell" }
local function wrapRead(socket)
local paused = true
local queue = {}
local waiting
local onRead
function onRead(err, chunk)
local data = err and {nil, err} or {chunk}
if waiting then
local thread = waiting
waiting = nil
assert(coroutine.resume(thread, unpack(data)))
else
queue[#queue + 1] = data
if not paused then
paused = true
assert(socket:read_stop())
end
end
end
return function ()
if #queue > 0 then
return unpack(table.remove(queue, 1))
end
if paused then
paused = false
assert(socket:read_start(onRead))
end
waiting = coroutine.running()
return coroutine.yield()
end
end
local function wrapWrite(socket)
local function wait()
local thread = coroutine.running()
return function (err)
assert(coroutine.resume(thread, err))
end
end
local function shutdown()
socket:shutdown(wait())
coroutine.yield()
if not socket:is_closing() then
socket:close()
end
end
return function (chunk)
if chunk == nil then
return shutdown()
end
assert(socket:write(chunk, wait()))
local err = coroutine.yield()
return not err, err
end
end
exports.wrapRead = wrapRead
exports.wrapWrite = wrapWrite
-- Given a raw uv_stream_t userdata, return coro-friendly read/write functions.
function exports.wrapStream(socket)
return wrapRead(socket), wrapWrite(socket)
end
function exports.chain(...)
local args = {...}
local nargs = select("#", ...)
return function (read, write)
local threads = {} -- coroutine thread for each item
local waiting = {} -- flag when waiting to pull from upstream
local boxes = {} -- storage when waiting to write to downstream
for i = 1, nargs do
threads[i] = coroutine.create(args[i])
waiting[i] = false
local r, w
if i == 1 then
r = read
else
function r()
local j = i - 1
if boxes[j] then
local data = boxes[j]
boxes[j] = nil
assert(coroutine.resume(threads[j]))
return unpack(data)
else
waiting[i] = true
return coroutine.yield()
end
end
end
if i == nargs then
w = write
else
function w(...)
local j = i + 1
if waiting[j] then
waiting[j] = false
assert(coroutine.resume(threads[j], ...))
else
boxes[i] = {...}
coroutine.yield()
end
end
end
assert(coroutine.resume(threads[i], r, w))
end
end
end
return exports

22
plugins/coro-fs/LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Tim Caswell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,2 @@
# luv-coro-fs
A luv port of lit's coro-fs module

222
plugins/coro-fs/init.lua Normal file
View File

@ -0,0 +1,222 @@
local exports = {}
exports.name = "creationix/coro-fs"
exports.version = "1.3.0"
exports.homepage = "https://github.com/luvit/lit/blob/master/deps/coro-fs.lua"
exports.description = "A coro style interface to the filesystem."
exports.tags = {"coro", "fs"}
exports.license = "MIT"
exports.author = { name = "Tim Caswell" }
local uv = require('luv')
local fs = exports
local pathJoin = require('path').join
local function noop() end
local function makeCallback()
local thread = coroutine.running()
return function (err, value, ...)
if err then
assert(coroutine.resume(thread, nil, err))
else
assert(coroutine.resume(thread, value == nil and true or value, ...))
end
end
end
function fs.mkdir(path, mode)
uv.fs_mkdir(path, mode or 511, makeCallback())
return coroutine.yield()
end
function fs.open(path, flags, mode)
uv.fs_open(path, flags or "r", mode or 438, makeCallback())
return coroutine.yield()
end
function fs.unlink(path)
uv.fs_unlink(path, makeCallback())
return coroutine.yield()
end
function fs.stat(path)
uv.fs_stat(path, makeCallback())
return coroutine.yield()
end
function fs.lstat(path)
uv.fs_lstat(path, makeCallback())
return coroutine.yield()
end
function fs.symlink(target, path)
uv.fs_symlink(target, path, makeCallback())
return coroutine.yield()
end
function fs.readlink(path)
uv.fs_readlink(path, makeCallback())
return coroutine.yield()
end
function fs.fstat(fd)
uv.fs_fstat(fd, makeCallback())
return coroutine.yield()
end
function fs.chmod(fd, path)
uv.fs_chmod(fd, path, makeCallback())
return coroutine.yield()
end
function fs.fchmod(fd, mode)
uv.fs_fchmod(fd, mode, makeCallback())
return coroutine.yield()
end
function fs.read(fd, length, offset)
uv.fs_read(fd, length or 1024*48, offset or -1, makeCallback())
return coroutine.yield()
end
function fs.write(fd, data, offset)
uv.fs_write(fd, data, offset or -1, makeCallback())
return coroutine.yield()
end
function fs.close(fd)
uv.fs_close(fd, makeCallback())
return coroutine.yield()
end
function fs.access(path, flags)
uv.fs_access(path, flags or "", makeCallback())
return coroutine.yield()
end
function fs.rename(path, newPath)
uv.fs_rename(path, newPath, makeCallback())
return coroutine.yield()
end
function fs.rmdir(path)
uv.fs_rmdir(path, makeCallback())
return coroutine.yield()
end
function fs.rmrf(path)
local success, err
success, err = fs.rmdir(path)
if success then return success end
if err:match("^ENOTDIR:") then return fs.unlink(path) end
if not err:match("^ENOTEMPTY:") then return success, err end
for entry in assert(fs.scandir(path)) do
local subPath = pathJoin(path, entry.name)
if entry.type == "directory" then
success, err = fs.rmrf(pathJoin(path, entry.name))
else
success, err = fs.unlink(subPath)
end
if not success then return success, err end
end
return fs.rmdir(path)
end
function fs.scandir(path)
uv.fs_scandir(path, makeCallback())
local req, err = coroutine.yield()
if not req then return nil, err end
return function ()
return uv.fs_scandir_next(req)
end
end
function fs.readFile(path)
local fd, stat, data, err
fd, err = fs.open(path)
if err then return nil, err end
stat, err = fs.fstat(fd)
if stat then
data, err = fs.read(fd, stat.size)
end
uv.fs_close(fd, noop)
return data, err
end
function fs.writeFile(path, data, mkdir)
local fd, success, err
fd, err = fs.open(path, "w")
if err then
if mkdir and string.match(err, "^ENOENT:") then
success, err = fs.mkdirp(pathJoin(path, ".."))
if success then return fs.writeFile(path, data) end
end
return nil, err
end
success, err = fs.write(fd, data)
uv.fs_close(fd, noop)
return success, err
end
function fs.mkdirp(path, mode)
local success, err = fs.mkdir(path, mode)
if success or string.match(err, "^EEXIST") then
return true
end
if string.match(err, "^ENOENT:") then
success, err = fs.mkdirp(pathJoin(path, ".."), mode)
if not success then return nil, err end
return fs.mkdir(path, mode)
end
return nil, err
end
function fs.chroot(base)
local chroot = {
base = base,
fstat = fs.fstat,
fchmod = fs.fchmod,
read = fs.read,
write = fs.write,
close = fs.close,
}
local function resolve(path)
assert(path, "path missing")
return pathJoin(base, pathJoin(path))
end
function chroot.mkdir(path, mode)
return fs.mkdir(resolve(path), mode)
end
function chroot.mkdirp(path, mode)
return fs.mkdirp(resolve(path), mode)
end
function chroot.open(path, flags, mode)
return fs.open(resolve(path), flags, mode)
end
function chroot.unlink(path)
return fs.unlink(resolve(path))
end
function chroot.stat(path)
return fs.stat(resolve(path))
end
function chroot.lstat(path)
return fs.lstat(resolve(path))
end
function chroot.symlink(target, path)
-- TODO: should we resolve absolute target paths or treat it as opaque data?
return fs.symlink(target, resolve(path))
end
function chroot.readlink(path)
return fs.readlink(resolve(path))
end
function chroot.chmod(path, mode)
return fs.chmod(resolve(path), mode)
end
function chroot.access(path, flags)
return fs.access(resolve(path), flags)
end
function chroot.rename(path, newPath)
return fs.rename(resolve(path), resolve(newPath))
end
function chroot.rmdir(path)
return fs.rmdir(resolve(path))
end
function chroot.rmrf(path)
return fs.rmrf(resolve(path))
end
function chroot.scandir(path, iter)
return fs.scandir(resolve(path), iter)
end
function chroot.readFile(path)
return fs.readFile(resolve(path))
end
function chroot.writeFile(path, data, mkdir)
return fs.writeFile(resolve(path), data, mkdir)
end
return chroot
end
return exports

22
plugins/coro-http/LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Tim Caswell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,2 @@
# luv-coro-http
A luv port of lit's coro-http library.

187
plugins/coro-http/init.lua Normal file
View File

@ -0,0 +1,187 @@
local exports = {}
exports.name = "creationix/coro-http"
exports.version = "1.2.1-1"
exports.dependencies = {
"creationix/coro-net@1.1.1",
"creationix/coro-tls@1.2.1",
"creationix/coro-wrapper@1.0.0",
"luvit/http-codec@1.0.0"
}
exports.homepage = "https://github.com/luvit/lit/blob/master/deps/coro-http.lua"
exports.description = "An coro style http(s) client and server helper."
exports.tags = {"coro", "http"}
exports.license = "MIT"
exports.author = { name = "Tim Caswell" }
local httpCodec = require('http-codec')
local net = require('coro-net')
local connect = net.connect
local createServer = net.createServer
--local tlsWrap = require('coro-tls').wrap
local wrapper = require('coro-wrapper')
function exports.createServer(options, onConnect)
createServer(options, function (rawRead, rawWrite, socket)
local read = wrapper.reader(rawRead, httpCodec.decoder())
local write = wrapper.writer(rawWrite, httpCodec.encoder())
for head in read do
local parts = {}
for part in read do
if #part > 0 then
parts[#parts + 1] = part
else
break
end
end
local body = table.concat(parts)
head, body = onConnect(head, body, socket)
write(head)
if body then write(body) end
write("")
if not head.keepAlive then break end
end
end)
end
local function parseUrl(url)
local protocol, host, hostname, port, path = url:match("^(https?:)//(([^/:]+):?([0-9]*))(/?.*)$")
if not protocol then error("Not a valid http url: " .. url) end
local tls = protocol == "https:"
port = port and tonumber(port) or (tls and 443 or 80)
if path == "" then path = "/" end
return {
tls = tls,
host = host,
hostname = hostname,
port = port,
path = path
}
end
exports.parseUrl = parseUrl
local connections = {}
local function getConnection(host, port, tls)
for i = #connections, 1, -1 do
local connection = connections[i]
if connection.host == host and connection.port == port and connection.tls == tls then
table.remove(connections, i)
-- Make sure the connection is still alive before reusing it.
if not connection.socket:is_closing() then
connection.reused = true
connection.socket:ref()
return connection
end
end
end
local read, write, socket = assert(connect({host=host,port=port}))
--if tls then
--read, write = tlsWrap(read, write)
--end
local httpRead, updateRead = wrapper.reader(read, httpCodec.decoder())
return {
socket = socket,
host = host,
port = port,
tls = tls,
read = httpRead,
write = wrapper.writer(write, httpCodec.encoder()),
reset = function ()
-- This is called after parsing the response head from a HEAD request.
-- If you forget, the codec might hang waiting for a body that doesn't exist.
updateRead(httpCodec.decoder())
end
}
end
exports.getConnection = getConnection
local function saveConnection(connection)
if connection.socket:is_closing() then return end
connections[#connections + 1] = connection
connection.socket:unref()
end
exports.saveConnection = saveConnection
function exports.request(method, url, headers, body)
local uri = parseUrl(url)
local connection = getConnection(uri.hostname, uri.port, uri.tls)
local read = connection.read
local write = connection.write
local req = {
method = method,
path = uri.path,
{"Host", uri.host}
}
local contentLength
local chunked
if headers then
for i = 1, #headers do
local key, value = unpack(headers[i])
key = key:lower()
if key == "content-length" then
contentLength = value
elseif key == "content-encoding" and value:lower() == "chunked" then
chunked = true
end
req[#req + 1] = headers[i]
end
end
if type(body) == "string" then
if not chunked and not contentLength then
req[#req + 1] = {"Content-Length", #body}
end
end
write(req)
if body then write(body) end
local res = read()
if not res then
write()
-- If we get an immediate close on a reused socket, try again with a new socket.
-- TODO: think about if this could resend requests with side effects and cause
-- them to double execute in the remote server.
if connection.reused then
return exports.request(method, url, headers, body)
end
error("Connection closed")
end
body = {}
if req.method == "HEAD" then
connection.reset()
else
while true do
local item = read()
if not item then
res.keepAlive = false
break
end
if #item == 0 then
break
end
body[#body + 1] = item
end
end
if res.keepAlive then
saveConnection(connection)
else
write()
end
-- Follow redirects
if method == "GET" and (res.code == 302 or res.code == 307) then
for i = 1, #res do
local key, location = unpack(res[i])
if key:lower() == "location" then
return exports.request(method, location, headers)
end
end
end
return res, table.concat(body)
end
return exports

22
plugins/coro-net/LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Tim Caswell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,2 @@
# luv-coro-net
A luv port of creationix/coro-net from lit.luvit.io

113
plugins/coro-net/init.lua Normal file
View File

@ -0,0 +1,113 @@
local exports = {}
exports.name = "creationix/coro-net"
exports.version = "1.1.1-1"
exports.dependencies = {
"creationix/coro-channel@1.2.0"
}
exports.homepage = "https://github.com/luvit/lit/blob/master/deps/coro-net.lua"
exports.description = "An coro style client and server helper for tcp and pipes."
exports.tags = {"coro", "tcp", "pipe", "net"}
exports.license = "MIT"
exports.author = { name = "Tim Caswell" }
local uv = require('luv')
local wrapStream = require('coro-channel').wrapStream
local function makeCallback(timeout)
local thread = coroutine.running()
local timer, done
if timeout then
timer = uv.new_timer()
timer:start(timeout, 0, function ()
if done then return end
done = true
timer:close()
return assert(coroutine.resume(thread, nil, "timeout"))
end)
end
return function (err, data)
if done then return end
done = true
if timer then timer:close() end
if err then
return assert(coroutine.resume(thread, nil, err))
end
return assert(coroutine.resume(thread, data or true))
end
end
exports.makeCallback = makeCallback
local function normalize(options)
local t = type(options)
if t == "string" then
options = {path=options}
elseif t == "number" then
options = {port=options}
elseif t ~= "table" then
assert("Net options must be table, string, or number")
end
if options.port or options.host then
return true,
options.host or "127.0.0.1",
assert(options.port, "options.port is required for tcp connections")
elseif options.path then
return false, options.path
else
error("Must set either options.path or options.port")
end
end
function exports.connect(options)
local socket, success, err
local isTcp, host, port = normalize(options)
if isTcp then
assert(uv.getaddrinfo(host, port, {
socktype = options.socktype or "stream",
family = options.family or "inet",
}, makeCallback(options.timeout)))
local res
res, err = coroutine.yield()
if not res then return nil, err end
socket = uv.new_tcp()
socket:connect(res[1].addr, res[1].port, makeCallback(options.timeout))
else
socket = uv.new_pipe(false)
socket:connect(host, makeCallback(options.timeout))
end
success, err = coroutine.yield()
if not success then return nil, err end
local read, write = wrapStream(socket)
return read, write, socket
end
function exports.createServer(options, onConnect)
local server
local isTcp, host, port = normalize(options)
if isTcp then
server = uv.new_tcp()
assert(server:bind(host, port))
else
server = uv.new_pipe(false)
assert(server:bind(host))
end
assert(server:listen(256, function (err)
assert(not err, err)
local socket = isTcp and uv.new_tcp() or uv.new_pipe(false)
server:accept(socket)
coroutine.wrap(function ()
local success, failure = xpcall(function ()
local read, write = wrapStream(socket)
return onConnect(read, write, socket)
end, debug.traceback)
if not success then
print(failure)
end
if not socket:is_closing() then
socket:close()
end
end)()
end))
return server
end
return exports

22
plugins/coro-tls/LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Tim Caswell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,2 @@
# luv-coro-tls
A luv port of lit's coro-tls module

122
plugins/coro-tls/init.lua Normal file
View File

@ -0,0 +1,122 @@
local exports = {}
exports.name = "creationix/coro-tls"
exports.version = "1.2.1"
exports.homepage = "https://github.com/luvit/lit/blob/master/deps/coro-tls.lua"
exports.description = "A coro-stream wrapper implementing tls sessions."
exports.tags = {"coro", "tls", "ssl"}
exports.license = "MIT"
exports.author = { name = "Tim Caswell" }
local openssl = require('openssl')
local bit = require('bit')
local DEFAULT_CIPHERS = 'ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:' .. -- TLS 1.2
'RC4:HIGH:!MD5:!aNULL:!EDH' -- TLS 1.0
-- Given a read/write pair, return a new read/write pair for plaintext
exports.wrap = function (read, write, options)
if not options then
options = {}
end
local ctx = openssl.ssl.ctx_new(options.protocol or 'TLSv1_2', options.ciphers or DEFAULT_CIPHERS)
local key, cert, ca
if options.key then
key = assert(openssl.pkey.read(options.key, true, 'pem'))
end
if options.cert then
cert = assert(openssl.x509.read(options.cert))
end
if options.ca then
if type(options.ca) == "string" then
ca = { assert(openssl.x509.read(options.ca)) }
elseif type(options.ca) == "table" then
ca = {}
for i = 1, #options.ca do
ca[i] = assert(openssl.x509.read(options.ca[i]))
end
else
error("options.ca must be string or table of strings")
end
end
if key and cert then
assert(ctx:use(key, cert))
end
if ca then
local store = openssl.x509.store:new()
for i = 1, #ca do
assert(store:add(ca[i]))
end
ctx:cert_store(store)
else
ctx:verify_mode({"none"})
end
ctx:options(bit.bor(
openssl.ssl.no_sslv2,
openssl.ssl.no_sslv3,
openssl.ssl.no_compression))
local bin, bout = openssl.bio.mem(8192), openssl.bio.mem(8192)
local ssl = ctx:ssl(bin, bout, options.server)
local function flush()
while bout:pending() > 0 do
write(bout:read())
end
end
-- Do handshake
while true do
if ssl:handshake() then break end
flush()
local chunk = read()
if chunk then
bin:write(chunk)
else
error("disconnect while handshaking")
end
end
flush()
local done = false
local function shutdown()
if done then return end
done = true
while true do
if ssl:shutdown() then break end
flush()
local chunk = read()
if chunk then
bin:write(chunk)
else
break
end
end
flush()
write()
end
local function plainRead()
while true do
local chunk = ssl:read()
if chunk then return chunk end
local cipher = read()
if not cipher then return end
bin:write(cipher)
end
end
local function plainWrite(plain)
if not plain then
return shutdown()
end
ssl:write(plain)
flush()
end
return plainRead, plainWrite
end
return exports

View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Tim Caswell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,2 @@
# luv-coro-wrapper
A luv port of lit's coro-wrapper module

View File

@ -0,0 +1,41 @@
local exports = {}
exports.name = "creationix/coro-wrapper"
exports.version = "1.0.0-1"
exports.homepage = "https://github.com/luvit/lit/blob/master/deps/coro-wrapper.lua"
exports.description = "An adapter for applying decoders to coro-streams."
exports.tags = {"coro", "decoder", "adapter"}
exports.license = "MIT"
exports.author = { name = "Tim Caswell" }
function exports.reader(read, decode)
local buffer = ""
return function ()
while true do
local item, extra = decode(buffer)
if item then
buffer = extra
return item
end
local chunk = read()
if not chunk then return end
buffer = buffer .. chunk
end
end,
function (newDecode)
decode = newDecode
end
end
function exports.writer(write, encode)
return function (item)
if not item then
return write()
end
return write(encode(item))
end,
function (newEncode)
encode = newEncode
end
end
return exports

202
plugins/http-codec/LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,2 @@
# luv-http-codec
A luv port of luvit's http-codec

291
plugins/http-codec/init.lua Normal file
View File

@ -0,0 +1,291 @@
--[[
Copyright 2014-2015 The Luvit Authors. All Rights Reserved.
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
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS-IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
local exports = {}
exports.name = "luvit/http-codec"
exports.version = "1.0.0-1"
exports.homepage = "https://github.com/luvit/luvit/blob/master/deps/http-codec.lua"
exports.description = "A simple pair of functions for converting between hex and raw strings."
exports.tags = {"codec", "http"}
exports.license = "Apache 2"
exports.author = { name = "Tim Caswell" }
local sub = string.sub
local gsub = string.gsub
local lower = string.lower
local find = string.find
local format = string.format
local concat = table.concat
local match = string.match
local STATUS_CODES = {
[100] = 'Continue',
[101] = 'Switching Protocols',
[102] = 'Processing', -- RFC 2518, obsoleted by RFC 4918
[200] = 'OK',
[201] = 'Created',
[202] = 'Accepted',
[203] = 'Non-Authoritative Information',
[204] = 'No Content',
[205] = 'Reset Content',
[206] = 'Partial Content',
[207] = 'Multi-Status', -- RFC 4918
[300] = 'Multiple Choices',
[301] = 'Moved Permanently',
[302] = 'Moved Temporarily',
[303] = 'See Other',
[304] = 'Not Modified',
[305] = 'Use Proxy',
[307] = 'Temporary Redirect',
[400] = 'Bad Request',
[401] = 'Unauthorized',
[402] = 'Payment Required',
[403] = 'Forbidden',
[404] = 'Not Found',
[405] = 'Method Not Allowed',
[406] = 'Not Acceptable',
[407] = 'Proxy Authentication Required',
[408] = 'Request Time-out',
[409] = 'Conflict',
[410] = 'Gone',
[411] = 'Length Required',
[412] = 'Precondition Failed',
[413] = 'Request Entity Too Large',
[414] = 'Request-URI Too Large',
[415] = 'Unsupported Media Type',
[416] = 'Requested Range Not Satisfiable',
[417] = 'Expectation Failed',
[418] = "I'm a teapot", -- RFC 2324
[422] = 'Unprocessable Entity', -- RFC 4918
[423] = 'Locked', -- RFC 4918
[424] = 'Failed Dependency', -- RFC 4918
[425] = 'Unordered Collection', -- RFC 4918
[426] = 'Upgrade Required', -- RFC 2817
[500] = 'Internal Server Error',
[501] = 'Not Implemented',
[502] = 'Bad Gateway',
[503] = 'Service Unavailable',
[504] = 'Gateway Time-out',
[505] = 'HTTP Version not supported',
[506] = 'Variant Also Negotiates', -- RFC 2295
[507] = 'Insufficient Storage', -- RFC 4918
[509] = 'Bandwidth Limit Exceeded',
[510] = 'Not Extended' -- RFC 2774
}
exports.encoder = function ()
local mode
local encodeHead, encodeRaw, encodeChunked
function encodeHead(item)
if not item or item == "" then
return item
elseif not (type(item) == "table") then
error("expected a table but got a " .. type(item) .. " when encoding data")
end
local head, chunkedEncoding
local version = item.version or 1.1
if item.method then
local path = item.path
assert(path and #path > 0, "expected non-empty path")
head = { item.method .. ' ' .. item.path .. ' HTTP/' .. version .. '\r\n' }
else
local reason = item.reason or STATUS_CODES[item.code]
head = { 'HTTP/' .. version .. ' ' .. item.code .. ' ' .. reason .. '\r\n' }
end
for i = 1, #item do
local key, value = unpack(item[i])
local lowerKey = lower(key)
if lowerKey == "transfer-encoding" then
chunkedEncoding = lower(value) == "chunked"
end
value = gsub(tostring(value), "[\r\n]+", " ")
head[#head + 1] = key .. ': ' .. tostring(value) .. '\r\n'
end
head[#head + 1] = '\r\n'
mode = chunkedEncoding and encodeChunked or encodeRaw
return concat(head)
end
function encodeRaw(item)
if type(item) ~= "string" then
mode = encodeHead
return encodeHead(item)
end
return item
end
function encodeChunked(item)
if type(item) ~= "string" then
mode = encodeHead
local extra = encodeHead(item)
if extra then
return "0\r\n\r\n" .. extra
else
return "0\r\n\r\n"
end
end
if #item == 0 then
mode = encodeHead
end
return format("%x", #item) .. "\r\n" .. item .. "\r\n"
end
mode = encodeHead
return function (item)
return mode(item)
end
end
exports.decoder = function ()
-- This decoder is somewhat stateful with 5 different parsing states.
local decodeHead, decodeEmpty, decodeRaw, decodeChunked, decodeCounted
local mode -- state variable that points to various decoders
local bytesLeft -- For counted decoder
-- This state is for decoding the status line and headers.
function decodeHead(chunk)
if not chunk then return end
local _, length = find(chunk, "\r?\n\r?\n", 1)
-- First make sure we have all the head before continuing
if not length then
if #chunk < 8 * 1024 then return end
-- But protect against evil clients by refusing heads over 8K long.
error("entity too large")
end
-- Parse the status/request line
local head = {}
local _, offset
local version
_, offset, version, head.code, head.reason =
find(chunk, "^HTTP/(%d%.%d) (%d+) ([^\r\n]+)\r?\n")
if offset then
head.code = tonumber(head.code)
else
_, offset, head.method, head.path, version =
find(chunk, "^(%u+) ([^ ]+) HTTP/(%d%.%d)\r?\n")
if not offset then
error("expected HTTP data")
end
end
version = tonumber(version)
head.version = version
head.keepAlive = version > 1.0
-- We need to inspect some headers to know how to parse the body.
local contentLength
local chunkedEncoding
-- Parse the header lines
while true do
local key, value
_, offset, key, value = find(chunk, "^([^:\r\n]+): *([^\r\n]+)\r?\n", offset + 1)
if not offset then break end
local lowerKey = lower(key)
-- Inspect a few headers and remember the values
if lowerKey == "content-length" then
contentLength = tonumber(value)
elseif lowerKey == "transfer-encoding" then
chunkedEncoding = lower(value) == "chunked"
elseif lowerKey == "connection" then
head.keepAlive = lower(value) == "keep-alive"
end
head[#head + 1] = {key, value}
end
if head.keepAlive and (not (chunkedEncoding or (contentLength and contentLength > 0)))
or (head.method == "GET" or head.method == "HEAD") then
mode = decodeEmpty
elseif chunkedEncoding then
mode = decodeChunked
elseif contentLength then
bytesLeft = contentLength
mode = decodeCounted
elseif not head.keepAlive then
mode = decodeRaw
end
return head, sub(chunk, length + 1)
end
-- This is used for inserting a single empty string into the output string for known empty bodies
function decodeEmpty(chunk)
mode = decodeHead
return "", chunk or ""
end
function decodeRaw(chunk)
if not chunk then return "", "" end
if #chunk == 0 then return end
return chunk, ""
end
function decodeChunked(chunk)
local len, term
len, term = match(chunk, "^(%x+)(..)")
if not len then return end
assert(term == "\r\n")
local length = tonumber(len, 16)
if #chunk < length + 4 + #len then return end
if length == 0 then
mode = decodeHead
end
chunk = sub(chunk, #len + 3)
assert(sub(chunk, length + 1, length + 2) == "\r\n")
return sub(chunk, 1, length), sub(chunk, length + 3)
end
function decodeCounted(chunk)
if bytesLeft == 0 then
mode = decodeEmpty
return mode(chunk)
end
local length = #chunk
-- Make sure we have at least one byte to process
if length == 0 then return end
if length >= bytesLeft then
mode = decodeEmpty
end
-- If the entire chunk fits, pass it all through
if length <= bytesLeft then
bytesLeft = bytesLeft - length
return chunk, ""
end
return sub(chunk, 1, bytesLeft), sub(chunk, bytesLeft + 1)
end
-- Switch between states by changing which decoder mode points to
mode = decodeHead
return function (chunk)
return mode(chunk)
end
end
return exports

22
plugins/json/LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Tim Caswell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

2
plugins/json/README.md Normal file
View File

@ -0,0 +1,2 @@
# luv-json
A luv port of luvit's json module

732
plugins/json/init.lua Normal file
View File

@ -0,0 +1,732 @@
local exports = {}
exports.name = "luvit/json"
exports.version = "2.5.0"
exports.homepage = "http://dkolf.de/src/dkjson-lua.fsl"
exports.description = "David Kolf's JSON library repackaged for lit."
exports.tags = {"json", "codec"}
exports.license = "MIT"
exports.author = {
name = "David Kolf",
homepage = "http://dkolf.de/",
}
exports.contributors = {
"Tim Caswell",
}
-- Module options:
local always_try_using_lpeg = false
local register_global_module_table = false
local global_module_name = 'json'
--[==[
David Kolf's JSON module for Lua 5.1/5.2
Version 2.5
For the documentation see the corresponding readme.txt or visit
<http://dkolf.de/src/dkjson-lua.fsl/>.
You can contact the author by sending an e-mail to 'david' at the
domain 'dkolf.de'.
Copyright (C) 2010-2013 David Heiko Kolf
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
--]==]
-- global dependencies:
local pairs, type, tostring, tonumber, getmetatable, setmetatable =
pairs, type, tostring, tonumber, getmetatable, setmetatable
local error, require, pcall, select = error, require, pcall, select
local floor, huge = math.floor, math.huge
local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat =
string.rep, string.gsub, string.sub, string.byte, string.char,
string.find, string.len, string.format
local strmatch = string.match
local concat = table.concat
local json = exports
json.original_version = "dkjson 2.5"
if register_global_module_table then
_G[global_module_name] = json
end
_ENV = nil -- blocking globals in Lua 5.2
pcall (function()
-- Enable access to blocked metatables.
-- Don't worry, this module doesn't change anything in them.
local debmeta = require "debug".getmetatable
if debmeta then getmetatable = debmeta end
end)
json.null = setmetatable ({}, {
__tojson = function () return "null" end
})
local function isarray (tbl)
local max, n, arraylen = 0, 0, 0
for k,v in pairs (tbl) do
if k == 'n' and type(v) == 'number' then
arraylen = v
if v > max then
max = v
end
else
if type(k) ~= 'number' or k < 1 or floor(k) ~= k then
return false
end
if k > max then
max = k
end
n = n + 1
end
end
if max > 10 and max > arraylen and max > n * 2 then
return false -- don't create an array with too many holes
end
return true, max
end
local escapecodes = {
["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f",
["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t"
}
local function escapeutf8 (uchar)
local value = escapecodes[uchar]
if value then
return value
end
local a, b, c, d = strbyte (uchar, 1, 4)
a, b, c, d = a or 0, b or 0, c or 0, d or 0
if a <= 0x7f then
value = a
elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then
value = (a - 0xc0) * 0x40 + b - 0x80
elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then
value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80
elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then
value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80
else
return ""
end
if value <= 0xffff then
return strformat ("\\u%.4x", value)
elseif value <= 0x10ffff then
-- encode as UTF-16 surrogate pair
value = value - 0x10000
local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400)
return strformat ("\\u%.4x\\u%.4x", highsur, lowsur)
else
return ""
end
end
local function fsub (str, pattern, repl)
-- gsub always builds a new string in a buffer, even when no match
-- exists. First using find should be more efficient when most strings
-- don't contain the pattern.
if strfind (str, pattern) then
return gsub (str, pattern, repl)
else
return str
end
end
local function quotestring (value)
-- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js
value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8)
if strfind (value, "[\194\216\220\225\226\239]") then
value = fsub (value, "\194[\128-\159\173]", escapeutf8)
value = fsub (value, "\216[\128-\132]", escapeutf8)
value = fsub (value, "\220\143", escapeutf8)
value = fsub (value, "\225\158[\180\181]", escapeutf8)
value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8)
value = fsub (value, "\226\129[\160-\175]", escapeutf8)
value = fsub (value, "\239\187\191", escapeutf8)
value = fsub (value, "\239\191[\176-\191]", escapeutf8)
end
return "\"" .. value .. "\""
end
json.quotestring = quotestring
local function replace(str, o, n)
local i, j = strfind (str, o, 1, true)
if i then
return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1)
else
return str
end
end
-- locale independent num2str and str2num functions
local decpoint, numfilter
local function updatedecpoint ()
decpoint = strmatch(tostring(0.5), "([^05+])")
-- build a filter that can be used to remove group separators
numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+"
end
updatedecpoint()
local function num2str (num)
return replace(fsub(tostring(num), numfilter, ""), decpoint, ".")
end
local function str2num (str)
local num = tonumber(replace(str, ".", decpoint))
if not num then
updatedecpoint()
num = tonumber(replace(str, ".", decpoint))
end
return num
end
local function addnewline2 (level, buffer, buflen)
buffer[buflen+1] = "\n"
buffer[buflen+2] = strrep (" ", level)
buflen = buflen + 2
return buflen
end
function json.addnewline (state)
if state.indent then
state.bufferlen = addnewline2 (state.level or 0,
state.buffer, state.bufferlen or #(state.buffer))
end
end
local encode2 -- forward declaration
local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state)
local kt = type (key)
if kt ~= 'string' and kt ~= 'number' then
return nil, "type '" .. kt .. "' is not supported as a key by JSON."
end
if prev then
buflen = buflen + 1
buffer[buflen] = ","
end
if indent then
buflen = addnewline2 (level, buffer, buflen)
end
buffer[buflen+1] = quotestring (key)
buffer[buflen+2] = ":"
return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state)
end
local function appendcustom(res, buffer, state)
local buflen = state.bufferlen
if type (res) == 'string' then
buflen = buflen + 1
buffer[buflen] = res
end
return buflen
end
local function exception(reason, value, state, buffer, buflen, defaultmessage)
defaultmessage = defaultmessage or reason
local handler = state.exception
if not handler then
return nil, defaultmessage
else
state.bufferlen = buflen
local ret, msg = handler (reason, value, state, defaultmessage)
if not ret then return nil, msg or defaultmessage end
return appendcustom(ret, buffer, state)
end
end
function json.encodeexception(reason, value, state, defaultmessage)
return quotestring("<" .. defaultmessage .. ">")
end
encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state)
local valtype = type (value)
local valmeta = getmetatable (value)
valmeta = type (valmeta) == 'table' and valmeta -- only tables
local valtojson = valmeta and valmeta.__tojson
if valtojson then
if tables[value] then
return exception('reference cycle', value, state, buffer, buflen)
end
tables[value] = true
state.bufferlen = buflen
local ret, msg = valtojson (value, state)
if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end
tables[value] = nil
buflen = appendcustom(ret, buffer, state)
elseif value == nil then
buflen = buflen + 1
buffer[buflen] = "null"
elseif valtype == 'number' then
local s
if value ~= value or value >= huge or -value >= huge then
-- This is the behaviour of the original JSON implementation.
s = "null"
else
s = num2str (value)
end
buflen = buflen + 1
buffer[buflen] = s
elseif valtype == 'boolean' then
buflen = buflen + 1
buffer[buflen] = value and "true" or "false"
elseif valtype == 'string' then
buflen = buflen + 1
buffer[buflen] = quotestring (value)
elseif valtype == 'table' then
if tables[value] then
return exception('reference cycle', value, state, buffer, buflen)
end
tables[value] = true
level = level + 1
local isa, n = isarray (value)
if n == 0 and valmeta and valmeta.__jsontype == 'object' then
isa = false
end
local msg
if isa then -- JSON array
buflen = buflen + 1
buffer[buflen] = "["
for i = 1, n do
buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state)
if not buflen then return nil, msg end
if i < n then
buflen = buflen + 1
buffer[buflen] = ","
end
end
buflen = buflen + 1
buffer[buflen] = "]"
else -- JSON object
local prev = false
buflen = buflen + 1
buffer[buflen] = "{"
local order = valmeta and valmeta.__jsonorder or globalorder
if order then
local used = {}
n = #order
for i = 1, n do
local k = order[i]
local v = value[k]
local _
if v then
used[k] = true
buflen, _ = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
prev = true -- add a seperator before the next element
end
end
for k,v in pairs (value) do
if not used[k] then
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
if not buflen then return nil, msg end
prev = true -- add a seperator before the next element
end
end
else -- unordered
for k,v in pairs (value) do
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
if not buflen then return nil, msg end
prev = true -- add a seperator before the next element
end
end
if indent then
buflen = addnewline2 (level - 1, buffer, buflen)
end
buflen = buflen + 1
buffer[buflen] = "}"
end
tables[value] = nil
else
return exception ('unsupported type', value, state, buffer, buflen,
"type '" .. valtype .. "' is not supported by JSON.")
end
return buflen
end
function json.encode (value, state)
state = state or {}
local oldbuffer = state.buffer
local buffer = oldbuffer or {}
state.buffer = buffer
updatedecpoint()
local ret, msg = encode2 (value, state.indent, state.level or 0,
buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state)
if not ret then
error (msg, 2)
elseif oldbuffer == buffer then
state.bufferlen = ret
return true
else
state.bufferlen = nil
state.buffer = nil
return concat (buffer)
end
end
local function loc (str, where)
local line, pos, linepos = 1, 1, 0
while true do
pos = strfind (str, "\n", pos, true)
if pos and pos < where then
line = line + 1
linepos = pos
pos = pos + 1
else
break
end
end
return "line " .. line .. ", column " .. (where - linepos)
end
local function unterminated (str, what, where)
return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where)
end
local function scanwhite (str, pos)
while true do
pos = strfind (str, "%S", pos)
if not pos then return nil end
local sub2 = strsub (str, pos, pos + 1)
if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then
-- UTF-8 Byte Order Mark
pos = pos + 3
elseif sub2 == "//" then
pos = strfind (str, "[\n\r]", pos + 2)
if not pos then return nil end
elseif sub2 == "/*" then
pos = strfind (str, "*/", pos + 2)
if not pos then return nil end
pos = pos + 2
else
return pos
end
end
end
local escapechars = {
["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f",
["n"] = "\n", ["r"] = "\r", ["t"] = "\t"
}
local function unichar (value)
if value < 0 then
return nil
elseif value <= 0x007f then
return strchar (value)
elseif value <= 0x07ff then
return strchar (0xc0 + floor(value/0x40),
0x80 + (floor(value) % 0x40))
elseif value <= 0xffff then
return strchar (0xe0 + floor(value/0x1000),
0x80 + (floor(value/0x40) % 0x40),
0x80 + (floor(value) % 0x40))
elseif value <= 0x10ffff then
return strchar (0xf0 + floor(value/0x40000),
0x80 + (floor(value/0x1000) % 0x40),
0x80 + (floor(value/0x40) % 0x40),
0x80 + (floor(value) % 0x40))
else
return nil
end
end
local function scanstring (str, pos)
local lastpos = pos + 1
local buffer, n = {}, 0
while true do
local nextpos = strfind (str, "[\"\\]", lastpos)
if not nextpos then
return unterminated (str, "string", pos)
end
if nextpos > lastpos then
n = n + 1
buffer[n] = strsub (str, lastpos, nextpos - 1)
end
if strsub (str, nextpos, nextpos) == "\"" then
lastpos = nextpos + 1
break
else
local escchar = strsub (str, nextpos + 1, nextpos + 1)
local value
if escchar == "u" then
value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16)
if value then
local value2
if 0xD800 <= value and value <= 0xDBff then
-- we have the high surrogate of UTF-16. Check if there is a
-- low surrogate escaped nearby to combine them.
if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then
value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16)
if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then
value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000
else
value2 = nil -- in case it was out of range for a low surrogate
end
end
end
value = value and unichar (value)
if value then
if value2 then
lastpos = nextpos + 12
else
lastpos = nextpos + 6
end
end
end
end
if not value then
value = escapechars[escchar] or escchar
lastpos = nextpos + 2
end
n = n + 1
buffer[n] = value
end
end
if n == 1 then
return buffer[1], lastpos
elseif n > 1 then
return concat (buffer), lastpos
else
return "", lastpos
end
end
local scanvalue -- forward declaration
local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta)
local tbl, n = {}, 0
local pos = startpos + 1
if what == 'object' then
setmetatable (tbl, objectmeta)
else
setmetatable (tbl, arraymeta)
end
while true do
pos = scanwhite (str, pos)
if not pos then return unterminated (str, what, startpos) end
local char = strsub (str, pos, pos)
if char == closechar then
return tbl, pos + 1
end
local val1, err
val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
if err then return nil, pos, err end
pos = scanwhite (str, pos)
if not pos then return unterminated (str, what, startpos) end
char = strsub (str, pos, pos)
if char == ":" then
if val1 == nil then
return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")"
end
pos = scanwhite (str, pos + 1)
if not pos then return unterminated (str, what, startpos) end
local val2
val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
if err then return nil, pos, err end
tbl[val1] = val2
pos = scanwhite (str, pos)
if not pos then return unterminated (str, what, startpos) end
char = strsub (str, pos, pos)
else
n = n + 1
tbl[n] = val1
end
if char == "," then
pos = pos + 1
end
end
end
scanvalue = function (str, pos, nullval, objectmeta, arraymeta)
pos = pos or 1
pos = scanwhite (str, pos)
if not pos then
return nil, strlen (str) + 1, "no valid JSON value (reached the end)"
end
local char = strsub (str, pos, pos)
if char == "{" then
return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta)
elseif char == "[" then
return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta)
elseif char == "\"" then
return scanstring (str, pos)
else
local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos)
if pstart then
local number = str2num (strsub (str, pstart, pend))
if number then
return number, pend + 1
end
end
pstart, pend = strfind (str, "^%a%w*", pos)
if pstart then
local name = strsub (str, pstart, pend)
if name == "true" then
return true, pend + 1
elseif name == "false" then
return false, pend + 1
elseif name == "null" then
return nullval, pend + 1
end
end
return nil, pos, "no valid JSON value at " .. loc (str, pos)
end
end
local function optionalmetatables(...)
if select("#", ...) > 0 then
return ...
else
return {__jsontype = 'object'}, {__jsontype = 'array'}
end
end
function json.decode (str, pos, nullval, ...)
local objectmeta, arraymeta = optionalmetatables(...)
return scanvalue (str, pos, nullval, objectmeta, arraymeta)
end
function json.use_lpeg ()
local g = require ("lpeg")
if g.version() == "0.11" then
error "due to a bug in LPeg 0.11, it cannot be used for JSON matching"
end
local pegmatch = g.match
local P, S, R = g.P, g.S, g.R
local function ErrorCall (str, pos, msg, state)
if not state.msg then
state.msg = msg .. " at " .. loc (str, pos)
state.pos = pos
end
return false
end
local function Err (msg)
return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall)
end
local SingleLineComment = P"//" * (1 - S"\n\r")^0
local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/"
local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0
local PlainChar = 1 - S"\"\\\n\r"
local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars
local HexDigit = R("09", "af", "AF")
local function UTF16Surrogate (match, pos, high, low)
high, low = tonumber (high, 16), tonumber (low, 16)
if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then
return true, unichar ((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000)
else
return false
end
end
local function UTF16BMP (hex)
return unichar (tonumber (hex, 16))
end
local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit))
local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP
local Char = UnicodeEscape + EscapeSequence + PlainChar
local String = P"\"" * g.Cs (Char ^ 0) * (P"\"" + Err "unterminated string")
local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0))
local Fractal = P"." * R"09"^0
local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1
local Number = (Integer * Fractal^(-1) * Exponent^(-1))/str2num
local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1)
local SimpleValue = Number + String + Constant
local ArrayContent, ObjectContent
-- The functions parsearray and parseobject parse only a single value/pair
-- at a time and store them directly to avoid hitting the LPeg limits.
local function parsearray (str, pos, nullval, state)
local obj, cont
local npos
local t, nt = {}, 0
repeat
obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state)
if not npos then break end
pos = npos
nt = nt + 1
t[nt] = obj
until cont == 'last'
return pos, setmetatable (t, state.arraymeta)
end
local function parseobject (str, pos, nullval, state)
local obj, key, cont
local npos
local t = {}
repeat
key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state)
if not npos then break end
pos = npos
t[key] = obj
until cont == 'last'
return pos, setmetatable (t, state.objectmeta)
end
local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) * Space * (P"]" + Err "']' expected")
local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) * Space * (P"}" + Err "'}' expected")
local Value = Space * (Array + Object + SimpleValue)
local ExpectedValue = Value + Space * Err "value expected"
ArrayContent = Value * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp()
local Pair = g.Cg (Space * String * Space * (P":" + Err "colon expected") * ExpectedValue)
ObjectContent = Pair * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp()
local DecodeValue = ExpectedValue * g.Cp ()
function json.decode (str, pos, nullval, ...)
local state = {}
state.objectmeta, state.arraymeta = optionalmetatables(...)
local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state)
if state.msg then
return nil, state.pos, state.msg
else
return obj, retpos
end
end
-- use this function only once:
json.use_lpeg = function () return json end
json.using_lpeg = true
return json -- so you can get the module using json = require "dkjson".use_lpeg()
end
if always_try_using_lpeg then
pcall (json.use_lpeg)
end
json.parse = json.decode
json.stringify = json.encode
return exports

22
plugins/mime/LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Tim Caswell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

2
plugins/mime/README.md Normal file
View File

@ -0,0 +1,2 @@
# luv-mime
A luv port of weblit's mime module

194
plugins/mime/init.lua Normal file
View File

@ -0,0 +1,194 @@
local exports = {}
exports.name = "creationix/mime"
exports.version = "0.1.2-1"
exports.description = "A simple mime type database useful for serving static files over http."
exports.tags = {"mime", "static"}
exports.license = "MIT"
exports.author = { name = "Tim Caswell" }
exports.homepage = "https://github.com/creationix/weblit/blob/master/libs/mime.lua"
local mime = exports
local table = {
["3gp"] = "video/3gpp",
a = "application/octet-stream",
ai = "application/postscript",
aif = "audio/x-aiff",
aiff = "audio/x-aiff",
asc = "application/pgp-signature",
asf = "video/x-ms-asf",
asm = "text/x-asm",
asx = "video/x-ms-asf",
atom = "application/atom+xml",
au = "audio/basic",
avi = "video/x-msvideo",
bat = "application/x-msdownload",
bin = "application/octet-stream",
bmp = "image/bmp",
bz2 = "application/x-bzip2",
c = "text/x-c",
cab = "application/vnd.ms-cab-compressed",
cc = "text/x-c",
chm = "application/vnd.ms-htmlhelp",
class = "application/octet-stream",
com = "application/x-msdownload",
conf = "text/plain",
cpp = "text/x-c",
crt = "application/x-x509-ca-cert",
css = "text/css",
csv = "text/csv",
cxx = "text/x-c",
deb = "application/x-debian-package",
der = "application/x-x509-ca-cert",
diff = "text/x-diff",
djv = "image/vnd.djvu",
djvu = "image/vnd.djvu",
dll = "application/x-msdownload",
dmg = "application/octet-stream",
doc = "application/msword",
dot = "application/msword",
dtd = "application/xml-dtd",
dvi = "application/x-dvi",
ear = "application/java-archive",
eml = "message/rfc822",
eps = "application/postscript",
exe = "application/x-msdownload",
f = "text/x-fortran",
f77 = "text/x-fortran",
f90 = "text/x-fortran",
flv = "video/x-flv",
["for"] = "text/x-fortran",
gem = "application/octet-stream",
gemspec = "text/x-script.ruby",
gif = "image/gif",
gz = "application/x-gzip",
h = "text/x-c",
hh = "text/x-c",
htm = "text/html",
html = "text/html",
ico = "image/vnd.microsoft.icon",
ics = "text/calendar",
ifb = "text/calendar",
iso = "application/octet-stream",
jar = "application/java-archive",
java = "text/x-java-source",
jnlp = "application/x-java-jnlp-file",
jpeg = "image/jpeg",
jpg = "image/jpeg",
js = "application/javascript",
json = "application/json",
less = "text/css",
log = "text/plain",
lua = "text/x-lua",
luac = "application/x-lua-bytecode",
m3u = "audio/x-mpegurl",
m4v = "video/mp4",
man = "text/troff",
manifest = "text/cache-manifest",
markdown = "text/markdown",
mathml = "application/mathml+xml",
mbox = "application/mbox",
mdoc = "text/troff",
md = "text/markdown",
me = "text/troff",
mid = "audio/midi",
midi = "audio/midi",
mime = "message/rfc822",
mml = "application/mathml+xml",
mng = "video/x-mng",
mov = "video/quicktime",
mp3 = "audio/mpeg",
mp4 = "video/mp4",
mp4v = "video/mp4",
mpeg = "video/mpeg",
mpg = "video/mpeg",
ms = "text/troff",
msi = "application/x-msdownload",
odp = "application/vnd.oasis.opendocument.presentation",
ods = "application/vnd.oasis.opendocument.spreadsheet",
odt = "application/vnd.oasis.opendocument.text",
ogg = "application/ogg",
p = "text/x-pascal",
pas = "text/x-pascal",
pbm = "image/x-portable-bitmap",
pdf = "application/pdf",
pem = "application/x-x509-ca-cert",
pgm = "image/x-portable-graymap",
pgp = "application/pgp-encrypted",
pkg = "application/octet-stream",
pl = "text/x-script.perl",
pm = "text/x-script.perl-module",
png = "image/png",
pnm = "image/x-portable-anymap",
ppm = "image/x-portable-pixmap",
pps = "application/vnd.ms-powerpoint",
ppt = "application/vnd.ms-powerpoint",
ps = "application/postscript",
psd = "image/vnd.adobe.photoshop",
py = "text/x-script.python",
qt = "video/quicktime",
ra = "audio/x-pn-realaudio",
rake = "text/x-script.ruby",
ram = "audio/x-pn-realaudio",
rar = "application/x-rar-compressed",
rb = "text/x-script.ruby",
rdf = "application/rdf+xml",
roff = "text/troff",
rpm = "application/x-redhat-package-manager",
rss = "application/rss+xml",
rtf = "application/rtf",
ru = "text/x-script.ruby",
s = "text/x-asm",
sgm = "text/sgml",
sgml = "text/sgml",
sh = "application/x-sh",
sig = "application/pgp-signature",
snd = "audio/basic",
so = "application/octet-stream",
svg = "image/svg+xml",
svgz = "image/svg+xml",
swf = "application/x-shockwave-flash",
t = "text/troff",
tar = "application/x-tar",
tbz = "application/x-bzip-compressed-tar",
tci = "application/x-topcloud",
tcl = "application/x-tcl",
tex = "application/x-tex",
texi = "application/x-texinfo",
texinfo = "application/x-texinfo",
text = "text/plain",
tif = "image/tiff",
tiff = "image/tiff",
torrent = "application/x-bittorrent",
tr = "text/troff",
ttf = "application/x-font-ttf",
txt = "text/plain",
vcf = "text/x-vcard",
vcs = "text/x-vcalendar",
vrml = "model/vrml",
war = "application/java-archive",
wav = "audio/x-wav",
webm = "video/webm",
wma = "audio/x-ms-wma",
wmv = "video/x-ms-wmv",
wmx = "video/x-ms-wmx",
wrl = "model/vrml",
wsdl = "application/wsdl+xml",
xbm = "image/x-xbitmap",
xhtml = "application/xhtml+xml",
xls = "application/vnd.ms-excel",
xml = "application/xml",
xpm = "image/x-xpixmap",
xsl = "application/xml",
xslt = "application/xslt+xml",
yaml = "text/yaml",
yml = "text/yaml",
zip = "application/zip",
}
mime.table = table
mime.default = "application/octet-stream"
function mime.getType(path)
return mime.table[path:lower():match("[^.]*$")] or mime.default
end
return mime

202
plugins/path/LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

2
plugins/path/README.md Normal file
View File

@ -0,0 +1,2 @@
# luv-path
A luv port of luvi's path module

139
plugins/path/init.lua Normal file
View File

@ -0,0 +1,139 @@
--[[
Copyright 2014 The Luvit Authors. All Rights Reserved.
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
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS-IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
-- Extracted from https://github.com/luvit/luvi/blob/master/src/lua/init.lua
local isWindows
if jit and jit.os then
-- Luajit provides explicit platform detection
isWindows = jit.os == "Windows"
else
-- Normal lua will only have \ for path separator on windows.
isWindows = package.config:find("\\") and true or false
end
local uv = require('luv')
local getPrefix, splitPath, joinParts
if isWindows then
-- Windows aware path utilities
function getPrefix(path)
return path:match("^%a:\\") or
path:match("^/") or
path:match("^\\+")
end
function splitPath(path)
local parts = {}
for part in string.gmatch(path, '([^/\\]+)') do
table.insert(parts, part)
end
return parts
end
function joinParts(prefix, parts, i, j)
if not prefix then
return table.concat(parts, '/', i, j)
elseif prefix ~= '/' then
return prefix .. table.concat(parts, '\\', i, j)
else
return prefix .. table.concat(parts, '/', i, j)
end
end
else
-- Simple optimized versions for UNIX systems
function getPrefix(path)
return path:match("^/")
end
function splitPath(path)
local parts = {}
for part in string.gmatch(path, '([^/]+)') do
table.insert(parts, part)
end
return parts
end
function joinParts(prefix, parts, i, j)
if prefix then
return prefix .. table.concat(parts, '/', i, j)
end
return table.concat(parts, '/', i, j)
end
end
local function pathJoin(...)
local inputs = {...}
local l = #inputs
-- Find the last segment that is an absolute path
-- Or if all are relative, prefix will be nil
local i = l
local prefix
while true do
prefix = getPrefix(inputs[i])
if prefix or i <= 1 then break end
i = i - 1
end
-- If there was one, remove its prefix from its segment
if prefix then
inputs[i] = inputs[i]:sub(#prefix)
end
-- Split all the paths segments into one large list
local parts = {}
while i <= l do
local sub = splitPath(inputs[i])
for j = 1, #sub do
parts[#parts + 1] = sub[j]
end
i = i + 1
end
-- Evaluate special segments in reverse order.
local skip = 0
local reversed = {}
for idx = #parts, 1, -1 do
local part = parts[idx]
if part == '.' then
-- Ignore
elseif part == '..' then
skip = skip + 1
elseif skip > 0 then
skip = skip - 1
else
reversed[#reversed + 1] = part
end
end
-- Reverse the list again to get the correct order
parts = reversed
for idx = 1, #parts / 2 do
local j = #parts - idx + 1
parts[idx], parts[j] = parts[j], parts[idx]
end
local path = joinParts(prefix, parts)
return path
end
return {
isWindows = isWindows,
join = pathJoin,
getPrefix = getPrefix,
splitPath = splitPath,
joinparts = joinParts,
}

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,2 @@
# luv-pretty-print
luvit's pretty-print module re-packaged as a luv submodule.

View File

@ -0,0 +1,362 @@
--[[
Copyright 2014-2015 The Luvit Authors. All Rights Reserved.
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
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS-IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
-- Luv port by Tim Caswell <tim@creationix.com>
local exports = {}
exports.name = "luvit/pretty-print"
exports.version = "1.0.3"
exports.homepage = "https://github.com/luvit/luvit/blob/master/deps/pretty-print.lua"
exports.description = "A lua value pretty printer and colorizer for terminals."
exports.tags = {"colors", "tty"}
exports.license = "Apache 2"
exports.author = { name = "Tim Caswell" }
local uv = require('luv')
local prettyPrint, dump, strip, color, colorize, loadColors
local theme = {}
local useColors = false
local defaultTheme
local stdout, stdin, stderr, width
local quote, quote2, dquote, dquote2, obracket, cbracket, obrace, cbrace, comma, equals, controls
local themes = {
-- nice color theme using 16 ansi colors
[16] = {
property = "0;37", -- white
sep = "1;30", -- bright-black
braces = "1;30", -- bright-black
["nil"] = "1;30", -- bright-black
boolean = "0;33", -- yellow
number = "1;33", -- bright-yellow
string = "0;32", -- green
quotes = "1;32", -- bright-green
escape = "1;32", -- bright-green
["function"] = "0;35", -- purple
thread = "1;35", -- bright-purple
table = "1;34", -- bright blue
userdata = "1;36", -- bright cyan
cdata = "0;36", -- cyan
err = "1;31", -- bright red
success = "1;33;42", -- bright-yellow on green
failure = "1;33;41", -- bright-yellow on red
highlight = "1;36;44", -- bright-cyan on blue
},
-- nice color theme using ansi 256-mode colors
[256] = {
property = "38;5;253",
braces = "38;5;247",
sep = "38;5;240",
["nil"] = "38;5;244",
boolean = "38;5;220", -- yellow-orange
number = "38;5;202", -- orange
string = "38;5;34", -- darker green
quotes = "38;5;40", -- green
escape = "38;5;46", -- bright green
["function"] = "38;5;129", -- purple
thread = "38;5;199", -- pink
table = "38;5;27", -- blue
userdata = "38;5;39", -- blue2
cdata = "38;5;69", -- teal
err = "38;5;196", -- bright red
success = "38;5;120;48;5;22", -- bright green on dark green
failure = "38;5;215;48;5;52", -- bright red on dark red
highlight = "38;5;45;48;5;236", -- bright teal on dark grey
},
}
local special = {
[7] = 'a',
[8] = 'b',
[9] = 't',
[10] = 'n',
[11] = 'v',
[12] = 'f',
[13] = 'r'
}
function strip(str)
return string.gsub(str, '\027%[[^m]*m', '')
end
function loadColors(index)
if index == nil then index = defaultTheme end
-- Remove the old theme
for key in pairs(theme) do
theme[key] = nil
end
if index then
local new = themes[index]
if not new then error("Invalid theme index: " .. tostring(index)) end
-- Add the new theme
for key in pairs(new) do
theme[key] = new[key]
end
useColors = true
else
useColors = false
end
quote = colorize('quotes', "'", 'string')
quote2 = colorize('quotes', "'")
dquote = colorize('quotes', '"', 'string')
dquote2 = colorize('quotes', '"')
obrace = colorize('braces', '{ ')
cbrace = colorize('braces', '}')
obracket = colorize('property', '[')
cbracket = colorize('property', ']')
comma = colorize('sep', ', ')
equals = colorize('sep', ' = ')
controls = {}
for i = 0, 31 do
local c = special[i]
if not c then
if i < 10 then
c = "00" .. tostring(i)
else
c = "0" .. tostring(i)
end
end
controls[i] = colorize('escape', '\\' .. c, 'string')
end
controls[92] = colorize('escape', '\\\\', 'string')
controls[34] = colorize('escape', '\\"', 'string')
controls[39] = colorize('escape', "\\'", 'string')
for i = 128, 255 do
local c
if i < 100 then
c = "0" .. tostring(i)
else
c = tostring(i)
end
controls[i] = colorize('escape', '\\' .. c, 'string')
end
end
function color(colorName)
return '\27[' .. (theme[colorName] or '0') .. 'm'
end
function colorize(colorName, string, resetName)
return useColors and
(color(colorName) .. tostring(string) .. color(resetName)) or
tostring(string)
end
local function stringEscape(c)
return controls[string.byte(c, 1)]
end
function dump(value, recurse, nocolor)
local seen = {}
local output = {}
local offset = 0
local stack = {}
local function recalcOffset(index)
for i = index + 1, #output do
local m = string.match(output[i], "\n([^\n]*)$")
if m then
offset = #(strip(m))
else
offset = offset + #(strip(output[i]))
end
end
end
local function write(text, length)
if not length then length = #(strip(text)) end
-- Create room for data by opening parent blocks
-- Start at the root and go down.
local i = 1
while offset + length > width and stack[i] do
local entry = stack[i]
if not entry.opened then
entry.opened = true
table.insert(output, entry.index + 1, "\n" .. string.rep(" ", i))
-- Recalculate the offset
recalcOffset(entry.index)
-- Bump the index of all deeper entries
for j = i + 1, #stack do
stack[j].index = stack[j].index + 1
end
end
i = i + 1
end
output[#output + 1] = text
offset = offset + length
if offset > width then
return dump(stack)
end
end
local function indent()
stack[#stack + 1] = {
index = #output,
opened = false,
}
end
local function unindent()
stack[#stack] = nil
end
local function process(value)
local typ = type(value)
if typ == 'string' then
if string.match(value, "'") and not string.match(value, '"') then
write(dquote .. string.gsub(value, '[%c\\\128-\255]', stringEscape) .. dquote2)
else
write(quote .. string.gsub(value, "[%c\\'\128-\255]", stringEscape) .. quote2)
end
elseif typ == 'table' and not seen[value] then
if not recurse then seen[value] = true end
write(obrace)
local i = 1
-- Count the number of keys so we know when to stop adding commas
local total = 0
for _ in pairs(value) do total = total + 1 end
local nextIndex = 1
for k, v in pairs(value) do
indent()
if k == nextIndex then
-- if the key matches the last numerical index + 1
-- This is how lists print without keys
nextIndex = k + 1
process(v)
else
if type(k) == "string" and string.find(k,"^[%a_][%a%d_]*$") then
write(colorize("property", k) .. equals)
else
write(obracket)
process(k)
write(cbracket .. equals)
end
if type(v) == "table" then
process(v)
else
indent()
process(v)
unindent()
end
end
if i < total then
write(comma)
else
write(" ")
end
i = i + 1
unindent()
end
write(cbrace)
else
write(colorize(typ, tostring(value)))
end
end
process(value)
local s = table.concat(output, "")
return nocolor and strip(s) or s
end
-- Print replacement that goes through libuv. This is useful on windows
-- to use libuv's code to translate ansi escape codes to windows API calls.
function _G.print(...)
local n = select('#', ...)
local arguments = {...}
for i = 1, n do
arguments[i] = tostring(arguments[i])
end
uv.write(stdout, table.concat(arguments, "\t") .. "\n")
end
function prettyPrint(...)
local n = select('#', ...)
local arguments = { ... }
for i = 1, n do
arguments[i] = dump(arguments[i])
end
print(table.concat(arguments, "\t"))
end
function strip(str)
return string.gsub(str, '\027%[[^m]*m', '')
end
if uv.guess_handle(0) == 'tty' then
stdin = assert(uv.new_tty(0, true))
else
stdin = uv.new_pipe(false)
uv.pipe_open(stdin, 0)
end
if uv.guess_handle(1) == 'tty' then
stdout = assert(uv.new_tty(1, false))
width = uv.tty_get_winsize(stdout)
if width == 0 then width = 80 end
-- auto-detect when 16 color mode should be used
local term = os.getenv("TERM")
if term == 'xterm' or term == 'xterm-256color' then
defaultTheme = 256
else
defaultTheme = 16
end
else
stdout = uv.new_pipe(false)
uv.pipe_open(stdout, 1)
width = 80
end
loadColors()
if uv.guess_handle(2) == 'tty' then
stderr = assert(uv.new_tty(2, false))
else
stderr = uv.new_pipe(false)
uv.pipe_open(stderr, 2)
end
exports.loadColors = loadColors
exports.theme = theme
exports.print = print
exports.prettyPrint = prettyPrint
exports.dump = dump
exports.color = color
exports.colorize = colorize
exports.stdin = stdin
exports.stdout = stdout
exports.stderr = stderr
exports.strip = strip
return exports

202
plugins/querystring/LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,2 @@
# luv-querystring
A luv port of luvit's querystring

View File

@ -0,0 +1,105 @@
--[[
Copyright 2015 The Luvit Authors. All Rights Reserved.
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
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS-IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
local exports = {}
exports.name = "luvit/querystring"
exports.version = "1.0.2"
exports.license = "Apache 2"
exports.homepage = "https://github.com/luvit/luvit/blob/master/deps/querystring.lua"
exports.description = "Node-style query-string codec for luvit"
exports.tags = {"luvit", "url", "codec"}
local find = string.find
local gsub = string.gsub
local char = string.char
local byte = string.byte
local format = string.format
local match = string.match
local gmatch = string.gmatch
function exports.urldecode(str)
str = gsub(str, '+', ' ')
str = gsub(str, '%%(%x%x)', function(h)
return char(tonumber(h, 16))
end)
str = gsub(str, '\r\n', '\n')
return str
end
function exports.urlencode(str)
if str then
str = gsub(str, '\n', '\r\n')
str = gsub(str, '([^%w])', function(c)
return format('%%%02X', byte(c))
end)
end
return str
end
local function stringifyPrimitive(v)
return tostring(v)
end
function exports.stringify(params, sep, eq)
if not sep then sep = '&' end
if not eq then eq = '=' end
if type(params) == "table" then
local fields = {}
for key,value in pairs(params) do
local keyString = exports.urlencode(stringifyPrimitive(key)) .. eq
if type(value) == "table" then
for _, v in ipairs(value) do
table.insert(fields, keyString .. exports.urlencode(stringifyPrimitive(v)))
end
else
table.insert(fields, keyString .. exports.urlencode(stringifyPrimitive(value)))
end
end
return table.concat(fields, sep)
end
return ''
end
-- parse querystring into table. urldecode tokens
function exports.parse(str, sep, eq)
if not sep then sep = '&' end
if not eq then eq = '=' end
local vars = {}
for pair in gmatch(tostring(str), '[^' .. sep .. ']+') do
if not find(pair, eq) then
vars[exports.urldecode(pair)] = ''
else
local key, value = match(pair, '([^' .. eq .. ']*)' .. eq .. '(.*)')
if key then
key = exports.urldecode(key)
value = exports.urldecode(value)
local type = type(vars[key])
if type=='nil' then
vars[key] = value
elseif type=='table' then
table.insert(vars[key], value)
else
vars[key] = {vars[key],value}
end
end
end
end
return vars
end
return exports

22
plugins/weblit/LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Tim Caswell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

239
plugins/weblit/README.md Normal file
View File

@ -0,0 +1,239 @@
# weblit
A web framework for luv (ported from luvit/lit)
Weblit is a collection of lit packages that together form a nice web framework.
## weblit/app
This is the core of the framework. It's export value is the app itself. The
config functions can be chained off this config for super terse syntax.
```lua
require('weblit/app')
.bind({
host = "0.0.0.0",
port = 8080
})
.use(require('weblit/logger'))
.use(require('weblit/auto-headers'))
.use(require('weblit/etag-cache'))
.route({
method = "GET",
path = "/do/:user/:action",
domain = "*.myapp.io"
}, function (req, res, go)
-- Handle route
end)
.start()
```
### bind(options)
Use this to configure your server. You can bind to multiple addresses and
ports. For example, the same server can listen on port `8080` using normal HTTP
while also listening on port `8443` using HTTPS.
```lua
-- Listen on port 8080 internally using plain HTTP.
.bind({
host = "127.0.0.1",
port = 8080
})
-- Also listen on port 8443 externally using encrypted HTTPS.
.bind({
host = "0.0.0.0",
port = 8443,
tls = {
cert = module:load("cert.pem"),
key = module:load("key.pem",
}
})
```
The `host` option defaults to `"127.0.0.1"`. The default port depends on if
you're running as root and if the connection is TLS encrypted.
| Root | User
------|-----:|------:
HTTP | 80 | 8080
HTTPS | 442 | 8443
### use(middleware)
This adds a raw middleware to the chain. It's signature is:
```lua
.use(function (req, res, go)
-- Log the request table
p("request", req)
-- Hand off to the next layer.
return go()
end)
```
The `req` table will contain information about the HTTP request. This includes
several fields:
- `socket` - The raw libuv `uv_tty_t` socket.
- `method` - The HTTP request method verb like `GET` or `POST`.
- `path` - The raw HTTP request path (including query string).
- `headers` - A list of headers. Each header is a table with two entries for
key and value. For convenience, there are special `__index` and
`__newindex` metamethods that let you treat this like a case insensitive
key/value store.
- `version` - The HTTP version (Usually either `1.0` or `1.1`).
- `keepAlive` - A flag telling you if this should be a keepalive connection.
- `body` - The request body as a string. In the future, this may also be a stream.
The `res` table also has some conventions used to form the response a piece at a
time. Initially it contains:
- `code` - The response status code. Initially contains `404`.
- `headers` - Another special headers table like in `req`.
- `body` - The response body to send. Initially contains `"Not Found\n"`.
The `go` function is to be called if you wish to not handle a request. This
allows other middleware layers to get a chance to respond to the request. Use a
tail call if there is nothing more to do.
Otherwise do further processing after `go` returns. At this point, all inner
layers have finished and a response is ready in `res`.
### route(options, middleware)
Route is like use, but allows you to pre-filter the requests before the middleware
is called.
```lua
.route({
method = "PUT",
path = "/upload/:username"
}, function (req, res, go)
local url = saveFile(req.params.username, req.body)
res.code = 201
res.headers.Location = url
end)
```
The route options accept several parameters:
- `method` - This is a simple filter on a specific HTTP method verb.
- `path` - This is either an exact match or can contain patterns. Segments
looking like `:name` will match single path segments while `:name:` will
match multiple segments. The matches will go into `req.params`. Also any
query string will be stripped off, parsed out, and stored in `req.query`.
- `host` - Will filter against the `Host` header. This can be an exact match
or a glob match like `*.mydomain.org`.
- `filter` - Filter is a custom lua function that accepts `req` and returns
`true` or `false`.
If the request matches all the requirements, then the middleware is called the
same as with `use`.
### start
Bind to the port(s), listen on the socket(s) and start accepting connections.
## weblit/logger
This is a simple middleware that logs the request method, url and user agent.
It also includes the response status code.
Make sure to use it at the top of your middleware chain so that it's able to see
the final response code sent to the client.
```lua
.use(require('weblit/logger'))
```
## weblit/auto-headers
This implements lots of conventions and useful defaults that help your app
implement a proper HTTP server.
You should always use this near the top of the list. The only middleware that
goes before this is the logger.
```lua
.use(require('weblit/auto-headers'))
```
## weblit/etag-cache
This caches responses in memory keyed by etag. If there is no etag, but there
is a response body, it will use the body to generate an etag.
Put this in your list after auto-headers, but before custom server logic.
```lua
.use(require('weblit/etag-cache'))
```
## weblit/static
This middleware serves static files to the user. Use this to serve your client-
side web assets.
Usage is pretty simplistic for now.
```lua
local static = require('weblit/static')
app.use(static("path/to/static/assets"))
```
If you want to only match a sub-path, use the router.
```lua
app.route({
path = "/blog/:path:"
}, static(pathJoin(module.dir, "articles")))
```
The `path` param will be used if it exists and the full path will be used
otherwise.
## weblit/websocket
This implements a websocket upgrade handler. You can choose the subprotocol and
other routing information.
```lua
app.websocket({
path = "/v2/socket", -- Prefix for matching
protocol = "virgo/2.0", -- Restrict to a websocket sub-protocol
}, function (req, read, write)
-- Log the request headers
p(req)
-- Log and echo all messages
for message in read do
write(message)
end
-- End the stream
write()
end)
```
## weblit
This is the metapackage that simply includes the other modules.
It exposes the other modules as a single exports table.
```lua
exports.app = require('weblit/app')
exports.autoHeaders = require('weblit/auto-headers')
exports.etagCache = require('weblit/etag-cache')
exports.logger = require('weblit/logger')
exports.static = require('weblit/static')
exports.websocket = require('weblit/websocket')
```

261
plugins/weblit/app.lua Normal file
View File

@ -0,0 +1,261 @@
local createServer = require('coro-net').createServer
local wrapper = require('coro-wrapper')
local readWrap, writeWrap = wrapper.reader, wrapper.writer
local httpCodec = require('http-codec')
--local tlsWrap = require('coro-tls').wrap
local parseQuery = require('querystring').parse
-- Ignore SIGPIPE if it exists on platform
local uv = require('luv')
if uv.constants.SIGPIPE then
uv.new_signal():start("sigpipe")
end
local server = {}
local handlers = {}
local bindings = {}
-- Provide a nice case insensitive interface to headers.
local headerMeta = {
__index = function (list, name)
if type(name) ~= "string" then
return rawget(list, name)
end
name = name:lower()
for i = 1, #list do
local key, value = unpack(list[i])
if key:lower() == name then return value end
end
end,
__newindex = function (list, name, value)
-- non-string keys go through as-is.
if type(name) ~= "string" then
return rawset(list, name, value)
end
-- First remove any existing pairs with matching key
local lowerName = name:lower()
for i = #list, 1, -1 do
if list[i][1]:lower() == lowerName then
table.remove(list, i)
end
end
-- If value is nil, we're done
if value == nil then return end
-- Otherwise, set the key(s)
if (type(value) == "table") then
-- We accept a table of strings
for i = 1, #value do
rawset(list, #list + 1, {name, tostring(value[i])})
end
else
-- Or a single value interperted as string
rawset(list, #list + 1, {name, tostring(value)})
end
end,
}
local function handleRequest(head, input, socket)
local req = {
socket = socket,
method = head.method,
path = head.path,
headers = setmetatable({}, headerMeta),
version = head.version,
keepAlive = head.keepAlive,
body = input
}
for i = 1, #head do
req.headers[i] = head[i]
end
local res = {
code = 404,
headers = setmetatable({}, headerMeta),
body = "Not Found\n",
}
local function run(i)
local success, err = pcall(function ()
i = i or 1
local go = i < #handlers
and function ()
return run(i + 1)
end
or function () end
return handlers[i](req, res, go)
end)
if not success then
res.code = 500
res.headers = setmetatable({}, headerMeta)
res.body = err
print(err)
end
end
run(1)
local out = {
code = res.code,
keepAlive = res.keepAlive,
}
for i = 1, #res.headers do
out[i] = res.headers[i]
end
return out, res.body, res.upgrade
end
local function handleConnection(rawRead, rawWrite, socket)
-- Speak in HTTP events
local read, updateDecoder = readWrap(rawRead, httpCodec.decoder())
local write, updateEncoder = writeWrap(rawWrite, httpCodec.encoder())
for head in read do
local parts = {}
for chunk in read do
if #chunk > 0 then
parts[#parts + 1] = chunk
else
break
end
end
local res, body, upgrade = handleRequest(head, #parts > 0 and table.concat(parts) or nil, socket)
write(res)
if upgrade then
return upgrade(read, write, updateDecoder, updateEncoder, socket)
end
write(body)
if not (res.keepAlive and head.keepAlive) then
break
end
end
write()
end
function server.bind(options)
if not options.host then
options.host = "127.0.0.1"
end
if not options.port then
options.port = require('uv').getuid() == 0 and
(options.tls and 443 or 80) or
(options.tls and 8443 or 8080)
end
bindings[#bindings + 1] = options
return server
end
function server.use(handler)
handlers[#handlers + 1] = handler
return server
end
function server.start()
if #bindings == 0 then
server.bind({})
end
for i = 1, #bindings do
local options = bindings[i]
createServer(options, function (rawRead, rawWrite, socket)
--local tls = options.tls
--if tls then
--rawRead, rawWrite = tlsWrap(rawRead, rawWrite, {
-- server = true,
--key = assert(tls.key, "tls key required"),
--cert = assert(tls.cert, "tls cert required"),
--})
--end
return handleConnection(rawRead, rawWrite, socket)
end)
print("HTTP server listening at http" .. (options.tls and "s" or "") .. "://" .. options.host .. (options.port == (options.tls and 443 or 80) and "" or ":" .. options.port) .. "/")
end
return server
end
local quotepattern = '(['..("%^$().[]*+-?"):gsub("(.)", "%%%1")..'])'
local function escape(str)
return str:gsub(quotepattern, "%%%1")
end
local function compileGlob(glob)
local parts = {"^"}
for a, b in glob:gmatch("([^*]*)(%**)") do
if #a > 0 then
parts[#parts + 1] = escape(a)
end
if #b > 0 then
parts[#parts + 1] = "(.*)"
end
end
parts[#parts + 1] = "$"
local pattern = table.concat(parts)
return function (string)
return string and string:match(pattern)
end
end
local function compileRoute(route)
local parts = {"^"}
local names = {}
for a, b, c, d in route:gmatch("([^:]*):([_%a][_%w]*)(:?)([^:]*)") do
if #a > 0 then
parts[#parts + 1] = escape(a)
end
if #c > 0 then
parts[#parts + 1] = "(.*)"
else
parts[#parts + 1] = "([^/]*)"
end
names[#names + 1] = b
if #d > 0 then
parts[#parts + 1] = escape(d)
end
end
if #parts == 1 then
return function (string)
if string == route then return {} end
end
end
parts[#parts + 1] = "$"
local pattern = table.concat(parts)
return function (string)
local matches = {string:match(pattern)}
if #matches > 0 then
local results = {}
for i = 1, #matches do
results[i] = matches[i]
results[names[i]] = matches[i]
end
return results
end
end
end
function server.route(options, handler)
local method = options.method
local path = options.path and compileRoute(options.path)
local host = options.host and compileGlob(options.host)
local filter = options.filter
server.use(function (req, res, go)
if method and req.method ~= method then return go() end
if host and not host(req.headers.host) then return go() end
if filter and not filter(req) then return go() end
local params
if path then
local pathname, query = req.path:match("^([^?]*)%??(.*)")
params = path(pathname)
if not params then return go() end
if #query > 0 then
req.query = parseQuery(query)
end
end
req.params = params or {}
return handler(req, res, go)
end)
return server
end
return server

View File

@ -0,0 +1,92 @@
--[[
Response automatic values:
- Auto Server header
- Auto Date Header
- code defaults to 404 with body "Not Found\n"
- if there is a string body add Content-Length and ETag if missing
- if string body and no Content-Type, use text/plain for valid utf-8, application/octet-stream otherwise
- Auto add "; charset=utf-8" to Content-Type when body is known to be valid utf-8
- Auto 304 responses for if-none-match requests
- Auto strip body with HEAD requests
- Auto chunked encoding if body with unknown length
- if Connection header set and not keep-alive, set res.keepAlive to false
- Add Connection Keep-Alive/Close if not found based on res.keepAlive
--TODO: utf8 scanning
]]
--local digest = require('openssl').digest.digest
local date = require('os').date
return function (req, res, go)
local isHead = false
if req.method == "HEAD" then
req.method = "GET"
isHead = true
end
local requested = req.headers["if-none-match"]
go()
-- We could use the fancy metatable, but this is much faster
local lowerHeaders = {}
local headers = res.headers
for i = 1, #headers do
local key, value = unpack(headers[i])
lowerHeaders[key:lower()] = value
end
if not lowerHeaders.server then
headers[#headers + 1] = {"Server", serverName}
end
if not lowerHeaders.date then
headers[#headers + 1] = {"Date", date("!%a, %d %b %Y %H:%M:%S GMT")}
end
if not lowerHeaders.connection then
if req.keepAlive then
lowerHeaders.connection = "Keep-Alive"
headers[#headers + 1] = {"Connection", "Keep-Alive"}
else
headers[#headers + 1] = {"Connection", "Close"}
end
end
res.keepAlive = lowerHeaders.connection and lowerHeaders.connection:lower() == "keep-alive"
local body = res.body
if body then
local needLength = not lowerHeaders["content-length"] and not lowerHeaders["transfer-encoding"]
if type(body) == "string" then
if needLength then
headers[#headers + 1] = {"Content-Length", #body}
end
-- if not lowerHeaders.etag then
-- local etag = '"' .. digest("sha1", body) .. '"'
-- lowerHeaders.etag = etag
--headers[#headers + 1] = {"ETag", etag}
-- end
else
if needLength then
headers[#headers + 1] = {"Transfer-Encoding", "chunked"}
end
end
if not lowerHeaders["content-type"] then
headers[#headers + 1] = {"Content-Type", "text/plain"}
end
end
local etag = lowerHeaders.etag
if requested and res.code >= 200 and res.code < 300 and requested == etag then
res.code = 304
body = nil
end
if isHead then body = nil end
res.body = body
end

View File

@ -0,0 +1,39 @@
local function clone(headers)
local copy = setmetatable({}, getmetatable(headers))
for i = 1, #headers do
copy[i] = headers[i]
end
return copy
end
local cache = {}
return function (req, res, go)
local requested = req.headers["If-None-Match"]
local host = req.headers.Host
local key = host and host .. "|" .. req.path or req.path
local cached = cache[key]
if not requested and cached then
req.headers["If-None-Match"] = cached.etag
end
go()
local etag = res.headers.ETag
if not etag then return end
if res.code >= 200 and res.code < 300 then
local body = res.body
if not body or type(body) == "string" then
cache[key] = {
etag = etag,
code = res.code,
headers = clone(res.headers),
body = body
}
end
elseif res.code == 304 then
if not requested and cached and etag == cached.etag then
res.code = cached.code
res.headers = clone(cached.headers)
res.body = cached.body
end
end
end

8
plugins/weblit/init.lua Normal file
View File

@ -0,0 +1,8 @@
local exports = {}
exports.app = require('weblit/app')
exports.autoHeaders = require('weblit/auto-headers')
exports.etagCache = require('weblit/etag-cache')
exports.logger = require('weblit/logger')
exports.static = require('weblit/static')
exports.websocket = require('weblit/websocket')
return exports

10
plugins/weblit/logger.lua Normal file
View File

@ -0,0 +1,10 @@
return function (req, res, go)
-- Skip this layer for clients who don't send User-Agent headers.
local userAgent = req.headers["user-agent"]
if not userAgent then return go() end
-- Run all inner layers first.
go()
-- And then log after everything is done
--print(string.format("%s %s %s %s", req.method, req.path, userAgent, res.code))
end

62
plugins/weblit/static.lua Normal file
View File

@ -0,0 +1,62 @@
local getType = require("mime").getType
local jsonStringify = require('json').stringify
local makeChroot = require('coro-fs').chroot
return function (rootPath)
local fs = makeChroot(rootPath)
return function (req, res, go)
if req.method ~= "GET" then return go() end
local path = (req.params and req.params.path) or req.path
path = path:match("^[^?#]*")
if path:byte(1) == 47 then
path = path:sub(2)
end
local stat = fs.stat(path)
if not stat then return go() end
local function renderFile()
local body = assert(fs.readFile(path))
res.code = 200
res.headers["Content-Type"] = getType(path)
res.body = body
return
end
local function renderDirectory()
if req.path:byte(-1) ~= 47 then
res.code = 301
res.headers.Location = req.path .. '/'
return
end
local files = {}
for entry in fs.scandir(path) do
if entry.name == "index.html" and entry.type == "file" then
path = (#path > 0 and path .. "/" or "") .. "index.html"
return renderFile()
end
files[#files + 1] = entry
entry.url = "http://" .. req.headers.host .. req.path .. entry.name
end
local body = jsonStringify(files) .. "\n"
res.code = 200
res.headers["Content-Type"] = "application/json"
res.body = body
return
end
if stat.type == "directory" then
return renderDirectory()
elseif stat.type == "file" then
if req.path:byte(-1) == 47 then
res.code = 301
res.headers.Location = req.path:match("^(.*[^/])/+$")
return
end
return renderFile()
end
end
end

View File

@ -0,0 +1,82 @@
local websocketCodec = require('websocket-codec')
local function websocketHandler(options, handler)
return function (req, res, go)
-- Websocket connections must be GET requests
-- with 'Upgrade: websocket'
-- and 'Connection: Upgrade' headers
local headers = req.headers
local connection = headers.connection
local upgrade = headers.upgrade
if not (
req.method == "GET" and
upgrade and upgrade:lower():find("websocket", 1, true) and
connection and connection:lower():find("upgrade", 1, true)
) then
return go()
end
if options.filter and not options.filter(req) then
return go()
end
-- If there is a sub-protocol specified, filter on it.
local protocol = options.protocol
if protocol then
local list = headers["sec-websocket-protocol"]
local foundProtocol
if list then
for item in list:gmatch("[^, ]+") do
if item == protocol then
foundProtocol = true
break
end
end
end
if not foundProtocol then
return go()
end
end
-- Make sure it's a new client speaking v13 of the protocol
assert(tonumber(headers["sec-websocket-version"]) >= 13, "only websocket protocol v13 supported")
-- Get the security key
local key = assert(headers["sec-websocket-key"], "websocket security required")
res.code = 101
headers = res.headers
headers.Upgrade = "websocket"
headers.Connection = "Upgrade"
headers["Sec-WebSocket-Accept"] = websocketCodec.acceptKey(key)
if protocol then
headers["Sec-WebSocket-Protocol"] = protocol
end
function res.upgrade(read, write, updateDecoder, updateEncoder)
updateDecoder(websocketCodec.decode)
updateEncoder(websocketCodec.encode)
local success, err = pcall(handler, req, read, write)
if not success then
print(err)
write({
opcode = 1,
payload = err,
})
return write()
end
end
end
end
local server = require('weblit-app')
function server.websocket(options, handler)
server.route({
method = "GET",
path = options.path,
host = options.host,
}, websocketHandler(options, handler))
return server
end
return websocketHandler

View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Tim Caswell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,2 @@
# luv-websocket-codec
A luv port of lit's websocket codec

View File

@ -0,0 +1,261 @@
local exports = {}
exports.name = "creationix/websocket-codec"
exports.version = "1.0.7"
exports.homepage = "https://github.com/luvit/lit/blob/master/deps/websocket-codec.lua"
exports.description = "A codec implementing websocket framing and helpers for handshakeing"
exports.tags = {"http", "websocket", "codec"}
exports.license = "MIT"
exports.author = { name = "Tim Caswell" }
local digest = require('openssl').digest.digest
local base64 = require('openssl').base64
local random = require('openssl').random
local bit = require('bit')
local band = bit.band
local bor = bit.bor
local bxor = bit.bxor
local rshift = bit.rshift
local lshift = bit.lshift
local char = string.char
local byte = string.byte
local sub = string.sub
local gmatch = string.gmatch
local lower = string.lower
local gsub = string.gsub
local concat = table.concat
local function applyMask(data, mask)
local bytes = {
[0] = byte(mask, 1),
[1] = byte(mask, 2),
[2] = byte(mask, 3),
[3] = byte(mask, 4)
}
local out = {}
for i = 1, #data do
out[i] = char(
bxor(byte(data, i), bytes[(i - 1) % 4])
)
end
return concat(out)
end
function exports.decode(chunk)
if #chunk < 2 then return end
local second = byte(chunk, 2)
local len = band(second, 0x7f)
local offset
if len == 126 then
if #chunk < 4 then return end
len = bor(
lshift(byte(chunk, 3), 8),
byte(chunk, 4))
offset = 4
elseif len == 127 then
if #chunk < 10 then return end
len = bor(
lshift(byte(chunk, 3), 56),
lshift(byte(chunk, 4), 48),
lshift(byte(chunk, 5), 40),
lshift(byte(chunk, 6), 32),
lshift(byte(chunk, 7), 24),
lshift(byte(chunk, 8), 16),
lshift(byte(chunk, 9), 8),
byte(chunk, 10))
offset = 10
else
offset = 2
end
local mask = band(second, 0x80) > 0
if mask then
offset = offset + 4
end
if #chunk < offset + len then return end
local first = byte(chunk, 1)
local payload = sub(chunk, offset + 1, offset + len)
assert(#payload == len, "Length mismatch")
if mask then
payload = applyMask(payload, sub(chunk, offset - 3, offset))
end
local extra = sub(chunk, offset + len + 1)
return {
fin = band(first, 0x80) > 0,
rsv1 = band(first, 0x40) > 0,
rsv2 = band(first, 0x20) > 0,
rsv3 = band(first, 0x10) > 0,
opcode = band(first, 0xf),
mask = mask,
len = len,
payload = payload
}, extra
end
function exports.encode(item)
if type(item) == "string" then
item = {
opcode = 2,
payload = item
}
end
local payload = item.payload
assert(type(payload) == "string", "payload must be string")
local len = #payload
local fin = item.fin
if fin == nil then fin = true end
local rsv1 = item.rsv1
local rsv2 = item.rsv2
local rsv3 = item.rsv3
local opcode = item.opcode or 2
local mask = item.mask
local chars = {
char(bor(
fin and 0x80 or 0,
rsv1 and 0x40 or 0,
rsv2 and 0x20 or 0,
rsv3 and 0x10 or 0,
opcode
)),
char(bor(
mask and 0x80 or 0,
len < 126 and len or (len < 0x10000) and 126 or 127
))
}
if len >= 0x10000 then
chars[3] = char(band(rshift(len, 56), 0xff))
chars[4] = char(band(rshift(len, 48), 0xff))
chars[5] = char(band(rshift(len, 40), 0xff))
chars[6] = char(band(rshift(len, 32), 0xff))
chars[7] = char(band(rshift(len, 24), 0xff))
chars[8] = char(band(rshift(len, 16), 0xff))
chars[9] = char(band(rshift(len, 8), 0xff))
chars[10] = char(band(len, 0xff))
elseif len >= 126 then
chars[3] = char(band(rshift(len, 8), 0xff))
chars[4] = char(band(len, 0xff))
end
if mask then
local key = random(4)
return concat(chars) .. key .. applyMask(payload, key)
end
return concat(chars) .. payload
end
local websocketGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
function exports.acceptKey(key)
return gsub(base64(digest("sha1", key .. websocketGuid, true)), "\n", "")
end
local acceptKey = exports.acceptKey
-- Make a client handshake connection
function exports.handshake(options, request)
local key = gsub(base64(random(20)), "\n", "")
local host = options.host
local path = options.path or "/"
local protocol = options.protocol
local req = {
method = "GET",
path = path,
{"Connection", "Upgrade"},
{"Upgrade", "websocket"},
{"Sec-WebSocket-Version", "13"},
{"Sec-WebSocket-Key", key},
}
for i = 1, #options do
req[#req + 1] = options[i]
end
if host then
req[#req + 1] = {"Host", host}
end
if protocol then
req[#req + 1] = {"Sec-WebSocket-Protocol", protocol}
end
local res = request(req)
if not res then
return nil, "Missing response from server"
end
-- Parse the headers for quick reading
if res.code ~= 101 then
return nil, "response must be code 101"
end
local headers = {}
for i = 1, #res do
local name, value = unpack(res[i])
headers[lower(name)] = value
end
if not headers.connection or lower(headers.connection) ~= "upgrade" then
return nil, "Invalid or missing connection upgrade header in response"
end
if headers["sec-websocket-accept"] ~= acceptKey(key) then
return nil, "challenge key missing or mismatched"
end
if protocol and headers["sec-websocket-protocol"] ~= protocol then
return nil, "protocol missing or mistmatched"
end
return true
end
function exports.handleHandshake(head, protocol)
-- WebSocket connections must be GET requests
if not head.method == "GET" then return end
-- Parse the headers for quick reading
local headers = {}
for i = 1, #head do
local name, value = unpack(head[i])
headers[lower(name)] = value
end
-- Must have 'Upgrade: websocket' and 'Connection: Upgrade' headers
if not (headers.connection and headers.upgrade and
headers.connection:lower():find("upgrade", 1, true) and
headers.upgrade:lower():find("websocket", 1, true)) then return end
-- Make sure it's a new client speaking v13 of the protocol
if tonumber(headers["sec-websocket-version"]) < 13 then
return nil, "only websocket protocol v13 supported"
end
local key = headers["sec-websocket-key"]
if not key then
return nil, "websocket security key missing"
end
-- If the server wants a specified protocol, check for it.
if protocol then
local foundProtocol = false
local list = headers["sec-websocket-protocol"]
if list then
for item in gmatch(list, "[^, ]+") do
if item == protocol then
foundProtocol = true
break
end
end
end
if not foundProtocol then
return nil, "specified protocol missing in request"
end
end
local accept = acceptKey(key)
local res = {
code = 101,
{"Upgrade", "websocket"},
{"Connection", "Upgrade"},
{"Sec-WebSocket-Accept", accept},
}
if protocol then
res[#res + 1] = {"Sec-WebSocket-Protocol", protocol}
end
return res
end
return exports

View File

@ -19,6 +19,7 @@
#include "uiinput.h"
#include "xmlfile.h"
#include "coreutil.h"
#include "luaengine.h"
#include <ctype.h>
@ -1928,6 +1929,8 @@ void device_debug::instruction_hook(offs_t curpc)
// flush any pending updates before waiting again
machine.debug_view().flush_osd_updates();
machine.manager().lua()->periodic_check();
// clear the memory modified flag and wait
global->memory_modified = false;
if (machine.debug_flags & DEBUG_FLAG_OSD_ENABLED)

View File

@ -43,6 +43,7 @@ const options_entry emu_options::s_option_entries[] =
{ OPTION_FONTPATH, ".", OPTION_STRING, "path to font files" },
{ OPTION_CHEATPATH, "cheat", OPTION_STRING, "path to cheat files" },
{ OPTION_CROSSHAIRPATH, "crosshair", OPTION_STRING, "path to crosshair files" },
{ OPTION_PLUGINSPATH, "plugins", OPTION_STRING, "path to plugin files" },
// output directory options
{ nullptr, nullptr, OPTION_HEADER, "CORE OUTPUT DIRECTORY OPTIONS" },

View File

@ -56,6 +56,7 @@ enum
#define OPTION_FONTPATH "fontpath"
#define OPTION_CHEATPATH "cheatpath"
#define OPTION_CROSSHAIRPATH "crosshairpath"
#define OPTION_PLUGINSPATH "pluginspath"
// core directory options
#define OPTION_CFG_DIRECTORY "cfg_directory"
@ -231,6 +232,7 @@ public:
const char *font_path() const { return value(OPTION_FONTPATH); }
const char *cheat_path() const { return value(OPTION_CHEATPATH); }
const char *crosshair_path() const { return value(OPTION_CROSSHAIRPATH); }
const char *plugins_path() const { return value(OPTION_PLUGINSPATH); }
// core directory options
const char *cfg_directory() const { return value(OPTION_CFG_DIRECTORY); }

View File

@ -387,8 +387,10 @@ int running_machine::run(bool firstrun)
// execute CPUs if not paused
if (!m_paused)
{
m_scheduler.timeslice();
manager().lua()->periodic_check();
}
// otherwise, just pump video updates through
else
m_video->frame_update();

View File

@ -167,6 +167,15 @@ int machine_manager::execute()
int error = MAMERR_NONE;
m_lua->initialize();
{
emu_file file(options().plugins_path(), OPEN_FLAG_READ);
file_error filerr = file.open("boot.lua");
if (filerr == FILERR_NONE)
{
m_lua->load_script(file.fullpath());
}
}
if (m_options.console()) {
m_lua->start_console();
}