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
async def send_notification(self, request): response = NotificationResult(request.notification_id, APNS_RESPONSE_CODE.BAD_REQUEST) response.description = "connection error" 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) return response logger.debug('Notification %s: connection %s acquired', request.notification_id, connection) response.description = "internal error" 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) except: logger.debug('internal error for %s', request.notification_id) return response
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.create_connection() except Exception as e: logger.error("Could not connect to server: %s", str(e)) self._lock.release() raise ConnectionError() self.connections.append(connection) logger.info("Connection established (total: %d)", len(self.connections)) 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
async def send_notification(self, request): failed_attempts = 0 while True: logger.debug('Notification %s: waiting for connection', request.notification_id) try: connection = await self.acquire() except ConnectionError: failed_attempts += 1 logger.warning('Could not send notification %s: ' 'ConnectionError', request.notification_id) if self.max_connection_attempts \ and failed_attempts > self.max_connection_attempts: logger.error('Failed to connect after %d attempts.', failed_attempts) raise 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)
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) connection = await self.acquire() 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_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()
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()
async def send_notification(self, request): attempts = 0 while attempts < self.max_connection_attempts: attempts += 1 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) logger.error("Failed to send after %d attempts.", attempts) raise MaxAttemptsExceeded
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 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: connection = await self.connect() 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_stream_ended(self, stream_id): if stream_id % 2 == 0: logger.warning('End stream: %d', stream_id) self.free_channels.release()