class WebServer: def __init__(self, loop): self.host = config.get('webserver.host') self.port = config.get('webserver.port') self.loop = loop self.app = None self.handler = None self.server = None def __enter__(self): self.app = Application(loop=self.loop) for api in api_registry: self.app.router.add_route(api.method, API_ROUTE + api.path, api.handler) setup_swagger(self.app, swagger_url=API_ROUTE, title='Skywall web API') if os.path.isdir('build'): self.app.router.add_static(BUILD_ROUTE, 'build') self.app.router.add_get('/{tail:.*}', get_frontend) self.handler = self.app.make_handler() self.loop.run_until_complete(self.app.startup()) self.server = self.loop.run_until_complete( self.loop.create_server(self.handler, self.host, self.port)) print('Web server listening on http://{}:{}'.format( self.host, self.port), flush=True) return self def __exit__(self, exc_type, exc_val, exc_tb): self.server.close() self.loop.run_until_complete(self.server.wait_closed()) self.loop.run_until_complete(self.app.shutdown()) self.loop.run_until_complete(self.handler.shutdown(60)) self.loop.run_until_complete(self.app.cleanup())
def gen_rpc_context(loop, host, port, rpc, rpc_route, routes=(), RpcContext=RpcContext): # make app app = Application(loop=loop) app.router.add_route(*rpc_route) for route in routes: app.router.add_route(*route) # make handler handler = app.make_handler() server = loop.run_until_complete(loop.create_server(handler, host, port)) # create RpcContext rpc_context = RpcContext(app, rpc, host, port, rpc_route[1]) yield rpc_context # teardown clients loop.run_until_complete(rpc_context.finish_connections()) # teardown server server.close() loop.run_until_complete(server.wait_closed()) loop.run_until_complete(app.shutdown()) loop.run_until_complete(handler.finish_connections(1)) loop.run_until_complete(app.cleanup())
class WebsocketServer: def __init__(self, loop): self.host = config.get('server.host') self.port = config.get('server.port') self.loop = loop self.app = None self.handler = None self.server = None self.connections = [] def __enter__(self): before_server_start.emit(server=self) self.app = Application(loop=self.loop) self.app.router.add_get('/', self._connect) self.app.on_shutdown.append(self._on_shutdown) self.handler = self.app.make_handler() self.loop.run_until_complete(self.app.startup()) self.server = self.loop.run_until_complete( self.loop.create_server(self.handler, self.host, self.port)) print('Websocket server listening on http://{}:{}'.format( self.host, self.port), flush=True) after_server_start.emit(server=self) return self def __exit__(self, exc_type, exc_val, exc_tb): before_server_stop.emit(server=self) self.server.close() self.loop.run_until_complete(self.server.wait_closed()) self.loop.run_until_complete(self.app.shutdown()) self.loop.run_until_complete(self.handler.shutdown(60)) self.loop.run_until_complete(self.app.cleanup()) after_server_stop.emit(server=self) async def _connect(self, request): connection = WebsocketConnection(self, request) # If a client connects again without closing his previous connection, close it manually. # Each client may have only one opened connection at any time. old_connection = self.get_connection(connection.client_id) if old_connection: await old_connection.close() self.connections.append(connection) try: await connection.connect() finally: self.connections.remove(connection) return connection.socket async def _on_shutdown(self, app): for connection in self.connections: await connection.close() def get_connection(self, client_id): for connection in self.connections: if connection.client_id == client_id: return connection return None
class ControlSocket(object): """Create a asynchronous HTTP server ready to receive commands.""" # TODO rivedere i test di questa classe def __init__(self, timetable, heating, cooling, thermometer, host, port, lock, loop): baselogger.debug('initializing control socket') if not isinstance(lock, asyncio.Condition): raise TypeError( 'the lock in ControlSocket must be an asyncio.Condition object' ) self.app = Application(middlewares=[exceptions_middleware], loop=loop) self.host = host self.port = port self.app['lock'] = lock self.app['monitors'] = asyncio.Queue(loop=loop) self.app['timetable'] = timetable self.app['heating'] = heating self.app['cooling'] = cooling self.app['thermometer'] = thermometer self.app.router.add_get('/{action}', GET_handler) self.app.router.add_post('/{action}', POST_handler) baselogger.debug('control socket initialized') def start(self): """Start the internal HTTP server.""" baselogger.debug('starting control socket') loop = self.app.loop loop.run_until_complete(self.app.startup()) self.handler = self.app.make_handler() self.srv = loop.run_until_complete( loop.create_server(self.handler, self.host, self.port)) baselogger.info('control socket listening on {}:{}', self.host, self.port) def stop(self): """Stop the internal HTTP server.""" baselogger.debug('stopping control socket') self.srv.close() loop = self.app.loop loop.run_until_complete(self.srv.wait_closed()) loop.run_until_complete(self.app.shutdown()) loop.run_until_complete(self.handler.shutdown(6)) loop.run_until_complete(self.app.cleanup()) baselogger.info('control socket halted') async def update_monitors(self, status): """Send new status to every connected monitor. The new status must be a subclass of `thermod.common.ThermodStatus` to be fully compliant. """ if not isinstance(status, ThermodStatus): raise TypeError( 'new status for monitors must be a ThermodStatus object') baselogger.debug('updating connected monitors') if self.app['monitors'].empty(): baselogger.debug('no monitors to be updated, the queue is empty') else: baselogger.debug('there are {} monitor(s) in the queue'.format( self.app['monitors'].qsize())) count = 0 while not self.app['monitors'].empty(): try: future = await self.app['monitors'].get() if not future.cancelled(): future.set_result(status) baselogger.debug('monitor {} updated'.format(count)) else: baselogger.debug('monitor {} disconnected'.format(count)) except asyncio.InvalidStateError: baselogger.debug( 'cannot update monitor {} because the client ' 'has probably closed the connection'.format(count)) count += 1