-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.py
executable file
·98 lines (84 loc) · 3.25 KB
/
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/usr/bin/python
import argparse
import base64
import fcntl
import json
import pty
import struct
import subprocess
import termios
import tornado.httpserver
import tornado.ioloop
import tornado.iostream
import tornado.web
import tornado.websocket
import uuid
parser = argparse.ArgumentParser(description='Browser-based terminal emulator')
parser.add_argument('-H', '--host', default='127.0.0.1', help='listener host for web server')
parser.add_argument('-P', '--port', default='8080', type=int, help='listener port for web server')
parser.add_argument('-s', '--size', default='80x24', help='size of emulated terminal')
parser.add_argument('command', nargs='+', help='command to run in the terminal emulator')
class TerminalHandler(tornado.websocket.WebSocketHandler):
def initialize(self, command, secret, size):
self.command = command
self.secret = secret
(self.width, self.height) = size
self.process = None
self.pipe = None
def open(self):
self.write_message(json.dumps({ \
'type': 'size', \
'width': self.width, \
'height': self.height, \
}))
def on_message(self, data):
message = json.loads(data)
if message['type'] == 'secret':
if message['secret'] != self.secret:
print 'Error: Wrong secret \'%s\', should be \'%s\'' % \
(message['secret'], self.secret)
return
if self.process is not None:
print 'Error: Process already started'
return
self.start_process()
elif message['type'] == 'input':
if self.pipe is None:
print 'Error: Process not started'
return
character = chr(int(message['key']))
print message['key'], character
self.pipe.write(character)
def start_process(self):
(master, slave) = pty.openpty()
fcntl.ioctl(master, termios.TIOCSWINSZ, \
struct.pack('HHHH', self.height, self.width, 100, 100))
self.process = subprocess.Popen(self.command, \
stdin=slave, stdout=slave, stderr=slave)
self.pipe = tornado.iostream.PipeIOStream(master)
self.pipe.read_until_close(callback=self.send_string, \
streaming_callback=self.send_string)
def send_string(self, string):
self.write_message(json.dumps({ \
'type': 'output', \
'output': base64.b64encode(string), \
}))
if __name__ == '__main__':
args = parser.parse_args()
secret = str(uuid.uuid4())
print 'Navigate your browser to <https://%s:%d/?%s> to use WSTerm' % \
(args.host, args.port, secret)
application = tornado.web.Application([
(r'/()', tornado.web.StaticFileHandler, { 'path': './static/index.html' }),
(r'/wsterm', TerminalHandler, { \
'command': args.command, \
'secret': secret, \
'size': map(int, args.size.split('x')), \
}),
], static_path='./static/')
server = tornado.httpserver.HTTPServer(application, ssl_options={ \
'keyfile': 'certificate.key',
'certfile': 'certificate.crt',
})
server.listen(args.port, address=args.host)
tornado.ioloop.IOLoop.instance().start()