Basic HTTP support
This commit is contained in:
parent
2851b99eb5
commit
a4747a5ac2
@ -9,6 +9,7 @@ extern char lua_init[];
|
|||||||
extern char lua_internet[];
|
extern char lua_internet[];
|
||||||
extern char lua_sandbox[];
|
extern char lua_sandbox[];
|
||||||
extern char lua_textgpu[];
|
extern char lua_textgpu[];
|
||||||
|
extern char lua_util_buffer[];
|
||||||
extern char lua_util_color[];
|
extern char lua_util_color[];
|
||||||
extern char lua_util_random[];
|
extern char lua_util_random[];
|
||||||
#endif
|
#endif
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
static int l_open(lua_State *L) { //TODO: Any mem leaks?
|
static int l_open(lua_State *L) { //TODO: Any mem leaks?
|
||||||
const char* hostaddr = lua_tostring(L, 1);
|
const char* hostaddr = lua_tostring(L, 1);
|
||||||
const char* port = lua_tostring(L, 2);
|
int port = lua_tonumber(L, 2);
|
||||||
|
|
||||||
struct addrinfo hints, *servinfo, *p;
|
struct addrinfo hints, *servinfo, *p;
|
||||||
int status;
|
int status;
|
||||||
@ -27,7 +27,7 @@ static int l_open(lua_State *L) { //TODO: Any mem leaks?
|
|||||||
hints.ai_family = AF_UNSPEC;
|
hints.ai_family = AF_UNSPEC;
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
if ((status = getaddrinfo(hostaddr, port, &hints, &servinfo)) != 0) {
|
if ((status = getaddrinfo(hostaddr, NULL, &hints, &servinfo)) != 0) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, gai_strerror(status));
|
lua_pushstring(L, gai_strerror(status));
|
||||||
return 2;
|
return 2;
|
||||||
@ -35,6 +35,12 @@ static int l_open(lua_State *L) { //TODO: Any mem leaks?
|
|||||||
|
|
||||||
int sockfd;
|
int sockfd;
|
||||||
for(p = servinfo; p != NULL; p = p->ai_next) {
|
for(p = servinfo; p != NULL; p = p->ai_next) {
|
||||||
|
if(p->ai_family == AF_INET) {
|
||||||
|
((struct sockaddr_in*)p->ai_addr)->sin_port = htons(port);
|
||||||
|
} else {
|
||||||
|
((struct sockaddr_in6*)p->ai_addr)->sin6_port = htons(port);
|
||||||
|
}
|
||||||
|
|
||||||
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
|
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ void setup_modules(lua_State *L) {
|
|||||||
pushstuple(L, "sandbox", lua_sandbox);
|
pushstuple(L, "sandbox", lua_sandbox);
|
||||||
pushstuple(L, "textgpu", lua_textgpu);
|
pushstuple(L, "textgpu", lua_textgpu);
|
||||||
pushstuple(L, "random", lua_util_random);
|
pushstuple(L, "random", lua_util_random);
|
||||||
|
pushstuple(L, "buffer", lua_util_buffer);
|
||||||
|
|
||||||
pushstuple(L, "eepromDefault", res_eepromDefault);
|
pushstuple(L, "eepromDefault", res_eepromDefault);
|
||||||
|
|
||||||
|
@ -73,8 +73,9 @@ function filesystem.register(basePath)
|
|||||||
checkArg(1, path, "string")
|
checkArg(1, path, "string")
|
||||||
checkArg(2, path, "string", "nil")
|
checkArg(2, path, "string", "nil")
|
||||||
local m = "r"
|
local m = "r"
|
||||||
if mode == "a" then m = "a"
|
mode = mode or ""
|
||||||
elseif mode == "w" then m = "w" end
|
if mode:match("a") then m = "a"
|
||||||
|
elseif mode:match("w") then m = "w" end
|
||||||
local fd = native.fs_open(realpath(path), m)
|
local fd = native.fs_open(realpath(path), m)
|
||||||
if not fd then
|
if not fd then
|
||||||
return nil, path
|
return nil, path
|
||||||
|
@ -43,6 +43,7 @@ function main()
|
|||||||
--Utils
|
--Utils
|
||||||
loadModule("random")
|
loadModule("random")
|
||||||
loadModule("color")
|
loadModule("color")
|
||||||
|
loadModule("buffer")
|
||||||
|
|
||||||
modules.address = modules.random.uuid() --TODO: PREALPHA: Make constant
|
modules.address = modules.random.uuid() --TODO: PREALPHA: Make constant
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ function internet.start()
|
|||||||
checkArg(1, address, "string")
|
checkArg(1, address, "string")
|
||||||
checkArg(2, port, "number")
|
checkArg(2, port, "number")
|
||||||
|
|
||||||
local sfd, reason = net.open(address, tostring(port))
|
local sfd, reason = net.open(address, port)
|
||||||
return {
|
return {
|
||||||
finishConnect = function()
|
finishConnect = function()
|
||||||
if not sfd then
|
if not sfd then
|
||||||
@ -35,18 +35,83 @@ function internet.start()
|
|||||||
return net.write(sfd, data)
|
return net.write(sfd, data)
|
||||||
end,
|
end,
|
||||||
close = function()
|
close = function()
|
||||||
native.close(sfd)
|
native.fs_close(sfd)
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function component.request(url, post)
|
function component.request(url, post)
|
||||||
local host = url:match("http://([^/]+)")
|
local host = url:match("http://([^/]+)")
|
||||||
local con = component.connect(host, 80)
|
local socket = component.connect(host, 80)
|
||||||
if con:finishConnect() then
|
if socket.finishConnect() then
|
||||||
con:write("GET " .. url .. " HTTP/1.1\r\nHost: " .. host .. "\r\n\r\n")
|
socket.write("GET " .. url .. " HTTP/1.1\r\nHost: " .. host .. "\r\nConnection: close\r\n\r\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local stream = {}
|
||||||
|
|
||||||
|
function stream:seek()
|
||||||
|
return nil, "bad file descriptor"
|
||||||
|
end
|
||||||
|
|
||||||
|
function stream:write()
|
||||||
|
return nil, "bad file descriptor"
|
||||||
|
end
|
||||||
|
|
||||||
|
function stream:read(n)
|
||||||
|
if not socket then
|
||||||
|
return nil, "connection is closed"
|
||||||
|
end
|
||||||
|
return socket.read(n)
|
||||||
|
end
|
||||||
|
|
||||||
|
function stream:close()
|
||||||
|
if socket then
|
||||||
|
socket.close()
|
||||||
|
socket = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local connection = modules.buffer.new("rb", stream)
|
||||||
|
connection.readTimeout = 10
|
||||||
|
local header = nil
|
||||||
|
|
||||||
|
--TODO: GC close
|
||||||
|
--TODO: Chunked support
|
||||||
|
|
||||||
|
local finishConnect = function() --Read header
|
||||||
|
header = {}
|
||||||
|
header.status = connection:read("*l"):match("HTTP/.%.. (%d+) (.+)\r")
|
||||||
|
while true do
|
||||||
|
local line = connection:read("*l")
|
||||||
|
if not line or line == "" or line == "\r" then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
io.stderr:write("\nline: " .. line .. "\n")
|
||||||
|
local k, v = line:match("([^:]+): (.+)\r")
|
||||||
|
header[k:lower()] = v
|
||||||
|
end
|
||||||
|
header["content-length"] = tonumber(header["content-length"])
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
finishConnect = finishConnect,
|
||||||
|
read = function(n)
|
||||||
|
if not header then
|
||||||
|
finishConnect()
|
||||||
|
end
|
||||||
|
if header["content-length"] < 1 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
checkArg(1, n, "number", "nil")
|
||||||
|
n = n or math.min(8192, header["content-length"])
|
||||||
|
local res = connection:read(n)
|
||||||
|
header["content-length"] = header["content-length"] - #res
|
||||||
|
return res
|
||||||
|
end,
|
||||||
|
close = function()
|
||||||
|
connection:close()
|
||||||
|
end
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
modules.component.api.register(nil, "internet", component)
|
modules.component.api.register(nil, "internet", component)
|
||||||
|
448
src/lua/core/util/buffer.lua
Normal file
448
src/lua/core/util/buffer.lua
Normal file
@ -0,0 +1,448 @@
|
|||||||
|
local unicode = {
|
||||||
|
len = utf8.len,
|
||||||
|
sub = function(s, i, j)
|
||||||
|
checkArg(1, s, "string")
|
||||||
|
i = i or 1
|
||||||
|
j = j or math.maxinteger
|
||||||
|
if i<1 or j<1 then
|
||||||
|
local n = utf8.len(s)
|
||||||
|
if not n then return nil end
|
||||||
|
if i<0 then i = n+1+i end
|
||||||
|
if j<0 then j = n+1+j end
|
||||||
|
if i<0 then i = 1 elseif i>n then i = n end
|
||||||
|
if j<0 then j = 1 elseif j>n then j = n end
|
||||||
|
end
|
||||||
|
if j<i then return "" end
|
||||||
|
i = utf8.offset(s,i) or math.maxinteger
|
||||||
|
j = utf8.offset(s,j+1) or math.maxinteger
|
||||||
|
if i and j then return s:sub(i,j-1)
|
||||||
|
elseif i then return s:sub(i)
|
||||||
|
else return ""
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
local buffer = {}
|
||||||
|
|
||||||
|
function buffer.new(mode, stream)
|
||||||
|
local result = {
|
||||||
|
mode = {},
|
||||||
|
stream = stream,
|
||||||
|
bufferRead = "",
|
||||||
|
bufferWrite = "",
|
||||||
|
bufferSize = math.max(512, math.min(8 * 1024, native.freeMemory() / 8)),
|
||||||
|
bufferMode = "full",
|
||||||
|
readTimeout = math.huge
|
||||||
|
}
|
||||||
|
mode = mode or "r"
|
||||||
|
for i = 1, unicode.len(mode) do
|
||||||
|
result.mode[unicode.sub(mode, i, i)] = true
|
||||||
|
end
|
||||||
|
local metatable = {
|
||||||
|
__index = buffer,
|
||||||
|
__metatable = "file"
|
||||||
|
}
|
||||||
|
return setmetatable(result, metatable)
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:close()
|
||||||
|
if self.mode.w or self.mode.a then
|
||||||
|
self:flush()
|
||||||
|
end
|
||||||
|
self.closed = true
|
||||||
|
return self.stream:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:flush()
|
||||||
|
if #self.bufferWrite > 0 then
|
||||||
|
local result, reason = self.stream:write(self.bufferWrite)
|
||||||
|
if result then
|
||||||
|
self.bufferWrite = ""
|
||||||
|
else
|
||||||
|
if reason then
|
||||||
|
return nil, reason
|
||||||
|
else
|
||||||
|
return nil, "bad file descriptor"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:lines(...)
|
||||||
|
local args = table.pack(...)
|
||||||
|
return function()
|
||||||
|
local result = table.pack(self:read(table.unpack(args, 1, args.n)))
|
||||||
|
if not result[1] and result[2] then
|
||||||
|
error(result[2])
|
||||||
|
end
|
||||||
|
return table.unpack(result, 1, result.n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:read(...)
|
||||||
|
local timeout = (native.uptime() / 1000) + self.readTimeout
|
||||||
|
|
||||||
|
local function readChunk()
|
||||||
|
if (native.uptime() / 1000) > timeout then
|
||||||
|
error("timeout")
|
||||||
|
end
|
||||||
|
local result, reason = self.stream:read(self.bufferSize)
|
||||||
|
if result then
|
||||||
|
self.bufferRead = self.bufferRead .. result
|
||||||
|
return self
|
||||||
|
else -- error or eof
|
||||||
|
return nil, reason
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function readBytesOrChars(n)
|
||||||
|
n = math.max(n, 0)
|
||||||
|
local len, sub
|
||||||
|
if self.mode.b then
|
||||||
|
len = rawlen
|
||||||
|
sub = string.sub
|
||||||
|
else
|
||||||
|
len = unicode.len
|
||||||
|
sub = unicode.sub
|
||||||
|
end
|
||||||
|
local buffer = ""
|
||||||
|
repeat
|
||||||
|
if len(self.bufferRead) == 0 then
|
||||||
|
local result, reason = readChunk()
|
||||||
|
if not result then
|
||||||
|
if reason then
|
||||||
|
return nil, reason
|
||||||
|
else -- eof
|
||||||
|
return #buffer > 0 and buffer or nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local left = n - len(buffer)
|
||||||
|
buffer = buffer .. sub(self.bufferRead, 1, left)
|
||||||
|
self.bufferRead = sub(self.bufferRead, left + 1)
|
||||||
|
until len(buffer) == n
|
||||||
|
return buffer
|
||||||
|
end
|
||||||
|
|
||||||
|
local function readNumber()
|
||||||
|
local len, sub
|
||||||
|
if self.mode.b then
|
||||||
|
len = rawlen
|
||||||
|
sub = string.sub
|
||||||
|
else
|
||||||
|
len = unicode.len
|
||||||
|
sub = unicode.sub
|
||||||
|
end
|
||||||
|
local buffer = ""
|
||||||
|
local first = true
|
||||||
|
local decimal = false
|
||||||
|
local last = false
|
||||||
|
local hex = false
|
||||||
|
local pat = "^[0-9]+"
|
||||||
|
local minbuf = 3 -- "+0x" (sign + hexadecimal tag)
|
||||||
|
-- this function is used to read trailing numbers (1e2, 0x1p2, etc)
|
||||||
|
local function readnum(checksign)
|
||||||
|
local _buffer = ""
|
||||||
|
local sign = ""
|
||||||
|
while true do
|
||||||
|
if len(self.bufferRead) == 0 then
|
||||||
|
local result, reason = readChunk()
|
||||||
|
if not result then
|
||||||
|
if reason then
|
||||||
|
return nil, reason
|
||||||
|
else -- eof
|
||||||
|
return #_buffer > 0 and (sign .. _buffer) or nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if checksign then
|
||||||
|
local _sign = sub(self.bufferRead, 1, 1)
|
||||||
|
if _sign == "+" or _sign == "-" then
|
||||||
|
-- "eat" the sign (Rio Lua behaviour)
|
||||||
|
sign = sub(self.bufferRead, 1, 1)
|
||||||
|
self.bufferRead = sub(self.bufferRead, 2)
|
||||||
|
end
|
||||||
|
checksign = false
|
||||||
|
else
|
||||||
|
local x,y = string.find(self.bufferRead, pat)
|
||||||
|
if not x then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
_buffer = _buffer .. sub(self.bufferRead, 1, y)
|
||||||
|
self.bufferRead = sub(self.bufferRead, y + 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return #_buffer > 0 and (sign .. _buffer) or nil
|
||||||
|
end
|
||||||
|
while true do
|
||||||
|
if len(self.bufferRead) == 0 or len(self.bufferRead) < minbuf then
|
||||||
|
local result, reason = readChunk()
|
||||||
|
if not result then
|
||||||
|
if reason then
|
||||||
|
return nil, reason
|
||||||
|
else -- eof
|
||||||
|
return #buffer > 0 and tonumber(buffer) or nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- these ifs are here so we run the buffer check above
|
||||||
|
if first then
|
||||||
|
local sign = sub(self.bufferRead, 1, 1)
|
||||||
|
if sign == "+" or sign == "-" then
|
||||||
|
-- "eat" the sign (Rio Lua behaviour)
|
||||||
|
buffer = buffer .. sub(self.bufferRead, 1, 1)
|
||||||
|
self.bufferRead = sub(self.bufferRead, 2)
|
||||||
|
end
|
||||||
|
local hextag = sub(self.bufferRead, 1, 2)
|
||||||
|
if hextag == "0x" or hextag == "0X" then
|
||||||
|
pat = "^[0-9A-Fa-f]+"
|
||||||
|
-- "eat" the 0x, see https://gist.github.com/SoniEx2/570a363d81b743353151
|
||||||
|
buffer = buffer .. sub(self.bufferRead, 1, 2)
|
||||||
|
self.bufferRead = sub(self.bufferRead, 3)
|
||||||
|
hex = true
|
||||||
|
end
|
||||||
|
minbuf = 0
|
||||||
|
first = false
|
||||||
|
elseif decimal then
|
||||||
|
local sep = sub(self.bufferRead, 1, 1)
|
||||||
|
if sep == "." then
|
||||||
|
buffer = buffer .. sep
|
||||||
|
self.bufferRead = sub(self.bufferRead, 2)
|
||||||
|
local temp = readnum(false) -- no sign
|
||||||
|
if temp then
|
||||||
|
buffer = buffer .. temp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not tonumber(buffer) then break end
|
||||||
|
decimal = false
|
||||||
|
last = true
|
||||||
|
minbuf = 1
|
||||||
|
elseif last then
|
||||||
|
local tag = sub(self.bufferRead, 1, 1)
|
||||||
|
if hex and (tag == "p" or tag == "P") then
|
||||||
|
local temp = sub(self.bufferRead, 1, 1)
|
||||||
|
self.bufferRead = sub(self.bufferRead, 2)
|
||||||
|
local temp2 = readnum(true) -- this eats the next sign if any
|
||||||
|
if temp2 then
|
||||||
|
buffer = buffer .. temp .. temp2
|
||||||
|
end
|
||||||
|
elseif tag == "e" or tag == "E" then
|
||||||
|
local temp = sub(self.bufferRead, 1, 1)
|
||||||
|
self.bufferRead = sub(self.bufferRead, 2)
|
||||||
|
local temp2 = readnum(true) -- this eats the next sign if any
|
||||||
|
if temp2 then
|
||||||
|
buffer = buffer .. temp .. temp2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
break
|
||||||
|
else
|
||||||
|
local x,y = string.find(self.bufferRead, pat)
|
||||||
|
if not x then
|
||||||
|
minbuf = 1
|
||||||
|
decimal = true
|
||||||
|
else
|
||||||
|
buffer = buffer .. sub(self.bufferRead, 1, y)
|
||||||
|
self.bufferRead = sub(self.bufferRead, y + 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return tonumber(buffer)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function readLine(chop)
|
||||||
|
local start = 1
|
||||||
|
while true do
|
||||||
|
local l = self.bufferRead:find("\n", start, true)
|
||||||
|
if l then
|
||||||
|
local result = self.bufferRead:sub(1, l + (chop and -1 or 0))
|
||||||
|
self.bufferRead = self.bufferRead:sub(l + 1)
|
||||||
|
return result
|
||||||
|
else
|
||||||
|
start = #self.bufferRead
|
||||||
|
local result, reason = readChunk()
|
||||||
|
if not result then
|
||||||
|
if reason then
|
||||||
|
return nil, reason
|
||||||
|
else -- eof
|
||||||
|
local result = #self.bufferRead > 0 and self.bufferRead or nil
|
||||||
|
self.bufferRead = ""
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function readAll()
|
||||||
|
repeat
|
||||||
|
local result, reason = readChunk()
|
||||||
|
if not result and reason then
|
||||||
|
return nil, reason
|
||||||
|
end
|
||||||
|
until not result -- eof
|
||||||
|
local result = self.bufferRead
|
||||||
|
self.bufferRead = ""
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
local function read(n, format)
|
||||||
|
if type(format) == "number" then
|
||||||
|
return readBytesOrChars(format)
|
||||||
|
else
|
||||||
|
if type(format) ~= "string" or unicode.sub(format, 1, 1) ~= "*" then
|
||||||
|
error("bad argument #" .. n .. " (invalid option)")
|
||||||
|
end
|
||||||
|
format = unicode.sub(format, 2, 2)
|
||||||
|
if format == "n" then
|
||||||
|
return readNumber()
|
||||||
|
elseif format == "l" then
|
||||||
|
return readLine(true)
|
||||||
|
elseif format == "L" then
|
||||||
|
return readLine(false)
|
||||||
|
elseif format == "a" then
|
||||||
|
return readAll()
|
||||||
|
else
|
||||||
|
error("bad argument #" .. n .. " (invalid format)")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.mode.w or self.mode.a then
|
||||||
|
self:flush()
|
||||||
|
end
|
||||||
|
|
||||||
|
local results = {}
|
||||||
|
local formats = table.pack(...)
|
||||||
|
if formats.n == 0 then
|
||||||
|
return readLine(true)
|
||||||
|
end
|
||||||
|
for i = 1, formats.n do
|
||||||
|
local result, reason = read(i, formats[i])
|
||||||
|
if result then
|
||||||
|
results[i] = result
|
||||||
|
elseif reason then
|
||||||
|
return nil, reason
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return table.unpack(results, 1, formats.n)
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:seek(whence, offset)
|
||||||
|
whence = tostring(whence or "cur")
|
||||||
|
assert(whence == "set" or whence == "cur" or whence == "end",
|
||||||
|
"bad argument #1 (set, cur or end expected, got " .. whence .. ")")
|
||||||
|
offset = offset or 0
|
||||||
|
checkArg(2, offset, "number")
|
||||||
|
assert(math.floor(offset) == offset, "bad argument #2 (not an integer)")
|
||||||
|
|
||||||
|
if self.mode.w or self.mode.a then
|
||||||
|
self:flush()
|
||||||
|
elseif whence == "cur" then
|
||||||
|
offset = offset - #self.bufferRead
|
||||||
|
end
|
||||||
|
local result, reason = self.stream:seek(whence, offset)
|
||||||
|
if result then
|
||||||
|
self.bufferRead = ""
|
||||||
|
return result
|
||||||
|
else
|
||||||
|
return nil, reason
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:setvbuf(mode, size)
|
||||||
|
mode = mode or self.bufferMode
|
||||||
|
size = size or self.bufferSize
|
||||||
|
|
||||||
|
assert(mode == "no" or mode == "full" or mode == "line",
|
||||||
|
"bad argument #1 (no, full or line expected, got " .. tostring(mode) .. ")")
|
||||||
|
assert(mode == "no" or type(size) == "number",
|
||||||
|
"bad argument #2 (number expected, got " .. type(size) .. ")")
|
||||||
|
|
||||||
|
self.bufferMode = mode
|
||||||
|
self.bufferSize = size
|
||||||
|
|
||||||
|
return self.bufferMode, self.bufferSize
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:getTimeout()
|
||||||
|
return self.readTimeout
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:setTimeout(value)
|
||||||
|
self.readTimeout = tonumber(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
function buffer:write(...)
|
||||||
|
if self.closed then
|
||||||
|
return nil, "bad file descriptor"
|
||||||
|
end
|
||||||
|
local args = table.pack(...)
|
||||||
|
for i = 1, args.n do
|
||||||
|
if type(args[i]) == "number" then
|
||||||
|
args[i] = tostring(args[i])
|
||||||
|
end
|
||||||
|
checkArg(i, args[i], "string")
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, args.n do
|
||||||
|
local arg = args[i]
|
||||||
|
local result, reason
|
||||||
|
|
||||||
|
if self.bufferMode == "full" then
|
||||||
|
if self.bufferSize - #self.bufferWrite < #arg then
|
||||||
|
result, reason = self:flush()
|
||||||
|
if not result then
|
||||||
|
return nil, reason
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #arg > self.bufferSize then
|
||||||
|
result, reason = self.stream:write(arg)
|
||||||
|
else
|
||||||
|
self.bufferWrite = self.bufferWrite .. arg
|
||||||
|
result = self
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif self.bufferMode == "line" then
|
||||||
|
local l
|
||||||
|
repeat
|
||||||
|
local idx = arg:find("\n", (l or 0) + 1, true)
|
||||||
|
if idx then
|
||||||
|
l = idx
|
||||||
|
end
|
||||||
|
until not idx
|
||||||
|
if l or #arg > self.bufferSize then
|
||||||
|
result, reason = self:flush()
|
||||||
|
if not result then
|
||||||
|
return nil, reason
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if l then
|
||||||
|
result, reason = self.stream:write(arg:sub(1, l))
|
||||||
|
if not result then
|
||||||
|
return nil, reason
|
||||||
|
end
|
||||||
|
arg = arg:sub(l + 1)
|
||||||
|
end
|
||||||
|
if #arg > self.bufferSize then
|
||||||
|
result, reason = self.stream:write(arg)
|
||||||
|
else
|
||||||
|
self.bufferWrite = self.bufferWrite .. arg
|
||||||
|
result = self
|
||||||
|
end
|
||||||
|
|
||||||
|
else -- self.bufferMode == "no"
|
||||||
|
result, reason = self.stream:write(arg)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not result then
|
||||||
|
return nil, reason
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
return buffer
|
Loading…
Reference in New Issue
Block a user