1
0
forked from aniani/vim

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)
This commit is contained in:
Yegappan Lakshmanan
2022-04-19 10:25:13 +01:00
committed by Bram Moolenaar
parent 9bd3ce22e3
commit bac9a9e5c2
4 changed files with 46 additions and 19 deletions

View File

@@ -2031,6 +2031,8 @@ channel_consume(channel_T *channel, ch_part_T part, int len)
* Collapses the first and second buffer for "channel"/"part". * Collapses the first and second buffer for "channel"/"part".
* Returns FAIL if that is not possible. * Returns FAIL if that is not possible.
* When "want_nl" is TRUE collapse more buffers until a NL is found. * 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 int
channel_collapse(channel_T *channel, ch_part_T part, int want_nl) channel_collapse(channel_T *channel, ch_part_T part, int want_nl)

View File

@@ -2466,7 +2466,7 @@ func LspOtCb(chan, msg)
endfunc endfunc
func LspTests(port) 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'}) let ch = ch_open(s:localhost .. a:port, #{mode: 'lsp', callback: 'LspCb'})
if ch_status(ch) == "fail" if ch_status(ch) == "fail"
call assert_report("Can't open the lsp channel") 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 " send a ping to make sure communication still works
call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result) 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 " Test for invoking an unsupported method
let resp = ch_evalexpr(ch, #{method: 'xyz', params: {}}, #{timeout: 200}) let resp = ch_evalexpr(ch, #{method: 'xyz', params: {}}, #{timeout: 200})
call assert_equal({}, resp) call assert_equal({}, resp)

View File

@@ -24,6 +24,11 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def setup(self): def setup(self):
self.request.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 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): def send_lsp_msg(self, msgid, resp_dict):
v = {'jsonrpc': '2.0', 'result': resp_dict} v = {'jsonrpc': '2.0', 'result': resp_dict}
if msgid != -1: if msgid != -1:
@@ -34,8 +39,7 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
resp += "\r\n" resp += "\r\n"
resp += s resp += s
if self.debug: if self.debug:
with open("Xlspdebug.log", "a") as myfile: self.debuglog("SEND: ({0} bytes) '{1}'\n".format(len(resp), resp))
myfile.write("\n=> send\n" + resp)
self.request.sendall(resp.encode('utf-8')) self.request.sendall(resp.encode('utf-8'))
def send_wrong_payload(self): def send_wrong_payload(self):
@@ -136,6 +140,10 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
time.sleep(0.2) time.sleep(0.2)
self.send_lsp_msg(-1, 'wrong-payload') 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): 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-1')
self.send_lsp_msg(-1, 'rpc-resp-incorrect-id-2') self.send_lsp_msg(-1, 'rpc-resp-incorrect-id-2')
@@ -185,8 +193,6 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def process_msg(self, msg): def process_msg(self, msg):
try: try:
decoded = json.loads(msg) decoded = json.loads(msg)
print("Decoded:")
print(str(decoded))
if 'method' in decoded: if 'method' in decoded:
test_map = { test_map = {
'ping': self.do_ping, 'ping': self.do_ping,
@@ -194,6 +200,7 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
'simple-rpc': self.do_simple_rpc, 'simple-rpc': self.do_simple_rpc,
'rpc-with-notif': self.do_rpc_with_notif, 'rpc-with-notif': self.do_rpc_with_notif,
'wrong-payload': self.do_wrong_payload, 'wrong-payload': self.do_wrong_payload,
'large-payload': self.do_large_payload,
'rpc-resp-incorrect-id': self.do_rpc_resp_incorrect_id, 'rpc-resp-incorrect-id': self.do_rpc_resp_incorrect_id,
'simple-notif': self.do_simple_notif, 'simple-notif': self.do_simple_notif,
'multi-notif': self.do_multi_notif, 'multi-notif': self.do_multi_notif,
@@ -211,28 +218,40 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
if decoded['method'] in test_map: if decoded['method'] in test_map:
test_map[decoded['method']](decoded) test_map[decoded['method']](decoded)
else: else:
print("Error: Unsupported method: " + decoded['method']) self.debuglog("Error: Unsupported method - " + decoded['method'] + "\n")
else: else:
print("Error: 'method' field is not found") self.debuglog("Error: 'method' field is not found\n")
except ValueError: except ValueError:
print("json decoding failed") self.debuglog("Error: json decoding failed\n")
def process_msgs(self, msgbuf): def process_msgs(self, msgbuf):
while True: while True:
sidx = msgbuf.find('Content-Length: ') sidx = msgbuf.find('Content-Length: ')
if sidx == -1: if sidx == -1:
# partial message received
return msgbuf return msgbuf
sidx += 16 sidx += 16
eidx = msgbuf.find('\r\n') eidx = msgbuf.find('\r\n')
if eidx == -1: if eidx == -1:
# partial message received
return msgbuf return msgbuf
msglen = int(msgbuf[sidx:eidx]) msglen = int(msgbuf[sidx:eidx])
hdrend = msgbuf.find('\r\n\r\n') hdrend = msgbuf.find('\r\n\r\n')
if hdrend == -1: if hdrend == -1:
# partial message received
return msgbuf 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 # Remove the header
msgbuf = msgbuf[hdrend + 4:] msgbuf = msgbuf[hdrend + 4:]
payload = msgbuf[:msglen] payload = msgbuf[:msglen]
@@ -243,27 +262,25 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
msgbuf = msgbuf[msglen:] msgbuf = msgbuf[msglen:]
def handle(self): def handle(self):
print("=== socket opened ===")
self.debug = False self.debug = False
self.debuglog("=== socket opened ===\n")
msgbuf = '' msgbuf = ''
while True: while True:
try: try:
received = self.request.recv(4096).decode('utf-8') received = self.request.recv(4096).decode('utf-8')
except socket.error: except socket.error:
print("=== socket error ===") self.debuglog("=== socket error ===\n")
break break
except IOError: except IOError:
print("=== socket closed ===") self.debuglog("=== socket closed ===\n")
break break
if received == '': if received == '':
print("=== socket closed ===") self.debuglog("=== socket closed ===\n")
break break
print("\nReceived:\n{0}".format(received))
# Write the received lines into the file for debugging # Write the received lines into the file for debugging
if self.debug: if self.debug:
with open("Xlspdebug.log", "a") as myfile: self.debuglog("RECV: ({0} bytes) '{1}'\n".format(len(received), received))
myfile.write("\n<= recv\n" + received)
# Can receive more than one line in a response or a partial line. # Can receive more than one line in a response or a partial line.
# Accumulate all the received characters and process one line at # Accumulate all the received characters and process one line at
@@ -287,8 +304,6 @@ def main(host, port, server_class=ThreadedTCPServer):
if len(sys.argv) >= 2 and sys.argv[1] == 'delay': if len(sys.argv) >= 2 and sys.argv[1] == 'delay':
port = 13684 port = 13684
writePortInFile(port) writePortInFile(port)
print("Wait for it...")
time.sleep(0.5) time.sleep(0.5)
server = server_class((host, port), ThreadedTCPRequestHandler) server = server_class((host, port), ThreadedTCPRequestHandler)
@@ -301,8 +316,6 @@ def main(host, port, server_class=ThreadedTCPServer):
writePortInFile(port) writePortInFile(port)
print("Listening on port {0}".format(port))
# Main thread terminates, but the server continues running # Main thread terminates, but the server continues running
# until server.shutdown() is called. # until server.shutdown() is called.
try: try:

View File

@@ -746,6 +746,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
4788,
/**/ /**/
4787, 4787,
/**/ /**/