Exemplo n.º 1
0
    def __init__(self,
                 m2m_ep,
                 _,
                 client_id,
                 handle_request_func=None,
                 subscribe_sys_topics=False,
                 ca_certs=None,
                 cert_file=None,
                 key_file=None,
                 insecure=False):
        """
        :param str m2m_ep:
        :param bool _:
        :param str client_id:
        :param call handle_request_func:
        :param bool subscribe_sys_topics: Whether to subscribe to $SYS topics or not
                    (cf <https://github.com/mqtt/mqtt.github.io/wiki/SYS-Topics>)
        """
        super(OneM2MMQTTClient, self).__init__()
        parsed_url = urlparse(m2m_ep)
        self._default_target_id = parsed_url.fragment

        def _default(x):
            if isinstance(x, datetime):
                try:
                    isoformat = x.isoformat
                except AttributeError:
                    raise TypeError("%s (%s)" % (x, type(x)))

                return isoformat()
            else:
                return x

        self._encode = JSONEncoder(default=_default).encode
        self._decode = JSONDecoder().decode

        self._handle_request_func = handle_request_func

        self._processed_request_ids = deque([], maxlen=200)
        self._request_promises = LRUCache(threadsafe=False, max_items=200)

        if client_id is None:
            import random
            import string
            client_id = ''.join(random.sample(string.letters, 16))
        else:
            client_id = self._get_client_id_from_originator(client_id)

        self._client = mqtt.Client(
            clean_session=False,
            client_id='::'.join([
                'C' if client_id[0].lower() in ['c', 'm'] else 'A',
                self._mqtt_mask(client_id),
            ]),
        )

        self._client_id = client_id

        @self.attach_callback()
        def on_connect(client, userdata, flags_dict, rc):
            """
            :param mqtt.Client client:
            :param All userdata:
            :param dict flags_dict:
            :param integer rc:
            :return void:
            """
            if not rc == mqtt.CONNACK_ACCEPTED:
                raise ConnectionFailed(mqtt.connack_string(rc))

            topics = [
                self._build_topic(originator=client_id,
                                  receiver='#',
                                  type='resp'),
            ]
            client.message_callback_add(topics[0], self._response_callback)

            if self._handle_request_func is not None:
                topics.append(self._build_topic(receiver=client_id) + '/+')
                client.message_callback_add(topics[1], self._request_callback)

            if subscribe_sys_topics:
                topics.append('$SYS/#')

            self.logger.debug('Subscribing to topic(s) %s ...' %
                              (', '.join(topics), ))
            client.subscribe([(str(topic), MQTT_QOS_LEVEL)
                              for topic in topics])

        @self.attach_callback()
        def on_disconnect(client, userdata, rc):
            """
            :param mqtt.Client client:
            :param All userdata:
            :param int rc:
            :return void:
            """
            if not rc == mqtt.MQTT_ERR_SUCCESS:
                self.logger.error(
                    'Involuntary connection loss: %s (code %d). Waiting for reconnect ...'
                    % (mqtt.error_string(rc), rc))

        @self.attach_callback()
        def on_message(client, userdata, message):
            """
            :param mqtt.Client client:
            :param All userdata:
            :param  mqtt.MQTTMessage message:
            :return void:
            """
            self.logger.debug('message received on topic %s' %
                              (message.topic, ))

        @self.attach_callback()
        def on_log(client, userdata, level, buf):
            """
            :param mqtt.Client client:
            :param All userdata:
            :param integer level:
            :param string buf:
            :return void:
            """
            self.logger.debug('pahomqtt-%d: %s' % (level, buf))

        if parsed_url.username:
            self._client.username_pw_set(parsed_url.username,
                                         parsed_url.password)

        if parsed_url.scheme != 'mqtt':
            self._client.tls_set(ca_certs=ca_certs,
                                 certfile=cert_file,
                                 keyfile=key_file)
            self._client.tls_insecure_set(insecure)

        try:
            self._client.connect(parsed_url.hostname, parsed_url.port
                                 or portmap[parsed_url.scheme])
        except SocketError as e:
            raise ConnectionFailed(e.message)

        def loop():
            try:
                while self._client.loop(
                        timeout=0.1) != mqtt.mqtt_cs_disconnecting:
                    gevent.sleep()
            except (SystemExit, KeyboardInterrupt, AttributeError):
                pass

        gevent.spawn(loop)
Exemplo n.º 2
0
#: Dictionary mapping supported schemes to port numbers
portmap = {
    'mqtt': 1883,
    'mqtts': 8883,
    # NB: The correct (i.e. registered with IANA) service-name for SSL/TLS-wrapped MQTT is
    # 'secure-mqtt' in an effort to prevent confusion with MQTT-S/N. But as the entire world seems
    # to insist on using 'mqtts' (including TS 0010, sec. 6.6) ... We are supporting both names here
    # for maximum compliance and robustness.
    'secure-mqtt': 8883,
}

