diff options
author | 2016-02-21 11:48:45 +0100 | |
---|---|---|
committer | 2016-02-21 11:48:45 +0100 | |
commit | cc24a339d8c0517259084b5c178d784626ba965c (patch) | |
tree | 9868e9687b5802ae0a3733712a3bbeb3bc75c953 /plugins/coro-http | |
parent | b5daabda5495dea5c50e17961ecfed2ea8619d76 (diff) |
Merge remote-tracking branch 'refs/remotes/mamedev/master'
Second attempt
Diffstat (limited to 'plugins/coro-http')
-rw-r--r-- | plugins/coro-http/LICENSE | 22 | ||||
-rw-r--r-- | plugins/coro-http/README.md | 2 | ||||
-rw-r--r-- | plugins/coro-http/init.lua | 187 | ||||
-rw-r--r-- | plugins/coro-http/plugin.json | 8 |
4 files changed, 219 insertions, 0 deletions
diff --git a/plugins/coro-http/LICENSE b/plugins/coro-http/LICENSE new file mode 100644 index 00000000000..5789b767285 --- /dev/null +++ b/plugins/coro-http/LICENSE @@ -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. + diff --git a/plugins/coro-http/README.md b/plugins/coro-http/README.md new file mode 100644 index 00000000000..271b6698f5d --- /dev/null +++ b/plugins/coro-http/README.md @@ -0,0 +1,2 @@ +# luv-coro-http +A luv port of lit's coro-http library. diff --git a/plugins/coro-http/init.lua b/plugins/coro-http/init.lua new file mode 100644 index 00000000000..49613403eaf --- /dev/null +++ b/plugins/coro-http/init.lua @@ -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 diff --git a/plugins/coro-http/plugin.json b/plugins/coro-http/plugin.json new file mode 100644 index 00000000000..0c8047c9ebe --- /dev/null +++ b/plugins/coro-http/plugin.json @@ -0,0 +1,8 @@ +{ + "plugin": { + "name": "coro-http", + "version": "1.2.1-1", + "author": "Tim Caswell", + "type": "library", + } +}
\ No newline at end of file |