]> granicus.if.org Git - vim/commitdiff
patch 8.0.0312: failure when a channel receives a split json message v8.0.0312
authorBram Moolenaar <Bram@vim.org>
Mon, 6 Feb 2017 20:56:09 +0000 (21:56 +0100)
committerBram Moolenaar <Bram@vim.org>
Mon, 6 Feb 2017 20:56:09 +0000 (21:56 +0100)
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.

src/channel.c
src/structs.h
src/testdir/test_channel.vim
src/testdir/test_channel_pipe.py
src/version.c

index bd31bf3cbad55d61026bc341f6d4eef9b0ce1024..d2439a95de874dba6b505759f5ceee271685f3da 100644 (file)
@@ -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)
index 45bd4a5edff8d7ae9a7e12fdb75611bc05313835..a53a1251630cc5b742881c73c1d30b879a3b1131 100644 (file)
@@ -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
index 81de17c34b6c05c937371b0d0529bf7b949664b8..28793b5762f5fc50448b30e2de23fac4bbd17d14 100644 (file)
@@ -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
index 639cc6c2669713a5bd41bc3f7a1406de6a7472e1..a67a81a85389b6406e830cf1182b7989b49cef53 100644 (file)
@@ -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()
index 72d17994fdc5bd3f809523ecaf20a013924d5a2d..22e26698fd50c67d6531b44a6d361aa8c8cebba9 100644 (file)
@@ -764,6 +764,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    312,
 /**/
     311,
 /**/