Skip to content

Commit 88989cc

Browse files
committed
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.
1 parent 544d3bc commit 88989cc

5 files changed

Lines changed: 50 additions & 23 deletions

File tree

src/channel.c

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1840,39 +1840,42 @@ channel_save(channel_T *channel, ch_part_T part, char_u *buf, int len,
18401840
return OK;
18411841
}
18421842

1843+
/*
1844+
* Try to fill the buffer of "reader".
1845+
* Returns FALSE when nothing was added.
1846+
*/
18431847
static int
18441848
channel_fill(js_read_T *reader)
18451849
{
18461850
channel_T *channel = (channel_T *)reader->js_cookie;
18471851
ch_part_T part = reader->js_cookie_arg;
18481852
char_u *next = channel_get(channel, part);
1849-
int unused;
1850-
int len;
1853+
int keeplen;
1854+
int addlen;
18511855
char_u *p;
18521856

18531857
if (next == NULL)
18541858
return FALSE;
18551859

1856-
unused = reader->js_end - reader->js_buf - reader->js_used;
1857-
if (unused > 0)
1860+
keeplen = reader->js_end - reader->js_buf;
1861+
if (keeplen > 0)
18581862
{
18591863
/* Prepend unused text. */
1860-
len = (int)STRLEN(next);
1861-
p = alloc(unused + len + 1);
1864+
addlen = (int)STRLEN(next);
1865+
p = alloc(keeplen + addlen + 1);
18621866
if (p == NULL)
18631867
{
18641868
vim_free(next);
18651869
return FALSE;
18661870
}
1867-
mch_memmove(p, reader->js_buf + reader->js_used, unused);
1868-
mch_memmove(p + unused, next, len + 1);
1871+
mch_memmove(p, reader->js_buf, keeplen);
1872+
mch_memmove(p + keeplen, next, addlen + 1);
18691873
vim_free(next);
18701874
next = p;
18711875
}
18721876

18731877
vim_free(reader->js_buf);
18741878
reader->js_buf = next;
1875-
reader->js_used = 0;
18761879
return TRUE;
18771880
}
18781881

@@ -1952,16 +1955,20 @@ channel_parse_json(channel_T *channel, ch_part_T part)
19521955
}
19531956

