blob: 150ddbee7c3f909ea1d5e6e9efd1bcf31d20b98a [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
47 except IOError:
Bram Moolenaar488a1302016-02-01 22:01:10 +010048 print("=== socket closed ===")
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010049 break
50 if data == '':
Bram Moolenaar488a1302016-02-01 22:01:10 +010051 print("=== socket closed ===")
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010052 break
Bram Moolenaare18c0b32016-03-20 21:08:34 +010053 print("received: {0}".format(data))
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010054 try:
55 decoded = json.loads(data)
56 except ValueError:
Bram Moolenaar488a1302016-02-01 22:01:10 +010057 print("json decoding failed")
58 decoded = [-1, '']
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010059
Bram Moolenaar488a1302016-02-01 22:01:10 +010060 # 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"
Bram Moolenaar47003982021-12-05 21:54:04 +000065 id = decoded[0]
66 elif decoded[1] == 'hello channel!':
67 response = "got that"
68 # response is not to a specific message callback but to the
69 # channel callback, need to use ID zero
70 id = 0
Bram Moolenaar488a1302016-02-01 22:01:10 +010071 else:
72 response = "what?"
Bram Moolenaar47003982021-12-05 21:54:04 +000073 id = decoded[0]
74 encoded = json.dumps([id, response])
Bram Moolenaare18c0b32016-03-20 21:08:34 +010075 print("sending {0}".format(encoded))
Bram Moolenaar488a1302016-02-01 22:01:10 +010076 self.request.sendall(encoded.encode('utf-8'))
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010077 thesocket = None
78
Bram Moolenaar488a1302016-02-01 22:01:10 +010079class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010080 pass
81
82if __name__ == "__main__":
83 HOST, PORT = "localhost", 8765
84
85 server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
86 ip, port = server.server_address
87
88 # Start a thread with the server -- that thread will then start one
89 # more thread for each request
90 server_thread = threading.Thread(target=server.serve_forever)
91
92 # Exit the server thread when the main thread terminates
93 server_thread.daemon = True
94 server_thread.start()
Bram Moolenaar488a1302016-02-01 22:01:10 +010095 print("Server loop running in thread: ", server_thread.name)
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010096
Bram Moolenaare18c0b32016-03-20 21:08:34 +010097 print("Listening on port {0}".format(PORT))
Bram Moolenaar3b5f9292016-01-28 22:37:01 +010098 while True:
99 typed = sys.stdin.readline()
100 if "quit" in typed:
Bram Moolenaar488a1302016-02-01 22:01:10 +0100101 print("Goodbye!")
Bram Moolenaar3b5f9292016-01-28 22:37:01 +0100102 break
103 if thesocket is None:
Bram Moolenaar488a1302016-02-01 22:01:10 +0100104 print("No socket yet")
Bram Moolenaar3b5f9292016-01-28 22:37:01 +0100105 else:
Bram Moolenaare18c0b32016-03-20 21:08:34 +0100106 print("sending {0}".format(typed))
Bram Moolenaar488a1302016-02-01 22:01:10 +0100107 thesocket.sendall(typed.encode('utf-8'))
Bram Moolenaar3b5f9292016-01-28 22:37:01 +0100108
109 server.shutdown()
110 server.server_close()