basic http requests starting to work

In the process we're starting to load almost all of luasocket by
default. And everything is working as expected, no unpleasant surprises.
This commit is contained in:
Kartik K. Agaram 2021-11-21 15:07:17 -08:00
parent f7ab5dd291
commit 3b44b9827d
14 changed files with 92 additions and 109 deletions

View File

@ -1,7 +1,23 @@
teliva_program = {
main = [==[
function main()
socket.http.get("http://example.com")
local body = http.request("https://example.com")
curses.mvaddstr(5, 5, body)
curses.refresh()
curses.getch()
end]==],
dump = [==[
-- https://stackoverflow.com/questions/9168058/how-to-dump-a-table-to-console
function dump(o)
if type(o) == 'table' then
local s = '{ '
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. dump(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end]==],
}

View File

@ -53,13 +53,13 @@ $(LUA_A): $(CORE_O) $(LIB_O)
$(AR) $@ $(CORE_O) $(LIB_O)
$(RANLIB) $@
$(LUA_T): $(LUA_O) $(LUA_A) luasocket/socket.a
$(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) luasocket/socket.a $(LIBS)
$(LUA_T): $(LUA_O) $(LUA_A) luasocket/socket.a luasocket/mime.a
$(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) luasocket/socket.a luasocket/mime.a $(LIBS)
$(LUAC_T): $(LUAC_O) $(LUA_A)
$(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS)
luasocket/socket.a:
luasocket/socket.a luasocket/mime.a:
make -C luasocket linux
clean:
@ -122,7 +122,7 @@ solaris:
$(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl"
# list targets that do not create files (but not all makes understand .PHONY)
.PHONY: all $(PLATS) default o a clean depend echo none luasocket/socket.a
.PHONY: all $(PLATS) default o a clean depend echo none luasocket/socket.a luasocket/mime.a
# DO NOT DELETE

View File

@ -24,6 +24,7 @@ static const luaL_Reg lualibs[] = {
{LUA_MATHLIBNAME, luaopen_math},
{LUA_CURSESLIBNAME, luaopen_curses},
{LUA_SOCKETCORELIBNAME, luaopen_socket_core},
{LUA_MIMECORELIBNAME, luaopen_mime_core},
{LUA_DBLIBNAME, luaopen_debug},
{NULL, NULL}
};

View File

@ -676,6 +676,16 @@ static int pmain (lua_State *L) {
if (status != 0) return 0;
status = dorequire(L, "src/luasocket/socket.lua", "socket");
if (status != 0) return 0;
status = dorequire(L, "src/luasocket/url.lua", "url");
if (status != 0) return 0;
status = dorequire(L, "src/luasocket/ltn12.lua", "ltn12");
if (status != 0) return 0;
status = dorequire(L, "src/luasocket/mime.lua", "mime");
if (status != 0) return 0;
status = dorequire(L, "src/luasocket/headers.lua", "headers");
if (status != 0) return 0;
status = dorequire(L, "src/luasocket/http.lua", "http");
if (status != 0) return 0;
status = dorequire(L, "src/json.lua", "json");
if (status != 0) return 0;
lua_gc(L, LUA_GCRESTART, 0);

View File

@ -39,6 +39,9 @@ LUALIB_API int (luaopen_curses) (lua_State *L);
#define LUA_SOCKETCORELIBNAME "socket"
LUALIB_API int (luaopen_socket_core) (lua_State *L);
#define LUA_MIMECORELIBNAME "mime"
LUALIB_API int (luaopen_mime_core) (lua_State *L);
#define LUA_DBLIBNAME "debug"
LUALIB_API int (luaopen_debug) (lua_State *L);

View File

@ -7,14 +7,7 @@
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-----------------------------------------------------------------------------
local base = _G
local table = require("table")
local string = require("string")
local math = require("math")
local socket = require("socket")
local url = require("socket.url")
local tp = require("socket.tp")
local ltn12 = require("ltn12")
socket.ftp = {}
local _M = socket.ftp
-----------------------------------------------------------------------------
@ -36,7 +29,7 @@ local metat = { __index = {} }
function _M.open(server, port, create)
local tp = socket.try(tp.connect(server, port or PORT, _M.TIMEOUT, create))
local f = base.setmetatable({ tp = tp }, metat)
local f = setmetatable({ tp = tp }, metat)
-- make sure everything gets closed in an exception
f.try = socket.newtry(function() f:close() end)
return f
@ -226,7 +219,7 @@ end
local function override(t)
if t.url then
local u = url.parse(t.url)
for i,v in base.pairs(t) do
for i,v in pairs(t) do
u[i] = v
end
return u
@ -274,7 +267,7 @@ local function sput(u, body)
end
_M.put = socket.protect(function(putt, body)
if base.type(putt) == "string" then return sput(putt, body)
if type(putt) == "string" then return sput(putt, body)
else return tput(putt) end
end)
@ -322,7 +315,7 @@ _M.command = socket.protect(function(cmdt)
end)
_M.get = socket.protect(function(gett)
if base.type(gett) == "string" then return sget(gett)
if type(gett) == "string" then return sget(gett)
else return tget(gett) end
end)

View File

@ -3,7 +3,6 @@
-- LuaSocket toolkit.
-- Author: Diego Nehab
-----------------------------------------------------------------------------
local socket = require("socket")
socket.headers = {}
local _M = socket.headers
@ -101,4 +100,4 @@ _M.canonic = {
["x-mailer"] = "X-Mailer",
}
return _M
return _M

View File

@ -7,14 +7,6 @@
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-------------------------------------------------------------------------------
local socket = require("socket")
local url = require("socket.url")
local ltn12 = require("ltn12")
local mime = require("mime")
local string = require("string")
local headers = require("socket.headers")
local base = _G
local table = require("table")
socket.http = {}
local _M = socket.http
@ -79,7 +71,7 @@ end
-- Extra sources and sinks
-----------------------------------------------------------------------------
socket.sourcet["http-chunked"] = function(sock, headers)
return base.setmetatable({
return setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
@ -87,7 +79,7 @@ socket.sourcet["http-chunked"] = function(sock, headers)
-- get chunk size, skip extention
local line, err = sock:receive()
if err then return nil, err end
local size = base.tonumber(string.gsub(line, ";.*", ""), 16)
local size = tonumber(string.gsub(line, ";.*", ""), 16)
if not size then return nil, "invalid chunk size" end
-- was it the last chunk?
if size > 0 then
@ -105,7 +97,7 @@ socket.sourcet["http-chunked"] = function(sock, headers)
end
socket.sinkt["http-chunked"] = function(sock)
return base.setmetatable({
return setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
@ -125,7 +117,7 @@ local metat = { __index = {} }
function _M.open(host, port, create)
-- create socket with user connect function, or with default
local c = socket.try(create())
local h = base.setmetatable({ c = c }, metat)
local h = setmetatable({ c = c }, metat)
-- create finalized try
h.try = socket.newtry(function() h:close() end)
-- set timeout before connecting
@ -143,7 +135,7 @@ end
function metat.__index:sendheaders(tosend)
local canonic = headers.canonic
local h = "\r\n"
for f, v in base.pairs(tosend) do
for f, v in pairs(tosend) do
h = (canonic[f] or f) .. ": " .. v .. "\r\n" .. h
end
self.try(self.c:send(h))
@ -172,7 +164,7 @@ function metat.__index:receivestatusline()
-- otherwise proceed reading a status line
status = self.try(self.c:receive("*l", status))
local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
return self.try(base.tonumber(code), status)
return self.try(tonumber(code), status)
end
function metat.__index:receiveheaders()
@ -182,11 +174,11 @@ end
function metat.__index:receivebody(headers, sink, step)
sink = sink or ltn12.sink.null()
step = step or ltn12.pump.step
local length = base.tonumber(headers["content-length"])
local length = tonumber(headers["content-length"])
local t = headers["transfer-encoding"] -- shortcut
local mode = "default" -- connection close
if t and t ~= "identity" then mode = "http-chunked"
elseif base.tonumber(headers["content-length"]) then mode = "by-length" end
elseif tonumber(headers["content-length"]) then mode = "by-length" end
return self.try(ltn12.pump.all(socket.source(mode, self.c, length),
sink, step))
end
@ -256,7 +248,7 @@ local function adjustheaders(reqt)
end
end
-- override with user headers
for i,v in base.pairs(reqt.headers or lower) do
for i,v in pairs(reqt.headers or lower) do
lower[string.lower(i)] = v
end
return lower
@ -272,7 +264,7 @@ local function adjustrequest(reqt)
-- parse url if provided
local nreqt = reqt.url and url.parse(reqt.url, default) or {}
-- explicit components override url
for i,v in base.pairs(reqt) do nreqt[i] = v end
for i,v in pairs(reqt) do nreqt[i] = v end
-- default to scheme particulars
local schemedefs, host, port, method
= SCHEMES[nreqt.scheme], nreqt.host, nreqt.port, nreqt.method
@ -280,7 +272,7 @@ local function adjustrequest(reqt)
if not (port and port ~= '') then nreqt.port = schemedefs.port end
if not (method and method ~= '') then nreqt.method = 'GET' end
if not (host and host ~= "") then
socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'")
socket.try(nil, "invalid host '" .. tostring(nreqt.host) .. "'")
end
-- compute uri if user hasn't overriden
nreqt.uri = reqt.uri or adjusturi(nreqt)
@ -412,7 +404,7 @@ local function srequest(u, b)
end
_M.request = socket.protect(function(reqt, body)
if base.type(reqt) == "string" then return srequest(reqt, body)
if type(reqt) == "string" then return srequest(reqt, body)
else return trequest(reqt) end
end)

View File

@ -7,14 +7,7 @@
-----------------------------------------------------------------------------
-- Declare module
-----------------------------------------------------------------------------
local string = require("string")
local table = require("table")
local unpack = unpack or table.unpack
local base = _G
local _M = {}
if module then -- heuristic for exporting a global package table
ltn12 = _M
end
local filter,source,sink,pump = {},{},{},{}
_M.filter = filter
@ -22,9 +15,6 @@ _M.source = source
_M.sink = sink
_M.pump = pump
local unpack = unpack or table.unpack
local select = base.select
-- 2048 seems to be better in windows...
_M.BLOCKSIZE = 2048
_M._VERSION = "LTN12 1.0.3"
@ -34,7 +24,7 @@ _M._VERSION = "LTN12 1.0.3"
-----------------------------------------------------------------------------
-- returns a high level filter that cycles a low-level filter
function filter.cycle(low, ctx, extra)
base.assert(low)
assert(low)
return function(chunk)
local ret
ret, ctx = low(ctx, chunk, extra)
@ -46,7 +36,7 @@ end
-- (thanks to Wim Couwenberg)
function filter.chain(...)
local arg = {...}
local n = base.select('#',...)
local n = select('#',...)
local top, index = 1, 1
local retry = ""
return function(chunk)
@ -68,7 +58,7 @@ function filter.chain(...)
elseif chunk then
if index == n then return chunk
else index = index + 1 end
else base.error("filter returned inappropriate nil") end
else error("filter returned inappropriate nil") end
end
end
end
@ -106,7 +96,7 @@ end
-- turns a fancy source into a simple source
function source.simplify(src)
base.assert(src)
assert(src)
return function()
local chunk, err_or_new = src()
src = err_or_new or src
@ -130,7 +120,7 @@ end
-- creates table source
function source.table(t)
base.assert('table' == type(t))
assert('table' == type(t))
local i = 0
return function()
i = i + 1
@ -140,7 +130,7 @@ end
-- creates rewindable source
function source.rewind(src)
base.assert(src)
assert(src)
local t = {}
return function(chunk)
if not chunk then
@ -156,13 +146,13 @@ end
-- chains a source with one or several filter(s)
function source.chain(src, f, ...)
if ... then f=filter.chain(f, ...) end
base.assert(src and f)
assert(src and f)
local last_in, last_out = "", ""
local state = "feeding"
local err
return function()
if not last_out then
base.error('source is empty!', 2)
error('source is empty!', 2)
end
while true do
if state == "feeding" then
@ -171,7 +161,7 @@ function source.chain(src, f, ...)
last_out = f(last_in)
if not last_out then
if last_in then
base.error('filter returned inappropriate nil')
error('filter returned inappropriate nil')
else
return nil
end
@ -186,11 +176,11 @@ function source.chain(src, f, ...)
if last_in == "" then
state = "feeding"
else
base.error('filter returned ""')
error('filter returned ""')
end
elseif not last_out then
if last_in then
base.error('filter returned inappropriate nil')
error('filter returned inappropriate nil')
else
return nil
end
@ -233,7 +223,7 @@ end
-- turns a fancy sink into a simple sink
function sink.simplify(snk)
base.assert(snk)
assert(snk)
return function(chunk, err)
local ret, err_or_new = snk(chunk, err)
if not ret then return nil, err_or_new end
@ -277,7 +267,7 @@ function sink.chain(f, snk, ...)
snk = table.remove(args, #args)
f = filter.chain(unpack(args))
end
base.assert(f and snk)
assert(f and snk)
return function(chunk, err)
if chunk ~= "" then
local filtered = f(chunk)
@ -305,7 +295,7 @@ end
-- pumps all data from a source to a sink, using a step function
function pump.all(src, snk, step)
base.assert(src and snk)
assert(src and snk)
step = step or pump.step
while true do
local ret, err = step(src, snk)

View File

@ -171,6 +171,8 @@ LUASOCKET_API int luaopen_mime_core(lua_State *L)
{
lua_newtable(L);
luaL_setfuncs(L, func, 0);
lua_pushvalue(L, -1);
lua_setglobal(L, "mime");
/* make version string available to scripts */
lua_pushstring(L, "_VERSION");
lua_pushstring(L, MIME_VERSION);

View File

@ -7,10 +7,6 @@
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-----------------------------------------------------------------------------
local base = _G
local ltn12 = require("ltn12")
local mime = require("mime.core")
local string = require("string")
local _M = mime
-- encode, decode and wrap algorithm tables
@ -18,17 +14,17 @@ local encodet, decodet, wrapt = {},{},{}
_M.encodet = encodet
_M.decodet = decodet
_M.wrapt = wrapt
_M.wrapt = wrapt
-- creates a function that chooses a filter by name from a given table
local function choose(table)
return function(name, opt1, opt2)
if base.type(name) ~= "string" then
if type(name) ~= "string" then
name, opt1, opt2 = "default", name, opt1
end
local f = table[name or "nil"]
if not f then
base.error("unknown key (" .. base.tostring(name) .. ")", 3)
error("unknown key (" .. tostring(name) .. ")", 3)
else return f(opt1, opt2) end
end
end

View File

@ -7,16 +7,7 @@
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-----------------------------------------------------------------------------
local base = _G
local coroutine = require("coroutine")
local string = require("string")
local math = require("math")
local os = require("os")
local socket = require("socket")
local tp = require("socket.tp")
local ltn12 = require("ltn12")
local headers = require("socket.headers")
local mime = require("mime")
socket.smtp = {}
local _M = socket.smtp
@ -103,8 +94,8 @@ end
-- send message or throw an exception
function metat.__index:send(mailt)
self:mail(mailt.from)
if base.type(mailt.rcpt) == "table" then
for i,v in base.ipairs(mailt.rcpt) do
if type(mailt.rcpt) == "table" then
for i,v in ipairs(mailt.rcpt) do
self:rcpt(v)
end
else
@ -116,7 +107,7 @@ end
function _M.open(server, port, create)
local tp = socket.try(tp.connect(server or _M.SERVER, port or _M.PORT,
_M.TIMEOUT, create))
local s = base.setmetatable({tp = tp}, metat)
local s = setmetatable({tp = tp}, metat)
-- make sure tp is closed if we get an exception
s.try = socket.newtry(function()
s:close()
@ -127,7 +118,7 @@ end
-- convert headers to lowercase
local function lower_headers(headers)
local lower = {}
for i,v in base.pairs(headers or lower) do
for i,v in pairs(headers or lower) do
lower[string.lower(i)] = v
end
return lower
@ -151,7 +142,7 @@ local send_message
local function send_headers(tosend)
local canonic = headers.canonic
local h = "\r\n"
for f,v in base.pairs(tosend) do
for f,v in pairs(tosend) do
h = (canonic[f] or f) .. ': ' .. v .. "\r\n" .. h
end
coroutine.yield(h)
@ -172,7 +163,7 @@ local function send_multipart(mesgt)
coroutine.yield("\r\n")
end
-- send each part separated by a boundary
for i, m in base.ipairs(mesgt.body) do
for i, m in ipairs(mesgt.body) do
coroutine.yield("\r\n--" .. bd .. "\r\n")
send_message(m)
end
@ -214,8 +205,8 @@ end
-- message source
function send_message(mesgt)
if base.type(mesgt.body) == "table" then send_multipart(mesgt)
elseif base.type(mesgt.body) == "function" then send_source(mesgt)
if type(mesgt.body) == "table" then send_multipart(mesgt)
elseif type(mesgt.body) == "function" then send_source(mesgt)
else send_string(mesgt) end
end
@ -253,4 +244,4 @@ _M.send = socket.protect(function(mailt)
return s:close()
end)
return _M
return _M

View File

@ -7,11 +7,6 @@
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-----------------------------------------------------------------------------
local base = _G
local string = require("string")
local socket = require("socket")
local ltn12 = require("ltn12")
socket.tp = {}
local _M = socket.tp
@ -57,19 +52,19 @@ end
function metat.__index:check(ok)
local code, reply = get_reply(self.c)
if not code then return nil, reply end
if base.type(ok) ~= "function" then
if base.type(ok) == "table" then
for i, v in base.ipairs(ok) do
if type(ok) ~= "function" then
if type(ok) == "table" then
for i, v in ipairs(ok) do
if string.find(code, v) then
return base.tonumber(code), reply
return tonumber(code), reply
end
end
return nil, reply
else
if string.find(code, ok) then return base.tonumber(code), reply
if string.find(code, ok) then return tonumber(code), reply
else return nil, reply end
end
else return ok(base.tonumber(code), reply) end
else return ok(tonumber(code), reply) end
end
function metat.__index:command(cmd, arg)
@ -128,7 +123,7 @@ function _M.connect(host, port, timeout, create)
c:close()
return nil, e
end
return base.setmetatable({c = c}, metat)
return setmetatable({c = c}, metat)
end
return _M

View File

@ -7,11 +7,6 @@
-----------------------------------------------------------------------------
-- Declare module
-----------------------------------------------------------------------------
local string = require("string")
local base = _G
local table = require("table")
local socket = require("socket")
socket.url = {}
local _M = socket.url
@ -43,7 +38,7 @@ end
-----------------------------------------------------------------------------
local function make_set(t)
local s = {}
for i,v in base.ipairs(t) do
for i,v in ipairs(t) do
s[t[i]] = 1
end
return s
@ -72,7 +67,7 @@ end
-----------------------------------------------------------------------------
function _M.unescape(s)
return (string.gsub(s, "%%(%x%x)", function(hex)
return string.char(base.tonumber(hex, 16))
return string.char(tonumber(hex, 16))
end))
end
@ -143,7 +138,7 @@ end
function _M.parse(url, default)
-- initialize default parameters
local parsed = {}
for i,v in base.pairs(default or parsed) do parsed[i] = v end
for i,v in pairs(default or parsed) do parsed[i] = v end
-- empty url is parsed to nil
if not url or url == "" then return nil, "invalid url" end
-- remove whitespace
@ -211,7 +206,7 @@ function _M.build(parsed)
if string.find(authority, ":") then -- IPv6?
authority = "[" .. authority .. "]"
end
if parsed.port then authority = authority .. ":" .. base.tostring(parsed.port) end
if parsed.port then authority = authority .. ":" .. tostring(parsed.port) end
local userinfo = parsed.userinfo
if parsed.user then
userinfo = parsed.user
@ -238,7 +233,7 @@ end
-----------------------------------------------------------------------------
function _M.absolute(base_url, relative_url)
local base_parsed
if base.type(base_url) == "table" then
if type(base_url) == "table" then
base_parsed = base_url
base_url = _M.build(base_parsed)
else