MQTT_QOS_LEVEL = 1
MQTT_RESPONSE_TIMEOUT = 1

_clients = LRUCache(threadsafe=False)


def get_client(m2m_ep,
               use_xml=False,
               client_id=None,
               handle_request_func=None,
               ca_certs=None,
               cert_file=None,
               key_file=None,
               insecure=False):
    """

    :param string m2m_ep:
    :param boolean use_xml:
    :param string client_id:
Exemplo n.º 3
0
    def __init__(self,
                 m2m_ep,
                 _,
                 client_id,
                 handle_request_func=None,
                 subscribe_sys_topics=False):
        """
        :param str m2m_ep:
        :param bool _:
        :param str client_id:
        :param call handle_request_func:
        :param bool subscribe_sys_topics: Whether to subscribe to $SYS topics or not
                    (cf <https://github.com/mqtt/mqtt.github.io/wiki/SYS-Topics>)
        """
        super(OneM2MMQTTClient, self).__init__()
        parsed_url = urlparse(m2m_ep)
        self._target_id = parsed_url.fragment

        self._encode = JSONEncoder().encode
        self._decode = JSONDecoder().decode

        self._handle_request_func = handle_request_func

        self._processed_request_ids = deque([], maxlen=200)
        self._request_promises = LRUCache(threadsafe=False, max_items=200)

        if client_id is None:
            import random
            import string
            client_id = ''.join(random.sample(string.letters, 16))

        self._client = mqtt.Client(
            clean_session=False,
            client_id='::'.join([
                'C' if client_id[0].lower() in ['c', 'm'] else 'A',
                self._mqtt_mask(client_id),
            ]),
        )

        @self.attach_callback()
        def on_connect(client, _, rc):
            """
            :param mqtt.Client client:
            :param All userdata:
            :param integer rc:
            :return void:
            """
            if not rc == mqtt.CONNACK_ACCEPTED:
                raise ConnectionFailed(mqtt.connack_string(rc))

            def request_callback(client, _, message):
                """
                Catch requests and

                :param mqtt.Client client:
                :param All _:
                :param mqtt.MQTTMessage message:
                :return void:
                """
                originator = message.topic.split('/')[3]
                try:
                    request = self._decode(message.payload)
                except JSONDecodeError as e:
                    self.logger.warn('Got rubbish request from client %s: %s' %
                                     (
                                         originator,
                                         e.message,
                                     ))
                    return

                try:
                    if request['rqi'] in self._processed_request_ids:
                        self.logger.info(
                            'Request %s already processed; discarding duplicate.'
                            % (request['rqi'], ))
                        return
                    else:
                        rqi = request['rqi']
                except KeyError:
                    self.logger.warn(
                        'Special treatment for special request w/o request id from %s.'
                        % (originator, ))
                    return

                try:
                    request['pc'] = decode_onem2m_content(
                        self._encode(request['pc']), 'application/json')
                    request['ty'] = type(request['pc'])
                except KeyError:
                    # No content, eh?
                    request['ty'] = None

                self.logger.debug('Decoded JSON request: %s' % (request, ))

                op = OneM2MOperation._member_map_.values()[request['op'] - 1]
                to = request['to']
                del request['op'], request['to']

                try:
                    response = self._handle_request_func(
                        OneM2MRequest(op, to, **request)).get()
                except OneM2MErrorResponse as response:
                    self.logger.error('OneM2MError: %s' % (response.message, ))
                except CSEError as e:
                    response = OneM2MErrorResponse(
                        status_code=e.response_status_code, rqi=rqi)

                if not response.rqi:
                    # This really should not happen. No, really, it shouldn't.
                    self.logger.debug(
                        'FIXUP! FIXUP! FIXUP! Adding missing request identifier to response: %s'
                        % (rqi, ))
                    response.rqi = rqi

                if response.content:
                    response.content = self._decode(
                        encode_onem2m_content(response.content,
                                              'application/json',
                                              path=response.to)[1])

                self._publish_message(
                    self._encode({
                        k: getattr(response, k)
                        for k in self.__response_fields
                        if getattr(response, k) is not None
                    }),
                    self._build_topic(originator, client_id, type='resp'),
                )
                self._processed_request_ids.append(rqi)

            def response_callback(client, _, message):
                """

                :param mqtt.Client client:
                :param All _:
                :param mqtt.MQTTMessage message:
                :return:
                """
                try:
                    response = self._decode(message.payload)
                except JSONDecodeError as e:
                    self.logger.error(
                        'Discarding response w/ damaged payload: %s',
                        (e.message, ))
                    return

                promise_key = (message.topic.split('/')[4], response['rqi'])
                try:
                    p = self._request_promises[promise_key]
                except KeyError:
                    self.logger.debug(
                        'Response %s could not be mapped to a request. Discarding.'
                        % (response['rqi'], ))
                    return

                try:
                    response['pc'] = decode_onem2m_content(
                        self._encode(response['pc']), 'application/json')
                except KeyError:
                    pass
                except CSEValueError as e:
                    self.logger.error(
                        'Content of response %s could not be parsed, throwing on the trash heap: %s'
                        % (response['rqi'], e.message))
                    p.reject(e)

                status_code = response['rsc']
                del response['rsc']
                if status_code >= ERROR_MIN:
                    p.reject(OneM2MErrorResponse(status_code, **response))
                else:
                    p.fulfill(OneM2MResponse(status_code, **response))

            topics = [
                self._build_topic(originator=client_id,
                                  receiver='#',
                                  type='resp'),
            ]
            client.message_callback_add(topics[0], response_callback)

            if self._handle_request_func is not None:
                topics.append(self._build_topic(receiver=client_id) + '/+')
                client.message_callback_add(topics[1], request_callback)

            if subscribe_sys_topics:
                topics.append('$SYS/#')

            self.logger.debug('Subscribing to topic(s) %s ...' %
                              (', '.join(topics), ))
            client.subscribe([(str(topic), MQTT_QOS_LEVEL)
                              for topic in topics])

        @self.attach_callback()
        def on_disconnect(client, userdata, rc):
            """
            :param mqtt.Client client:
            :param All userdata:
            :param int rc:
            :return void:
            """
            if not rc == mqtt.MQTT_ERR_SUCCESS:
                self.logger.error(
                    'Involuntary connection loss: %s (code %d). Waiting for reconnect ...'
                    % (mqtt.error_string(rc), rc))

        @self.attach_callback()
        def on_message(client, userdata, message):
            """
            :param mqtt.Client client:
            :param All userdata:
            :param  mqtt.MQTTMessage message:
            :return void:
            """
            self.logger.debug('message received on topic %s' %
                              (message.topic, ))

        @self.attach_callback()
        def on_log(client, userdata, level, buf):
            """
            :param mqtt.Client client:
            :param All userdata:
            :param integer level:
            :param string buf:
            :return void:
            """
            self.logger.debug('pahomqtt-%d: %s' % (level, buf))

        if parsed_url.username:
            self._client.username_pw_set(parsed_url.username,
                                         parsed_url.password)

        try:
            self._client.connect(parsed_url.hostname, parsed_url.port
                                 or portmap[parsed_url.scheme])
        except SocketError as e:
            raise ConnectionFailed(e.message)

        def loop():
            try:
                while self._client.loop(
                        timeout=0.1) != mqtt.mqtt_cs_disconnecting:
                    gevent.sleep()
            except (KeyboardInterrupt, SystemExit):
                pass

        gevent.spawn(loop)
	def __init__(self, uri, username=None, password=None, certfile=None,
				 keyfile=None, cacertfile=None, content_type="text/plain",
				 headers=None,
				 cache=True, timeout=None, get_timeout=None,
				 component_name = "server", connection_manager = None,
				 *args, **kw):
		super(RestClient, self).__init__(*args, **kw)

		self.logger.debug("Creating RestClient for %s", uri)

		self.timeout = timeout or self.timeout
		self.get_timeout = get_timeout or timeout or self.get_timeout

		if cache:
			if cache is True:
				from futile.caching import LRUCache
				cache = LRUCache()
		self.__cache = cache

		if "://" not in uri:
			uri = "http://" + uri

		self.__content_type = content_type
		self.component_name = component_name

		info = urlparse(uri)

		self.logger.debug("Restclient certfile is %s"%certfile)
		if info.scheme == "https":
			if bool(certfile) ^ bool(keyfile):
				raise ValueError("Must give both certfile and keyfile if any")
			if certfile:
				from os.path import exists
				if not exists(certfile):
					raise ValueError("Certificate file not found: %s" % (certfile, ))
				if not exists(keyfile):
					raise ValueError("Key file not found: %s" % (keyfile, ))
		elif info.scheme != "http":
			raise ValueError(info.scheme)
		else:
			# In case of http, we do not want any certificates
			keyfile = certfile = None

		port = info.port and int(info.port) or getservbyname(info.scheme)

		self.__base = info.path or ""
		#if not self.__base.endswith("/"):
		#	self.__base += "/"

		if not username:
			username = info.username

		if not headers:
			headers = {}

		headers.setdefault("Accept", "*/*")
		headers["Accept-Encoding"] = "identity"

		if username:
			password = password or info.password or ""
			headers["Authorization"] = "Basic " + b64encode("%s:%s" % (username, password))

		self.__headers = headers

		if not connection_manager:
			#from SimpleConnectionManager import SimpleConnectionManager as connection_manager
			from ConnectionPoolManager import ConnectionPoolManager as connection_manager

		self.__connection_manager = connection_manager(host=info.hostname,
													   port=port,
						certfile = certfile, keyfile = keyfile, cacertfile = cacertfile, force_ssl = info.scheme == "https")
Exemplo n.º 5
0
    def _init(self):
        # TODO: make max_items configurable
        self._clients = LRUCache(threadsafe=False)
        self.api.register_client(("http", "https"), self.send_request)

        self._initialized()