blob: d8830c519f851696140c7fca224b55907727f5a9 [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 Moolenaar66624ff2016-02-03 23:59:43 +010096 elif decoded[1] == 'eval-bad':
97 # Send an eval request missing the third argument.
98 cmd = '["eval","xxx"]'
99 print("sending: {}".format(cmd))
100 self.request.sendall(cmd.encode('utf-8'))
101 response = "ok"
Bram Moolenaarf4160862016-02-05 23:09:12 +0100102 elif decoded[1] == 'an expr':
103 # Send an expr request.
104 cmd = '["expr","setline(\\"$\\", [\\"one\\",\\"two\\",\\"three\\"])"]'
105 print("sending: {}".format(cmd))
106 self.request.sendall(cmd.encode('utf-8'))
107 response = "ok"
108 elif decoded[1] == 'redraw':
109 cmd = '["redraw",""]'
110 print("sending: {}".format(cmd))
111 self.request.sendall(cmd.encode('utf-8'))
112 response = "ok"
113 elif decoded[1] == 'redraw!':
114 cmd = '["redraw","force"]'
115 print("sending: {}".format(cmd))
116 self.request.sendall(cmd.encode('utf-8'))
117 response = "ok"
Bram Moolenaar6076fe12016-02-05 22:49:56 +0100118 elif decoded[1] == 'empty-request':
119 cmd = '[]'
120 print("sending: {}".format(cmd))
121 self.request.sendall(cmd.encode('utf-8'))
122 response = "ok"
Bram Moolenaare7bed622016-02-03 22:20:29 +0100123 elif decoded[1] == 'eval-result':
124 # Send back the last received eval result.
125 response = last_eval
126 elif decoded[1] == '!quit!':
127 # we're done
Bram Moolenaarb3e2f002016-02-04 00:11:37 +0100128 self.server.shutdown()
129 break
Bram Moolenaare7bed622016-02-03 22:20:29 +0100130 elif decoded[1] == '!crash!':
131 # Crash!
132 42 / 0
133 else:
134 response = "what?"
135
136 encoded = json.dumps([decoded[0], response])
137 print("sending: {}".format(encoded))
Bram Moolenaar3b05b132016-02-03 23:25:07 +0100138 self.request.sendall(encoded.encode('utf-8'))
Bram Moolenaare7bed622016-02-03 22:20:29 +0100139
140 # Negative numbers are used for "eval" responses.
141 elif decoded[0] < 0:
142 last_eval = decoded
Bram Moolenaarfcb1e3d2016-02-03 21:32:46 +0100143
Bram Moolenaard7ece102016-02-02 23:23:02 +0100144class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
145 pass
146
147if __name__ == "__main__":
148 HOST, PORT = "localhost", 0
149
150 server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
151 ip, port = server.server_address
152
Bram Moolenaar6076fe12016-02-05 22:49:56 +0100153 # Start a thread with the server. That thread will then start a new thread
154 # for each connection.
Bram Moolenaard7ece102016-02-02 23:23:02 +0100155 server_thread = threading.Thread(target=server.serve_forever)
Bram Moolenaard7ece102016-02-02 23:23:02 +0100156 server_thread.start()
157
158 # Write the port number in Xportnr, so that the test knows it.
159 f = open("Xportnr", "w")
160 f.write("{}".format(port))
161 f.close()
162
Bram Moolenaard7ece102016-02-02 23:23:02 +0100163 print("Listening on port {}".format(port))
Bram Moolenaarb3e2f002016-02-04 00:11:37 +0100164
165 # Main thread terminates, but the server continues running
166 # until server.shutdown() is called.