diff options
author | Bram Moolenaar <Bram@vim.org> | 2017-02-06 21:56:09 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2017-02-06 21:56:09 +0100 |
commit | 88989cc381c764978f7d7c8e387f3efc21333b4b (patch) | |
tree | 6e21020d48604772c0e4ea94d690f9f569dc9557 | |
parent | 544d3bc9f0e494cb712a33b61558b8e8e12b1e0b (diff) | |
download | vim-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.c | 46 | ||||
-rw-r--r-- | src/structs.h | 8 | ||||
-rw-r--r-- | src/testdir/test_channel.vim | 12 | ||||
-rw-r--r-- | src/testdir/test_channel_pipe.py | 5 | ||||
-rw-r--r-- | src/version.c | 2 |
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, |