async def _runShareLoop(self): try: async for item in self.item: if self.isfini: break retn = (True, item) mesg = ('share:data', {'share': self.iden, 'data': retn}) await self.link.tx(mesg) # purposely yield for fair scheduling await asyncio.sleep(0) except Exception as e: retn = s_common.retnexc(e) mesg = ('share:data', {'share': self.iden, 'data': retn}) await self.link.tx(mesg) finally: mesg = ('share:data', {'share': self.iden, 'data': None}) await self.link.tx(mesg) await self.fini()
async def inetHttpConnect(self, url, headers=None, ssl_verify=True, timeout=300): sock = await WebSocket.anit() proxyurl = await self.runt.snap.core.getConfOpt('http:proxy') connector = None if proxyurl is not None: connector = aiohttp_socks.ProxyConnector.from_url(proxyurl) timeout = aiohttp.ClientTimeout(total=timeout) try: sess = await sock.enter_context(aiohttp.ClientSession(connector=connector, timeout=timeout)) sock.resp = await sock.enter_context(sess.ws_connect(url, headers=headers, ssl=ssl_verify, timeout=timeout)) sock._syn_refs = 0 self.runt.onfini(sock) return (True, sock) except asyncio.CancelledError: # pragma: no cover raise except Exception as e: # pragma: no cover await sock.fini() return s_common.retnexc(e)
async def _onTaskInit(self, link, mesg): task = mesg[1].get('task') name = mesg[1].get('name') sess = link.get('sess') if sess is None: raise s_exc.NoSuchObj(name=name) item = sess.getSessItem(name) if item is None: raise s_exc.NoSuchObj(name=name) try: methname, args, kwargs = mesg[1].get('todo') if methname[0] == '_': raise s_exc.NoSuchMeth(name=methname) meth = getattr(item, methname, None) if meth is None: logger.warning(f'{item!r} has no method: {methname}') raise s_exc.NoSuchMeth(name=methname) valu = await self._runTodoMeth(link, meth, args, kwargs) mesg = self._getTaskFiniMesg(task, valu) await link.tx(mesg) # if it's a Share(), spin off the share loop if isinstance(valu, s_share.Share): if isinstance(item, s_base.Base): item.onfini(valu) async def spinshareloop(): try: await valu._runShareLoop() except asyncio.CancelledError: pass except Exception: logger.exception('Error running %r', valu) finally: await valu.fini() self.schedCoro(spinshareloop()) except Exception as e: logger.exception('on task:init: %r' % (mesg,)) retn = s_common.retnexc(e) await link.tx( ('task:fini', {'task': task, 'retn': retn}) )
async def _onTaskInit(self, link, mesg): task = mesg[1].get('task') name = mesg[1].get('name') sess = link.get('sess') if sess is None: raise s_exc.NoSuchObj(name=name) item = sess.getSessItem(name) if item is None: raise s_exc.NoSuchObj(name=name) try: methname, args, kwargs = mesg[1].get('todo') if methname[0] == '_': raise s_exc.NoSuchMeth(name=methname) meth = getattr(item, methname, None) if meth is None: logger.warning('%r has no method: %s', item, methname) raise s_exc.NoSuchMeth(name=methname) valu = await self._runTodoMeth(link, meth, args, kwargs) mesg = self._getTaskFiniMesg(task, valu) await link.tx(mesg) # if it's a Share(), spin off the share loop if isinstance(valu, s_share.Share): if isinstance(item, s_base.Base): item.onfini(valu) async def spinshareloop(): try: await valu._runShareLoop() except asyncio.CancelledError: pass except Exception: logger.exception('Error running %r', valu) finally: await valu.fini() self.schedCoro(spinshareloop()) except (asyncio.CancelledError, Exception) as e: logger.exception('on task:init: %r', mesg) retn = s_common.retnexc(e) await link.tx( ('task:fini', {'task': task, 'retn': retn}) )
async def _onTeleSyn(self, link, mesg): reply = ('tele:syn', { 'vers': self.televers, 'retn': (True, None), }) try: vers = mesg[1].get('vers') if vers[0] != s_telepath.televers[0]: raise s_exc.BadMesgVers(vers=vers, myvers=s_telepath.televers) path = () name = mesg[1].get('name') if not name: name = '*' if '/' in name: name, rest = name.split('/', 1) if rest: path = rest.split('/') item = self.shared.get(name) if item is None: raise s_exc.NoSuchName(name=name) sess = await Sess.anit() async def sessfini(): self.sessions.pop(sess.iden, None) sess.onfini(sessfini) link.onfini(sess.fini) self.sessions[sess.iden] = sess sess.conninfo = link.getAddrInfo() link.set('sess', sess) if isinstance(item, s_telepath.Aware): item = await s_coro.ornot(item.getTeleApi, link, mesg, path) if isinstance(item, s_base.Base): link.onfini(item) reply[1]['sharinfo'] = s_reflect.getShareInfo(item) sess.setSessItem(None, item) reply[1]['sess'] = sess.iden except Exception as e: logger.exception('tele:syn error') reply[1]['retn'] = s_common.retnexc(e) await link.tx(reply)
async def _onTeleSyn(self, link, mesg): reply = ('tele:syn', { 'vers': self.televers, 'retn': (True, None), }) try: vers = mesg[1].get('vers') if vers[0] != s_telepath.televers[0]: raise s_exc.BadMesgVers(vers=vers, myvers=s_telepath.televers) path = () name = mesg[1].get('name') if not name: name = '*' if '/' in name: name, rest = name.split('/', 1) if rest: path = rest.split('/') item = self.shared.get(name) if item is None: raise s_exc.NoSuchName(name=name) sess = await Sess.anit() async def sessfini(): self.sessions.pop(sess.iden, None) sess.onfini(sessfini) link.onfini(sess.fini) self.sessions[sess.iden] = sess link.set('sess', sess) if isinstance(item, s_telepath.Aware): item = await s_coro.ornot(item.getTeleApi, link, mesg, path) if isinstance(item, s_base.Base): link.onfini(item.fini) reply[1]['sharinfo'] = s_reflect.getShareInfo(item) sess.setSessItem(None, item) reply[1]['sess'] = sess.iden except Exception as e: logger.exception('tele:syn error') reply[1]['retn'] = s_common.retnexc(e) await link.tx(reply)
async def _onTeleSyn(self, link, mesg): reply = ('tele:syn', { 'vers': self.televers, 'retn': (True, None), }) try: vers = mesg[1].get('vers') if vers[0] != s_telepath.televers[0]: raise s_exc.BadMesgVers(vers=vers, myvers=s_telepath.televers) name = mesg[1].get('name') item = self.shared.get(name) # allow a telepath aware object a shot at dynamic share names if item is None and name.find('/') != -1: path = name.split('/') base = self.shared.get(path[0]) if base is not None and isinstance(base, s_telepath.Aware): item = await s_coro.ornot(base.onTeleOpen, link, path) if item is None: raise s_exc.NoSuchName(name=name) sess = await Sess.anit() async def sessfini(): self.sessions.pop(sess.iden, None) sess.onfini(sessfini) link.onfini(sess.fini) self.sessions[sess.iden] = sess link.set('sess', sess) if isinstance(item, s_telepath.Aware): item = await s_coro.ornot(item.getTeleApi, link, mesg) if isinstance(item, s_base.Base): link.onfini(item.fini) sess.setSessItem(None, item) reply[1]['sess'] = sess.iden except Exception as e: logger.exception('tele:syn error') reply[1]['retn'] = s_common.retnexc(e) await link.tx(reply)
async def tx(self, mesg): try: mesg = await s_stormtypes.toprim(mesg) await self.resp.send_bytes(json.dumps(mesg).encode()) return (True, None) except asyncio.CancelledError: # pragma: no cover raise except Exception as e: # pragma: no cover return s_common.retnexc(e)
async def rx(self, timeout=None): try: _type, data, extra = await asyncio.wait_for(self.resp.receive(), timeout=timeout) if _type == aiohttp.WSMsgType.BINARY: return (True, json.loads(data)) if _type == aiohttp.WSMsgType.TEXT: return (True, json.loads(data.encode())) if _type == aiohttp.WSMsgType.CLOSED: # pragma: no cover return (True, None) return (False, ('BadMesgFormat', {'mesg': f'WebSocket RX unhandled type: {_type.name}'})) # pragma: no cover except asyncio.CancelledError: # pragma: no cover raise except Exception as e: # pragma: no cover return s_common.retnexc(e)
async def _onTaskV2Init(self, link, mesg): # t2:init is used by the pool sockets on the client name = mesg[1].get('name') sidn = mesg[1].get('sess') todo = mesg[1].get('todo') try: if sidn is None or todo is None: raise s_exc.NoSuchObj(name=name) sess = self.sessions.get(sidn) if sess is None: raise s_exc.NoSuchObj(name=name) item = sess.getSessItem(name) if item is None: raise s_exc.NoSuchObj(name=name) s_scope.set('sess', sess) s_scope.set('link', link) methname, args, kwargs = todo if methname[0] == '_': raise s_exc.NoSuchMeth(name=methname) meth = getattr(item, methname, None) if meth is None: logger.warning('%r has no method: %r', item, methname) raise s_exc.NoSuchMeth(name=methname) sessitem = await t2call(link, meth, args, kwargs) if sessitem is not None: sess.onfini(sessitem) except Exception as e: logger.exception('on t2:init: %r' % (mesg, )) if not link.isfini: retn = s_common.retnexc(e) await link.tx(('t2:fini', {'retn': retn}))
async def _runShareLoop(self): try: async for item in self.item: if self.isfini: break retn = (True, item) mesg = ('share:data', {'share': self.iden, 'data': retn}) await self.link.tx(mesg) except Exception as e: retn = s_common.retnexc(e) mesg = ('share:data', {'share': self.iden, 'data': retn}) await self.link.tx(mesg) finally: mesg = ('share:data', {'share': self.iden, 'data': None}) await self.link.tx(mesg) await self.fini()
async def tx(self, mesg): ''' Async transmit routine which will wait for writer drain(). ''' if self.isfini: raise s_exc.IsFini() byts = s_msgpack.en(mesg) try: self.writer.write(byts) # Avoid Python bug. See https://bugs.python.org/issue29930 async with self._drain_lock: await self.writer.drain() except Exception as e: await self.fini() einfo = s_common.retnexc(e) logger.debug('link.tx connection trouble %s', einfo) raise
async def _onTaskV2Init(self, link, mesg): # t2:init is used by the pool sockets on the client name = mesg[1].get('name') sidn = mesg[1].get('sess') todo = mesg[1].get('todo') try: if sidn is None or todo is None: raise s_exc.NoSuchObj(name=name) sess = self.sessions.get(sidn) if sess is None: raise s_exc.NoSuchObj(name=name) item = sess.getSessItem(name) if item is None: raise s_exc.NoSuchObj(name=name) s_scope.set('sess', sess) # TODO set user.... methname, args, kwargs = todo if methname[0] == '_': raise s_exc.NoSuchMeth(name=methname) meth = getattr(item, methname, None) if meth is None: logger.warning(f'{item!r} has no method: {methname}') raise s_exc.NoSuchMeth(name=methname) valu = meth(*args, **kwargs) if s_coro.iscoro(valu): valu = await valu try: if isinstance(valu, types.AsyncGeneratorType): desc = 'async generator' await link.tx(('t2:genr', {})) async for item in valu: await link.tx(('t2:yield', {'retn': (True, item)})) await link.tx(('t2:yield', {'retn': None})) return elif isinstance(valu, types.GeneratorType): desc = 'generator' await link.tx(('t2:genr', {})) for item in valu: await link.tx(('t2:yield', {'retn': (True, item)})) await link.tx(('t2:yield', {'retn': None})) return except Exception as e: logger.exception(f'error during {desc} task: {methname}') if not link.isfini: retn = s_common.retnexc(e) await link.tx(('t2:yield', {'retn': retn})) return if isinstance(valu, s_share.Share): sess.onfini(valu) iden = s_common.guid() sess.setSessItem(iden, valu) info = s_reflect.getShareInfo(valu) await link.tx(('t2:share', {'iden': iden, 'sharinfo': info})) return await link.tx(('t2:fini', {'retn': (True, valu)})) except Exception as e: logger.exception('on t2:init: %r' % (mesg,)) if not link.isfini: retn = s_common.retnexc(e) await link.tx(('t2:fini', {'retn': retn}))
async def t2call(link, meth, args, kwargs): ''' Call the given ``meth(*args, **kwargs)`` and handle the response to provide telepath task v2 events to the given link. ''' try: valu = meth(*args, **kwargs) if s_coro.iscoro(valu): valu = await valu try: first = True if isinstance(valu, types.AsyncGeneratorType): async for item in valu: if first: await link.tx(('t2:genr', {})) first = False await link.tx(('t2:yield', {'retn': (True, item)})) if first: await link.tx(('t2:genr', {})) await link.tx(('t2:yield', {'retn': None})) return elif isinstance(valu, types.GeneratorType): for item in valu: if first: await link.tx(('t2:genr', {})) first = False await link.tx(('t2:yield', {'retn': (True, item)})) if first: await link.tx(('t2:genr', {})) await link.tx(('t2:yield', {'retn': None})) return except s_exc.DmonSpawn as e: context = e.__context__ if context: if not isinstance(context, asyncio.CancelledError): logger.error('Error during DmonSpawn call: %r', context) await link.fini() return except (asyncio.CancelledError, Exception) as e: if isinstance(e, asyncio.CancelledError): logger.info('t2call task %s cancelled', meth.__name__) else: logger.exception('error during task %s', meth.__name__) if isinstance(valu, types.AsyncGeneratorType): await valu.aclose() elif isinstance(valu, types.GeneratorType): valu.close() if not link.isfini: if first: await link.tx(('t2:genr', {})) retn = s_common.retnexc(e) await link.tx(('t2:yield', {'retn': retn})) return if isinstance(valu, s_share.Share): info = s_reflect.getShareInfo(valu) await link.tx(('t2:share', {'iden': valu.iden, 'sharinfo': info})) return valu await link.tx(('t2:fini', {'retn': (True, valu)})) except s_exc.DmonSpawn as e: context = e.__context__ if context: logger.error('Error during DmonSpawn call: %r', context) await link.fini() return except (asyncio.CancelledError, Exception) as e: logger.exception('error during task: %s', meth.__name__) if not link.isfini: retn = s_common.retnexc(e) await link.tx(('t2:fini', {'retn': retn}))
async def _onTaskV2Init(self, link, mesg): # t2:init is used by the pool sockets on the client name = mesg[1].get('name') sidn = mesg[1].get('sess') todo = mesg[1].get('todo') try: if sidn is None or todo is None: raise s_exc.NoSuchObj(name=name) sess = self.sessions.get(sidn) if sess is None: raise s_exc.NoSuchObj(name=name) item = sess.getSessItem(name) if item is None: raise s_exc.NoSuchObj(name=name) s_scope.set('sess', sess) # TODO set user.... methname, args, kwargs = todo if methname[0] == '_': raise s_exc.NoSuchMeth(name=methname) meth = getattr(item, methname, None) if meth is None: logger.warning(f'{item!r} has no method: {methname}') raise s_exc.NoSuchMeth(name=methname) valu = meth(*args, **kwargs) if s_coro.iscoro(valu): valu = await valu if isinstance(valu, types.AsyncGeneratorType): try: await link.tx(('t2:genr', {})) async for item in valu: await link.tx(('t2:yield', {'retn': (True, item)})) await link.tx(('t2:yield', {'retn': None})) except Exception as e: if not link.isfini: retn = s_common.retnexc(e) await link.tx(('t2:yield', {'retn': retn})) return if isinstance(valu, types.GeneratorType): try: await link.tx(('t2:genr', {})) for item in valu: await link.tx(('t2:yield', {'retn': (True, item)})) await link.tx(('t2:yield', {'retn': None})) except Exception as e: if not link.isfini: retn = s_common.retnexc(e) await link.tx(('t2:yield', {'retn': (False, retn)})) return if isinstance(valu, s_share.Share): iden = s_common.guid() sess.setSessItem(iden, valu) await link.tx(('t2:share', {'iden': iden})) return await link.tx(('t2:fini', {'retn': (True, valu)})) except Exception as e: logger.exception('on task:init: %r' % (mesg, )) if not link.isfini: retn = s_common.retnexc(e) await link.tx(('t2:fini', {'retn': retn}))