1#!/usr/local/bin/python3.8
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')
6#
7# Then Vim can send requests to the server:
8#  :let response = ch_sendexpr(handle, 'hello!')
9#
10# And you can control Vim by typing a JSON message here, e.g.:
11#   ["ex","echo 'hi there'"]
12#
13# There is no prompt, just type a line and press Enter.
14# To exit cleanly type "quit<Enter>".
15#
16# See ":help channel-demo" in Vim.
17#
18# This requires Python 2.6 or later.
19
20from __future__ import print_function
21import json
22import socket
23import sys
24import threading
25
26try:
27    # Python 3
28    import socketserver
29except ImportError:
30    # Python 2
31    import SocketServer as socketserver
32
33thesocket = None
34
35class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
36
37    def handle(self):
38        print("=== socket opened ===")
39        global thesocket
40        thesocket = self.request
41        while True:
42            try:
43                data = self.request.recv(4096).decode('utf-8')
44            except socket.error:
45                print("=== socket error ===")
46                break
47            except IOError:
48                print("=== socket closed ===")
49                break
50            if data == '':
51                print("=== socket closed ===")
52                break
53            print("received: {0}".format(data))
54            try:
55                decoded = json.loads(data)
56            except ValueError:
57                print("json decoding failed")
58                decoded = [-1, '']
59
60            # Send a response if the sequence number is positive.
61            # Negative numbers are used for "eval" responses.
62            if decoded[0] >= 0:
63                if decoded[1] == 'hello!':
64                    response = "got it"
65                else:
66                    response = "what?"
67                encoded = json.dumps([decoded[0], response])
68                print("sending {0}".format(encoded))
69                self.request.sendall(encoded.encode('utf-8'))
70        thesocket = None
71
72class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
73    pass
74
75if __name__ == "__main__":
76    HOST, PORT = "localhost", 8765
77
78    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
79    ip, port = server.server_address
80
81    # Start a thread with the server -- that thread will then start one
82    # more thread for each request
83    server_thread = threading.Thread(target=server.serve_forever)
84
85    # Exit the server thread when the main thread terminates
86    server_thread.daemon = True
87    server_thread.start()
88    print("Server loop running in thread: ", server_thread.name)
89
90    print("Listening on port {0}".format(PORT))
91    while True:
92        typed = sys.stdin.readline()
93        if "quit" in typed:
94            print("Goodbye!")
95            break
96        if thesocket is None:
97            print("No socket yet")
98        else:
99            print("sending {0}".format(typed))
100            thesocket.sendall(typed.encode('utf-8'))
101
102    server.shutdown()
103    server.server_close()
104