async def read_any( self, expected_wire_types: Iterable[int]) -> protobuf.MessageType: if __debug__: log.debug( __name__, "%s:%x expect: %s", self.iface.iface_num(), self.sid, expected_wire_types, ) # Load the full message into a buffer, parse out type and data payload msg = await self.read_from_wire() # If we got a message with unexpected type, raise the message via # `UnexpectedMessageError` and let the session handler deal with it. if msg.type not in expected_wire_types: raise UnexpectedMessageError(msg) # find the protobuf type exptype = messages.get_type(msg.type) if __debug__: log.debug(__name__, "%s:%x read: %s", self.iface.iface_num(), self.sid, exptype) workflow.idle_timer.touch() # parse the message and return it return protobuf.load_message(msg.data, exptype)
def test_validate_enum(self): # ok message: msg = Message(-42, 5) writer = ByteArrayWriter() await_result(protobuf.dump_message(writer, msg)) reader = ByteReader(bytes(writer.buf)) nmsg = await_result(protobuf.load_message(reader, Message)) self.assertEqual(msg.sint_field, nmsg.sint_field) self.assertEqual(msg.enum_field, nmsg.enum_field) # bad enum value: msg = Message(-42, 42) writer = ByteArrayWriter() await_result(protobuf.dump_message(writer, msg)) reader = ByteReader(bytes(writer.buf)) with self.assertRaises(TypeError): await_result(protobuf.load_message(reader, Message))
def get() -> protobuf.MessageType | None: stored_auth_type = storage.cache.get( storage.cache.APP_COMMON_AUTHORIZATION_TYPE) if not stored_auth_type: return None msg_wire_type = int.from_bytes(stored_auth_type, "big") msg_type = messages.get_type(msg_wire_type) buffer = storage.cache.get(storage.cache.APP_COMMON_AUTHORIZATION_DATA) reader = utils.BufferReader(buffer) return protobuf.load_message(reader, msg_type)
def _wrap_protobuf_load( reader: protobuf.Reader, expected_type: Type[protobuf.LoadedMessageType], field_cache: protobuf.FieldCache = None, ) -> protobuf.LoadedMessageType: try: return protobuf.load_message(reader, expected_type, field_cache) except Exception as e: if e.args: raise DataError("Failed to decode message: {}".format(e.args[0])) else: raise DataError("Failed to decode message")
def test_validate_enum(self): # ok message: msg = Message(-42, 5) length = protobuf.count_message(msg) buffer_writer = BufferWriter(bytearray(length)) protobuf.dump_message(buffer_writer, msg) buffer_reader = BufferReader(buffer_writer.buffer) nmsg = protobuf.load_message(buffer_reader, Message) self.assertEqual(msg.sint_field, nmsg.sint_field) self.assertEqual(msg.enum_field, nmsg.enum_field) # bad enum value: buffer_writer.seek(0) msg = Message(-42, 42) # XXX this assumes the message will have equal size protobuf.dump_message(buffer_writer, msg) buffer_reader.seek(0) with self.assertRaises(TypeError): protobuf.load_message(buffer_reader, Message)
async def read( self, expected_type: Type[protobuf.LoadedMessageType], field_cache: protobuf.FieldCache = None, ) -> protobuf.LoadedMessageType: if __debug__: log.debug( __name__, "%s:%x expect: %s", self.iface.iface_num(), self.sid, expected_type, ) # Load the full message into a buffer, parse out type and data payload msg = await self.read_from_wire() # If we got a message with unexpected type, raise the message via # `UnexpectedMessageError` and let the session handler deal with it. if msg.type != expected_type.MESSAGE_WIRE_TYPE: raise UnexpectedMessageError(msg) if __debug__: log.debug( __name__, "%s:%x read: %s", self.iface.iface_num(), self.sid, expected_type, ) workflow.idle_timer.touch() # look up the protobuf class and parse the message pbtype = messages.get_type(msg.type) return protobuf.load_message(msg.data, pbtype, field_cache) # type: ignore
def load_message(msg_type, buffer: bytearray) -> protobuf.MessageType: return protobuf.load_message(BufferReader(buffer), msg_type)
async def handle_session(iface: WireInterface, session_id: int, use_workflow: bool = True) -> None: ctx = Context(iface, session_id) next_msg = None # type: Optional[codec_v1.Message] res_msg = None # type: Optional[protobuf.MessageType] req_type = None req_msg = None while True: try: if next_msg is None: # We are not currently reading a message, so let's wait for one. # If the decoding fails, exception is raised and we try again # (with the same `Reader` instance, it's OK). Even in case of # de-synchronized wire communication, report with a message # header is eventually received, after a couple of tries. msg = await ctx.read_from_wire() if __debug__: try: msg_type = messages.get_type(msg.type).__name__ except KeyError: msg_type = "%d - unknown message type" % msg.type log.debug( __name__, "%s:%x receive: <%s>", iface.iface_num(), session_id, msg_type, ) else: # We have a reader left over from earlier. We should process # this message instead of waiting for new one. msg = next_msg next_msg = None # Now we are in a middle of reading a message and we need to decide # what to do with it, based on its type from the message header. # From this point on, we should take care to read it in full and # send a response. # Take a mark of modules that are imported at this point, so we can # roll back and un-import any others. Should not raise. modules = utils.unimport_begin() # We need to find a handler for this message type. Should not # raise. handler = find_handler(iface, msg.type) if handler is None: # If no handler is found, we can skip decoding and directly # respond with failure. Should not raise. res_msg = unexpected_message() else: # We found a valid handler for this message type. # Workflow task, declared for the finally block wf_task = None # type: Optional[HandlerTask] # Here we make sure we always respond with a Failure response # in case of any errors. try: # Find a protobuf.MessageType subclass that describes this # message. Raises if the type is not found. req_type = messages.get_type(msg.type) # Try to decode the message according to schema from # `req_type`. Raises if the message is malformed. req_msg = protobuf.load_message(msg.data, req_type) # At this point, message reports are all processed and # correctly parsed into `req_msg`. # Create the workflow task. wf_task = handler(ctx, req_msg) # Run the workflow task. Workflow can do more on-the-wire # communication inside, but it should eventually return a # response message, or raise an exception (a rather common # thing to do). Exceptions are handled in the code below. if use_workflow: res_msg = await workflow.spawn(wf_task) else: res_msg = await wf_task except UnexpectedMessageError as exc: # Workflow was trying to read a message from the wire, and # something unexpected came in. See Context.read() for # example, which expects some particular message and raises # UnexpectedMessageError if another one comes in. # In order not to lose the message, we pass on the reader # to get picked up by the workflow logic in the beginning of # the cycle, which processes it in the usual manner. # TODO: # We might handle only the few common cases here, like # Initialize and Cancel. next_msg = exc.msg res_msg = None except Exception as exc: # Either: # - the first workflow message had a type that has a # registered handler, but does not have a protobuf class # - the first workflow message was not a valid protobuf # - workflow raised some kind of an exception while running # - something canceled the workflow from the outside if __debug__: if isinstance(exc, ActionCancelled): log.debug(__name__, "cancelled: {}".format(exc.message)) elif isinstance(exc, loop.TaskClosed): log.debug(__name__, "cancelled: loop task was closed") else: log.exception(__name__, exc) res_msg = failure(exc) finally: # If we ran a workflow task, and a default workflow is on, make sure # we do not race against the layout that is inside. # TODO: this is very hacky and complects wire with the ui if wf_task is not None and workflow.default_task is not None: await ui.wait_until_layout_is_running() if res_msg is not None: # Either the workflow returned a response, or we created one. # Write it on the wire. Possibly, the incoming message haven't # been read in full. We ignore this case here and let the rest # of the reports get processed while waiting for the message # header. # TODO: if the write fails, we do not unimport the loaded modules await ctx.write(res_msg) # Cleanup, so garbage collection triggered after un-importing can # pick up the trash. req_type = None req_msg = None res_msg = None handler = None wf_task = None # Unload modules imported by the workflow. Should not raise. utils.unimport_end(modules) except Exception as exc: # The session handling should never exit, just log and continue. if __debug__: log.exception(__name__, exc)