blob: 9684bb933a147923e2e71552618b201b7b1636ec [file] [log] [blame]
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +02001#!/usr/bin/env python
Bram Moolenaard7ece102016-02-02 23:23:02 +01002#
3# Server that will accept connections from a Vim channel.
Bram Moolenaar77073442016-02-13 23:23:53 +01004# Used by test_channel.vim.
Bram Moolenaard7ece102016-02-02 23:23:02 +01005#
6# This requires Python 2.6 or later.
7
8from __future__ import print_function
9import json
10import socket
11import sys
Bram Moolenaar81661fb2016-02-18 22:23:34 +010012import time
Bram Moolenaard7ece102016-02-02 23:23:02 +010013import threading
14
15try:
16 # Python 3
17 import socketserver
18except ImportError:
19 # Python 2
20 import SocketServer as socketserver
21
Bram Moolenaard7ece102016-02-02 23:23:02 +010022class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
23
Bram Moolenaar0b39ec32020-05-17 22:33:53 +020024 def setup(self):
25 self.request.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
26
Bram Moolenaard7ece102016-02-02 23:23:02 +010027 def handle(self):
28 print("=== socket opened ===")
Bram Moolenaard7ece102016-02-02 23:23:02 +010029 while True:
30 try:
Bram Moolenaar608a8912016-02-03 22:39:51 +010031 received = self.request.recv(4096).decode('utf-8')
Bram Moolenaard7ece102016-02-02 23:23:02 +010032 except socket.error:
33 print("=== socket error ===")
34 break
35 except IOError:
36 print("=== socket closed ===")
37 break
Bram Moolenaar608a8912016-02-03 22:39:51 +010038 if received == '':
Bram Moolenaard7ece102016-02-02 23:23:02 +010039 print("=== socket closed ===")
40 break
Bram Moolenaara63cdb52016-03-20 18:24:45 +010041 print("received: {0}".format(received))
Bram Moolenaard7ece102016-02-02 23:23:02 +010042
Bram Moolenaare7bed622016-02-03 22:20:29 +010043 # We may receive two messages at once. Take the part up to the
Bram Moolenaarf1f07922016-08-26 17:58:53 +020044 # newline, which should be after the matching "]".
Bram Moolenaar608a8912016-02-03 22:39:51 +010045 todo = received
46 while todo != '':
Bram Moolenaarf1f07922016-08-26 17:58:53 +020047 splitidx = todo.find('\n')
Bram Moolenaare7bed622016-02-03 22:20:29 +010048 if splitidx < 0:
Bram Moolenaar608a8912016-02-03 22:39:51 +010049 used = todo
50 todo = ''
Bram Moolenaard7ece102016-02-02 23:23:02 +010051 else:
Bram Moolenaarf1f07922016-08-26 17:58:53 +020052 used = todo[:splitidx]
Bram Moolenaar608a8912016-02-03 22:39:51 +010053 todo = todo[splitidx + 1:]
54 if used != received:
Bram Moolenaara63cdb52016-03-20 18:24:45 +010055 print("using: {0}".format(used))
Bram Moolenaard7ece102016-02-02 23:23:02 +010056
Bram Moolenaare7bed622016-02-03 22:20:29 +010057 try:
Bram Moolenaar608a8912016-02-03 22:39:51 +010058 decoded = json.loads(used)
Bram Moolenaare7bed622016-02-03 22:20:29 +010059 except ValueError:
60 print("json decoding failed")
61 decoded = [-1, '']
Bram Moolenaard7ece102016-02-02 23:23:02 +010062
Bram Moolenaare7bed622016-02-03 22:20:29 +010063 # Send a response if the sequence number is positive.
64 if decoded[0] >= 0:
65 if decoded[1] == 'hello!':
66 # simply send back a string
67 response = "got it"
Bram Moolenaar5d7ead32018-02-27 17:17:42 +010068 elif decoded[1] == 'malformed1':
69 cmd = '["ex",":"]wrong!["ex","smi"]'
70 print("sending: {0}".format(cmd))
71 self.request.sendall(cmd.encode('utf-8'))
72 response = "ok"
73 # Need to wait for Vim to give up, otherwise it
74 # sometimes fails on OS X.
75 time.sleep(0.2)
76 elif decoded[1] == 'malformed2':
77 cmd = '"unterminated string'
78 print("sending: {0}".format(cmd))
79 self.request.sendall(cmd.encode('utf-8'))
80 response = "ok"
81 # Need to wait for Vim to give up, otherwise the double
82 # quote in the "ok" response terminates the string.
83 time.sleep(0.2)
84 elif decoded[1] == 'malformed3':
85 cmd = '["ex","missing ]"'
86 print("sending: {0}".format(cmd))
87 self.request.sendall(cmd.encode('utf-8'))
88 response = "ok"
89 # Need to wait for Vim to give up, otherwise the ]
90 # in the "ok" response terminates the list.
91 time.sleep(0.2)
92 elif decoded[1] == 'split':
93 cmd = '["ex","let '
94 print("sending: {0}".format(cmd))
95 self.request.sendall(cmd.encode('utf-8'))
96 time.sleep(0.01)
97 cmd = 'g:split = 123"]'
98 print("sending: {0}".format(cmd))
99 self.request.sendall(cmd.encode('utf-8'))
100 response = "ok"
Bram Moolenaard6547fc2016-03-03 19:35:02 +0100101 elif decoded[1].startswith("echo "):
102 # send back the argument
103 response = decoded[1][5:]
Bram Moolenaare7bed622016-02-03 22:20:29 +0100104 elif decoded[1] == 'make change':
Bram Moolenaar66624ff2016-02-03 23:59:43 +0100105 # Send two ex commands at the same time, before
106 # replying to the request.
Bram Moolenaare7bed622016-02-03 22:20:29 +0100107 cmd = '["ex","call append(\\"$\\",\\"added1\\")"]'
108 cmd += '["ex","call append(\\"$\\",\\"added2\\")"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100109 print("sending: {0}".format(cmd))
Bram Moolenaar3b05b132016-02-03 23:25:07 +0100110 self.request.sendall(cmd.encode('utf-8'))
Bram Moolenaare7bed622016-02-03 22:20:29 +0100111 response = "ok"
Bram Moolenaarb836f632021-07-01 22:11:28 +0200112 elif decoded[1] == 'echoerr':
113 cmd = '["ex","echoerr \\\"this is an error\\\""]'
114 print("sending: {0}".format(cmd))
115 self.request.sendall(cmd.encode('utf-8'))
116 response = "ok"
Bram Moolenaarc4dcd602016-03-26 22:56:46 +0100117 elif decoded[1] == 'bad command':
118 cmd = '["ex","foo bar"]'
119 print("sending: {0}".format(cmd))
120 self.request.sendall(cmd.encode('utf-8'))
121 response = "ok"
Bram Moolenaarf4160862016-02-05 23:09:12 +0100122 elif decoded[1] == 'do normal':
123 # Send a normal command.
124 cmd = '["normal","G$s more\u001b"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100125 print("sending: {0}".format(cmd))
Bram Moolenaarf4160862016-02-05 23:09:12 +0100126 self.request.sendall(cmd.encode('utf-8'))
127 response = "ok"
Bram Moolenaare7bed622016-02-03 22:20:29 +0100128 elif decoded[1] == 'eval-works':
129 # Send an eval request. We ignore the response.
Bram Moolenaarece61b02016-02-20 21:39:05 +0100130 cmd = '["expr","\\"foo\\" . 123", -1]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100131 print("sending: {0}".format(cmd))
Bram Moolenaar3b05b132016-02-03 23:25:07 +0100132 self.request.sendall(cmd.encode('utf-8'))
Bram Moolenaare7bed622016-02-03 22:20:29 +0100133 response = "ok"
Bram Moolenaarfa8b2e12016-03-26 22:19:27 +0100134 elif decoded[1] == 'eval-special':
135 # Send an eval request. We ignore the response.
136 cmd = '["expr","\\"foo\x7f\x10\x01bar\\"", -2]'
137 print("sending: {0}".format(cmd))
138 self.request.sendall(cmd.encode('utf-8'))
139 response = "ok"
140 elif decoded[1] == 'eval-getline':
141 # Send an eval request. We ignore the response.
142 cmd = '["expr","getline(3)", -3]'
143 print("sending: {0}".format(cmd))
144 self.request.sendall(cmd.encode('utf-8'))
145 response = "ok"
Bram Moolenaare7bed622016-02-03 22:20:29 +0100146 elif decoded[1] == 'eval-fails':
147 # Send an eval request that will fail.
Bram Moolenaarfa8b2e12016-03-26 22:19:27 +0100148 cmd = '["expr","xxx", -4]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100149 print("sending: {0}".format(cmd))
Bram Moolenaar3b05b132016-02-03 23:25:07 +0100150 self.request.sendall(cmd.encode('utf-8'))
Bram Moolenaare7bed622016-02-03 22:20:29 +0100151 response = "ok"
Bram Moolenaar55fab432016-02-07 16:53:13 +0100152 elif decoded[1] == 'eval-error':
153 # Send an eval request that works but the result can't
154 # be encoded.
Bram Moolenaarfa8b2e12016-03-26 22:19:27 +0100155 cmd = '["expr","function(\\"tr\\")", -5]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100156 print("sending: {0}".format(cmd))
Bram Moolenaar55fab432016-02-07 16:53:13 +0100157 self.request.sendall(cmd.encode('utf-8'))
158 response = "ok"
Bram Moolenaar66624ff2016-02-03 23:59:43 +0100159 elif decoded[1] == 'eval-bad':
160 # Send an eval request missing the third argument.
Bram Moolenaarece61b02016-02-20 21:39:05 +0100161 cmd = '["expr","xxx"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100162 print("sending: {0}".format(cmd))
Bram Moolenaar66624ff2016-02-03 23:59:43 +0100163 self.request.sendall(cmd.encode('utf-8'))
164 response = "ok"
Bram Moolenaarf4160862016-02-05 23:09:12 +0100165 elif decoded[1] == 'an expr':
166 # Send an expr request.
167 cmd = '["expr","setline(\\"$\\", [\\"one\\",\\"two\\",\\"three\\"])"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100168 print("sending: {0}".format(cmd))
Bram Moolenaarf4160862016-02-05 23:09:12 +0100169 self.request.sendall(cmd.encode('utf-8'))
170 response = "ok"
Bram Moolenaarece61b02016-02-20 21:39:05 +0100171 elif decoded[1] == 'call-func':
172 cmd = '["call","MyFunction",[1,2,3], 0]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100173 print("sending: {0}".format(cmd))
Bram Moolenaarece61b02016-02-20 21:39:05 +0100174 self.request.sendall(cmd.encode('utf-8'))
175 response = "ok"
Bram Moolenaarf4160862016-02-05 23:09:12 +0100176 elif decoded[1] == 'redraw':
177 cmd = '["redraw",""]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100178 print("sending: {0}".format(cmd))
Bram Moolenaarf4160862016-02-05 23:09:12 +0100179 self.request.sendall(cmd.encode('utf-8'))
180 response = "ok"
181 elif decoded[1] == 'redraw!':
182 cmd = '["redraw","force"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100183 print("sending: {0}".format(cmd))
Bram Moolenaarf4160862016-02-05 23:09:12 +0100184 self.request.sendall(cmd.encode('utf-8'))
185 response = "ok"
Bram Moolenaar6076fe12016-02-05 22:49:56 +0100186 elif decoded[1] == 'empty-request':
187 cmd = '[]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100188 print("sending: {0}".format(cmd))
Bram Moolenaar6076fe12016-02-05 22:49:56 +0100189 self.request.sendall(cmd.encode('utf-8'))
190 response = "ok"
Bram Moolenaare7bed622016-02-03 22:20:29 +0100191 elif decoded[1] == 'eval-result':
192 # Send back the last received eval result.
193 response = last_eval
Bram Moolenaarf6157282016-02-10 21:07:14 +0100194 elif decoded[1] == 'call me':
195 cmd = '[0,"we called you"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100196 print("sending: {0}".format(cmd))
Bram Moolenaarf6157282016-02-10 21:07:14 +0100197 self.request.sendall(cmd.encode('utf-8'))
198 response = "ok"
199 elif decoded[1] == 'call me again':
200 cmd = '[0,"we did call you"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100201 print("sending: {0}".format(cmd))
Bram Moolenaarf6157282016-02-10 21:07:14 +0100202 self.request.sendall(cmd.encode('utf-8'))
203 response = ""
Bram Moolenaar5983ad02016-03-05 20:54:36 +0100204 elif decoded[1] == 'send zero':
205 cmd = '[0,"zero index"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100206 print("sending: {0}".format(cmd))
Bram Moolenaar5983ad02016-03-05 20:54:36 +0100207 self.request.sendall(cmd.encode('utf-8'))
208 response = "sent zero"
Bram Moolenaar4e221c92016-02-23 13:20:22 +0100209 elif decoded[1] == 'close me':
210 print("closing")
211 self.request.close()
212 response = ""
Bram Moolenaarece61b02016-02-20 21:39:05 +0100213 elif decoded[1] == 'wait a bit':
214 time.sleep(0.2)
215 response = "waited"
Bram Moolenaare7bed622016-02-03 22:20:29 +0100216 elif decoded[1] == '!quit!':
217 # we're done
Bram Moolenaarb3e2f002016-02-04 00:11:37 +0100218 self.server.shutdown()
Bram Moolenaarb92abad2016-02-08 22:37:24 +0100219 return
Bram Moolenaare7bed622016-02-03 22:20:29 +0100220 elif decoded[1] == '!crash!':
221 # Crash!
222 42 / 0
223 else:
224 response = "what?"
225
Bram Moolenaarf6157282016-02-10 21:07:14 +0100226 if response == "":
227 print("no response")
228 else:
229 encoded = json.dumps([decoded[0], response])
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100230 print("sending: {0}".format(encoded))
Bram Moolenaarf6157282016-02-10 21:07:14 +0100231 self.request.sendall(encoded.encode('utf-8'))
Bram Moolenaare7bed622016-02-03 22:20:29 +0100232
233 # Negative numbers are used for "eval" responses.
234 elif decoded[0] < 0:
235 last_eval = decoded
Bram Moolenaarfcb1e3d2016-02-03 21:32:46 +0100236
Bram Moolenaard7ece102016-02-02 23:23:02 +0100237class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
238 pass
239
Bram Moolenaar81661fb2016-02-18 22:23:34 +0100240def writePortInFile(port):
241 # Write the port number in Xportnr, so that the test knows it.
242 f = open("Xportnr", "w")
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100243 f.write("{0}".format(port))
Bram Moolenaar81661fb2016-02-18 22:23:34 +0100244 f.close()
245
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200246def main(host, port, server_class=ThreadedTCPServer):
Bram Moolenaar81661fb2016-02-18 22:23:34 +0100247 # Wait half a second before opening the port to test waittime in ch_open().
248 # We do want to get the port number, get that first. We cannot open the
249 # socket, guess a port is free.
250 if len(sys.argv) >= 2 and sys.argv[1] == 'delay':
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200251 port = 13684
252 writePortInFile(port)
Bram Moolenaar81661fb2016-02-18 22:23:34 +0100253
254 print("Wait for it...")
255 time.sleep(0.5)
256
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200257 server = server_class((host, port), ThreadedTCPRequestHandler)
258 ip, port = server.server_address[0:2]
Bram Moolenaard7ece102016-02-02 23:23:02 +0100259
Bram Moolenaar6076fe12016-02-05 22:49:56 +0100260 # Start a thread with the server. That thread will then start a new thread
261 # for each connection.
Bram Moolenaard7ece102016-02-02 23:23:02 +0100262 server_thread = threading.Thread(target=server.serve_forever)
Bram Moolenaard7ece102016-02-02 23:23:02 +0100263 server_thread.start()
264
Bram Moolenaar81661fb2016-02-18 22:23:34 +0100265 writePortInFile(port)
Bram Moolenaard7ece102016-02-02 23:23:02 +0100266
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100267 print("Listening on port {0}".format(port))
Bram Moolenaarb3e2f002016-02-04 00:11:37 +0100268
269 # Main thread terminates, but the server continues running
270 # until server.shutdown() is called.
Bram Moolenaarddbe7d22016-02-20 18:26:48 +0100271 try:
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200272 while server_thread.is_alive():
Bram Moolenaarddbe7d22016-02-20 18:26:48 +0100273 server_thread.join(1)
274 except (KeyboardInterrupt, SystemExit):
275 server.shutdown()
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200276
277if __name__ == "__main__":
278 main("localhost", 0)