19541957
if (status == OK)
1955-
chanpart->ch_waiting = FALSE;
1958+
chanpart->ch_wait_len = 0;
19561959
else if (status == MAYBE)
19571960
{
1958-
if (!chanpart->ch_waiting)
1961+
size_t buflen = STRLEN(reader.js_buf);
1962+
1963+
if (chanpart->ch_wait_len < buflen)
19591964
{
1960-
/* First time encountering incomplete message, set a deadline of
1961-
* 100 msec. */
1962-
ch_log(channel, "Incomplete message - wait for more");
1965+
/* First time encountering incomplete message or after receiving
1966+
* more (but still incomplete): set a deadline of 100 msec. */
1967+
ch_logn(channel,
1968+
"Incomplete message (%d bytes) - wait 100 msec for more",
1969+
buflen);
19631970
reader.js_used = 0;
1964-
chanpart->ch_waiting = TRUE;
1971+
chanpart->ch_wait_len = buflen;
19651972
#ifdef WIN32
19661973
chanpart->ch_deadline = GetTickCount() + 100L;
19671974
#else
@@ -1992,7 +1999,8 @@ channel_parse_json(channel_T *channel, ch_part_T part)
19921999
if (timeout)
19932000
{
19942001
status = FAIL;
1995-
chanpart->ch_waiting = FALSE;
2002+
chanpart->ch_wait_len = 0;
2003+
ch_log(channel, "timed out");
19962004
}
19972005
else
19982006
{
@@ -2006,7 +2014,7 @@ channel_parse_json(channel_T *channel, ch_part_T part)
20062014
{
20072015
ch_error(channel, "Decoding failed - discarding input");
20082016
ret = FALSE;
2009-
chanpart->ch_waiting = FALSE;
2017+
chanpart->ch_wait_len = 0;
20102018
}
20112019
else if (reader.js_buf[reader.js_used] != NUL)
20122020
{
@@ -3369,7 +3377,7 @@ channel_read_json_block(
33693377
/* Wait for up to the timeout. If there was an incomplete message
33703378
* use the deadline for that. */
33713379
timeout = timeout_arg;
3372-
if (chanpart->ch_waiting)
3380+
if (chanpart->ch_wait_len > 0)
33733381
{
33743382
#ifdef WIN32
33753383
timeout = chanpart->ch_deadline - GetTickCount() + 1;
@@ -3389,7 +3397,7 @@ channel_read_json_block(
33893397
{
33903398
/* Something went wrong, channel_parse_json() didn't
33913399
* discard message. Cancel waiting. */
3392-
chanpart->ch_waiting = FALSE;
3400+
chanpart->ch_wait_len = 0;
33933401
timeout = timeout_arg;
33943402
}
33953403
else if (timeout > timeout_arg)

src/structs.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,9 +1563,11 @@ typedef struct {
15631563
jsonq_T ch_json_head; /* header for circular json read queue */
15641564
int ch_block_id; /* ID that channel_read_json_block() is
15651565
waiting for */
1566-
/* When ch_waiting is TRUE use ch_deadline to wait for incomplete message
1567-
* to be complete. */
1568-
int ch_waiting;
1566+
/* When ch_wait_len is non-zero use ch_deadline to wait for incomplete
1567+
* message to be complete. The value is the length of the incomplete
1568+
* message when the deadline was set. If it gets longer (something was
1569+
* received) the deadline is reset. */
1570+
size_t ch_wait_len;
15691571
#ifdef WIN32
15701572
DWORD ch_deadline;
15711573
#else

src/testdir/test_channel.vim

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1141,7 +1141,11 @@ func Test_out_cb()
11411141

11421142
let dict = {'thisis': 'dict: '}
11431143
func dict.outHandler(chan, msg) dict
1144-
let g:Ch_outmsg = self.thisis . a:msg
1144+
if type(a:msg) == v:t_string
1145+
let g:Ch_outmsg = self.thisis . a:msg
1146+
else
1147+
let g:Ch_outobj = a:msg
1148+
endif
11451149
endfunc
11461150
func dict.errHandler(chan, msg) dict
11471151
let g:Ch_errmsg = self.thisis . a:msg
@@ -1161,6 +1165,12 @@ func Test_out_cb()
11611165
call assert_equal("dict: hello", g:Ch_outmsg)
11621166
call WaitFor('g:Ch_errmsg != ""')
11631167
call assert_equal("dict: there", g:Ch_errmsg)
1168+
1169+
" Receive a json object split in pieces
1170+
unlet! g:Ch_outobj
1171+
call ch_sendraw(job, "echosplit [0, {\"one\": 1,| \"tw|o\": 2, \"three\": 3|}]\n")
1172+
call WaitFor('exists("g:Ch_outobj")')
1173+
call assert_equal({'one': 1, 'two': 2, 'three': 3}, g:Ch_outobj)
11641174
finally
11651175
call job_stop(job)
11661176
endtry

src/testdir/test_channel_pipe.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929
if typed.startswith("echo "):
3030
print(typed[5:-1])
3131
sys.stdout.flush()
32+
if typed.startswith("echosplit "):
33+
for part in typed[10:-1].split('|'):
34+
sys.stdout.write(part)
35+
sys.stdout.flush()
36+
time.sleep(0.05)
3237
if typed.startswith("double "):
3338
print(typed[7:-1] + "\nAND " + typed[7:-1])
3439
sys.stdout.flush()

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,8 @@ static char *(features[]) =
764764

765765
static int included_patches[] =
766766
{ /* Add new patch number below this line */
767+
/**/
768+
312,
767769
/**/
768770
311,
769771
/**/

0 commit comments

Comments
 (0)