blob: 311fbc07c8072a6ccb0669c80232ed1b481e99a2 [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.
4# Run this server and then in Vim you can open the channel:
5# :let handle = ch_open('localhost:8765', 'json')
6#
7# Then Vim can send requests to the server:
8# :let response = ch_sendexpr(handle, 'hello!')
9#
Bram Moolenaard7ece102016-02-02 23:23:02 +010010# See ":help channel-demo" in Vim.
11#
12# This requires Python 2.6 or later.
13
14from __future__ import print_function
15import json
16import socket
17import sys
18import threading
19
20try:
21 # Python 3
22 import socketserver
23except ImportError:
24 # Python 2
25 import SocketServer as socketserver
26
Bram Moolenaard7ece102016-02-02 23:23:02 +010027class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
28
29 def handle(self):
30 print("=== socket opened ===")
Bram Moolenaard7ece102016-02-02 23:23:02 +010031 while True:
32 try:
Bram Moolenaar608a8912016-02-03 22:39:51 +010033 received = self.request.recv(4096).decode('utf-8')
Bram Moolenaard7ece102016-02-02 23:23:02 +010034 except socket.error:
35 print("=== socket error ===")
36 break
37 except IOError:
38 print("=== socket closed ===")
39 break
Bram Moolenaar608a8912016-02-03 22:39:51 +010040 if received == '':
Bram Moolenaard7ece102016-02-02 23:23:02 +010041 print("=== socket closed ===")
42 break
Bram Moolenaar608a8912016-02-03 22:39:51 +010043 print("received: {}".format(received))
Bram Moolenaard7ece102016-02-02 23:23:02 +010044
Bram Moolenaare7bed622016-02-03 22:20:29 +010045 # We may receive two messages at once. Take the part up to the
46 # matching "]" (recognized by finding "][").
Bram Moolenaar608a8912016-02-03 22:39:51 +010047 todo = received
48 while todo != '':
49 splitidx = todo.find('][')
Bram Moolenaare7bed622016-02-03 22:20:29 +010050 if splitidx < 0:
Bram Moolenaar608a8912016-02-03 22:39:51 +010051 used = todo
52 todo = ''
Bram Moolenaard7ece102016-02-02 23:23:02 +010053 else:
Bram Moolenaar608a8912016-02-03 22:39:51 +010054 used = todo[:splitidx + 1]
55 todo = todo[splitidx + 1:]
56 if used != received:
57 print("using: {}".format(used))
Bram Moolenaard7ece102016-02-02 23:23:02 +010058
Bram Moolenaare7bed622016-02-03 22:20:29 +010059 try:
Bram Moolenaar608a8912016-02-03 22:39:51 +010060 decoded = json.loads(used)
Bram Moolenaare7bed622016-02-03 22:20:29 +010061 except ValueError:
62 print("json decoding failed")
63 decoded = [-1, '']
Bram Moolenaard7ece102016-02-02 23:23:02 +010064
Bram Moolenaare7bed622016-02-03 22:20:29 +010065 # Send a response if the sequence number is positive.
66 if decoded[0] >= 0:
67 if decoded[1] == 'hello!':
68 # simply send back a string
69 response = "got it"
70 elif decoded[1] == 'make change':
Bram Moolenaar66624ff2016-02-03 23:59:43 +010071 # Send two ex commands at the same time, before
72 # replying to the request.
Bram Moolenaare7bed622016-02-03 22:20:29 +010073 cmd = '["ex","call append(\\"$\\",\\"added1\\")"]'
74 cmd += '["ex","call append(\\"$\\",\\"added2\\")"]'
75 print("sending: {}".format(cmd))
Bram Moolenaar3b05b132016-02-03 23:25:07 +010076 self.request.sendall(cmd.encode('utf-8'))
Bram Moolenaare7bed622016-02-03 22:20:29 +010077 response = "ok"
Bram Moolenaarf4160862016-02-05 23:09:12 +010078 elif decoded[1] == 'do normal':
79 # Send a normal command.
80 cmd = '["normal","G$s more\u001b"]'
81 print("sending: {}".format(cmd))
82 self.request.sendall(cmd.encode('utf-8'))
83 response = "ok"
Bram Moolenaare7bed622016-02-03 22:20:29 +010084 elif decoded[1] == 'eval-works':
85 # Send an eval request. We ignore the response.
86 cmd = '["eval","\\"foo\\" . 123", -1]'
87 print("sending: {}".format(cmd))
Bram Moolenaar3b05b132016-02-03 23:25:07 +010088 self.request.sendall(cmd.encode('utf-8'))
Bram Moolenaare7bed622016-02-03 22:20:29 +010089 response = "ok"
90 elif decoded[1] == 'eval-fails':
91 # Send an eval request that will fail.
92 cmd = '["eval","xxx", -2]'
93 print("sending: {}".format(cmd))
Bram Moolenaar3b05b132016-02-03 23:25:07 +010094 self.request.sendall(cmd.encode('utf-8'))
Bram Moolenaare7bed622016-02-03 22:20:29 +010095 response = "ok"
Bram Moolenaar55fab432016-02-07 16:53:13 +010096 elif decoded[1] == 'eval-error':
97 # Send an eval request that works but the result can't
98 # be encoded.
99 cmd = '["eval","function(\\"tr\\")", -3]'
100 print("sending: {}".format(cmd))
101 self.request.sendall(cmd.encode('utf-8'))
102 response = "ok"
Bram Moolenaar66624ff2016-02-03 23:59:43 +0100103 elif decoded[1] == 'eval-bad':
104 # Send an eval request missing the third argument.
105 cmd = '["eval","xxx"]'
106 print("sending: {}".format(cmd))
107 self.request.sendall(cmd.encode('utf-8'))
108 response = "ok"
Bram Moolenaarf4160862016-02-05 23:09:12 +0100109 elif decoded[1] == 'an expr':
110 # Send an expr request.
111 cmd = '["expr","setline(\\"$\\", [\\"one\\",\\"two\\",\\"three\\"])"]'
112 print("sending: {}".format(cmd))
113 self.request.sendall(cmd.encode('utf-8'))
114 response = "ok"
115 elif decoded[1] == 'redraw':
116 cmd = '["redraw",""]'
117 print("sending: {}".format(cmd))
118 self.request.sendall(cmd.encode('utf-8'))
119 response = "ok"
120 elif decoded[1] == 'redraw!':
121 cmd = '["redraw","force"]'
122 print("sending: {}".format(cmd))
123 self.request.sendall(cmd.encode('utf-8'))
124 response = "ok"
Bram Moolenaar6076fe12016-02-05 22:49:56 +0100125 elif decoded[1] == 'empty-request':
126 cmd = '[]'
127 print("sending: {}".format(cmd))
128 self.request.sendall(cmd.encode('utf-8'))
129 response = "ok"
Bram Moolenaare7bed622016-02-03 22:20:29 +0100130 elif decoded[1] == 'eval-result':
131 # Send back the last received eval result.
132 response = last_eval
Bram Moolenaarf6157282016-02-10 21:07:14 +0100133 elif decoded[1] == 'call me':
134 cmd = '[0,"we called you"]'
135 print("sending: {}".format(cmd))
136 self.request.sendall(cmd.encode('utf-8'))
137 response = "ok"
138 elif decoded[1] == 'call me again':
139 cmd = '[0,"we did call you"]'
140 print("sending: {}".format(cmd))
141 self.request.sendall(cmd.encode('utf-8'))
142 response = ""
Bram Moolenaare7bed622016-02-03 22:20:29 +0100143 elif decoded[1] == '!quit!':
144 # we're done
Bram Moolenaarb3e2f002016-02-04 00:11:37 +0100145 self.server.shutdown()
Bram Moolenaarb92abad2016-02-08 22:37:24 +0100146 return
Bram Moolenaare7bed622016-02-03 22:20:29 +0100147 elif decoded[1] == '!crash!':
148 # Crash!
149 42 / 0
150 else:
151 response = "what?"
152
Bram Moolenaarf6157282016-02-10 21:07:14 +0100153 if response == "":
154 print("no response")
155 else:
156 encoded = json.dumps([decoded[0], response])
157 print("sending: {}".format(encoded))
158 self.request.sendall(encoded.encode('utf-8'))
Bram Moolenaare7bed622016-02-03 22:20:29 +0100159
160 # Negative numbers are used for "eval" responses.
161 elif decoded[0] < 0:
162 last_eval = decoded
Bram Moolenaarfcb1e3d2016-02-03 21:32:46 +0100163
Bram Moolenaard7ece102016-02-02 23:23:02 +0100164class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
165 pass
166
167if __name__ == "__main__":
168 HOST, PORT = "localhost", 0
169
170 server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
171 ip, port = server.server_address
172
Bram Moolenaar6076fe12016-02-05 22:49:56 +0100173 # Start a thread with the server. That thread will then start a new thread
174 # for each connection.
Bram Moolenaard7ece102016-02-02 23:23:02 +0100175 server_thread = threading.Thread(target=server.serve_forever)
Bram Moolenaard7ece102016-02-02 23:23:02 +0100176 server_thread.start()
177
178 # Write the port number in Xportnr, so that the test knows it.
179 f = open("Xportnr", "w")
180 f.write("{}".format(port))
181 f.close()
182
Bram Moolenaard7ece102016-02-02 23:23:02 +0100183 print("Listening on port {}".format(port))
Bram Moolenaarb3e2f002016-02-04 00:11:37 +0100184
185 # Main thread terminates, but the server continues running
186 # until server.shutdown() is called.