pipeserver first implementation
commit
8f310cbddf
@ -0,0 +1,73 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
background: #888;
|
||||||
|
}
|
||||||
|
body.connected {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
#shell {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
height: 40em;
|
||||||
|
width: 80em;
|
||||||
|
overflow-y: auto;
|
||||||
|
font-family: monospace;
|
||||||
|
background: black;
|
||||||
|
color: green;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>socket to me</h1>
|
||||||
|
<p>This page uses <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications">WebSockets!</a>.</p>
|
||||||
|
<p><span id="connections">0</span> active connections</p>
|
||||||
|
|
||||||
|
<div id="shell"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var ws_addr = 'ws://'+window.location.host+window.location.pathname,
|
||||||
|
sock = null,
|
||||||
|
shell = document.getElementById("shell"),
|
||||||
|
connections = document.getElementById("connections");
|
||||||
|
|
||||||
|
function connect () {
|
||||||
|
sock = new WebSocket(ws_addr);
|
||||||
|
sock.onopen = function (event) {
|
||||||
|
console.log("socket opened");
|
||||||
|
document.body.classList.add("connected");
|
||||||
|
// sock.send(JSON.stringify({
|
||||||
|
// src: "connect",
|
||||||
|
// }));
|
||||||
|
};
|
||||||
|
sock.onmessage = function (event) {
|
||||||
|
// console.log("message", event);
|
||||||
|
if (typeof(event.data) == "string") {
|
||||||
|
var msg = JSON.parse(event.data);
|
||||||
|
// console.log("message JSON", msg);
|
||||||
|
if (msg.src == "stdin" && msg.line) {
|
||||||
|
var line = document.createElement("div");
|
||||||
|
line.classList.add("line");
|
||||||
|
line.innerHTML = msg.line;
|
||||||
|
shell.appendChild(line);
|
||||||
|
} else if (msg.src == "connect") {
|
||||||
|
connections.innerHTML = msg.connections;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sock.onclose = function (event) {
|
||||||
|
// console.log("socket closed");
|
||||||
|
connections.innerHTML = "?";
|
||||||
|
document.body.classList.remove("connected");
|
||||||
|
sock = null;
|
||||||
|
window.setTimeout(connect, 2500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connect();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,91 @@
|
|||||||
|
import sys, os, asyncio, json, argparse
|
||||||
|
import aiohttp, aiohttp_jinja2, jinja2
|
||||||
|
from aiohttp import web
|
||||||
|
# from urllib.parse import urlparse, unquote as urlunquote, quote as urlquote
|
||||||
|
|
||||||
|
|
||||||
|
async def index (request):
|
||||||
|
ws = web.WebSocketResponse()
|
||||||
|
ws_ready = ws.can_prepare(request)
|
||||||
|
if not ws_ready.ok:
|
||||||
|
return aiohttp_jinja2.render_template(request.app['template'], request, {})
|
||||||
|
|
||||||
|
await ws.prepare(request)
|
||||||
|
# print('index.Websocket connection ready')
|
||||||
|
connections = request.app['websockets']
|
||||||
|
connections.append(ws)
|
||||||
|
|
||||||
|
await ws.send_str(json.dumps({'src': 'connect', 'connections': len(connections)}))
|
||||||
|
print ("{} active connections".format(len(connections)))
|
||||||
|
|
||||||
|
# incoming messages
|
||||||
|
async for msg in ws:
|
||||||
|
print ("index.got msg")
|
||||||
|
if msg.type == aiohttp.WSMsgType.TEXT:
|
||||||
|
print(msg.data)
|
||||||
|
if msg.data == 'close':
|
||||||
|
await ws.close()
|
||||||
|
# else:
|
||||||
|
# await ws.send_str(msg.data + '/answer')
|
||||||
|
|
||||||
|
# if the connection closes, the above loop simply finishes
|
||||||
|
# and we arrive here
|
||||||
|
connections.remove(ws)
|
||||||
|
print ("{} active connections".format(len(connections)))
|
||||||
|
return ws
|
||||||
|
|
||||||
|
# The following is a fusion of:
|
||||||
|
# https://stackoverflow.com/questions/31510190/aysncio-cannot-read-stdin-on-windows#36785819
|
||||||
|
# https://docs.aiohttp.org/en/stable/web_advanced.html#background-tasks
|
||||||
|
async def listen_to_stdin(app):
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
line = await loop.run_in_executor(None, sys.stdin.readline)
|
||||||
|
for ws in app['websockets']:
|
||||||
|
await ws.send_str(json.dumps({'src': 'stdin', 'line': line.rstrip()}))
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def start_background_tasks(app):
|
||||||
|
app['stdin_listener'] = asyncio.create_task(listen_to_stdin(app))
|
||||||
|
|
||||||
|
async def cleanup_background_tasks(app):
|
||||||
|
app['stdin_listener'].cancel()
|
||||||
|
await app['stdin_listener']
|
||||||
|
|
||||||
|
def main ():
|
||||||
|
ap = argparse.ArgumentParser("make & serve")
|
||||||
|
ap.add_argument("--host", default="localhost")
|
||||||
|
ap.add_argument("--port", type=int, default=8080)
|
||||||
|
ap.add_argument("--static", nargs=2, default=None, action="append")
|
||||||
|
ap.add_argument("--template", default="pipeserver.html", help="Template for web page to serve")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
# On Windows, the default event loop is SelectorEventLoop which does not support run_in_executor. ProactorEventLoop should be used instead.
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
loop = asyncio.ProactorEventLoop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
|
||||||
|
app = web.Application()
|
||||||
|
app['websockets'] = []
|
||||||
|
template_path, app['template'] = os.path.split(args.template)
|
||||||
|
jinja_env = aiohttp_jinja2.setup(
|
||||||
|
app, loader=jinja2.FileSystemLoader(template_path))
|
||||||
|
# jinja_env.filters['datetimeformat'] = format_datetime
|
||||||
|
|
||||||
|
# setup routes
|
||||||
|
app.add_routes([web.get('/', index)])
|
||||||
|
if args.static:
|
||||||
|
for name, path in args.static:
|
||||||
|
print ("Adding static route {0} -> {1}".format(name, path))
|
||||||
|
app.router.add_static(name, path)
|
||||||
|
|
||||||
|
app.on_startup.append(start_background_tasks)
|
||||||
|
app.on_cleanup.append(cleanup_background_tasks)
|
||||||
|
web.run_app(app, host=args.host, port=args.port)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in New Issue