[tarantool-patches] [PATCH 4/6] box/console: Fix hang in remote console lua mode
Cyrill Gorcunov
gorcunov at gmail.com
Fri Sep 6 00:28:13 MSK 2019
If we change output mode on remote machine via
text-based session
node a (server)
------
require('console').listen('unix/:/tmp/X.sock')
...
node b (client)
------
require('console').connect('unix/:/tmp/X.sock')
connected to unix/:/tmp/X.sock
...
unix/:/tmp/X.sock> \set output lua
the client get stuck forever, it is because the text
wire protocol waits for yaml eos terminator which of
course never hit the peer, because lua mode uses own
one.
Thus to fix this problem we have to preprocess the text
we're passing to the server, just like we do in local
console. So we reuse command parsing code and remember
current output terminator in text_connection_mt instance.
Another problem is that named default output mode. There
could be a mixed environment where server operates in
default lua mode but client connects with yaml mode. To
break a tie we yield "\set output" command with current
output mode when establishing a connection. Since the
output format is per-session bound this doesn't affect
any other connections on a server.
Part-of #3834
---
v3:
- Use current_output() helper in current_eos()
instead of local variable
- Rename output_cmd_string() to output_to_cmd_string()
- Use string.format in output_to_cmd_string
- Leave preprocess_eval inside metatable because moving
this method out of text_connection_mt is really confusing,
everything specific to console text connection should sit
inside text_connection_mt for better design
- More comments
src/box/lua/console.lua | 57 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 54 insertions(+), 3 deletions(-)
diff --git a/src/box/lua/console.lua b/src/box/lua/console.lua
index 04cca35ca..a6ff0e659 100644
--- a/src/box/lua/console.lua
+++ b/src/box/lua/console.lua
@@ -157,6 +157,24 @@ local function current_output()
return d
end
+--
+-- Return current EOS value for currently
+-- active output format.
+local function current_eos()
+ return output_eos[current_output()["fmt"]]
+end
+
+--
+-- Map output format descriptor into a "\set" command.
+local function output_to_cmd_string(desc)
+ if desc["opts"] then
+ string.format("\\set output %s,%s", desc["fmt"], desc["opts"])
+ else
+ string.format("\\set output %s", desc["fmt"])
+ end
+ return cmd
+end
+
local function format(status, ...)
local d = current_output()
return output_handlers[d["fmt"]](status, d["opts"], ...)
@@ -346,15 +364,39 @@ local text_connection_mt = {
-- @retval nil Error.
--
read = function(self)
- local ret = self._socket:read(output_eos["yaml"])
+ local ret = self._socket:read(self.eos)
if ret and ret ~= '' then
return ret
end
end,
--
+ -- The command might modify EOS so
+ -- we have to parse and preprocess it
+ -- first to not stuck in :read() forever.
+ --
+ -- Same time the EOS is per connection
+ -- value so we keep it inside the metatable
+ -- instace, and because we can't use same
+ -- name as output_eos() function we name
+ -- it simplier as self.eos.
+ preprocess_eval = function(self, text)
+ local command = get_command(text)
+ if command == nil then
+ return
+ end
+ local nr_items, items = parse_operators(command)
+ if nr_items == 3 then
+ local err, fmt, opts = output_parse(items[3])
+ if not err then
+ self.eos = output_eos[fmt]
+ end
+ end
+ end,
+ --
-- Write + Read.
--
eval = function(self, text)
+ self:preprocess_eval(text)
text = text..'$EOF$\n'
if not self:write(text) then
error(self:set_error())
@@ -409,9 +451,18 @@ local function wrap_text_socket(connection, url, print_f)
host = url.host or 'localhost',
port = url.service,
print_f = print_f,
+ eos = current_eos(),
}, text_connection_mt)
- if not conn:write('require("console").delimiter("$EOF$")\n') or
- not conn:read() then
+ --
+ -- Prepare the connection: setup EOS symbol
+ -- by executing the \set command on remote machine
+ -- explicitly, and then setup a delimiter.
+ local cmd_set_output = output_to_cmd_string(current_output()) .. '\n'
+ if not conn:write(cmd_set_output) or not conn:read() then
+ conn:set_error()
+ end
+ local cmd_delimiter = 'require("console").delimiter("$EOF$")\n'
+ if not conn:write(cmd_delimiter) or not conn:read() then
conn:set_error()
end
return conn
--
2.20.1
More information about the Tarantool-patches
mailing list