async def acquire(self): for connection in self.connections: if not connection.is_busy: return connection else: await self._lock.acquire() for connection in self.connections: if not connection.is_busy: self._lock.release() return connection if len(self.connections) < self.max_connections: try: connection = await self.connect() except Exception as e: logger.error('Could not connect to server: %s', str(e)) self._lock.release() raise ConnectionError() self.connections.append(connection) self._lock.release() return connection else: self._lock.release() logger.warning('Pool is busy, wait...') while True: await asyncio.sleep(0.01) for connection in self.connections: if not connection.is_busy: return connection
def on_response_received(self, headers): notification_id = headers.get(b'apns-id').decode('utf8') status = headers.get(b':status').decode('utf8') if status == APNS_RESPONSE_CODE.SUCCESS: request = self.requests.pop(notification_id, None) if request: result = NotificationResult(notification_id, status) request.set_result(result) else: logger.warning( 'Got response for unknown notification request %s', notification_id) else: self.request_statuses[notification_id] = status
def data_received(self, data): for event in self.conn.receive_data(data): if isinstance(event, ResponseReceived): headers = dict(event.headers) self.on_response_received(headers) elif isinstance(event, DataReceived): self.on_data_received(event.data, event.stream_id) elif isinstance(event, RemoteSettingsChanged): self.on_remote_settings_changed(event.changed_settings) elif isinstance(event, StreamEnded): self.on_stream_ended(event.stream_id) elif isinstance(event, ConnectionTerminated): self.on_connection_terminated(event) elif isinstance(event, WindowUpdated): pass else: logger.warning('Unknown event: %s', event) self.flush()
def on_data_received(self, data, stream_id): data = json.loads(data.decode()) reason = data.get('reason', '') if not reason: return notification_id = self.request_streams.pop(stream_id, None) if notification_id: request = self.requests.pop(notification_id, None) if request: # TODO: Теоретически здесь может быть ошибка, если нет ключа status = self.request_statuses.pop(notification_id) result = NotificationResult(notification_id, status, description=reason) request.set_result(result) else: logger.warning('Could not find request %s', notification_id) else: logger.warning('Could not find notification by stream %s', stream_id)
async def send_notification(self, request): attempt = 0 while True: attempt += 1 if attempt > self.MAX_ATTEMPTS: logger.warning('Trying to send notification %s: attempt #%s', request.notification_id, attempt) logger.debug('Notification %s: waiting for connection', request.notification_id) try: connection = await self.acquire() except ConnectionError: logger.warning( 'Could not send notification %s: ' 'ConnectionError', request.notification_id) await asyncio.sleep(1) continue logger.debug('Notification %s: connection %s acquired', request.notification_id, connection) try: response = await connection.send_notification(request) return response except NoAvailableStreamIDError: connection.close() except ConnectionClosed: logger.warning( 'Could not send notification %s: ' 'ConnectionClosed', request.notification_id) except FlowControlError: logger.debug('Got FlowControlError for notification %s', request.notification_id) await asyncio.sleep(1)
def on_stream_ended(self, stream_id): if stream_id % 2 == 0: logger.warning('End stream: %d', stream_id) self.free_channels.release()
def on_connection_terminated(self, event): logger.warning( 'Connection %s terminated: code=%s, additional_data=%s, ' 'last_stream_id=%s', self, event.error_code, event.additional_data, event.last_stream_id) self.close()