mirror of
https://github.com/holub/mame
synced 2025-10-04 16:34:53 +03:00
Added plugins and boot.lua as startup script [Miodrag Milanovic]
This commit is contained in:
parent
2db4908814
commit
ccae0382bb
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@
|
||||
!/hlsl/
|
||||
!/keymaps/
|
||||
!/nl_examples/
|
||||
!/plugins/
|
||||
!/regtests/
|
||||
!/samples/
|
||||
!/scripts/
|
||||
|
26
plugins/boot.lua
Normal file
26
plugins/boot.lua
Normal 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()
|
||||
|
22
plugins/coro-channel/LICENSE
Normal file
22
plugins/coro-channel/LICENSE
Normal 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/coro-channel/README.md
Normal file
2
plugins/coro-channel/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# luv-coro-channel
|
||||
A luv port of creationix/coro-channel from lit.luvit.io
|
128
plugins/coro-channel/init.lua
Normal file
128
plugins/coro-channel/init.lua
Normal 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
22
plugins/coro-fs/LICENSE
Normal 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/coro-fs/README.md
Normal file
2
plugins/coro-fs/README.md
Normal 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
222
plugins/coro-fs/init.lua
Normal 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
22
plugins/coro-http/LICENSE
Normal 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/coro-http/README.md
Normal file
2
plugins/coro-http/README.md
Normal 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
187
plugins/coro-http/init.lua
Normal 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
22
plugins/coro-net/LICENSE
Normal 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/coro-net/README.md
Normal file
2
plugins/coro-net/README.md
Normal 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
113
plugins/coro-net/init.lua
Normal 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
22
plugins/coro-tls/LICENSE
Normal 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/coro-tls/README.md
Normal file
2
plugins/coro-tls/README.md
Normal 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
122
plugins/coro-tls/init.lua
Normal 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
|
22
plugins/coro-wrapper/LICENSE
Normal file
22
plugins/coro-wrapper/LICENSE
Normal 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/coro-wrapper/README.md
Normal file
2
plugins/coro-wrapper/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# luv-coro-wrapper
|
||||
A luv port of lit's coro-wrapper module
|
41
plugins/coro-wrapper/init.lua
Normal file
41
plugins/coro-wrapper/init.lua
Normal 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
202
plugins/http-codec/LICENSE
Normal 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/http-codec/README.md
Normal file
2
plugins/http-codec/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# luv-http-codec
|
||||
A luv port of luvit's http-codec
|
291
plugins/http-codec/init.lua
Normal file
291
plugins/http-codec/init.lua
Normal 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
22
plugins/json/LICENSE
Normal 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
2
plugins/json/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# luv-json
|
||||
A luv port of luvit's json module
|
732
plugins/json/init.lua
Normal file
732
plugins/json/init.lua
Normal 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
22
plugins/mime/LICENSE
Normal 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
2
plugins/mime/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# luv-mime
|
||||
A luv port of weblit's mime module
|
194
plugins/mime/init.lua
Normal file
194
plugins/mime/init.lua
Normal 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
202
plugins/path/LICENSE
Normal 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
2
plugins/path/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# luv-path
|
||||
A luv port of luvi's path module
|
139
plugins/path/init.lua
Normal file
139
plugins/path/init.lua
Normal 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,
|
||||
}
|
202
plugins/pretty-print/LICENSE
Normal file
202
plugins/pretty-print/LICENSE
Normal 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/pretty-print/README.md
Normal file
2
plugins/pretty-print/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# luv-pretty-print
|
||||
luvit's pretty-print module re-packaged as a luv submodule.
|
362
plugins/pretty-print/init.lua
Normal file
362
plugins/pretty-print/init.lua
Normal 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
202
plugins/querystring/LICENSE
Normal 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/querystring/README.md
Normal file
2
plugins/querystring/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# luv-querystring
|
||||
A luv port of luvit's querystring
|
105
plugins/querystring/init.lua
Normal file
105
plugins/querystring/init.lua
Normal 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
22
plugins/weblit/LICENSE
Normal 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
239
plugins/weblit/README.md
Normal 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
261
plugins/weblit/app.lua
Normal 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
|
92
plugins/weblit/auto-headers.lua
Normal file
92
plugins/weblit/auto-headers.lua
Normal 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
|
39
plugins/weblit/etag-cache.lua
Normal file
39
plugins/weblit/etag-cache.lua
Normal 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
8
plugins/weblit/init.lua
Normal 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
10
plugins/weblit/logger.lua
Normal 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
62
plugins/weblit/static.lua
Normal 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
|
82
plugins/weblit/websocket.lua
Normal file
82
plugins/weblit/websocket.lua
Normal 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
|
22
plugins/websocket-codec/LICENSE
Normal file
22
plugins/websocket-codec/LICENSE
Normal 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/websocket-codec/README.md
Normal file
2
plugins/websocket-codec/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# luv-websocket-codec
|
||||
A luv port of lit's websocket codec
|
261
plugins/websocket-codec/init.lua
Normal file
261
plugins/websocket-codec/init.lua
Normal 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
|
@ -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)
|
||||
|
@ -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" },
|
||||
|
@ -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); }
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user