From 8f310cbddf7efcb3da6310e4e3f085ce09f01cc8 Mon Sep 17 00:00:00 2001 From: Michael Murtaugh Date: Mon, 25 May 2020 15:35:08 +0200 Subject: [PATCH] pipeserver first implementation --- pipeserver.html | 73 +++++++++++++++++++++++++++++++++++++++ pipeserver.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 pipeserver.html create mode 100644 pipeserver.py diff --git a/pipeserver.html b/pipeserver.html new file mode 100644 index 0000000..2b8ad9c --- /dev/null +++ b/pipeserver.html @@ -0,0 +1,73 @@ + + + + + + +

socket to me

+

This page uses WebSockets!.

+

0 active connections

+ +
+ + + + \ No newline at end of file diff --git a/pipeserver.py b/pipeserver.py new file mode 100644 index 0000000..204eb19 --- /dev/null +++ b/pipeserver.py @@ -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()