Skip to content
This repository has been archived by the owner on Jun 30, 2023. It is now read-only.

[RFC] corrupted msgpack streams are printed to screen and/or saved #38

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 50 additions & 6 deletions nvim/msgpack_rpc_stream.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,31 @@ local Tabpage = {}
Tabpage.__index = Tabpage
function Tabpage.new(id) return setmetatable({id=id}, Tabpage) end

local function hexdump(str)
local len = string.len(str)
local dump = ""
local hex = ""
local asc = ""

for i = 1, len do
if 1 == i % 8 then
dump = dump .. hex .. asc .. "\n"
hex = string.format("%04x: ", i - 1)
asc = ""
end

local ord = string.byte(str, i)
hex = hex .. string.format("%02x ", ord)
if ord >= 32 and ord <= 126 then
asc = asc .. string.char(ord)
else
asc = asc .. "."
end
end

return dump .. hex .. string.rep(" ", 8 - len % 8) .. asc
end

local Response = {}
Response.__index = Response

Expand Down Expand Up @@ -39,6 +64,7 @@ MsgpackRpcStream.__index = MsgpackRpcStream
function MsgpackRpcStream.new(stream)
return setmetatable({
_stream = stream,
_previous_chunk = nil,
_pack = mpack.Packer({
ext = {
[Buffer] = function(o) return 0, mpack.pack(o.id) end,
Expand Down Expand Up @@ -76,20 +102,38 @@ function MsgpackRpcStream:read_start(request_cb, notification_cb, eof_cb)
if not data then
return eof_cb()
end
local type, id_or_cb
local status, type_, id_or_cb
local pos = 1
local len = #data
while pos <= len do
type, id_or_cb, method_or_error, args_or_result, pos =
self._session:receive(data, pos)
if type == 'request' or type == 'notification' then
if type == 'request' then
-- grab a copy of pos since pcall() will set it to nil on error
local oldpos = pos
status, type_, id_or_cb, method_or_error, args_or_result, pos = pcall(
self._session.receive, self._session, data, pos)
if not status then
-- write the full blob of bad data to a specific file
local outfile = io.open('./msgpack-invalid-data', 'w')
outfile:write(data)
outfile:close()

-- build a printable representation of the bad part of the string
local printable = hexdump(data:sub(oldpos, oldpos + 8 * 10))

print(string.format("Error deserialising msgpack data stream at pos %d:\n%s\n",
oldpos, printable))
print(string.format("... occurred after %s", self._previous_chunk))
error(type_)
end
if type_ == 'request' or type_ == 'notification' then
self._previous_chunk = string.format('%s<%s>', type_, method_or_error)
if type_ == 'request' then
request_cb(method_or_error, args_or_result, Response.new(self,
id_or_cb))
else
notification_cb(method_or_error, args_or_result)
end
elseif type == 'response' then
elseif type_ == 'response' then
self._previous_chunk = string.format('response<%s>', type(args_or_result))
if method_or_error == mpack.NIL then
method_or_error = nil
else
Expand Down