0
0
mirror of https://github.com/vim/vim.git synced 2025-09-25 03:54:15 -04:00

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:
Bram Moolenaar
2017-02-06 21:56:09 +01:00
parent 544d3bc9f0
commit 88989cc381
5 changed files with 50 additions and 23 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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
let g:Ch_outmsg = self.thisis . a:msg if type(a:msg) == v:t_string
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

View File

@@ -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()

View File

@@ -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,
/**/ /**/