blob: 40a2043cc028c19e74a19b04d5dae239af7f0df9 [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
133 elif decoded[1] == '!quit!':
134 # we're done
Bram Moolenaarb3e2f002016-02-04 00:11:37 +0100135 self.server.shutdown()
136 break
Bram Moolenaare7bed622016-02-03 22:20:29 +0100137 elif decoded[1] == '!crash!':
138 # Crash!
139 42 / 0
140 else:
141 response = "what?"
142
143 encoded = json.dumps([decoded[0], response])
144 print("sending: {}".format(encoded))
Bram Moolenaar3b05b132016-02-03 23:25:07 +0100145 self.request.sendall(encoded.encode('utf-8'))
Bram Moolenaare7bed622016-02-03 22:20:29 +0100146
147 # Negative numbers are used for "eval" responses.
148 elif decoded[0] < 0:
149 last_eval = decoded
Bram Moolenaarfcb1e3d2016-02-03 21:32:46 +0100150
Bram Moolenaard7ece102016-02-02 23:23:02 +0100151class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
152 pass
153
154if __name__ == "__main__":
155 HOST, PORT = "localhost", 0
156
157 server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
158 ip, port = server.server_address
159
Bram Moolenaar6076fe12016-02-05 22:49:56 +0100160 # Start a thread with the server. That thread will then start a new thread
161 # for each connection.
Bram Moolenaard7ece102016-02-02 23:23:02 +0100162 server_thread = threading.Thread(target=server.serve_forever)
Bram Moolenaard7ece102016-02-02 23:23:02 +0100163 server_thread.start()
164
165 # Write the port number in Xportnr, so that the test knows it.
166 f = open("Xportnr", "w")
167 f.write("{}".format(port))
168 f.close()
169
Bram Moolenaard7ece102016-02-02 23:23:02 +0100170 print("Listening on port {}".format(port))
Bram Moolenaarb3e2f002016-02-04 00:11:37 +0100171
172 # Main thread terminates, but the server continues running
173 # until server.shutdown() is called.