blob: 6668752c4266ce05351ad94294bc0a288bc9e697 [file] [log] [blame]
Bram Moolenaard7ece102016-02-02 23:23:02 +01001#!/usr/bin/python
2#
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
24 def handle(self):
25 print("=== socket opened ===")
Bram Moolenaard7ece102016-02-02 23:23:02 +010026 while True:
27 try:
Bram Moolenaar608a8912016-02-03 22:39:51 +010028 received = self.request.recv(4096).decode('utf-8')
Bram Moolenaard7ece102016-02-02 23:23:02 +010029 except socket.error:
30 print("=== socket error ===")
31 break
32 except IOError:
33 print("=== socket closed ===")
34 break
Bram Moolenaar608a8912016-02-03 22:39:51 +010035 if received == '':
Bram Moolenaard7ece102016-02-02 23:23:02 +010036 print("=== socket closed ===")
37 break
Bram Moolenaara63cdb52016-03-20 18:24:45 +010038 print("received: {0}".format(received))
Bram Moolenaard7ece102016-02-02 23:23:02 +010039
Bram Moolenaare7bed622016-02-03 22:20:29 +010040 # We may receive two messages at once. Take the part up to the
Bram Moolenaarf1f07922016-08-26 17:58:53 +020041 # newline, which should be after the matching "]".
Bram Moolenaar608a8912016-02-03 22:39:51 +010042 todo = received
43 while todo != '':
Bram Moolenaarf1f07922016-08-26 17:58:53 +020044 splitidx = todo.find('\n')
Bram Moolenaare7bed622016-02-03 22:20:29 +010045 if splitidx < 0:
Bram Moolenaar608a8912016-02-03 22:39:51 +010046 used = todo
47 todo = ''
Bram Moolenaard7ece102016-02-02 23:23:02 +010048 else:
Bram Moolenaarf1f07922016-08-26 17:58:53 +020049 used = todo[:splitidx]
Bram Moolenaar608a8912016-02-03 22:39:51 +010050 todo = todo[splitidx + 1:]
51 if used != received:
Bram Moolenaara63cdb52016-03-20 18:24:45 +010052 print("using: {0}".format(used))
Bram Moolenaard7ece102016-02-02 23:23:02 +010053
Bram Moolenaare7bed622016-02-03 22:20:29 +010054 try:
Bram Moolenaar608a8912016-02-03 22:39:51 +010055 decoded = json.loads(used)
Bram Moolenaare7bed622016-02-03 22:20:29 +010056 except ValueError:
57 print("json decoding failed")
58 decoded = [-1, '']
Bram Moolenaard7ece102016-02-02 23:23:02 +010059
Bram Moolenaare7bed622016-02-03 22:20:29 +010060 # Send a response if the sequence number is positive.
61 if decoded[0] >= 0:
62 if decoded[1] == 'hello!':
63 # simply send back a string
64 response = "got it"
Bram Moolenaard6547fc2016-03-03 19:35:02 +010065 elif decoded[1].startswith("echo "):
66 # send back the argument
67 response = decoded[1][5:]
Bram Moolenaare7bed622016-02-03 22:20:29 +010068 elif decoded[1] == 'make change':
Bram Moolenaar66624ff2016-02-03 23:59:43 +010069 # Send two ex commands at the same time, before
70 # replying to the request.
Bram Moolenaare7bed622016-02-03 22:20:29 +010071 cmd = '["ex","call append(\\"$\\",\\"added1\\")"]'
72 cmd += '["ex","call append(\\"$\\",\\"added2\\")"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +010073 print("sending: {0}".format(cmd))
Bram Moolenaar3b05b132016-02-03 23:25:07 +010074 self.request.sendall(cmd.encode('utf-8'))
Bram Moolenaare7bed622016-02-03 22:20:29 +010075 response = "ok"
Bram Moolenaarc4dcd602016-03-26 22:56:46 +010076 elif decoded[1] == 'bad command':
77 cmd = '["ex","foo bar"]'
78 print("sending: {0}".format(cmd))
79 self.request.sendall(cmd.encode('utf-8'))
80 response = "ok"
Bram Moolenaarf4160862016-02-05 23:09:12 +010081 elif decoded[1] == 'do normal':
82 # Send a normal command.
83 cmd = '["normal","G$s more\u001b"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +010084 print("sending: {0}".format(cmd))
Bram Moolenaarf4160862016-02-05 23:09:12 +010085 self.request.sendall(cmd.encode('utf-8'))
86 response = "ok"
Bram Moolenaare7bed622016-02-03 22:20:29 +010087 elif decoded[1] == 'eval-works':
88 # Send an eval request. We ignore the response.
Bram Moolenaarece61b02016-02-20 21:39:05 +010089 cmd = '["expr","\\"foo\\" . 123", -1]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +010090 print("sending: {0}".format(cmd))
Bram Moolenaar3b05b132016-02-03 23:25:07 +010091 self.request.sendall(cmd.encode('utf-8'))
Bram Moolenaare7bed622016-02-03 22:20:29 +010092 response = "ok"
Bram Moolenaarfa8b2e12016-03-26 22:19:27 +010093 elif decoded[1] == 'eval-special':
94 # Send an eval request. We ignore the response.
95 cmd = '["expr","\\"foo\x7f\x10\x01bar\\"", -2]'
96 print("sending: {0}".format(cmd))
97 self.request.sendall(cmd.encode('utf-8'))
98 response = "ok"
99 elif decoded[1] == 'eval-getline':
100 # Send an eval request. We ignore the response.
101 cmd = '["expr","getline(3)", -3]'
102 print("sending: {0}".format(cmd))
103 self.request.sendall(cmd.encode('utf-8'))
104 response = "ok"
Bram Moolenaare7bed622016-02-03 22:20:29 +0100105 elif decoded[1] == 'eval-fails':
106 # Send an eval request that will fail.
Bram Moolenaarfa8b2e12016-03-26 22:19:27 +0100107 cmd = '["expr","xxx", -4]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100108 print("sending: {0}".format(cmd))
Bram Moolenaar3b05b132016-02-03 23:25:07 +0100109 self.request.sendall(cmd.encode('utf-8'))
Bram Moolenaare7bed622016-02-03 22:20:29 +0100110 response = "ok"
Bram Moolenaar55fab432016-02-07 16:53:13 +0100111 elif decoded[1] == 'eval-error':
112 # Send an eval request that works but the result can't
113 # be encoded.
Bram Moolenaarfa8b2e12016-03-26 22:19:27 +0100114 cmd = '["expr","function(\\"tr\\")", -5]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100115 print("sending: {0}".format(cmd))
Bram Moolenaar55fab432016-02-07 16:53:13 +0100116 self.request.sendall(cmd.encode('utf-8'))
117 response = "ok"
Bram Moolenaar66624ff2016-02-03 23:59:43 +0100118 elif decoded[1] == 'eval-bad':
119 # Send an eval request missing the third argument.
Bram Moolenaarece61b02016-02-20 21:39:05 +0100120 cmd = '["expr","xxx"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100121 print("sending: {0}".format(cmd))
Bram Moolenaar66624ff2016-02-03 23:59:43 +0100122 self.request.sendall(cmd.encode('utf-8'))
123 response = "ok"
Bram Moolenaarba61ac02016-03-20 16:40:37 +0100124 elif decoded[1] == 'malformed1':
Bram Moolenaarac74d5e2016-03-20 14:31:00 +0100125 cmd = '["ex",":"]wrong!["ex","smi"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100126 print("sending: {0}".format(cmd))
Bram Moolenaarac74d5e2016-03-20 14:31:00 +0100127 self.request.sendall(cmd.encode('utf-8'))
128 response = "ok"
Bram Moolenaar3fad98e2016-12-03 15:23:40 +0100129 # Need to wait for Vim to give up, otherwise it
130 # sometimes fails on OS X.
131 time.sleep(0.2)
Bram Moolenaarba61ac02016-03-20 16:40:37 +0100132 elif decoded[1] == 'malformed2':
133 cmd = '"unterminated string'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100134 print("sending: {0}".format(cmd))
Bram Moolenaarba61ac02016-03-20 16:40:37 +0100135 self.request.sendall(cmd.encode('utf-8'))
136 response = "ok"
137 # Need to wait for Vim to give up, otherwise the double
138 # quote in the "ok" response terminates the string.
139 time.sleep(0.2)
140 elif decoded[1] == 'malformed3':
141 cmd = '["ex","missing ]"'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100142 print("sending: {0}".format(cmd))
Bram Moolenaarba61ac02016-03-20 16:40:37 +0100143 self.request.sendall(cmd.encode('utf-8'))
144 response = "ok"
145 # Need to wait for Vim to give up, otherwise the ]
146 # in the "ok" response terminates the list.
147 time.sleep(0.2)
148 elif decoded[1] == 'split':
149 cmd = '["ex","let '
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100150 print("sending: {0}".format(cmd))
Bram Moolenaarba61ac02016-03-20 16:40:37 +0100151 self.request.sendall(cmd.encode('utf-8'))
152 time.sleep(0.01)
153 cmd = 'g:split = 123"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100154 print("sending: {0}".format(cmd))
Bram Moolenaarba61ac02016-03-20 16:40:37 +0100155 self.request.sendall(cmd.encode('utf-8'))
156 response = "ok"
Bram Moolenaarf4160862016-02-05 23:09:12 +0100157 elif decoded[1] == 'an expr':
158 # Send an expr request.
159 cmd = '["expr","setline(\\"$\\", [\\"one\\",\\"two\\",\\"three\\"])"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100160 print("sending: {0}".format(cmd))
Bram Moolenaarf4160862016-02-05 23:09:12 +0100161 self.request.sendall(cmd.encode('utf-8'))
162 response = "ok"
Bram Moolenaarece61b02016-02-20 21:39:05 +0100163 elif decoded[1] == 'call-func':
164 cmd = '["call","MyFunction",[1,2,3], 0]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100165 print("sending: {0}".format(cmd))
Bram Moolenaarece61b02016-02-20 21:39:05 +0100166 self.request.sendall(cmd.encode('utf-8'))
167 response = "ok"
Bram Moolenaarf4160862016-02-05 23:09:12 +0100168 elif decoded[1] == 'redraw':
169 cmd = '["redraw",""]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100170 print("sending: {0}".format(cmd))
Bram Moolenaarf4160862016-02-05 23:09:12 +0100171 self.request.sendall(cmd.encode('utf-8'))
172 response = "ok"
173 elif decoded[1] == 'redraw!':
174 cmd = '["redraw","force"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100175 print("sending: {0}".format(cmd))
Bram Moolenaarf4160862016-02-05 23:09:12 +0100176 self.request.sendall(cmd.encode('utf-8'))
177 response = "ok"
Bram Moolenaar6076fe12016-02-05 22:49:56 +0100178 elif decoded[1] == 'empty-request':
179 cmd = '[]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100180 print("sending: {0}".format(cmd))
Bram Moolenaar6076fe12016-02-05 22:49:56 +0100181 self.request.sendall(cmd.encode('utf-8'))
182 response = "ok"
Bram Moolenaare7bed622016-02-03 22:20:29 +0100183 elif decoded[1] == 'eval-result':
184 # Send back the last received eval result.
185 response = last_eval
Bram Moolenaarf6157282016-02-10 21:07:14 +0100186 elif decoded[1] == 'call me':
187 cmd = '[0,"we called you"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100188 print("sending: {0}".format(cmd))
Bram Moolenaarf6157282016-02-10 21:07:14 +0100189 self.request.sendall(cmd.encode('utf-8'))
190 response = "ok"
191 elif decoded[1] == 'call me again':
192 cmd = '[0,"we did call you"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100193 print("sending: {0}".format(cmd))
Bram Moolenaarf6157282016-02-10 21:07:14 +0100194 self.request.sendall(cmd.encode('utf-8'))
195 response = ""
Bram Moolenaar5983ad02016-03-05 20:54:36 +0100196 elif decoded[1] == 'send zero':
197 cmd = '[0,"zero index"]'
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100198 print("sending: {0}".format(cmd))
Bram Moolenaar5983ad02016-03-05 20:54:36 +0100199 self.request.sendall(cmd.encode('utf-8'))
200 response = "sent zero"
Bram Moolenaar4e221c92016-02-23 13:20:22 +0100201 elif decoded[1] == 'close me':
202 print("closing")
203 self.request.close()
204 response = ""
Bram Moolenaarece61b02016-02-20 21:39:05 +0100205 elif decoded[1] == 'wait a bit':
206 time.sleep(0.2)
207 response = "waited"
Bram Moolenaare7bed622016-02-03 22:20:29 +0100208 elif decoded[1] == '!quit!':
209 # we're done
Bram Moolenaarb3e2f002016-02-04 00:11:37 +0100210 self.server.shutdown()
Bram Moolenaarb92abad2016-02-08 22:37:24 +0100211 return
Bram Moolenaare7bed622016-02-03 22:20:29 +0100212 elif decoded[1] == '!crash!':
213 # Crash!
214 42 / 0
215 else:
216 response = "what?"
217
Bram Moolenaarf6157282016-02-10 21:07:14 +0100218 if response == "":
219 print("no response")
220 else:
221 encoded = json.dumps([decoded[0], response])
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100222 print("sending: {0}".format(encoded))
Bram Moolenaarf6157282016-02-10 21:07:14 +0100223 self.request.sendall(encoded.encode('utf-8'))
Bram Moolenaare7bed622016-02-03 22:20:29 +0100224
225 # Negative numbers are used for "eval" responses.
226 elif decoded[0] < 0:
227 last_eval = decoded
Bram Moolenaarfcb1e3d2016-02-03 21:32:46 +0100228
Bram Moolenaard7ece102016-02-02 23:23:02 +0100229class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
230 pass
231
Bram Moolenaar81661fb2016-02-18 22:23:34 +0100232def writePortInFile(port):
233 # Write the port number in Xportnr, so that the test knows it.
234 f = open("Xportnr", "w")
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100235 f.write("{0}".format(port))
Bram Moolenaar81661fb2016-02-18 22:23:34 +0100236 f.close()
237
Bram Moolenaard7ece102016-02-02 23:23:02 +0100238if __name__ == "__main__":
239 HOST, PORT = "localhost", 0
240
Bram Moolenaar81661fb2016-02-18 22:23:34 +0100241 # Wait half a second before opening the port to test waittime in ch_open().
242 # We do want to get the port number, get that first. We cannot open the
243 # socket, guess a port is free.
244 if len(sys.argv) >= 2 and sys.argv[1] == 'delay':
245 PORT = 13684
246 writePortInFile(PORT)
247
248 print("Wait for it...")
249 time.sleep(0.5)
250
Bram Moolenaard7ece102016-02-02 23:23:02 +0100251 server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
252 ip, port = server.server_address
253
Bram Moolenaar6076fe12016-02-05 22:49:56 +0100254 # Start a thread with the server. That thread will then start a new thread
255 # for each connection.
Bram Moolenaard7ece102016-02-02 23:23:02 +0100256 server_thread = threading.Thread(target=server.serve_forever)
Bram Moolenaard7ece102016-02-02 23:23:02 +0100257 server_thread.start()
258
Bram Moolenaar81661fb2016-02-18 22:23:34 +0100259 writePortInFile(port)
Bram Moolenaard7ece102016-02-02 23:23:02 +0100260
Bram Moolenaara63cdb52016-03-20 18:24:45 +0100261 print("Listening on port {0}".format(port))
Bram Moolenaarb3e2f002016-02-04 00:11:37 +0100262
263 # Main thread terminates, but the server continues running
264 # until server.shutdown() is called.
Bram Moolenaarddbe7d22016-02-20 18:26:48 +0100265 try:
266 while server_thread.isAlive():
267 server_thread.join(1)
268 except (KeyboardInterrupt, SystemExit):
269 server.shutdown()