summaryrefslogtreecommitdiffstats
path: root/testing/tools/websocketprocessbridge
diff options
context:
space:
mode:
Diffstat (limited to 'testing/tools/websocketprocessbridge')
-rw-r--r--testing/tools/websocketprocessbridge/websocketprocessbridge.py106
-rw-r--r--testing/tools/websocketprocessbridge/websocketprocessbridge_requirements.txt13
2 files changed, 119 insertions, 0 deletions
diff --git a/testing/tools/websocketprocessbridge/websocketprocessbridge.py b/testing/tools/websocketprocessbridge/websocketprocessbridge.py
new file mode 100644
index 000000000..57bab31a4
--- /dev/null
+++ b/testing/tools/websocketprocessbridge/websocketprocessbridge.py
@@ -0,0 +1,106 @@
+# vim: set ts=4 et sw=4 tw=80
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from twisted.internet import protocol, reactor
+from twisted.internet.task import LoopingCall
+import txws
+import psutil
+
+import argparse
+import sys
+import os
+
+# maps a command issued via websocket to running an executable with args
+commands = {
+ 'iceserver' : [sys.executable,
+ "-u",
+ os.path.join("iceserver", "iceserver.py")]
+}
+
+class ProcessSide(protocol.ProcessProtocol):
+ """Handles the spawned process (I/O, process termination)"""
+
+ def __init__(self, socketSide):
+ self.socketSide = socketSide
+
+ def outReceived(self, data):
+ if self.socketSide:
+ lines = data.splitlines()
+ for line in lines:
+ self.socketSide.transport.write(line)
+
+ def errReceived(self, data):
+ self.outReceived(data)
+
+ def processEnded(self, reason):
+ if self.socketSide:
+ self.outReceived(str(reason))
+ self.socketSide.processGone()
+
+ def socketGone(self):
+ self.socketSide = None
+ self.transport.loseConnection()
+ self.transport.signalProcess("KILL")
+
+
+class SocketSide(protocol.Protocol):
+ """
+ Handles the websocket (I/O, closed connection), and spawning the process
+ """
+
+ def __init__(self):
+ self.processSide = None
+
+ def dataReceived(self, data):
+ if not self.processSide:
+ self.processSide = ProcessSide(self)
+ # We deliberately crash if |data| isn't on the "menu",
+ # or there is some problem spawning.
+ reactor.spawnProcess(self.processSide,
+ commands[data][0],
+ commands[data],
+ env=os.environ)
+
+ def connectionLost(self, reason):
+ if self.processSide:
+ self.processSide.socketGone()
+
+ def processGone(self):
+ self.processSide = None
+ self.transport.loseConnection()
+
+
+class ProcessSocketBridgeFactory(protocol.Factory):
+ """Builds sockets that can launch/bridge to a process"""
+
+ def buildProtocol(self, addr):
+ return SocketSide()
+
+# Parent process could have already exited, so this is slightly racy. Only
+# alternative is to set up a pipe between parent and child, but that requires
+# special cooperation from the parent.
+parent_process = psutil.Process(os.getpid()).parent()
+
+def check_parent():
+ """ Checks if parent process is still alive, and exits if not """
+ if not parent_process.is_running():
+ print("websocket/process bridge exiting because parent process is gone")
+ reactor.stop()
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description='Starts websocket/process bridge.')
+ parser.add_argument("--port", type=str, dest="port", default="8191",
+ help="Port for websocket/process bridge. Default 8191.")
+ args = parser.parse_args()
+
+ parent_checker = LoopingCall(check_parent)
+ parent_checker.start(1)
+
+ bridgeFactory = ProcessSocketBridgeFactory()
+ reactor.listenTCP(int(args.port), txws.WebSocketFactory(bridgeFactory))
+ print("websocket/process bridge listening on port %s" % args.port)
+ reactor.run()
+
+
diff --git a/testing/tools/websocketprocessbridge/websocketprocessbridge_requirements.txt b/testing/tools/websocketprocessbridge/websocketprocessbridge_requirements.txt
new file mode 100644
index 000000000..51e548094
--- /dev/null
+++ b/testing/tools/websocketprocessbridge/websocketprocessbridge_requirements.txt
@@ -0,0 +1,13 @@
+twisted==10.2.0
+
+# websocket adapter for twisted, might be built into twisted someday
+txws==0.9.1
+
+psutil==3.1.1
+
+# needed by txws, but pypi doesn't know about it
+six==1.10.0
+
+# Needed by iceserver
+ipaddr==2.1.11
+passlib==1.6.5