summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2017-02-06 21:56:09 +0100
committerBram Moolenaar <Bram@vim.org>2017-02-06 21:56:09 +0100
commit88989cc381c764978f7d7c8e387f3efc21333b4b (patch)
tree6e21020d48604772c0e4ea94d690f9f569dc9557
parent544d3bc9f0e494cb712a33b61558b8e8e12b1e0b (diff)
downloadvim-88989cc381c764978f7d7c8e387f3efc21333b4b.zip
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.
-rw-r--r--src/channel.c46
-rw-r--r--src/structs.h8
-rw-r--r--src/testdir/test_channel.vim12
-rw-r--r--src/testdir/test_channel_pipe.py5
-rw-r--r--src/version.c2
5 files changed, 50 insertions, 23 deletions
diff --git a/src/channel.c b/src/channel.c
index bd31bf3cb..d2439a95d 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -1840,39 +1840,42 @@ channel_save(channel_T *channel, ch_part_T part, char_u *buf, int len,
return OK;
}
+/*
+ * Try to fill the buffer of "reader".
+ * Returns FALSE when nothing was added.
+ */
static int
channel_fill(js_read_T *reader)
{
channel_T *channel = (channel_T *)reader->js_cookie;
ch_part_T part = reader->js_cookie_arg;
char_u *next = channel_get(channel, part);
- int unused;
- int len;
+ int keeplen;
+ int addlen;
char_u *p;
if (next == NULL)
return FALSE;
- unused = reader->js_end - reader->js_buf - reader->js_used;
- if (unused > 0)
+ keeplen = reader->js_end - reader->js_buf;
+ if (keeplen > 0)
{
/* Prepend unused text. */
- len = (int)STRLEN(next);
- p = alloc(unused + len + 1);
+ addlen = (int)STRLEN(next);
+ p = alloc(keeplen + addlen + 1);
if (p == NULL)
{
vim_free(next);
return FALSE;
}
- mch_memmove(p, reader->js_buf + reader->js_used, unused);
- mch_memmove(p + unused, next, len + 1);
+ mch_memmove(p, reader->js_buf, keeplen);
+ mch_memmove(p + keeplen, next, addlen + 1);
vim_free(next);
next = p;
}
vim_free(reader->js_buf);
reader->js_buf = next;
- reader->js_used = 0;
return TRUE;
}
@@ -1952,16 +1955,20 @@ channel_parse_json(channel_T *channel, ch_part_T part)
}
if (status == OK)
- chanpart->ch_waiting = FALSE;
+ chanpart->ch_wait_len = 0;
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
- * 100 msec. */
- ch_log(channel, "Incomplete message - wait for more");
+ /* First time encountering incomplete message or after receiving
+ * more (but still incomplete): set a deadline of 100 msec. */
+ ch_logn(channel,
+ "Incomplete message (%d bytes) - wait 100 msec for more",
+ buflen);
reader.js_used = 0;
- chanpart->ch_waiting = TRUE;
+ chanpart->ch_wait_len = buflen;
#ifdef WIN32
chanpart->ch_deadline = GetTickCount() + 100L;
#else
@@ -1992,7 +1999,8 @@ channel_parse_json(channel_T *channel, ch_part_T part)
if (timeout)
{
status = FAIL;
- chanpart->ch_waiting = FALSE;
+ chanpart->ch_wait_len = 0;
+ ch_log(channel, "timed out");
}
else
{
@@ -2006,7 +2014,7 @@ channel_parse_json(channel_T *channel, ch_part_T part)
{
ch_error(channel, "Decoding failed - discarding input");
ret = FALSE;
- chanpart->ch_waiting = FALSE;
+ chanpart->ch_wait_len = 0;
}
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
* use the deadline for that. */
timeout = timeout_arg;
- if (chanpart->ch_waiting)
+ if (chanpart->ch_wait_len > 0)
{
#ifdef WIN32
timeout = chanpart->ch_deadline - GetTickCount() + 1;
@@ -3389,7 +3397,7 @@ channel_read_json_block(
{
/* Something went wrong, channel_parse_json() didn't
* discard message. Cancel waiting. */
- chanpart->ch_waiting = FALSE;
+ chanpart->ch_wait_len = 0;
timeout = timeout_arg;
}
else if (timeout > timeout_arg)
diff --git a/src/structs.h b/src/structs.h
index 45bd4a5ed..a53a12516 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1563,9 +1563,11 @@ typedef struct {
jsonq_T ch_json_head; /* header for circular json read queue */
int ch_block_id; /* ID that channel_read_json_block() is
waiting for */
- /* When ch_waiting is TRUE use ch_deadline to wait for incomplete message
- * to be complete. */
- int ch_waiting;
+ /* When ch_wait_len is non-zero use ch_deadline to wait for incomplete
+ * message to be complete. The value is the length of the incomplete
+ * message when the deadline was set. If it gets longer (something was
+ * received) the deadline is reset. */
+ size_t ch_wait_len;
#ifdef WIN32
DWORD ch_deadline;
#else
diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim
index 81de17c34..28793b576 100644
--- a/src/testdir/test_channel.vim
+++ b/src/testdir/test_channel.vim
@@ -1141,7 +1141,11 @@ func Test_out_cb()
let dict = {'thisis': '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
func dict.errHandler(chan, msg) dict
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 WaitFor('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
call job_stop(job)
endtry
diff --git a/src/testdir/test_channel_pipe.py b/src/testdir/test_channel_pipe.py
index 639cc6c26..a67a81a85 100644
--- a/src/testdir/test_channel_pipe.py
+++ b/src/testdir/test_channel_pipe.py
@@ -29,6 +29,11 @@ if __name__ == "__main__":
if typed.startswith("echo "):
print(typed[5:-1])
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 "):
print(typed[7:-1] + "\nAND " + typed[7:-1])
sys.stdout.flush()
diff --git a/src/version.c b/src/version.c
index 72d17994f..22e26698f 100644
--- a/src/version.c
+++ b/src/version.c
@@ -765,6 +765,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 312,
+/**/
311,
/**/
310,