blob: 486ff4d7c6beeb416c85eab268840c18d025ca6e [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:
37 data = self.request.recv(4096).decode('utf-8')
38 except socket.error:
39 print("=== socket error ===")
40 break
41 except IOError:
42 print("=== socket closed ===")
43 break
44 if data == '':
45 print("=== socket closed ===")
46 break
47 print("received: {}".format(data))
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 "][").
51 while data != '':
52 splitidx = data.find('][')
53 if splitidx < 0:
54 todo = data
55 data = ''
Bram Moolenaard7ece102016-02-02 23:23:02 +010056 else:
Bram Moolenaare7bed622016-02-03 22:20:29 +010057 todo = data[:splitidx + 1]
58 data = data[splitidx + 1:]
59 print("using: {}".format(todo))
Bram Moolenaard7ece102016-02-02 23:23:02 +010060
Bram Moolenaare7bed622016-02-03 22:20:29 +010061 try:
62 decoded = json.loads(todo)
63 except ValueError:
64 print("json decoding failed")
65 decoded = [-1, '']
Bram Moolenaard7ece102016-02-02 23:23:02 +010066
Bram Moolenaare7bed622016-02-03 22:20:29 +010067 # Send a response if the sequence number is positive.
68 if decoded[0] >= 0:
69 if decoded[1] == 'hello!':
70 # simply send back a string
71 response = "got it"
72 elif decoded[1] == 'make change':
73 # Send two ex commands at the same time, before replying to
74 # the request.
75 cmd = '["ex","call append(\\"$\\",\\"added1\\")"]'
76 cmd += '["ex","call append(\\"$\\",\\"added2\\")"]'
77 print("sending: {}".format(cmd))
78 thesocket.sendall(cmd.encode('utf-8'))
79 response = "ok"
80 elif decoded[1] == 'eval-works':
81 # Send an eval request. We ignore the response.
82 cmd = '["eval","\\"foo\\" . 123", -1]'
83 print("sending: {}".format(cmd))
84 thesocket.sendall(cmd.encode('utf-8'))
85 response = "ok"
86 elif decoded[1] == 'eval-fails':
87 # Send an eval request that will fail.
88 cmd = '["eval","xxx", -2]'
89 print("sending: {}".format(cmd))
90 thesocket.sendall(cmd.encode('utf-8'))
91 response = "ok"
92 elif decoded[1] == 'eval-result':
93 # Send back the last received eval result.
94 response = last_eval
95 elif decoded[1] == '!quit!':
96 # we're done
97 sys.exit(0)
98 elif decoded[1] == '!crash!':
99 # Crash!
100 42 / 0
101 else:
102 response = "what?"
103
104 encoded = json.dumps([decoded[0], response])
105 print("sending: {}".format(encoded))
106 thesocket.sendall(encoded.encode('utf-8'))
107
108 # Negative numbers are used for "eval" responses.
109 elif decoded[0] < 0:
110 last_eval = decoded
Bram Moolenaarfcb1e3d2016-02-03 21:32:46 +0100111
Bram Moolenaard7ece102016-02-02 23:23:02 +0100112 thesocket = None
113
114class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
115 pass
116
117if __name__ == "__main__":
118 HOST, PORT = "localhost", 0
119
120 server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
121 ip, port = server.server_address
122
123 # Start a thread with the server -- that thread will then start one
124 # more thread for each request
125 server_thread = threading.Thread(target=server.serve_forever)
126
127 # Exit the server thread when the main thread terminates
128 server_thread.daemon = True
129 server_thread.start()
130
131 # Write the port number in Xportnr, so that the test knows it.
132 f = open("Xportnr", "w")
133 f.write("{}".format(port))
134 f.close()
135
136 # Block here
137 print("Listening on port {}".format(port))
138 server.serve_forever()