blob: 2667aed3961eaff777911b8c21030a37a03c2fee [file] [log] [blame]
Bram Moolenaar3b5f9292016-01-28 22:37:01 +01001#!/usr/bin/python
Bram Moolenaarf57969a2016-02-02 20:47:49 +01002#
Bram Moolenaar3b5f9292016-01-28 22:37:01 +01003# Server that will accept connections from a Vim channel.
4# Run this server and then in Vim you can open the channel:
Bram Moolenaare0fa3742016-02-20 15:47:01 +01005# :let handle = ch_open('localhost:8765')
Bram Moolenaar3b5f9292016-01-28 22:37:01 +01006#
7# Then Vim can send requests to the server:
Bram Moolenaarf57969a2016-02-02 20:47:49 +01008# :let response = ch_sendexpr(handle, 'hello!')
Bram Moolenaar3b5f9292016-01-28 22:37:01 +01009#
10# And you can control Vim by typing a JSON message here, e.g.:
11# ["ex","echo 'hi there'"]
12#
Bram Moolenaarf57969a2016-02-02 20:47:49 +010013# There is no prompt, just type a line and press Enter.
14# To exit cleanly type "quit<Enter>".
15#
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010016# See ":help channel-demo" in Vim.
Bram Moolenaarf57969a2016-02-02 20:47:49 +010017#
18# This requires Python 2.6 or later.
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010019
Bram Moolenaar488a1302016-02-01 22:01:10 +010020from __future__ import print_function
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010021import json
22import socket
23import sys
24import threading
25
Bram Moolenaar488a1302016-02-01 22:01:10 +010026try:
27 # Python 3
28 import socketserver
29except ImportError:
30 # Python 2
31 import SocketServer as socketserver
32
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010033thesocket = None
34
Bram Moolenaar488a1302016-02-01 22:01:10 +010035class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010036
37 def handle(self):
Bram Moolenaar488a1302016-02-01 22:01:10 +010038 print("=== socket opened ===")
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010039 global thesocket
40 thesocket = self.request
41 while True:
42 try:
Bram Moolenaar488a1302016-02-01 22:01:10 +010043 data = self.request.recv(4096).decode('utf-8')
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010044 except socket.error:
Bram Moolenaar488a1302016-02-01 22:01:10 +010045 print("=== socket error ===")
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010046 break
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010047 if data == '':
Bram Moolenaar488a1302016-02-01 22:01:10 +010048 print("=== socket closed ===")
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010049 break
Bram Moolenaare18c0b32016-03-20 21:08:34 +010050 print("received: {0}".format(data))
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010051 try:
52 decoded = json.loads(data)
53 except ValueError:
Bram Moolenaar488a1302016-02-01 22:01:10 +010054 print("json decoding failed")
55 decoded = [-1, '']
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010056
Bram Moolenaar488a1302016-02-01 22:01:10 +010057 # Send a response if the sequence number is positive.
58 # Negative numbers are used for "eval" responses.
59 if decoded[0] >= 0:
60 if decoded[1] == 'hello!':
61 response = "got it"
Bram Moolenaar47003982021-12-05 21:54:04 +000062 id = decoded[0]
63 elif decoded[1] == 'hello channel!':
64 response = "got that"
65 # response is not to a specific message callback but to the
66 # channel callback, need to use ID zero
67 id = 0
Bram Moolenaar488a1302016-02-01 22:01:10 +010068 else:
69 response = "what?"
Bram Moolenaar47003982021-12-05 21:54:04 +000070 id = decoded[0]
71 encoded = json.dumps([id, response])
Bram Moolenaare18c0b32016-03-20 21:08:34 +010072 print("sending {0}".format(encoded))
Bram Moolenaar488a1302016-02-01 22:01:10 +010073 self.request.sendall(encoded.encode('utf-8'))
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010074 thesocket = None
75
Bram Moolenaar488a1302016-02-01 22:01:10 +010076class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010077 pass
78
79if __name__ == "__main__":
80 HOST, PORT = "localhost", 8765
81
82 server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
83 ip, port = server.server_address
84
85 # Start a thread with the server -- that thread will then start one
86 # more thread for each request
87 server_thread = threading.Thread(target=server.serve_forever)
88
89 # Exit the server thread when the main thread terminates
90 server_thread.daemon = True
91 server_thread.start()
Bram Moolenaar488a1302016-02-01 22:01:10 +010092 print("Server loop running in thread: ", server_thread.name)
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010093
Bram Moolenaare18c0b32016-03-20 21:08:34 +010094 print("Listening on port {0}".format(PORT))
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010095 while True:
96 typed = sys.stdin.readline()
97 if "quit" in typed:
Bram Moolenaar488a1302016-02-01 22:01:10 +010098 print("Goodbye!")
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010099 break
100 if thesocket is None:
Bram Moolenaar488a1302016-02-01 22:01:10 +0100101 print("No socket yet")
Bram Moolenaar3b5f9292016-01-28 22:37:01 +0100102 else:
Bram Moolenaare18c0b32016-03-20 21:08:34 +0100103 print("sending {0}".format(typed))
Bram Moolenaar488a1302016-02-01 22:01:10 +0100104 thesocket.sendall(typed.encode('utf-8'))
Bram Moolenaar3b5f9292016-01-28 22:37:01 +0100105
106 server.shutdown()
107 server.server_close()