From 7ec7e69f4e8927d9d7bf503b016593412b9624e2 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 22 Sep 2023 13:53:32 +0800 Subject: [PATCH] bugfix --- ahserver/configuredServer.py | 6 +- ahserver/processorResource.py | 22 +++++-- ahserver/url2file.py | 5 +- ahserver/websocketProcessor.py | 106 ++++++++++++++++++++++++++++++--- 4 files changed, 124 insertions(+), 15 deletions(-) diff --git a/ahserver/configuredServer.py b/ahserver/configuredServer.py index 14141c8..6dd1c96 100755 --- a/ahserver/configuredServer.py +++ b/ahserver/configuredServer.py @@ -46,18 +46,20 @@ class ConfiguredServer(AppLogger): await auth.setupAuth(self.app) return self.app - def run(self): + def run(self, port=None): config = getConfig() self.configPath(config) a = TmpFileRecord() ssl_context = None + if port is None: + port = config.website.port or 8080 if config.website.ssl: ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_context.load_cert_chain(config.website.ssl.crtfile, config.website.ssl.keyfile) web.run_app(self.init_auth(),host=config.website.host or '0.0.0.0', - port=config.website.port or 8080, + port=port, ssl_context=ssl_context) def configPath(self,config): diff --git a/ahserver/processorResource.py b/ahserver/processorResource.py index 7de0e1c..ae7b7fb 100755 --- a/ahserver/processorResource.py +++ b/ahserver/processorResource.py @@ -40,7 +40,7 @@ from appPublic.app_logger import AppLogger from .baseProcessor import getProcessor from .xlsxdsProcessor import XLSXDataSourceProcessor -from .websocketProcessor import WebsocketProcessor +from .websocketProcessor import WebsocketProcessor, XtermProcessor from .sqldsProcessor import SQLDataSourceProcessor from .functionProcessor import FunctionProcessor from .proxyProcessor import ProxyProcessor @@ -253,6 +253,7 @@ class ProcessorResource(AppLogger, StaticResource,Url2File): self.y_env.i18nDict = i18nDICT self.y_env.terminalType = getClientType(request) self.y_env.entire_url = partial(self.entireUrl,request) + self.y_env.websocket_url = partial(self.websocketUrl,request) self.y_env.abspath = self.abspath self.y_env.request2ns = getArgs self.y_env.aiohttp_client = client @@ -367,12 +368,25 @@ class ProcessorResource(AppLogger, StaticResource,Url2File): for word, handlername in self.y_processors: if fpath.endswith(word): Klass = getProcessor(handlername) - processor = Klass(path,self) - return processor + try: + processor = Klass(path,self) + return processor + except Exception as e: + print('Exception:',e, 'handlername=', handlername) + return None return None + def websocketUrl(self, request, url): + url = entireUrl(request, url) + if url.startswith('https'): + return 'wss' + url[5:] + return 'ws' + url[4:] + def entireUrl(self, request, url): - if url.startswith('http://') or url.startswith('https://'): + if url.startswith('http://') or \ + url.startswith('https://') or \ + url.startswith('ws://') or \ + url.startswith('wss://'): return url scheme = request.headers.get('X-Forwarded-Scheme') or request.scheme port = request.headers.get('X-Forwarded-Port') or str(request['port']) diff --git a/ahserver/url2file.py b/ahserver/url2file.py index 64dbe17..0cfcbf0 100755 --- a/ahserver/url2file.py +++ b/ahserver/url2file.py @@ -26,7 +26,10 @@ class Url2File: if len(url) > 0 and url[-1] == '/': url = url[:-1] paths = url.split('/') - if url.startswith('http://') or url.startswith('https://'): + if url.startswith('http://') or \ + url.startswith('https://') or \ + url.startswith('ws://') or \ + url.startswith('wss://'): paths = paths[3:] f = os.path.join(self.path,*paths) real_path = os.path.abspath(f) diff --git a/ahserver/websocketProcessor.py b/ahserver/websocketProcessor.py index 1e6240a..74c4524 100755 --- a/ahserver/websocketProcessor.py +++ b/ahserver/websocketProcessor.py @@ -1,31 +1,121 @@ +import asyncio import aiohttp +import json +import codecs from aiohttp import web -from .baseProcessor import PythonScriptProcessor +import aiohttp_cors +from appPublic.sshx import SSHNode +from .baseProcessor import BaseProcessor, PythonScriptProcessor + +class XtermProcessor(BaseProcessor): + @classmethod + def isMe(self,name): + return name=='xterm' + + async def ws_2_process(self, ws): + async for msg in ws: + if msg.type == aiohttp.WSMsgType.TEXT: + self.p_obj.stdin.write(msg.data) + elif msg.type == aiohttp.WSMsgType.ERROR: + print('ws connection closed with exception %s' % ws.exception()) + return + + async def process_2_ws(self, ws): + while self.running: + x = await self.p_obj.stdout.read(1024) + await self.ws_sendstr(ws, x) + + async def datahandle(self,request): + await self.path_call(request) + + async def path_call(self, request, params={}): + await self.set_run_env(request) + lenv = self.run_ns.copy() + lenv.update(params) + del lenv['request'] + ws = web.WebSocketResponse() + await ws.prepare(request) + await self.create_process() + self.ws_sendstr(ws, 'Welcom to sshclient') + r1 = self.ws_2_process(ws) + r2 = self.process_2_ws(ws) + await asyncio.gather(r1,r2) + self.retResponse = ws + return ws + + def get_login_info(self): + with codecs.open(self.real_path, 'r', 'utf-8') as f: + self.login_info = json.load(f) + print(f'{self.login_info=}') + + async def create_process(self): + # id = lenv['params_kw'].get('termid') + self.get_login_info() + host = self.login_info['host'] + port = self.login_info.get('port', 22) + username = self.login_info.get('username', 'root') + password = self.login_info.get('password',None) + self.sshnode = SSHNode(host, username=username, + password=password, + port=port) + await self.sshnode.connect() + self.p_obj = await self.sshnode._process('bash', + term_type='vt100', + term_size=(80, 24), + encoding='utf-8') + self.running = True + + async def ws_sendstr(self, ws:web.WebSocketResponse, s:str): + data = { + "type":1, + "data":s + } + await ws.send_str(json.dumps(data)) + + def close_process(self): + self.sshnode.close() + self.p_obj.close() class WebsocketProcessor(PythonScriptProcessor): @classmethod def isMe(self,name): return name=='ws' + async def ws_sendstr(self, ws:web.WebSocketResponse, s:str): + data = { + "type":1, + "data":s + } + await ws.send_str(json.dumps(data)) + async def path_call(self, request,params={}): + print('1----------------------------------') await self.set_run_env(request) lenv = self.run_ns.copy() lenv.update(params) del lenv['request'] + print('2----------------------------------') txt = self.loadScript(self.real_path) exec(txt,lenv,lenv) func = lenv['myfunc'] + print('3----------------------------------') ws = web.WebSocketResponse() await ws.prepare(request) + print('4----------------------------------', aiohttp.WSMsgType.TEXT) + await self.ws_sendstr(ws, 'Welcome to websock') async for msg in ws: - if msg.type == aiohttp/WSMsgType.TEXT: - if msg.data == 'close': - await ws.close() - else: - lenv['ws_data'] = msg.data - resp = await func(request,**lenv) - await ws.send_str(resp) + if msg.type == aiohttp.WSMsgType.TEXT: + print('msg=:', msg) + lenv['ws_data'] = msg.data + # resp = await func(request,**lenv) + await self.ws_sendstr(ws, msg.data) + print('msg.data=', msg.data) elif msg.type == aiohttp.WSMsgType.ERROR: print('ws connection closed with exception %s' % ws.exception()) + else: + print('datatype error', msg.type) + print('5----------------------------------') + self.retResponse = ws + await ws.close() return ws