From 1e05869bd8eed8d63663d12a58f1667b9e52af3c Mon Sep 17 00:00:00 2001 From: Er2 Date: Mon, 7 Mar 2022 17:53:29 +0300 Subject: [PATCH] refactoring --- http.lua | 11 ++++ main.lua | 154 +++++++++++++++++++++++++++++++++--------------------- readme.md | 2 +- tcp.lua | 62 ++++++++++++++++++++++ utf8.lua | 89 +++++++++++++++++++++++++++++++ 5 files changed, 258 insertions(+), 60 deletions(-) create mode 100644 http.lua create mode 100644 tcp.lua create mode 100644 utf8.lua diff --git a/http.lua b/http.lua new file mode 100644 index 0000000..e0f15f1 --- /dev/null +++ b/http.lua @@ -0,0 +1,11 @@ + +local function _loop(b, v) + writeln(v, 'HTTP/1.1 200 OK', '') + write(v, 'Please use telnet to use this service.') + close(v) +end + +return { + -- submodule + _loop = _loop, +} diff --git a/main.lua b/main.lua index 45e7f1d..57c3fd5 100644 --- a/main.lua +++ b/main.lua @@ -1,8 +1,5 @@ -local socket = require 'socket' -local tcp = socket.bind('*', 8080) -tcp:settimeout(0) -motd = { +local motd = { ' __ ' , ' ___________/__\\___________ ', ' / _ _ -__ - ___ - \\', @@ -21,48 +18,70 @@ motd = { local cls = {} local tmp = {} -local function get(v) - v.buf = v.buf or '' - local l, err - while not err do - l, err = v.cli:receive(1) - if l == '\r' or l == '\n' then - if v.le then v.cli:send '\r\n' end - l, v.buf = v.buf, nil - break - elseif l == '\127' - then v.cli:send '\008' - v.buf = v.buf:sub(1, -2) - elseif l then - v.buf = v.buf.. l - if v.le then v.cli:send(l) end - end - end - return l, err +utf8 = require '.utf8' + +local back = { + tcp = require '.tcp', + http = require '.http', +-- ws = require '.ws', +} + +local main = { + 'tcp', +-- 'ws', +} + +function get(v, ...) return back[v.back].get(v, ...) end +function close(v, ...) return back[v.back].close(v, ...) end +function write(v, ...) return back[v.back].write(v, ...) end +function writeln(v, ...) return back[v.back].writeln(v, ...) end + +function makev(cli, back) + if not cli then return end + return { + cli = cli, + st = 0, + back = back, + } end -while true do - local cli = tcp:accept() - if cli then - cli:settimeout(0) - if (cli:receive() or ''):match 'GET /.* HTTP.*' - then cli:send('HTTP/1.1 200 OK\r\n\r\nPlease use telnet to use this service.') - cli:close() - else cli:send(table.concat(motd, '\r\n')..'\r\n') - table.insert(tmp, { - cli = cli, - st = 0, - }) +function init(v) + writeln(v, table.unpack(motd)) + writeln(v, '') + table.insert(tmp, v) +end + +function forc(fn, t, c) + if c == nil or c == true then + for k, v in pairs(cls) do + fn(v, k) + if v.closed then table.remove(cls, k) end end end - for k, v in pairs(tmp) do + if t then + for k, v in pairs(tmp) do + fn(v, k) + if v.closed then table.remove(tmp, k) end + end + end +end + +local runs = true +while runs do + for _, v in pairs(main) do + local p, w = back[v]._loop(v) + if p and back[p] + then back[p]._loop(v, w) end + end + + forc(function(v, k) if v.reads then -- nothing - elseif v.st == 0 then v.cli:send('Do you need input echo? [YNQ] ') - elseif v.st == 1 then v.cli:send('Username: ') - elseif v.st == 2 then v.cli:send('Password: ') - elseif v.st == 3 then v.cli:send('Welcome, '.. v.user ..'!\r\n') + elseif v.st == 0 then write (v, 'Do you need input echo? [YNHQ] ') + elseif v.st == 1 then write (v, 'Username: ') + elseif v.st == 2 then write (v, 'Password: ') + elseif v.st == 3 then writeln(v, 'Welcome, '.. v.user ..'!') for _, w in pairs(cls) do - w.cli:send('\r\n-- '.. v.user ..' joined --\r\n') + writeln(w, '', '-- '.. v.user ..' joined --') end v.st, v.reads = nil, nil table.remove(tmp, k) @@ -71,30 +90,47 @@ while true do if v.st then v.reads = true local l, err = get(v) if err == 'timeout' then -- nothing - elseif err then table.remove(tmp, k) - elseif (l or ''):match '^%s*$' then v.reads = false + elseif err then close(v) + --elseif (l or ''):match '^%s*$' then v.reads = false elseif v.st == 0 then v.st, v.reads = 1, false - if l:match 'y' - then v.le = true; v.cli:send(l.. '\r\n') - elseif l:match 'q' - then v.cli:close(); table.remove(tmp, k) + if l:match '[Yy]' + then v.le = true; writeln(v, l) + elseif l:match '[QqEe]' then close(v) + elseif l:match '[Nn]' then -- nothing + else v.st = 0 + writeln(v, l, + 'Say N if you see your input twice.', + 'Say Y if not.', + 'Say Q to exit.') + end + elseif v.st == 1 then v.reads = false + if #(l:match '^%S+' or '') >= 3 + then v.user, v.st = l:match '^%S+', 3 + else writeln(v, 'Provide username with length >= 3 symbols') end - elseif v.st == 1 then v.st, v.user, v.reads = 3, l:match '^%S+', false elseif v.st == 2 then v.st, v.reads = 1, false end end - end - for k, v in pairs(cls) do + end, true, false) + + forc(function(v, k) local l, err = get(v) - if l == '/leave' then v.cli:close(); err = 'closed' - end - if err and err ~= 'timeout' then table.remove(cls, k) end - for j, w in pairs(cls) do - if err and err ~= 'timeout' - then w.cli:send('\r\n-- '.. v.user ..' leaved --\r\n') - elseif (l or ''):match '^%s*$' then -- nothing - elseif k ~= j then w.cli:send('\r\n'.. v.user ..': '.. l ..'\r\n') + if err and err ~= 'timeout' + then close(v) end + if l == '/help' then writeln(v, '-- HELP --', + '/leave - Close chat' + ) + elseif l == '/leave' then close(v) + elseif (l or ''):sub(1, 1) == '/' + then writeln(v, 'Unknown command') + + elseif not (l or ''):match '^%s*$' + then forc(function(w) + if w == v then -- ignore yourself + elseif v.closed + then writeln(w, '', '-- '.. v.user ..' leaves --') + else writeln(w, '', v.user ..': '.. l) end - end - end + end) end + end, false, true) end diff --git a/readme.md b/readme.md index b6488b3..781f3e9 100644 --- a/readme.md +++ b/readme.md @@ -12,7 +12,7 @@ ___|___|____/____\_____|___|___ Welcome to Er2Chat! You need to login to continue -Do you need input echo? [YNQ] +Do you need input echo? [YNHQ] ``` --- diff --git a/tcp.lua b/tcp.lua new file mode 100644 index 0000000..7c07c46 --- /dev/null +++ b/tcp.lua @@ -0,0 +1,62 @@ +local socket = require 'socket' +local tcp = socket.bind('*', 8080) +tcp:settimeout(0) + +local function get(v) + v.buf = v.buf or '' + local l, err + while not err do + l, err = v.cli:receive(1) + if l == '\r' or l == '\n' then + if v.le then writeln(v, '') end + local n = v.cli:receive(1) + if not n or n == '\n' or n == '\r' + then l, v.buf = v.buf, nil + else l = n end + end + if v.buf == nil then break + elseif l == '\127' + then write(v, '\008') + v.buf = utf8.sub(v.buf, 1, -2) + elseif l then + v.buf = v.buf.. l + if v.le then write(v, l) end + end + end + return l, err +end + +local function write(v, ...) + v.cli:send(table.concat({...}, '\r\n')) +end + +local function writeln(v, ...) + write(v, ...) + write(v, '\r\n') +end + +local function close(v) + v.cli:close() + v.closed = true +end + +local function _loop(b) + local v = makev(tcp:accept(), b) + if v then + v.cli:settimeout(0) + local l = v.cli:receive() + if (l or ''):match 'GET /.* HTTP.*' then + return 'http', v + else init(v) + end + end +end + +return { + get = get, + write = write, + writeln = writeln, + close = close, + _loop = _loop, + tcp = tcp, +} diff --git a/utf8.lua b/utf8.lua new file mode 100644 index 0000000..e730f22 --- /dev/null +++ b/utf8.lua @@ -0,0 +1,89 @@ +-- Unicode library + +local pattern = '[%z\1-\127\194-\244][\128-\191]*' + +-- helper function +local function posrelat(pos, len) + if pos < 0 then + pos = len + pos + 1 + end + return pos +end + +local utf8 = {} + +-- THE MEAT + +function utf8.map(s, f, no_subs) + local i = 0 + + if no_subs then + for b, e in s:gmatch('()' .. pattern .. '()') do + i = i + 1 + local c = e - b + f(i, c, b) + end + else + for b, c in s:gmatch('()(' .. pattern .. ')') do + i = i + 1 + f(i, c, b) + end + end +end + +-- THE REST + +function utf8.chars(s, no_subs) + return coroutine.wrap(function() + return utf8.map(s, coroutine.yield, no_subs) + end) +end + +function utf8.len(s) + return select(2, s:gsub('[^\128-\193]', '')) +end + +function utf8.replace(s, map) + return s:gsub(pattern, map) +end + +function utf8.reverse(s) + s = s:gsub(pattern, function(c) + return #c > 1 and c:reverse() + end) + return s:reverse() +end + +function utf8.strip(s) + return s:gsub(pattern, function(c) + return #c > 1 and '' + end) +end + +function utf8.sub(s, i, j) + local l = utf8.len(s) + + i = posrelat(i, l) + j = j and posrelat(j, l) or l + + if i < 1 then i = 1 end + if j > l then j = l end + if i > j then return '' end + + local diff = j - i + local iter = utf8.chars(s, true) + + for _ = 1, i - 1 do iter() end + local c, b = select(2, iter()) + + if diff == 0 + then return s:sub(b, b + c - 1) end + + i = b + for _ = 1, diff - 1 do iter() end + local c, b = select(2, iter()) + + return s:sub(i, b + c - 1) +end + +return utf8