forked from aniani/vim
patch 8.0.0312: failure when a channel receives a split json message
Problem: When a json message arrives in pieces, the start is dropped and the decoding fails. Solution: Do not drop the start when it is still needed. (Kay Zheng) Add a test. Reset the timeout when something is received.
This commit is contained in:
@@ -1840,39 +1840,42 @@ channel_save(channel_T *channel, ch_part_T part, char_u *buf, int len,
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to fill the buffer of "reader".
|
||||||
|
* Returns FALSE when nothing was added.
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
channel_fill(js_read_T *reader)
|
channel_fill(js_read_T *reader)
|
||||||
{
|
{
|
||||||
channel_T *channel = (channel_T *)reader->js_cookie;
|
channel_T *channel = (channel_T *)reader->js_cookie;
|
||||||
ch_part_T part = reader->js_cookie_arg;
|
ch_part_T part = reader->js_cookie_arg;
|
||||||
char_u *next = channel_get(channel, part);
|
char_u *next = channel_get(channel, part);
|
||||||
int unused;
|
int keeplen;
|
||||||
int len;
|
int addlen;
|
||||||
char_u *p;
|
char_u *p;
|
||||||
|
|
||||||
if (next == NULL)
|
if (next == NULL)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
unused = reader->js_end - reader->js_buf - reader->js_used;
|
keeplen = reader->js_end - reader->js_buf;
|
||||||
if (unused > 0)
|
if (keeplen > 0)
|
||||||
{
|
{
|
||||||
/* Prepend unused text. */
|
/* Prepend unused text. */
|
||||||
len = (int)STRLEN(next);
|
addlen = (int)STRLEN(next);
|
||||||
p = alloc(unused + len + 1);
|
p = alloc(keeplen + addlen + 1);
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
{
|
{
|
||||||
vim_free(next);
|
vim_free(next);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
mch_memmove(p, reader->js_buf + reader->js_used, unused);
|
mch_memmove(p, reader->js_buf, keeplen);
|
||||||
mch_memmove(p + unused, next, len + 1);
|
mch_memmove(p + keeplen, next, addlen + 1);
|
||||||
vim_free(next);
|
vim_free(next);
|
||||||
next = p;
|
next = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
vim_free(reader->js_buf);
|
vim_free(reader->js_buf);
|
||||||
reader->js_buf = next;
|
reader->js_buf = next;
|
||||||
reader->js_used = 0;
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1952,16 +1955,20 @@ channel_parse_json(channel_T *channel, ch_part_T part)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (status == OK)
|
if (status == OK)
|
||||||
chanpart->ch_waiting = FALSE;
|
chanpart->ch_wait_len = 0;
|
||||||
else if (status == MAYBE)
|
else if (status == MAYBE)
|
||||||
{
|
{
|
||||||
if (!chanpart->ch_waiting)
|
size_t buflen = STRLEN(reader.js_buf);
|
||||||
|
|
||||||
|
if (chanpart->ch_wait_len < buflen)
|
||||||
{
|
{
|
||||||
/* First time encountering incomplete message, set a deadline of
|
/* First time encountering incomplete message or after receiving
|
||||||
* 100 msec. */
|
* more (but still incomplete): set a deadline of 100 msec. */
|
||||||
ch_log(channel, "Incomplete message - wait for more");
|
ch_logn(channel,
|
||||||
|
"Incomplete message (%d bytes) - wait 100 msec for more",
|
||||||
|
buflen);
|
||||||
reader.js_used = 0;
|
reader.js_used = 0;
|
||||||
chanpart->ch_waiting = TRUE;
|
chanpart->ch_wait_len = buflen;
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
chanpart->ch_deadline = GetTickCount() + 100L;
|
chanpart->ch_deadline = GetTickCount() + 100L;
|
||||||
#else
|
#else
|
||||||
@@ -1992,7 +1999,8 @@ channel_parse_json(channel_T *channel, ch_part_T part)
|
|||||||
if (timeout)
|
if (timeout)
|
||||||
{
|
{
|
||||||
status = FAIL;
|
status = FAIL;
|
||||||
chanpart->ch_waiting = FALSE;
|
chanpart->ch_wait_len = 0;
|
||||||
|
ch_log(channel, "timed out");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2006,7 +2014,7 @@ channel_parse_json(channel_T *channel, ch_part_T part)
|
|||||||
{
|
{
|
||||||
ch_error(channel, "Decoding failed - discarding input");
|
ch_error(channel, "Decoding failed - discarding input");
|
||||||
ret = FALSE;
|
ret = FALSE;
|
||||||
chanpart->ch_waiting = FALSE;
|
chanpart->ch_wait_len = 0;
|
||||||
}
|
}
|
||||||
else if (reader.js_buf[reader.js_used] != NUL)
|
else if (reader.js_buf[reader.js_used] != NUL)
|
||||||
{
|
{
|
||||||
@@ -3369,7 +3377,7 @@ channel_read_json_block(
|
|||||||
/* Wait for up to the timeout. If there was an incomplete message
|
/* Wait for up to the timeout. If there was an incomplete message
|
||||||
* use the deadline for that. */
|
* use the deadline for that. */
|
||||||
timeout = timeout_arg;
|
timeout = timeout_arg;
|
||||||
if (chanpart->ch_waiting)
|
if (chanpart->ch_wait_len > 0)
|
||||||
{
|
{
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
timeout = chanpart->ch_deadline - GetTickCount() + 1;
|
timeout = chanpart->ch_deadline - GetTickCount() + 1;
|
||||||
@@ -3389,7 +3397,7 @@ channel_read_json_block(
|
|||||||
{
|
{
|
||||||
/* Something went wrong, channel_parse_json() didn't
|
/* Something went wrong, channel_parse_json() didn't
|
||||||
* discard message. Cancel waiting. */
|
* discard message. Cancel waiting. */
|
||||||
chanpart->ch_waiting = FALSE;
|
chanpart->ch_wait_len = 0;
|
||||||
timeout = timeout_arg;
|
timeout = timeout_arg;
|
||||||
}
|
}
|
||||||
else if (timeout > timeout_arg)
|
else if (timeout > timeout_arg)
|
||||||
|
@@ -1563,9 +1563,11 @@ typedef struct {
|
|||||||
jsonq_T ch_json_head; /* header for circular json read queue */
|
jsonq_T ch_json_head; /* header for circular json read queue */
|
||||||
int ch_block_id; /* ID that channel_read_json_block() is
|
int ch_block_id; /* ID that channel_read_json_block() is
|
||||||
waiting for */
|
waiting for */
|
||||||
/* When ch_waiting is TRUE use ch_deadline to wait for incomplete message
|
/* When ch_wait_len is non-zero use ch_deadline to wait for incomplete
|
||||||
* to be complete. */
|
* message to be complete. The value is the length of the incomplete
|
||||||
int ch_waiting;
|
* message when the deadline was set. If it gets longer (something was
|
||||||
|
* received) the deadline is reset. */
|
||||||
|
size_t ch_wait_len;
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
DWORD ch_deadline;
|
DWORD ch_deadline;
|
||||||
#else
|
#else
|
||||||
|
@@ -1141,7 +1141,11 @@ func Test_out_cb()
|
|||||||
|
|
||||||
let dict = {'thisis': 'dict: '}
|
let dict = {'thisis': 'dict: '}
|
||||||
func dict.outHandler(chan, msg) dict
|
func dict.outHandler(chan, msg) dict
|
||||||
|
if type(a:msg) == v:t_string
|
||||||
let g:Ch_outmsg = self.thisis . a:msg
|
let g:Ch_outmsg = self.thisis . a:msg
|
||||||
|
else
|
||||||
|
let g:Ch_outobj = a:msg
|
||||||
|
endif
|
||||||
endfunc
|
endfunc
|
||||||
func dict.errHandler(chan, msg) dict
|
func dict.errHandler(chan, msg) dict
|
||||||
let g:Ch_errmsg = self.thisis . a:msg
|
let g:Ch_errmsg = self.thisis . a:msg
|
||||||
@@ -1161,6 +1165,12 @@ func Test_out_cb()
|
|||||||
call assert_equal("dict: hello", g:Ch_outmsg)
|
call assert_equal("dict: hello", g:Ch_outmsg)
|
||||||
call WaitFor('g:Ch_errmsg != ""')
|
call WaitFor('g:Ch_errmsg != ""')
|
||||||
call assert_equal("dict: there", g:Ch_errmsg)
|
call assert_equal("dict: there", g:Ch_errmsg)
|
||||||
|
|
||||||
|
" Receive a json object split in pieces
|
||||||
|
unlet! g:Ch_outobj
|
||||||
|
call ch_sendraw(job, "echosplit [0, {\"one\": 1,| \"tw|o\": 2, \"three\": 3|}]\n")
|
||||||
|
call WaitFor('exists("g:Ch_outobj")')
|
||||||
|
call assert_equal({'one': 1, 'two': 2, 'three': 3}, g:Ch_outobj)
|
||||||
finally
|
finally
|
||||||
call job_stop(job)
|
call job_stop(job)
|
||||||
endtry
|
endtry
|
||||||
|
@@ -29,6 +29,11 @@ if __name__ == "__main__":
|
|||||||
if typed.startswith("echo "):
|
if typed.startswith("echo "):
|
||||||
print(typed[5:-1])
|
print(typed[5:-1])
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
if typed.startswith("echosplit "):
|
||||||
|
for part in typed[10:-1].split('|'):
|
||||||
|
sys.stdout.write(part)
|
||||||
|
sys.stdout.flush()
|
||||||
|
time.sleep(0.05)
|
||||||
if typed.startswith("double "):
|
if typed.startswith("double "):
|
||||||
print(typed[7:-1] + "\nAND " + typed[7:-1])
|
print(typed[7:-1] + "\nAND " + typed[7:-1])
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
@@ -764,6 +764,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
312,
|
||||||
/**/
|
/**/
|
||||||
311,
|
311,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user