/examples/web/terminal/terminal.py
Python | 139 lines | 109 code | 27 blank | 3 comment | 14 complexity | f6e1c3bd50d1e0340b10174e05a7d6bb MD5 | raw file
1#!/usr/bin/env python 2 3 4import os 5import signal 6from StringIO import StringIO 7from subprocess import Popen, PIPE 8 9from circuits.io import File 10from circuits.tools import inspect 11from circuits.net.events import write 12from circuits.web.events import stream 13from circuits import handler, Event, Component 14from circuits.web import Server, Controller, Logger, Static, Sessions 15 16BUFFERING = 1 17STREAMING = 2 18 19 20class kill(Event): 21 """kill Event""" 22 23 24class input(Event): 25 """input Event""" 26 27 28class Command(Component): 29 30 channel = "cmd" 31 32 def __init__(self, request, response, command, channel=channel): 33 super(Command, self).__init__(channel=channel) 34 35 self._request = request 36 self._response = response 37 self._command = command 38 39 self._state = BUFFERING 40 self._buffer = None 41 42 self._p = Popen( 43 command, shell=True, stdout=PIPE, stderr=PIPE, 44 close_fds=True, preexec_fn=os.setsid 45 ) 46 47 self._stdin = None 48 if self._p.stdin is not None: 49 self._stdin = File(self._p.stdin, channel="%s.stdin" % channel) 50 self._stdin.register(self) 51 52 self._stdout = File(self._p.stdout, channel="%s.stdout" % channel) 53 self.addHandler( 54 handler("eof", channel="%s.stdout" % channel)(self._on_stdout_eof) 55 ) 56 self.addHandler( 57 handler("read", channel="%s.stdout" % channel)( 58 self._on_stdout_read 59 ) 60 ) 61 self._stdout.register(self) 62 63 @handler("disconnect", channel="web") 64 def disconnect(self, sock): 65 if sock == self._request.sock: 66 self.fire(kill(), self) 67 68 @handler("response", channel="web", priority=-1) 69 def response(self, response): 70 if response == self._response: 71 self._state = STREAMING 72 73 def kill(self): 74 os.killpg(self._p.pid, signal.SIGINT) 75 self.unregister() 76 77 def input(self, data): 78 if self._stdin is not None: 79 self.fire(write(data), self._stdin) 80 81 @staticmethod 82 def _on_stdout_eof(self): 83 if self._buffer is not None: 84 self._buffer.flush() 85 data = self._buffer.getvalue() 86 self.fire(stream(self._response, data), "web") 87 self.fire(stream(self._response, None), "web") 88 self.fire(kill()) 89 90 @staticmethod 91 def _on_stdout_read(self, data): 92 if self._state == BUFFERING: 93 if self._buffer is None: 94 self._buffer = StringIO() 95 self._buffer.write(data) 96 elif self._state == STREAMING: 97 if self._buffer is not None: 98 self._buffer.write(data) 99 self._buffer.flush() 100 data = self._buffer.getvalue() 101 self._buffer = None 102 self.fire(stream(self._response, data), "web") 103 else: 104 self.fire(stream(self._response, data), "web") 105 106 107class Root(Controller): 108 109 def GET(self, *args, **kwargs): 110 self.expires(60 * 60 * 24 * 30) 111 return self.serve_file(os.path.abspath("static/index.xhtml")) 112 113 def POST(self, input=None): 114 if not input: 115 return "" 116 117 self.response.headers["Content-Type"] = "text/plain" 118 119 if input.strip() == "inspect": 120 return inspect(self) 121 122 self.response.stream = True 123 124 sid = self.request.sid 125 self += Command(self.request, self.response, input, channel=sid) 126 127 return self.response 128 129 130from circuits import Debugger 131 132app = Server(("0.0.0.0", 8000)) 133Debugger().register(app) 134Static("/js", docroot="static/js").register(app) 135Static("/css", docroot="static/css").register(app) 136Sessions().register(app) 137Logger().register(app) 138Root().register(app) 139app.run()