async def leave(self, channel: str, wait_ack: bool = False) -> HorseResult: """ Leavess from a channel :param channel: Channel name :param wait_ack: If true, waits for acknowledge from server :return: If waits for ack, ack result. Otherview Ok if message is sent successfuly """ msg = HorseMessage() msg.type = MessageType.Server msg.content_type = KnownContentTypes.LEAVE.value msg.target = channel msg.pending_response = wait_ack result: HorseResult if wait_ack: msg.message_id = unique_generator.create() result = await self.request(msg) else: result = self.send(msg) # remove channel from joined channel list if result.code == ResultCode.Ok: self.__joined_channels.remove(channel) return result
async def join(self, channel: str, wait_ack: bool = False) -> HorseResult: """ Joins to a channel :param channel: Channel name :param wait_ack: If true, waits for acknowledge from server :return: If waits for ack, ack result. Otherview Ok if message is sent successfuly """ msg = HorseMessage() msg.type = MessageType.Server msg.content_type = KnownContentTypes.JOIN.value msg.target = channel msg.pending_response = wait_ack result: HorseResult if wait_ack: msg.message_id = unique_generator.create() result = await self.request(msg) else: result = self.send(msg) # add channel to joined list (if not already added) if result.code == ResultCode.Ok: has = next((x for x in self.__joined_channels if x == channel), None) if not has: self.__joined_channels.append(channel) return result
async def send_get_ack( self, msg: HorseMessage, additional_headers: List[MessageHeader] = None ) -> HorseResult: # Awaitable[HorseResult]: """ Sends a message and waits for acknowledge :param msg: Sending message :param additional_headers: Additional message headers :return: Returns a result after acknowledge received or timed out """ future: asyncio.Future = None try: writer = ProtocolWriter() if msg.source_len == 0: msg.source = self.id if not msg.message_id: msg.message_id = unique_generator.create() msg.pending_response = False if not msg.pending_acknowledge: msg.pending_acknowledge = True tracking = await self.__tracker.track(msg, self.ack_timeout) bytes = writer.write(msg, additional_headers) self.__socket.sendall(bytes.getbuffer()) while not tracking.future.done(): time.sleep(0.001) resp: HorseMessage = await tracking.future result = HorseResult() if resp is None: result.code = ResultCode.RequestTimeout result.reason = "timeout" else: nack_value = resp.get_header( HorseHeaders.NEGATIVE_ACKNOWLEDGE_REASON) if nack_value is None: result.code = ResultCode.Ok result.reason = "" else: result.code = ResultCode.Failed result.reason = nack_value return result except: self.disconnect() if future is not None: await self.__tracker.forget(msg) result = HorseResult() result.code = ResultCode.SendError result.reason = "" return result
async def request( self, msg: HorseMessage, additional_headers: List[MessageHeader] = None) -> HorseResult: """ Sends a request and waits for response :param msg: Request message :param additional_headers: Additional headers :return: Response message is message variable of Horse result """ future: asyncio.Future = None try: writer = ProtocolWriter() if msg.source_len == 0: msg.source = self.id msg.pending_acknowledge = False if not msg.pending_response: msg.pending_response = True tracking = await self.__tracker.track(msg, self.request_timeout) bytes = writer.write(msg, additional_headers) self.__socket.sendall(bytes.getbuffer()) while not tracking.future.done(): time.sleep(0.001) resp: HorseMessage = await tracking.future result = HorseResult() if resp is None: result.code = ResultCode.RequestTimeout result.reason = "timeout" else: result.code = resp.content_type result.message = resp result.reason = resp.get_header(HorseHeaders.REASON) return result except: self.disconnect() if future is not None: await self.__tracker.forget(msg) result = HorseResult() result.code = ResultCode.SendError return result
def __read_frame(self, array: bytearray, sock: socket, msg: HorseMessage) -> int: """ Reads frame data """ first = array[0] second = array[1] if first >= 128: msg.first_acquirer = True first -= 128 if first >= 64: msg.high_priority = True first -= 64 msg.type = first if second >= 128: msg.pending_response = True second -= 128 if second >= 64: msg.pending_acknowledge = True second -= 64 if second >= 32 and msg.type != MessageType.Ping.value and msg.type != MessageType.Pong.value: msg.has_header = True second -= 32 msg.ttl = second id_len = array[2] source_len = array[3] target_len = array[4] size_bytes = [array[5], array[6]] msg.content_type = int.from_bytes(size_bytes, byteorder='little', signed=False) message_len = 0 # length is unsigned in 16 if array[7] == 253: short_len = self.__read_certain(sock, 2) message_len = int.from_bytes(short_len, byteorder='little', signed=False) # length is unsigned int 32 elif array[7] == 254: int_len = self.__read_certain(sock, 4) message_len = int.from_bytes(int_len, byteorder='little', signed=False) # length is unsigned int 64 elif array[7] == 255: long_len = self.__read_certain(sock, 8) message_len = int.from_bytes(long_len, byteorder='little', signed=False) # length is byte else: message_len = array[7] if id_len > 0: id_bytes = self.__read_certain(sock, id_len) msg.message_id = id_bytes.decode('UTF-8') if source_len > 0: source_bytes = self.__read_certain(sock, source_len) msg.source = source_bytes.decode('UTF-8') if target_len > 0: target_bytes = self.__read_certain(sock, target_len) msg.target = target_bytes.decode('UTF-8') return message_len