# HG changeset patch # User Bram Moolenaar # Date 1650360605 -7200 # Node ID 6b1da12297e58cc0601bba63039e356e743368a0 # Parent 1386340aaedcb56b216ea0de7cbdf5c8910ff794 patch 8.2.4788: large payload for LSP message not tested Commit: https://github.com/vim/vim/commit/bac9a9e5c233dcf9cf734c61e4e4311fe57eccd1 Author: Yegappan Lakshmanan 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) diff --git a/src/channel.c b/src/channel.c --- 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) diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim --- 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) diff --git a/src/testdir/test_channel_lsp.py b/src/testdir/test_channel_lsp.py --- 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: diff --git a/src/version.c b/src/version.c --- 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,