async def get_remote_session_url(self): url = self.__queueUrl + '?' + urllib.parse.urlencode( {"auth": self.__auth.current_user['idToken']}) timeout = aiohttp.ClientTimeout(total=6000) client_session = aiohttp.ClientSession(timeout=timeout) rs_url = None rs_post_url = None is_offerer = True async with sse_client.EventSource( url, session=client_session) as event_source: try: async for event in event_source: print(event) if event.message == 'put' or event.message == 'patch': data = json.loads(event.data) data_path = data['path'] data_data = data['data'] if data_path == '/': if 'rsUrl' in data_data: rs_url = data_data['rsUrl'] if 'rsPostUrl' in data_data: rs_post_url = data_data['rsPostUrl'] if 'createAnswer' in data_data: is_offerer = not data_data['createAnswer'] elif data_path == '/rsUrl': rs_url = data_data if rs_url != None: break print(event) self.__taskQueueKeepAlive.cancel() except ConnectionError: pass await client_session.close() return rs_url, rs_post_url, is_offerer
async def _streamSSEAsync(url, exit=None): """internal""" from asyncio import Event from aiohttp_sse_client import client as sse_client from aiostream.stream import merge async with sse_client.EventSource(url) as event_source: if isinstance(exit, Event): async def _waitExit(): yield await exit.wait() waits = (_waitExit(), event_source) else: waits = (event_source,) try: async with merge(*waits).stream() as stream: try: async for event in stream: if event == True: # noqa: E712 return yield json.loads(event.data) except ConnectionError: raise PyEXception("Could not connect to SSE Stream") except PyEXStopSSE: return except BaseException: raise except (json.JSONDecodeError, KeyboardInterrupt): raise
async def __aiter__(self) -> AsyncIterator[events.CommandEventContext]: from aiohttp_sse_client import client as sse_client # type: ignore api_client = self._api.api_client host = api_client.configuration.host headers = api_client.default_headers api_client.update_params_for_auth(headers, None, ["app_key"]) activity_id = self._activity_id batch_id = self._batch_id last_idx = self._size - 1 async with sse_client.EventSource( f"{host}/activity/{activity_id}/exec/{batch_id}", headers=headers, timeout=self.seconds_left(), ) as event_source: try: async for msg_event in event_source: try: evt_ctx = _command_event_ctx(msg_event) except Exception as exc: # noqa _log.error(f"Event stream exception (batch {batch_id}): {exc}") else: yield evt_ctx if evt_ctx.computation_finished(last_idx): break except ClientPayloadError as exc: _log.error(f"Event payload error (batch {batch_id}): {exc}") except ConnectionError: raise except asyncio.TimeoutError: raise BatchTimeoutError()
async def test_eventsource_reconnect_event(): """Test EventSource: reconnection event. ..seealso: https://github.com/web-platform-tests/wpt/blob/master/ eventsource/eventsource-reconnect.htm """ opened = False reconnected = False def on_error(): nonlocal reconnected assert source.ready_state == sse_client.READY_STATE_CONNECTING assert opened is True reconnected = True async with sse_client.EventSource( WPT_SERVER + 'resources/status-reconnect.py?status=200&ok_first&id=2', reconnection_time=timedelta(milliseconds=2), on_error=on_error) as source: async for e in source: if not opened: opened = True assert reconnected is False assert e.data == "ok" else: assert reconnected is True assert e.data == "data" break
async def test_eventsource_request_cancellation(): """Test EventSource: reconnection event. ..seealso: https://github.com/web-platform-tests/wpt/blob/master/ eventsource/eventsource-request-cancellation.htm """ closed = False def on_open(): if closed: assert False def on_error(): assert source.ready_state == sse_client.READY_STATE_CLOSED try: async with sse_client.EventSource( WPT_SERVER + 'resources/message.py?sleep=1000&message=' + "retry:1000\ndata:abc\n\n", on_open=on_open, on_error=on_error) as source: raise ConnectionAbortedError except ConnectionAbortedError: closed = True await asyncio.sleep(1) assert source.ready_state == sse_client.READY_STATE_CLOSED pass
async def connect(self, reconnect_time: Optional[float] = 5): """ Connect to the beefweb eventsource and begin listening for events. :param Optional[float] reconnect_time: The number of seconds to wait between reconnection attempts. """ if self.is_connected(): return reconnect_time = timedelta(seconds=reconnect_time) # necessary to prevent connection from timing out after 5 minutes timeout = ClientTimeout(sock_read=0) self._session = ClientSession(timeout=timeout) self._connection = sse_client.EventSource( self._url, session=self._session, headers=self._headers, reconnection_time=reconnect_time, max_connect_retry=1000000000, on_error=self._connection_error_handler, ) loop = asyncio.get_event_loop() self._event_reader = loop.create_task(self._read_events())
async def test_format_field_id_2(): """Test EventSource: Last-Event-ID (2). ..seealso: https://github.com/web-platform-tests/wpt/blob/master/ eventsource/format-field-id-2.htm """ counter = 0 async with sse_client.EventSource( WPT_SERVER + 'resources/last-event-id.py', ) as source: async for e in source: if counter == 0: counter += 1 assert e.data == "hello" # default last event id is Unicode U+2026 assert e.last_event_id == "…" last_id = e.last_event_id elif counter in (1, 2): counter += 1 assert e.data == last_id assert e.last_event_id == last_id break else: fail("Unexpected counter {}".format(counter))
async def test_eventsource_close(): """Test EventSource: close. ..seealso: https://github.com/web-platform-tests/wpt/blob/master/ eventsource/eventsource-close.htm """ source = sse_client.EventSource(WPT_SERVER + 'resources/message.py') assert source.ready_state == sse_client.READY_STATE_CONNECTING await source.connect() assert source.ready_state == sse_client.READY_STATE_OPEN await source.close() assert source.ready_state == sse_client.READY_STATE_CLOSED count = 0 reconnected = False def on_error(): nonlocal count, reconnected if count == 1: assert source.ready_state == sse_client.READY_STATE_CONNECTING reconnected = True elif count == 2: assert source.ready_state == sse_client.READY_STATE_CONNECTING count += 1 elif count == 3: assert source.ready_state == sse_client.READY_STATE_CLOSED else: assert False async with sse_client.EventSource( WPT_SERVER + 'resources/reconnect-fail.py?id=' + str(datetime.utcnow().timestamp()), reconnection_time=timedelta(milliseconds=2), on_error=on_error) as source: try: async for e in source: if count == 0: assert reconnected is False assert e.data == "opened" elif count == 1: assert reconnected is True assert e.data == "reconnected" else: assert False count += 1 except ConnectionError: pass
async def test_request_post_to_connect(): """Test EventSource option method for connection. """ source = sse_client.EventSource(WPT_SERVER + 'resources/message.py', option={'method': "POST"}) await source.connect() async for e in source: assert e.data == "data" break await source.close()
async def publish(): """Read events from pub-sub channel.""" redis = await aioredis.create_redis_pool(redis_url) while True: async with sse_client.EventSource(wikimedia_stream) as event_source: try: async for event in event_source: json.loads(event.data) await redis.publish(pubsub_channel, event.data) except (ConnectionError, aio.TimeoutError, aio.CancelledError): pass
async def _req(url=url, json=json, wrap=wrap, field=field): async with sse_client.EventSource(url) as event_source: async for event in event_source: data = event.data if json: data = JSON.loads(data) if field: data = data[field] if wrap: data = [data] yield data
async def get_stream(url_from: str): async with sse_client.EventSource(url_from) as event_source: try: async for event in event_source: try: change = json.loads(event.data) except ValueError: pass else: yield change except Exception as error: logger.error(f"Error occurred: {error}")
async def test_eventsource_close(): """Test EventSource: close. ..seealso: https://github.com/web-platform-tests/wpt/blob/master/ eventsource/eventsource-close.htm """ source = sse_client.EventSource(WPT_SERVER + 'resources/message.py') assert source.ready_state == sse_client.READY_STATE_CONNECTING await source.connect() assert source.ready_state == sse_client.READY_STATE_OPEN await source.close() assert source.ready_state == sse_client.READY_STATE_CLOSED
async def get_events(): conn = aiohttp.TCPConnector() auth = aiohttp.BasicAuth(args.user, args.password) client = aiohttp.ClientSession(connector=conn, auth=auth) async with sse_client.EventSource("http://" + args.controller + ":8181" + args.uri, session=client) as event_source: try: async for event in event_source: logger.info(event) except ConnectionError: pass
async def subscribe_collateral(): async with sse_client.EventSource(args.url + '/borrower/events', headers=headers, timeout=-1) as event_source: try: async for msg in event_source: print(msg) except Exception as error: err_msg = repr(error) print(err_msg) pass
async def test(status): def on_error(): assert source.ready_state == sse_client.READY_STATE_CLOSED def on_message(): assert source.ready_state == sse_client.READY_STATE_OPEN source = sse_client.EventSource( WPT_SERVER + 'resources/status-error.py?status=' + str(status), on_message=on_message, on_error=on_error) with pytest.raises(ConnectionError): await source.connect()
async def test_rquest_accept(): """Test EventSource: Accept header. ..seealso: https://github.com/web-platform-tests/wpt/blob/master/ eventsource/request-accept.htm """ source = sse_client.EventSource(WPT_SERVER + 'resources/accept.event_stream?pipe=sub') await source.connect() async for e in source: assert e.data == "text/event-stream" break await source.close()
async def test_eventsource_reconnect(): """Test EventSource: reconnection. ..seealso: https://github.com/web-platform-tests/wpt/blob/master/ eventsource/eventsource-reconnect.htm """ source = sse_client.EventSource(WPT_SERVER + 'resources/status-reconnect.py?status=200') await source.connect() async for e in source: assert e.data == 'data' break await source.close()
async def test_rquest_cache_control(): """Test EventSource: Cache-Control. ..seealso: https://github.com/web-platform-tests/wpt/blob/master/ eventsource/request-cache-control.htm """ source = sse_client.EventSource( WPT_SERVER + 'resources/cache-control.event_stream?pipe=sub') await source.connect() async for e in source: assert e.data == "no-cache" break await source.close()
async def process_updates(self): from aiohttp_sse_client import client as sse_client from aiohttp import ClientTimeout _LOGGER.debug('Starting sse reader') token = await self._auth_session.token() headers = {'Accept-Language': 'en-US', 'Authorization': token} tries = 0 while True: try: async with sse_client.EventSource( _build_api_url('/homeappliances/{haid}/events', self._haId), session=self._auth_session.session, headers=headers, timeout=ClientTimeout(total=None)) as event_source: self._hass.async_create_task(self.fetch_initial_state()) async for event in event_source: tries = 0 # Reset backoff if we read any event successfully if event.type != 'KEEP-ALIVE': _LOGGER.debug('Received event: %s', event) if event.data: try: data = json.loads(event.data) for item in data['items']: if 'key' in item and 'value' in item: self.handle_key_value( item['key'], item['value']) except Exception as e: _LOGGER.debug('SSE reader failed parsing %s', event.data) elif event.type == 'DISCONNECTED': self.handle_key_value('DISCONNECTED', '') pass elif event.type == 'CONNECTED': self._hass.async_create_task( self.fetch_initial_state()) pass except ConnectionError as ce: _LOGGER.debug('SSE reader caught connection error: %s', ce) if '401' in ce.args[0]: # Ugly way to extract http status _LOGGER.debug('Fetching new access token') token = await self._auth_session.token( headers['Authorization']) headers['Authorization'] = token tries += 1 except Exception as e: _LOGGER.debug('SSE reader caught exception: %s', e) tries += 1 await asyncio.sleep(min(600, 2**tries))
async def stream_events(url='https://api.blaseball.com/events/streamData', retry_base=0.01, retry_max=300, on_parse_error='LOG'): """ Async generator for the events API. `retry_base` will be the minimum time to delay if there's a connection error `retry_max` is the maximum time to delay if there's a connection error """ retry_delay = retry_base event_current = {} delta_previous = None while True: try: async with sse_client.EventSource(url, read_bufsize=2 ** 19) as src: async for event in src: retry_delay = retry_base # reset backoff if not event.data: continue raw_event = ujson.loads(event.data) if 'value' in raw_event.keys(): # New, full event delta_previous = None event_current = raw_event['value'] payload = event_current elif 'delta' in raw_event.keys(): # Delta event if raw_event['delta'] == delta_previous: continue delta_previous = raw_event['delta'] jsonpatch.apply_patch(event_current, raw_event['delta'], in_place=True) payload = event_current else: raise ValueError("Unknown event type: {}".format(raw_event.keys())) yield payload except (ConnectionError, TimeoutError, ClientPayloadError, futures.TimeoutError, asyncio.exceptions.TimeoutError, ClientConnectorError, ServerDisconnectedError): await asyncio.sleep(retry_delay) retry_delay = min(retry_delay * 2, retry_max) except (jsonpatch.JsonPatchConflict, jsonpointer.JsonPointerException, IndexError, ValueError) as error: if on_parse_error.lower()=='skip': pass elif on_parse_error.lower()=='raise': print("Event parse error.") raise else: # log and restart, default print("Event parse error.") print(error)
async def start_sse_event_listener(): try: # cache so we don't have to look up every event _load_json = load_json _see_handler = on_sse_event event_prefix = 'openhab' if not IS_OH2 else 'smarthome' async with sse_client.EventSource( url=f'{HTTP_PREFIX}/rest/events?topics=' f'{event_prefix}/items/,' # Item updates f'{event_prefix}/channels/,' # Channel update f'{event_prefix}/things/*/status,' # Thing status updates f'{event_prefix}/things/*/statuschanged' # Thing status changes , session=HTTP_SESSION) as event_source: async for event in event_source: e_str = event.data try: e_json = _load_json(e_str) except ValueError: log_events.warning(f'Invalid json: {e_str}') continue except TypeError: log_events.warning(f'Invalid json: {e_str}') continue # Log sse event if log_events.isEnabledFor(logging.DEBUG): log_events._log(logging.DEBUG, e_str, []) # process _see_handler(e_json) except asyncio.CancelledError: # This exception gets raised if we cancel the coroutine # since this is normal behaviour we ignore this exception pass except Exception as e: disconnect = is_disconnect_exception(e) lvl = logging.WARNING if disconnect else logging.ERROR log.log(lvl, f'SSE request Error: {e}') for line in traceback.format_exc().splitlines(): log.log(lvl, line) # reconnect even if we have an unexpected error if not disconnect: set_offline(f'Uncaught error in process_sse_events: {e}')
async def start_sse_event_listener(): try: # cache so we don't have to look up every event call = ON_SSE_EVENT options = {} if HABApp.CONFIG.openhab.connection.user or HABApp.CONFIG.openhab.connection.password: options['with_credentials'] = True event_prefix = 'openhab' if not IS_OH2 else 'smarthome' async with sse_client.EventSource( url=f'{HTTP_PREFIX}/rest/events?topics=' f'{event_prefix}/items/,' # Item updates f'{event_prefix}/channels/,' # Channel update f'{event_prefix}/things/*/status,' # Thing status updates f'{event_prefix}/things/*/statuschanged' # Thing status changes , option=options, session=HTTP_SESSION) as event_source: async for event in event_source: try: event = load_json(event.data) except ValueError: continue except TypeError: continue # Log sse event if log_events.isEnabledFor(logging.DEBUG): log_events._log(logging.DEBUG, event, []) # process call(event) except asyncio.CancelledError: # This exception gets raised if we cancel the coroutine # since this is normal behaviour we ignore this exception pass except Exception as e: disconnect = is_disconnect_exception(e) lvl = logging.WARNING if disconnect else logging.ERROR log.log(lvl, f'SSE request Error: {e}') for line in traceback.format_exc().splitlines(): log.log(lvl, line) # reconnect even if we have an unexpected error if not disconnect: set_offline(f'Uncaught error in process_sse_events: {e}')
async def test(status): def on_error(): assert False def on_open(): assert source.ready_state == sse_client.READY_STATE_OPEN source = sse_client.EventSource(WPT_SERVER.replace( 'eventsource', 'common/redirect.py?' 'location=/eventsource/resources/message.py&status=' + str(status)), on_open=on_open, on_error=on_error) await source.connect() await source.close()
async def connect(self): """Connect to Nest Stream API.""" if self._session is None: self._session = aiohttp.ClientSession() if self._session.closed: raise ConnectionRefusedError('session is closed') if (self._access_token_expires_at is not None and self._access_token_expires_at < datetime.utcnow()): raise ConnectionRefusedError('access token is expired') self._event_stream = sse_client.EventSource( API_URL + '/', headers={'Authorization': 'Bearer {}'.format(self._access_token)}, session=self._session) await self._event_stream.connect()
async def test_eventsource_onopen(): """Test EventSource: open (announcing the connection). ..seealso: https://github.com/web-platform-tests/wpt/blob/master/ eventsource/eventsource-onopen.htm """ def on_open(): """Callback for open event.""" assert source.ready_state == sse_client.READY_STATE_OPEN source = sse_client.EventSource(WPT_SERVER + 'resources/message.py', on_open=on_open) assert source.ready_state == sse_client.READY_STATE_CONNECTING await source.connect() assert source.ready_state == sse_client.READY_STATE_OPEN await source.close()
async def startJukeboxConnection(self): try: newdevice = {"jukebox": {"name": "Jukebox", "nowplaying": {}}} await self.dataset.ingest({"player": newdevice}) while self.running == True: try: self.log.info('.. starting jukebox SSE connection') # This should establish an SSE connection with the Jukebox timeout = aiohttp.ClientTimeout(total=0) async with sse_client.EventSource( self.config.jukebox_url + "/sse", timeout=timeout) as event_source: try: async for event in event_source: #self.log.info('.. SSE: %s' % event) try: data = json.loads(event.data) if 'nowplaying' in data: self.log.info('<< %s' % data) await self.dataset.ingest({ "player": { "jukebox": { "nowplaying": data['nowplaying'] } } }) except: self.log.error('error parsing data', exc_info=True) except ConnectionError: self.log.error('!! error with SSE connection', exc_info=True) self.log.warning('!! SSE connection ended?') except aiohttp.client_exceptions.ClientConnectorCertificateError: self.log.error( '!! error - jukebox SSL certificate error') except concurrent.futures._base.TimeoutError: self.log.error('!! error - jukebox SSE timeout') except: self.log.error( '!! error starting jukebox SSE connection', exc_info=True) except: self.log.error('!! Error in Jukebox connection loop', exc_info=True)
async def test_eventsource_onmessage(): """Test EventSource: onmessage. ..seealso: https://github.com/web-platform-tests/wpt/blob/master/ eventsource/eventsource-onmessage.htm """ def on_message(event): """Callback for message event.""" assert event.data == "data" source = sse_client.EventSource(WPT_SERVER + 'resources/message.py', on_message=on_message) await source.connect() async for e in source: assert e.data == "data" break await source.close()
async def _streamSSEAsync(url, accrue=False): """internal""" from aiohttp_sse_client import client as sse_client async with sse_client.EventSource(url) as event_source: try: async for event in event_source: yield json.loads(event.data) except (json.JSONDecodeError, KeyboardInterrupt): raise except ConnectionError: raise PyEXception("Could not connect to SSE Stream") except PyEXStopSSE: return except Exception: raise return
async def connect(self): self.__received_messages = {} # empty dict for received messages headers = { "Authorization": "Bearer " + self.__auth.current_user['idToken'], 'content-type': 'application/json' } self._http = aiohttp.ClientSession(headers=headers) path = self.__rs_url + '?' + urllib.parse.urlencode( {"auth": self.__auth.current_user['idToken']}) timeout = aiohttp.ClientTimeout(total=6000) self._session = aiohttp.ClientSession(timeout=timeout) self.__event_source = sse_client.EventSource( path, session=self._session, on_message=self.rs_message, on_error=self.rs_error) await self.__event_source.connect() self.__rs_task = asyncio.ensure_future(self.rs_listen())