PageRenderTime 14ms CodeModel.GetById 1ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

/examples/web/terminal/terminal.py

https://bitbucket.org/prologic/circuits/
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()