import sys import json import aiohttp import asyncio from flask import current_app from capsulflask.db import my_exec_info_message from capsulflask.db_model import OnlineHost from typing import List class HTTPResult: def __init__(self, status_code, body=None): self.status_code = status_code self.body = body class MyHTTPClient: def __init__(self, timeout_seconds = 5): self.client_session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=timeout_seconds)) self.event_loop = asyncio.get_event_loop() def make_requests_sync(self, online_hosts: List[OnlineHost], body: str) -> List(HTTPResult): self.event_loop.run_until_complete(self.make_requests(online_hosts=online_hosts, body=body)) def post_json_sync(self, url: str, body: str, method="POST", authorization_header=None) -> HTTPResult: self.event_loop.run_until_complete(self.post_json_sync(method=method, url=url, body=body)) async def post_json(self, url: str, body: str, method="POST", authorization_header=None) -> HTTPResult: response = None try: headers = {} if authorization_header != None: headers['Authorization'] = authorization_header response = await self.client_session.request( method=method, url=url, json=body, headers=headers, auth=aiohttp.BasicAuth("hub", current_app.config['HUB_TOKEN']), verify_ssl=True, ) except: error_message = my_exec_info_message(sys.exc_info()) response_body = json.dumps({"error_message": f"error contacting spoke: {error_message}"}) current_app.logger.error(f""" error contacting spoke: post_json (HTTP {method} {url}) failed with: {error_message}""" ) return HTTPResult(-1, response_body) response_body = None try: response_body = await response.text() except: error_message = my_exec_info_message(sys.exc_info()) response_body = json.dumps({"error_message": f"error reading response from spoke: {error_message}"}) current_app.logger.error(f""" error reading response from spoke: HTTP {method} {url} (status {response.status}) failed with: {error_message}""" ) return HTTPResult(response.status, response_body) async def make_requests(self, online_hosts: List[OnlineHost], body: str) -> List(HTTPResult): tasks = [] # append to tasks in the same order as online_hosts for host in online_hosts: tasks.append( self.post_json(url=host.url, body=body) ) # gather is like Promise.all from javascript, it returns a future which resolves to an array of results # in the same order as the tasks that we passed in -- which were in the same order as online_hosts results = await asyncio.gather(*tasks) return results