Example #1
0
    def execute_cmd(self,
                    dst_dev_uuid,
                    method,
                    namespace,
                    payload,
                    callback=None,
                    timeout=SHORT_TIMEOUT):
        # If the underlying mqttclient is not connected, let's fast-fail.
        # Otherwise, if it's connected but not yet subscribed to relevant topics, it's still OK to
        # queue messages: the client will dispatch them as soon it subscribes to the relevant topics.
        if not self._mqtt_client.is_connected():
            l.error("The underlying mqtt client is not connected.")
            raise ConnectionError()

        start = time.time()
        # Build the mqtt message we will send to the broker
        message, message_id = self._build_mqtt_message(method, namespace,
                                                       payload)

        # Register the waiting handler for that message
        handle = PendingMessageResponse(message_id=message_id,
                                        callback=callback)
        with self._pending_responses_lock:
            self._pending_response_messages[message_id] = handle

        # Send the message to the broker
        l.debug("Executing message-id %s, %s on %s command for device %s" %
                (message_id, method, namespace, dst_dev_uuid))

        self._mqtt_client.publish(
            topic=build_client_request_topic(dst_dev_uuid), payload=message)

        # If the caller has specified a callback, we don't need to actively wait for the message ACK. So we can
        # immediately return.
        if callback is not None:
            return None

        # Otherwise, we need to wait until the message is received.
        l.debug("Waiting for response to message-id %s" % message_id)
        success, resp = handle.wait_for_response(timeout=timeout)
        if not success:
            raise CommandTimeoutException(
                "A timeout occurred while waiting for the ACK: %d" % timeout)

        elapsed = time.time() - start

        l.debug("Message-id: %s, command %s-%s command for device %s took %s" %
                (message_id, method, namespace, dst_dev_uuid, str(elapsed)))
        return resp['payload']
Example #2
0
    def _on_message(self, client, userdata, msg):
        """
        This handler is called when a message is received from the MQTT broker, on the subscribed topics.
        The current implementation checks the validity of the message itself, by verifying its signature.

        :param client: is the MQTT client reference, useful to respond back
        :param userdata: metadata about the received data
        :param msg: message that was received
        :return: nothing, it simply handles the message accordingly.
        """
        networkl.debug(msg.topic + " --> " + str(msg.payload))

        try:
            message = json.loads(str(msg.payload, "utf8"))
            header = message['header']

            message_hash = md5()
            strtohash = "%s%s%s" % (header['messageId'], self._cloud_creds.key,
                                    header['timestamp'])
            message_hash.update(strtohash.encode("utf8"))
            expected_signature = message_hash.hexdigest().lower()

            if header['sign'] != expected_signature:
                raise InvalidSignatureException(
                    'The signature did not match!',
                    expected_signature=expected_signature,
                    provided_signature=header['sign'],
                    data=message)

            # Check if there is any thread waiting for this message or if there is a callback that we need to invoke.
            # If so, do it here.
            handle = None
            with self._pending_responses_lock:
                msg_id = header['messageId']
                handle = self._pending_response_messages.get(msg_id)

            from_myself = False
            if handle is not None:
                # There was a handle for this message-id. It means it is a response message to some
                # request performed by the library itself.
                from_myself = True
                try:
                    l.debug("Calling handle event handler for message %s" %
                            msg_id)
                    # Call the handler
                    handle.notify_message_received(error=None,
                                                   response=message)
                    l.debug("Done handler for message %s" % msg_id)

                    # Remove the message from the pending queue
                    with self._pending_responses_lock:
                        del self._pending_response_messages[msg_id]
                except:
                    l.exception(
                        "Error occurred while invoking message handler")

            # Let's also catch all the "PUSH" notifications and dispatch them to the push_notification_callback.
            if self._push_message_callback is not None and header[
                    'method'] == "PUSH" and 'namespace' in header:
                self._push_message_callback(message, from_myself=from_myself)

        except InvalidSignatureException as e:
            l.error(
                "Invalid signature received. Expecting: %s, received %s. Message: %s"
                % (e.expected_signature, e.provided_signature,
                   json.dumps(e.data)))

        except Exception:
            l.exception("Failed to process message.")