To: vim_dev@googlegroups.com Subject: Patch 8.2.4788 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4788 Problem: Large payload for LSP message not tested. Solution: Add a test with a large LSP payload. (Yegappan Lakshmanan, closes #10223) Files: src/channel.c, src/testdir/test_channel.vim, src/testdir/test_channel_lsp.py *** ../vim-8.2.4787/src/channel.c 2022-04-18 14:07:42.538262252 +0100 --- src/channel.c 2022-04-19 10:21:27.853706364 +0100 *************** *** 2031,2036 **** --- 2031,2038 ---- * 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) *** ../vim-8.2.4787/src/testdir/test_channel.vim 2022-04-18 14:07:42.538262252 +0100 --- src/testdir/test_channel.vim 2022-04-19 10:21:27.853706364 +0100 *************** *** 2466,2472 **** endfunc func LspTests(port) ! " call ch_logfile('Xlsprpc.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") --- 2466,2472 ---- endfunc func LspTests(port) ! " 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,2625 **** --- 2620,2635 ---- " 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) *** ../vim-8.2.4787/src/testdir/test_channel_lsp.py 2022-04-18 14:07:42.538262252 +0100 --- src/testdir/test_channel_lsp.py 2022-04-19 10:21:27.853706364 +0100 *************** *** 24,29 **** --- 24,34 ---- 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,41 **** resp += "\r\n" resp += s if self.debug: ! with open("Xlspdebug.log", "a") as myfile: ! myfile.write("\n=> send\n" + resp) self.request.sendall(resp.encode('utf-8')) def send_wrong_payload(self): --- 39,45 ---- resp += "\r\n" resp += s if self.debug: ! self.debuglog("SEND: ({0} bytes) '{1}'\n".format(len(resp), resp)) self.request.sendall(resp.encode('utf-8')) def send_wrong_payload(self): *************** *** 136,141 **** --- 140,149 ---- 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,192 **** 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, --- 193,198 ---- *************** *** 194,199 **** --- 200,206 ---- '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,238 **** if decoded['method'] in test_map: test_map[decoded['method']](decoded) else: ! print("Error: Unsupported method: " + decoded['method']) else: ! print("Error: 'method' field is not found") except ValueError: ! print("json decoding failed") def process_msgs(self, msgbuf): while True: sidx = msgbuf.find('Content-Length: ') if sidx == -1: return msgbuf sidx += 16 eidx = msgbuf.find('\r\n') if eidx == -1: return msgbuf msglen = int(msgbuf[sidx:eidx]) hdrend = msgbuf.find('\r\n\r\n') if hdrend == -1: return msgbuf # Remove the header msgbuf = msgbuf[hdrend + 4:] payload = msgbuf[:msglen] --- 218,257 ---- if decoded['method'] in test_map: test_map[decoded['method']](decoded) else: ! self.debuglog("Error: Unsupported method - " + decoded['method'] + "\n") else: ! self.debuglog("Error: 'method' field is not found\n") except ValueError: ! 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,269 **** msgbuf = msgbuf[msglen:] def handle(self): - print("=== socket opened ===") self.debug = False msgbuf = '' while True: try: received = self.request.recv(4096).decode('utf-8') except socket.error: ! print("=== socket error ===") break except IOError: ! print("=== socket closed ===") break if received == '': ! print("=== socket closed ===") 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) # Can receive more than one line in a response or a partial line. # Accumulate all the received characters and process one line at --- 262,286 ---- msgbuf = msgbuf[msglen:] def handle(self): self.debug = False + self.debuglog("=== socket opened ===\n") msgbuf = '' while True: try: received = self.request.recv(4096).decode('utf-8') except socket.error: ! self.debuglog("=== socket error ===\n") break except IOError: ! self.debuglog("=== socket closed ===\n") break if received == '': ! self.debuglog("=== socket closed ===\n") break # Write the received lines into the file for debugging if self.debug: ! 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,294 **** 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) --- 304,309 ---- *************** *** 301,308 **** writePortInFile(port) - print("Listening on port {0}".format(port)) - # Main thread terminates, but the server continues running # until server.shutdown() is called. try: --- 316,321 ---- *** ../vim-8.2.4787/src/version.c 2022-04-18 21:53:59.090171080 +0100 --- src/version.c 2022-04-19 10:24:33.981420182 +0100 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 4788, /**/ -- How To Keep A Healthy Level Of Insanity: 1. At lunch time, sit in your parked car with sunglasses on and point a hair dryer at passing cars. See if they slow down. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///