changeset 28528:6b1da12297e5 v8.2.4788

patch 8.2.4788: large payload for LSP message not tested Commit: https://github.com/vim/vim/commit/bac9a9e5c233dcf9cf734c61e4e4311fe57eccd1 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Tue Apr 19 10:25:13 2022 +0100 patch 8.2.4788: large payload for LSP message not tested Problem: Large payload for LSP message not tested. Solution: Add a test with a large LSP payload. (Yegappan Lakshmanan, closes #10223)
author Bram Moolenaar <Bram@vim.org>
date Tue, 19 Apr 2022 11:30:05 +0200
parents 1386340aaedc
children 5f29eb9fcbc8
files src/channel.c src/testdir/test_channel.vim src/testdir/test_channel_lsp.py src/version.c
diffstat 4 files changed, 46 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/src/channel.c
+++ b/src/channel.c
@@ -2031,6 +2031,8 @@ channel_consume(channel_T *channel, ch_p
  * Collapses the first and second buffer for "channel"/"part".
  * Returns FAIL if that is not possible.
  * When "want_nl" is TRUE collapse more buffers until a NL is found.
+ * When the channel part mode is "lsp", collapse all the buffers as the http
+ * header and the JSON content can be present in multiple buffers.
  */
     int
 channel_collapse(channel_T *channel, ch_part_T part, int want_nl)
--- a/src/testdir/test_channel.vim
+++ b/src/testdir/test_channel.vim
@@ -2466,7 +2466,7 @@ func LspOtCb(chan, msg)
 endfunc
 
 func LspTests(port)
-  " call ch_logfile('Xlsprpc.log', 'w')
+  " call ch_logfile('Xlspclient.log', 'w')
   let ch = ch_open(s:localhost .. a:port, #{mode: 'lsp', callback: 'LspCb'})
   if ch_status(ch) == "fail"
     call assert_report("Can't open the lsp channel")
@@ -2620,6 +2620,16 @@ func LspTests(port)
   " send a ping to make sure communication still works
   call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
 
+  " Test for a large payload
+  let content = repeat('abcdef', 11000)
+  let resp = ch_evalexpr(ch, #{method: 'large-payload',
+        \ params: #{text: content}})
+  call assert_equal(#{jsonrpc: '2.0', id: 26, result:
+        \ #{method: 'large-payload', jsonrpc: '2.0', id: 26,
+        \ params: #{text: content}}}, resp)
+  " send a ping to make sure communication still works
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
+
   " Test for invoking an unsupported method
   let resp = ch_evalexpr(ch, #{method: 'xyz', params: {}}, #{timeout: 200})
   call assert_equal({}, resp)
--- a/src/testdir/test_channel_lsp.py
+++ b/src/testdir/test_channel_lsp.py
@@ -24,6 +24,11 @@ class ThreadedTCPRequestHandler(socketse
     def setup(self):
         self.request.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
 
+    def debuglog(self, msg):
+        if self.debug:
+            with open("Xlspserver.log", "a") as myfile:
+                myfile.write(msg)
+
     def send_lsp_msg(self, msgid, resp_dict):
         v = {'jsonrpc': '2.0', 'result': resp_dict}
         if msgid != -1:
@@ -34,8 +39,7 @@ class ThreadedTCPRequestHandler(socketse
         resp += "\r\n"
         resp += s
         if self.debug:
-            with open("Xlspdebug.log", "a") as myfile:
-                myfile.write("\n=> send\n" + resp)
+            self.debuglog("SEND: ({0} bytes) '{1}'\n".format(len(resp), resp))
         self.request.sendall(resp.encode('utf-8'))
 
     def send_wrong_payload(self):
@@ -136,6 +140,10 @@ class ThreadedTCPRequestHandler(socketse
         time.sleep(0.2)
         self.send_lsp_msg(-1, 'wrong-payload')
 
+    def do_large_payload(self, payload):
+        # test for sending a large (> 64K) payload
+        self.send_lsp_msg(payload['id'], payload)
+
     def do_rpc_resp_incorrect_id(self, payload):
         self.send_lsp_msg(-1, 'rpc-resp-incorrect-id-1')
         self.send_lsp_msg(-1, 'rpc-resp-incorrect-id-2')
@@ -185,8 +193,6 @@ class ThreadedTCPRequestHandler(socketse
     def process_msg(self, msg):
         try:
             decoded = json.loads(msg)
-            print("Decoded:")
-            print(str(decoded))
             if 'method' in decoded:
                 test_map = {
                         'ping': self.do_ping,
@@ -194,6 +200,7 @@ class ThreadedTCPRequestHandler(socketse
                         'simple-rpc': self.do_simple_rpc,
                         'rpc-with-notif': self.do_rpc_with_notif,
                         'wrong-payload': self.do_wrong_payload,
+                        'large-payload': self.do_large_payload,
                         'rpc-resp-incorrect-id': self.do_rpc_resp_incorrect_id,
                         'simple-notif': self.do_simple_notif,
                         'multi-notif': self.do_multi_notif,
@@ -211,28 +218,40 @@ class ThreadedTCPRequestHandler(socketse
                 if decoded['method'] in test_map:
                     test_map[decoded['method']](decoded)
                 else:
-                    print("Error: Unsupported method: " + decoded['method'])
+                    self.debuglog("Error: Unsupported method - " + decoded['method'] + "\n")
             else:
-                print("Error: 'method' field is not found")
+                self.debuglog("Error: 'method' field is not found\n")
 
         except ValueError:
-            print("json decoding failed")
+            self.debuglog("Error: json decoding failed\n")
 
     def process_msgs(self, msgbuf):
         while True:
             sidx = msgbuf.find('Content-Length: ')
             if sidx == -1:
+                # partial message received
                 return msgbuf
             sidx += 16
             eidx = msgbuf.find('\r\n')
             if eidx == -1:
+                # partial message received
                 return msgbuf
             msglen = int(msgbuf[sidx:eidx])
 
             hdrend = msgbuf.find('\r\n\r\n')
             if hdrend == -1:
+                # partial message received
                 return msgbuf
 
+            if msglen > len(msgbuf[hdrend + 4:]):
+                if self.debug:
+                    self.debuglog("Partial message ({0} bytes)\n".format(len(msgbuf)))
+                # partial message received
+                return msgbuf
+
+            if self.debug:
+                self.debuglog("Complete message ({0} bytes) received\n".format(msglen))
+
             # Remove the header
             msgbuf = msgbuf[hdrend + 4:]
             payload = msgbuf[:msglen]
@@ -243,27 +262,25 @@ class ThreadedTCPRequestHandler(socketse
             msgbuf = msgbuf[msglen:]
 
     def handle(self):
-        print("=== socket opened ===")
         self.debug = False
+        self.debuglog("=== socket opened ===\n")
         msgbuf = ''
         while True:
             try:
                 received = self.request.recv(4096).decode('utf-8')
             except socket.error:
-                print("=== socket error ===")
+                self.debuglog("=== socket error ===\n")
                 break
             except IOError:
-                print("=== socket closed ===")
+                self.debuglog("=== socket closed ===\n")
                 break
             if received == '':
-                print("=== socket closed ===")
+                self.debuglog("=== socket closed ===\n")
                 break
-            print("\nReceived:\n{0}".format(received))
 
             # Write the received lines into the file for debugging
             if self.debug:
-                with open("Xlspdebug.log", "a") as myfile:
-                    myfile.write("\n<= recv\n" + received)
+                self.debuglog("RECV: ({0} bytes) '{1}'\n".format(len(received), received))
 
             # Can receive more than one line in a response or a partial line.
             # Accumulate all the received characters and process one line at
@@ -287,8 +304,6 @@ def main(host, port, server_class=Thread
     if len(sys.argv) >= 2 and sys.argv[1] == 'delay':
         port = 13684
         writePortInFile(port)
-
-        print("Wait for it...")
         time.sleep(0.5)
 
     server = server_class((host, port), ThreadedTCPRequestHandler)
@@ -301,8 +316,6 @@ def main(host, port, server_class=Thread
 
     writePortInFile(port)
 
-    print("Listening on port {0}".format(port))
-
     # Main thread terminates, but the server continues running
     # until server.shutdown() is called.
     try:
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4788,
+/**/
     4787,
 /**/
     4786,