blob: 3133c2f408e7291ebd0ee54f30f59600019daa50 [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
27thesocket = None
28
29class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
30
31 def handle(self):
32 print("=== socket opened ===")
33 global thesocket
34 thesocket = self.request
35 while True:
36 try:
Bram Moolenaar608a8912016-02-03 22:39:51 +010037 received = self.request.recv(4096).decode('utf-8')
Bram Moolenaard7ece102016-02-02 23:23:02 +010038 except socket.error:
39 print("=== socket error ===")
40 break
41 except IOError:
42 print("=== socket closed ===")
43 break
Bram Moolenaar608a8912016-02-03 22:39:51 +010044 if received == '':
Bram Moolenaard7ece102016-02-02 23:23:02 +010045 print("=== socket closed ===")
46 break
Bram Moolenaar608a8912016-02-03 22:39:51 +010047 print("received: {}".format(received))
Bram Moolenaard7ece102016-02-02 23:23:02 +010048
Bram Moolenaare7bed622016-02-03 22:20:29 +010049 # We may receive two messages at once. Take the part up to the
50 # matching "]" (recognized by finding "][").
Bram Moolenaar608a8912016-02-03 22:39:51 +010051 todo = received
52 while todo != '':
53 splitidx = todo.find('][')
Bram Moolenaare7bed622016-02-03 22:20:29 +010054 if splitidx < 0:
Bram Moolenaar608a8912016-02-03 22:39:51 +010055 used = todo
56 todo = ''
Bram Moolenaard7ece102016-02-02 23:23:02 +010057 else:
Bram Moolenaar608a8912016-02-03 22:39:51 +010058 used = todo[:splitidx + 1]
59 todo = todo[splitidx + 1:]
60 if used != received:
61 print("using: {}".format(used))
Bram Moolenaard7ece102016-02-02 23:23:02 +010062
Bram Moolenaare7bed622016-02-03 22:20:29 +010063 try:
Bram Moolenaar608a8912016-02-03 22:39:51 +010064 decoded = json.loads(used)
Bram Moolenaare7bed622016-02-03 22:20:29 +010065 except ValueError:
66 print("json decoding failed")
67 decoded = [-1, '']
Bram Moolenaard7ece102016-02-02 23:23:02 +010068
Bram Moolenaare7bed622016-02-03 22:20:29 +010069 # Send a response if the sequence number is positive.
70 if decoded[0] >= 0:
71 if decoded[1] == 'hello!':
72 # simply send back a string
73 response = "got it"
74 elif decoded[1] == 'make change':
75 # Send two ex commands at the same time, before replying to
76 # the request.
77 cmd = '["ex","call append(\\"$\\",\\"added1\\")"]'
78 cmd += '["ex","call append(\\"$\\",\\"added2\\")"]'
79 print("sending: {}".format(cmd))
80 thesocket.sendall(cmd.encode('utf-8'))
81 response = "ok"
82 elif decoded[1] == 'eval-works':
83 # Send an eval request. We ignore the response.
84 cmd = '["eval","\\"foo\\" . 123", -1]'
85 print("sending: {}".format(cmd))
86 thesocket.sendall(cmd.encode('utf-8'))
87 response = "ok"
88 elif decoded[1] == 'eval-fails':
89 # Send an eval request that will fail.
90 cmd = '["eval","xxx", -2]'
91 print("sending: {}".format(cmd))
92 thesocket.sendall(cmd.encode('utf-8'))
93 response = "ok"
94 elif decoded[1] == 'eval-result':
95 # Send back the last received eval result.
96 response = last_eval
97 elif decoded[1] == '!quit!':
98 # we're done
99 sys.exit(0)
100 elif decoded[1] == '!crash!':
101 # Crash!
102 42 / 0
103 else:
104 response = "what?"
105
106 encoded = json.dumps([decoded[0], response])
107 print("sending: {}".format(encoded))
108 thesocket.sendall(encoded.encode('utf-8'))
109
110 # Negative numbers are used for "eval" responses.
111 elif decoded[0] < 0:
112 last_eval = decoded
Bram Moolenaarfcb1e3d2016-02-03 21:32:46 +0100113
Bram Moolenaard7ece102016-02-02 23:23:02 +0100114 thesocket = None
115
116class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
117 pass
118
119if __name__ == "__main__":
120 HOST, PORT = "localhost", 0
121
122 server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
123 ip, port = server.server_address
124
125 # Start a thread with the server -- that thread will then start one
126 # more thread for each request
127 server_thread = threading.Thread(target=server.serve_forever)
128
129 # Exit the server thread when the main thread terminates
130 server_thread.daemon = True
131 server_thread.start()
132
133 # Write the port number in Xportnr, so that the test knows it.
134 f = open("Xportnr", "w")
135 f.write("{}".format(port))
136 f.close()
137
138 # Block here
139 print("Listening on port {}".format(port))
140 server.serve_forever()