Beispiel #1
0
    def _register_service(self, endpoint):
        # type: (beans.ExportEndpoint) -> None
        """
        Register a local endpoint

        :param endpoint: A local endpoint
        """
        # Prepare node content
        path = self._endpoint_path(self._fw_uid, endpoint.uid)
        data = to_bytes(
            EDEFWriter().to_string(
                [beans.EndpointDescription.from_export(endpoint)]
            )
        )

        try:
            try:
                # Create an ephemeral node
                self._zk.create(path, data, True)
            except NodeExistsError:
                # Service already exists: update it
                self._zk.set(path, data)
        except KazooException as ex:
            _logger.warning(
                "Error registering local service: %s", type(ex).__name__
            )
Beispiel #2
0
    def _register_framework(self):
        """
        Registers the framework and its current services in Redis
        """
        # The framework key
        fw_node = self._framework_path(self._fw_uid)

        # The host name
        hostname = socket.gethostname()
        if hostname == "localhost":
            logging.warning(
                "Hostname is '%s': this will be a problem for "
                "multi-host remote services",
                hostname,
            )

        # Prepare an ephemeral Z-Node
        self._zk.create(fw_node, to_bytes(hostname), True)

        # Load existing services
        self._load_existing_endpoints()

        # Register all already exported services
        for endpoint in self._dispatcher.get_endpoints():
            self._register_service(endpoint)
def make_heartbeat(port, path, peer_uid, app_id):
    """
    Prepares the heart beat UDP packet

    Format : Little endian
    * Kind of beat (1 byte)
    * Herald HTTP server port (2 bytes)
    * Herald HTTP servlet path length (2 bytes)
    * Herald HTTP servlet path (variable, UTF-8)
    * Peer UID length (2 bytes)
    * Peer UID (variable, UTF-8)
    * Application ID length (2 bytes)
    * Application ID (variable, UTF-8)

    :param port: The port to access the Herald HTTP server
    :param path: The path to the Herald HTTP servlet
    :param peer_uid: The UID of the peer
    :param app_id: Application ID
    :return: The heart beat packet content (byte array)
    """
    # Type and port...
    packet = struct.pack("<BH", PACKET_TYPE_HEARTBEAT, port)
    for string in (path, peer_uid, app_id):
        # Strings...
        string_bytes = to_bytes(string)
        packet += struct.pack("<H", len(string_bytes))
        packet += string_bytes

    return packet
def make_heartbeat(port, path, peer_uid, node_uid, app_id):
    """
    Prepares the heart beat UDP packet

    Format : Little endian
    * Kind of beat (1 byte)
    * Herald HTTP server port (2 bytes)
    * Herald HTTP servlet path length (2 bytes)
    * Herald HTTP servlet path (variable, UTF-8)
    * Peer UID length (2 bytes)
    * Peer UID (variable, UTF-8)
    * Node UID length (2 bytes)
    * Node UID (variable, UTF-8)
    * Application ID length (2 bytes)
    * Application ID (variable, UTF-8)

    :param port: The port to access the Herald HTTP server
    :param path: The path to the Herald HTTP servlet
    :param peer_uid: The UID of the peer
    :param node_uid: The UID of the node
    :param app_id: Application ID
    :return: The heart beat packet content (byte array)
    """
    # Type and port...
    packet = struct.pack("<BBH", PACKET_FORMAT_VERSION, PACKET_TYPE_HEARTBEAT, port)
    for string in (path, peer_uid, node_uid, app_id):
        # Strings...
        string_bytes = to_bytes(string)
        packet += struct.pack("<H", len(string_bytes))
        packet += string_bytes

    return packet
Beispiel #5
0
    def _register_service(self, endpoint):
        # type: (beans.ExportEndpoint) -> None
        """
        Register a local endpoint

        :param endpoint: A local endpoint
        """
        # Prepare node content
        path = self._endpoint_path(self._fw_uid, endpoint.uid)
        data = to_bytes(
            EDEFWriter().to_string(
                [beans.EndpointDescription.from_export(endpoint)]
            )
        )

        try:
            try:
                # Create an ephemeral node
                self._zk.create(path, data, True)
            except NodeExistsError:
                # Service already exists: update it
                self._zk.set(path, data)
        except KazooException as ex:
            _logger.warning(
                "Error registering local service: %s", type(ex).__name__
            )
Beispiel #6
0
    def _register_framework(self):
        """
        Registers the framework and its current services in Redis
        """
        # The framework key
        fw_node = self._framework_path(self._fw_uid)

        # The host name
        hostname = socket.gethostname()
        if hostname == "localhost":
            logging.warning(
                "Hostname is '%s': this will be a problem for "
                "multi-host remote services",
                hostname,
            )

        # Prepare an ephemeral Z-Node
        self._zk.create(fw_node, to_bytes(hostname), True)

        # Load existing services
        self._load_existing_endpoints()

        # Register all already exported services
        for endpoint in self._dispatcher.get_endpoints():
            self._register_service(endpoint)
Beispiel #7
0
    def _write_bytes(self, data):
        """
        Converts the given data then writes it

        :param data: Data to be written
        :return: The result of ``self.output.write()``
        """
        self.output.write(to_bytes(data, self.encoding))
Beispiel #8
0
    def _write_bytes(self, data):
        """
        Converts the given data then writes it

        :param data: Data to be written
        :return: The result of ``self.output.write()``
        """
        self.output.write(to_bytes(data, self.encoding))
Beispiel #9
0
        def test_remote_main(self):
            """
            Tests the remote shell 'main' method
            """
            # Get shell PS1 (static method)
            import pelix.shell.core
            ps1 = pelix.shell.core._ShellService.get_ps1()

            # Start the remote shell process
            port = 9000
            process = subprocess.Popen([
                sys.executable, '-m', 'coverage', 'run', '-m',
                'pelix.shell.remote', '-a', '127.0.0.1', '-p',
                str(port)
            ],
                                       stdin=subprocess.PIPE,
                                       stdout=subprocess.PIPE)

            # Wait a little to ensure that the socket is here
            time.sleep(1)

            try:
                # Check if the remote shell port has been opened
                client = ShellClient(None, ps1, self.fail)
                client.connect(("127.0.0.1", port))

                test_string = "running"
                self.assertEqual(
                    client.run_command("echo {0}".format(test_string)),
                    test_string)

                # Good enough: stop there
                client.close()

                # Avoid being blocked...
                timer = threading.Timer(5, process.terminate)
                timer.start()

                # Stop the interpreter with a result code
                rc_code = 42
                stop_line = "import sys; sys.exit({0})".format(rc_code)
                process.communicate(to_bytes(stop_line))

                # We're should be good
                timer.cancel()

                # Check result code
                self.assertEqual(process.returncode, rc_code)

                # The ShellClient must fail a new connection
                self.assertRaises(IOError, client.connect, ("localhost", port))
            finally:
                try:
                    # Kill it in any case
                    process.terminate()
                except OSError:
                    # Process was already stopped
                    pass
Beispiel #10
0
        def test_remote_main(self):
            """
            Tests the remote shell 'main' method
            """
            # Get shell PS1 (static method)
            import pelix.shell.core
            ps1 = pelix.shell.core._ShellService.get_ps1()

            # Start the remote shell process
            port = 9000
            process = subprocess.Popen(
                [sys.executable, '-m', 'coverage', 'run', '-m',
                 'pelix.shell.remote', '-a', '127.0.0.1', '-p', str(port)],
                stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                stderr=subprocess.PIPE)

            # Wait a little to ensure that the socket is here
            time.sleep(1)

            try:
                # Check if the remote shell port has been opened
                client = ShellClient(None, ps1, self.fail)
                client.connect(("127.0.0.1", port))

                test_string = "running"
                self.assertEqual(
                    client.run_command("echo {0}".format(test_string)),
                    test_string)

                # Good enough: stop there
                client.close()

                # Avoid being blocked...
                timer = threading.Timer(5, process.terminate)
                timer.start()

                # Stop the interpreter with a result code
                rc_code = 42
                stop_line = "import sys; sys.exit({0})".format(rc_code)
                process.communicate(to_bytes(stop_line))

                # We should be good
                timer.cancel()

                # Check result code
                self.assertEqual(process.returncode, rc_code)

                # The ShellClient must fail a new connection
                self.assertRaises(IOError, client.connect, ("localhost", port))
            finally:
                try:
                    # Kill it in any case
                    process.terminate()
                except OSError:
                    # Process was already stopped
                    pass
Beispiel #11
0
    def pton(family, address):
        """
        Calls inet_pton

        :param family: Socket family
        :param address: A string address
        :return: The binary form of the given address
        """
        if family == socket.AF_INET:
            return socket.inet_aton(address)

        elif family == socket.AF_INET6:
            # Do it using WinSocks
            import ctypes

            winsock = ctypes.windll.ws2_32

            # Prepare structure
            class sockaddr_in6(ctypes.Structure):
                # pylint: disable=C0103, R0903
                """
                Definition of the C structure 'sockaddr_in6'
                """

                _fields_ = [
                    ("sin6_family", ctypes.c_short),
                    ("sin6_port", ctypes.c_ushort),
                    ("sin6_flowinfo", ctypes.c_ulong),
                    ("sin6_addr", ctypes.c_ubyte * 16),
                    ("sin6_scope_id", ctypes.c_ulong),
                ]

            # Prepare pointers
            addr_ptr = ctypes.c_char_p(to_bytes(address))

            out_address = sockaddr_in6()
            size = len(sockaddr_in6)
            size_ptr = ctypes.pointer(size)

            # Second call
            winsock.WSAStringToAddressA(
                addr_ptr, family, 0, out_address, size_ptr
            )

            # Convert the array...
            bin_addr = 0
            for part in out_address.sin6_addr:
                bin_addr = bin_addr * 16 + part

            return bin_addr

        else:
            raise ValueError("Unhandled socket family: {0}".format(family))
    def pton(family, address):
        """
        Calls inet_pton

        :param family: Socket family
        :param address: A string address
        :return: The binary form of the given address
        """
        if family == socket.AF_INET:
            return socket.inet_aton(address)

        elif family == socket.AF_INET6:
            # Do it using WinSocks
            import ctypes
            winsock = ctypes.windll.ws2_32

            # Prepare structure
            class sockaddr_in6(ctypes.Structure):
                """
                Definition of the C structure sockaddr_in6
                """
                # pylint: disable=C0103
                _fields_ = [("sin6_family", ctypes.c_short),
                            ("sin6_port", ctypes.c_ushort),
                            ("sin6_flowinfo", ctypes.c_ulong),
                            ("sin6_addr", ctypes.c_ubyte * 16),
                            ("sin6_scope_id", ctypes.c_ulong)]

            # Prepare pointers
            addr_ptr = ctypes.c_char_p(to_bytes(address))

            out_address = sockaddr_in6()
            size = len(sockaddr_in6)
            size_ptr = ctypes.pointer(size)

            # Second call
            winsock.WSAStringToAddressA(addr_ptr, family, 0, out_address,
                                        size_ptr)

            # Convert the array...
            bin_addr = 0
            for part in out_address.sin6_addr:
                bin_addr = bin_addr * 16 + part

            return bin_addr

        else:
            raise ValueError("Unhandled socket family: {0}".format(family))
Beispiel #13
0
    def __send_packet(self, data, target=None):
        """
        Sends a UDP datagram to the given target, if given, or to the multicast
        group.

        :param data: The content of the datagram
        :param target: The packet target (can be None)
        """
        if target is None:
            # Use the multicast target by default
            target = self._target

        # Converts data to bytes
        data = to_bytes(data)

        # Send the data
        self._socket.sendto(data, 0, target)
Beispiel #14
0
    def send_content(
        self,
        http_code,
        content,
        mime_type="text/html",
        http_message=None,
        content_length=-1,
    ):
        # type: (int, str, str, str, int) -> None
        """
        Utility method to send the given content as an answer.
        You can still use get_wfile or write afterwards, if you forced the
        content length.

        If content_length is negative (default), it will be computed as the
        length of the content;
        if it is positive, the given value will be used;
        if it is None, the content-length header won't be sent.

        :param http_code: HTTP result code
        :param content: Data to be sent (must be a string)
        :param mime_type: Content MIME type (content-type)
        :param http_message: HTTP code description
        :param content_length: Forced content length
        """
        self.set_response(http_code, http_message)
        if mime_type and not self.is_header_set("content-type"):
            self.set_header("content-type", mime_type)

        # Convert the content
        raw_content = to_bytes(content)

        if content_length is not None and not self.is_header_set(
            "content-length"
        ):
            if content_length < 0:
                # Compute the length
                content_length = len(raw_content)

            # Send the length
            self.set_header("content-length", content_length)

        self.end_headers()

        # Send the content
        self.write(raw_content)
Beispiel #15
0
    def __send_packet(self, data, target=None):
        """
        Sends a UDP datagram to the given target, if given, or to the multicast
        group.

        :param data: The content of the datagram
        :param target: The packet target (can be None)
        """
        if target is None:
            # Use the multicast target by default
            target = self._target

        # Converts data to bytes
        data = to_bytes(data)

        # Send the data
        self._socket.sendto(data, 0, target)
Beispiel #16
0
    def room_jid(self, room_name):
        """
        Prepares a JID object for the given room in the current MUC domain

        :param room_name: The short name of a room
        :return: A JID object
        """
        if self.__muc_service == "groupchat.google.com":
            # Special case: Google Talk requires a specific room name format
            # Make a MD5 hash of the full room name
            app_id = self._directory.get_local_peer().app_id
            full_name = "cohorte-{0}-{1}".format(app_id, room_name)
            md5 = hashlib.md5(to_bytes(full_name)).hexdigest()

            # Format the room name to be Google Talk-compatible
            room_name = "private-chat-{0}".format(str(uuid.UUID(md5)))

        return sleekxmpp.JID(local=room_name, domain=self.__muc_service)
Beispiel #17
0
    def room_jid(self, room_name):
        """
        Prepares a JID object for the given room in the current MUC domain

        :param room_name: The short name of a room
        :return: A JID object
        """
        if self.__muc_service == "groupchat.google.com":
            # Special case: Google Talk requires a specific room name format
            # Make a MD5 hash of the full room name
            app_id = self._directory.get_local_peer().app_id
            full_name = "cohorte-{0}-{1}".format(app_id, room_name)
            md5 = hashlib.md5(to_bytes(full_name)).hexdigest()

            # Format the room name to be Google Talk-compatible
            room_name = "private-chat-{0}".format(str(uuid.UUID(md5)))

        return sleekxmpp.JID(local=room_name, domain=self.__muc_service)
Beispiel #18
0
    def run_command(self, command, disconnect=False):
        """
        Runs a command on the remote shell
        """
        # Wait for the first prompt
        if self.__wait_prompt:
            self.wait_prompt()
            self.__wait_prompt = False

        # Run the command
        self._socket.send(to_bytes(command + "\n"))

        # Disconnect if required
        if disconnect:
            self.close()
            return

        # Get its result
        data = self.wait_prompt(False)
        return data.strip()
def make_lastbeat(peer_uid, app_id):
    """
    Prepares the last beat UDP packet (when the peer is going away)

    Format : Little endian
    * Kind of beat (1 byte)
    * Peer UID length (2 bytes)
    * Peer UID (variable, UTF-8)
    * Application ID length (2 bytes)
    * Application ID (variable, UTF-8)

    :param peer_uid: Peer UID
    :param app_id: Application ID
    :return: The last beat packet content (byte array)
    """
    packet = struct.pack("<B", PACKET_TYPE_LASTBEAT)
    for string in (peer_uid, app_id):
        string_bytes = to_bytes(string)
        packet += struct.pack("<H", len(string_bytes))
        packet += string_bytes

    return packet
def make_lastbeat(peer_uid, app_id):
    """
    Prepares the last beat UDP packet (when the peer is going away)

    Format : Little endian
    * Kind of beat (1 byte)
    * Peer UID length (2 bytes)
    * Peer UID (variable, UTF-8)
    * Application ID length (2 bytes)
    * Application ID (variable, UTF-8)

    :param peer_uid: Peer UID
    :param app_id: Application ID
    :return: The last beat packet content (byte array)
    """
    packet = struct.pack("<BB", PACKET_FORMAT_VERSION, PACKET_TYPE_LASTBEAT)
    for string in (peer_uid, app_id):
        string_bytes = to_bytes(string)
        packet += struct.pack("<H", len(string_bytes))
        packet += string_bytes

    return packet
Beispiel #21
0
        def test_properties(self):
            """
            Tests the console shell properties parameter
            """
            # Prepare some properties
            key1 = self.random_str()[:5]
            key2 = self.random_str()[:5]

            val1 = self.random_str()
            val2 = self.random_str()

            # Start the shell process
            process = subprocess.Popen(
                [sys.executable, '-m', 'pelix.shell',
                 '-D', '{}={}'.format(key1, val1), '{}={}'.format(key2, val2)],
                stdin=subprocess.PIPE, stdout=subprocess.PIPE)

            try:
                # List properties, stop and get output
                output = to_str(process.communicate(to_bytes("properties"))[0])

                found = 0
                for line in output.splitlines(False):
                    if key1 in line:
                        self.assertIn(val1, line)
                        found += 1
                    elif key2 in line:
                        self.assertIn(val2, line)
                        found += 1

                self.assertEqual(found, 2, "Wrong number of properties")
            finally:
                try:
                    # Kill it in any case
                    process.terminate()
                except OSError:
                    # Process was already stopped
                    pass
Beispiel #22
0
    def do_POST(self, request, response):
        """
        Handles a POST request, i.e. the reception of a message

        :param request: The HTTP request bean
        :param response: The HTTP response handler
        """
        # pylint: disable=C0103
        # Default code and content
        code = 200
        content = ""

        # Extract headers
        content_type = request.get_header('content-type')
        subject = request.get_header('herald-subject')
        uid = request.get_header('herald-uid')
        reply_to = request.get_header('herald-reply-to')
        timestamp = request.get_header('herald-timestamp')
        sender_uid = request.get_header('herald-sender-uid')
        raw_content = to_unicode(request.read_data())

        # Client information
        host = utils.normalize_ip(request.get_client_address()[0])

        if not uid or not subject or content_type != CONTENT_TYPE_JSON:
            # Raw message
            uid = str(uuid.uuid4())
            subject = herald.SUBJECT_RAW
            msg_content = raw_content
            port = -1
            extra = {'host': host, 'raw': True}
        else:
            # Herald message
            msg_content = jabsorb.from_jabsorb(json.loads(raw_content))

            # Store sender information
            port = int(request.get_header('herald-port', 80))
            extra = {'host': host, 'port': port,
                     'path': request.get_header('herald-path'),
                     'parent_uid': uid}

            try:
                # Check the sender UID port
                # (not perfect, but can avoid spoofing)
                if not self._http_directory.check_access(
                        sender_uid, host, port):
                    # Port doesn't match: invalid UID
                    sender_uid = "<invalid>"
            except ValueError:
                # Unknown peer UID: keep it as is
                pass

        # Prepare the bean
        message = herald.beans.MessageReceived(uid, subject, msg_content,
                                               sender_uid, reply_to,
                                               ACCESS_ID, timestamp, extra)

        # Log before giving message to Herald
        self._probe.store(
            herald.PROBE_CHANNEL_MSG_RECV,
            {"uid": message.uid, "timestamp": time.time(),
             "transport": ACCESS_ID, "subject": message.subject,
             "source": sender_uid, "repliesTo": reply_to or "",
             "transportSource": "[{0}]:{1}".format(host, port)})

        if subject.startswith(peer_contact.SUBJECT_DISCOVERY_PREFIX):
            # Handle discovery message
            self.__contact.herald_message(self._core, message)
        else:
            # All other messages are given to Herald Core
            self._core.handle_message(message)

        # Convert content (Python 3)
        if content:
            content = jabsorb.to_jabsorb(content)

        content = to_bytes(content)

        # Send response
        response.send_content(code, content, CONTENT_TYPE_JSON)
Beispiel #23
0
        def test_remote_main(self):
            """
            Tests the remote shell 'main' method
            """
            # Prepare certificates
            certs_dir = tempfile.mkdtemp(prefix="ipopo-tests-shell-tls")
            make_certs(certs_dir, PASSWORD)
            ca_chain = os.path.join(certs_dir, "ca.crt")
            srv_cert = os.path.join(certs_dir, "server.crt")
            srv_key = os.path.join(certs_dir, "server.key")
            client_cert = os.path.join(certs_dir, "client.crt")
            client_key = os.path.join(certs_dir, "client.key")

            # Get shell PS1 (static method)
            import pelix.shell.core
            ps1 = pelix.shell.core._ShellService.get_ps1()

            # Start the remote shell process
            port = 9000
            process = subprocess.Popen(
                [sys.executable, '-m', 'coverage', 'run', '-m',
                 'pelix.shell.remote', '-a', '127.0.0.1', '-p', str(port),
                 '--cert', srv_cert, '--key', srv_key,
                 '--ca-chain', ca_chain],
                stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                stderr=subprocess.PIPE)

            # Wait a little to ensure that the socket is here
            time.sleep(1)

            try:
                # Check if the remote shell port has been opened
                client = TLSShellClient(
                    ps1, self.fail, client_cert, client_key)

                client.connect(("127.0.0.1", port))

                test_string = "running"
                self.assertEqual(
                    client.run_command("echo {0}".format(test_string)),
                    test_string)

                # Good enough: stop there
                client.close()

                # Avoid being blocked...
                timer = threading.Timer(5, process.terminate)
                timer.start()

                # Stop the interpreter with a result code
                rc_code = 42
                stop_line = "import sys; sys.exit({0})".format(rc_code)
                process.communicate(to_bytes(stop_line))

                # We should be good
                timer.cancel()

                # Check result code
                self.assertEqual(process.returncode, rc_code)

                # The ShellClient must fail a new connection
                self.assertRaises(IOError, client.connect, ("localhost", port))
            finally:
                try:
                    # Kill it in any case
                    process.terminate()
                except OSError:
                    # Process was already stopped
                    pass
Beispiel #24
0
    def do_POST(self, request, response):
        """
        Handles a POST request, i.e. the reception of a message

        :param request: The HTTP request bean
        :param response: The HTTP response handler
        """
        # pylint: disable=C0103
        # Default code and content
        code = 200
        content = ""

        # Check content type
        content_type = request.get_header('content-type')
        if content_type not in (None, CONTENT_TYPE_JSON):
            # Unknown content type -> Error 412 "Precondition failed"
            _logger.critical("Bad content type: %s", content_type)
            code, content = _make_json_result(412, "Unknown content type")

        else:
            # Extract headers
            subject = request.get_header('herald-subject')
            uid = request.get_header('herald-uid')
            reply_to = request.get_header('herald-reply-to')
            timestamp = request.get_header('herald-timestamp')
            sender_uid = request.get_header('herald-sender-uid')
            json_content = to_unicode(request.read_data())
            msg_content = jabsorb.from_jabsorb(json.loads(json_content))

            # Store sender information
            host = request.get_client_address()[0]
            port = int(request.get_header('herald-port', 80))
            extra = {'host': host, 'port': port,
                     'path': request.get_header('herald-path'),
                     'parent_uid': uid}

            try:
                # Check the sender UID port
                # (not perfect, but can avoid spoofing)
                if not self._http_directory.check_access(sender_uid,
                                                         host, port):
                    # Port doesn't match: invalid UID
                    sender_uid = "<invalid>"
            except ValueError as ex:
                # Unknown peer UID: keep it as is
                pass

            # Let Herald handle the message
            message = herald.beans.MessageReceived(uid, subject, msg_content,
                                                   sender_uid, reply_to,
                                                   ACCESS_ID, timestamp, extra)
            self._core.handle_message(message)

        # Convert content (Python 3)
        if content:
            content = jabsorb.to_jabsorb(content)

        content = to_bytes(content)

        # Send response
        response.send_content(code, content, CONTENT_TYPE_JSON)
Beispiel #25
0
    def do_POST(self, request, response):
        """
        Handles a POST request, i.e. the reception of a message

        :param request: The HTTP request bean
        :param response: The HTTP response handler
        """
        # pylint: disable=C0103
        # Default code and content
        code = 200
        content = ""

        # Check content type
        content_type = request.get_header('content-type')
        if content_type not in (None, CONTENT_TYPE_JSON):
            # Unknown content type -> Error 412 "Precondition failed"
            _logger.critical("Bad content type: %s", content_type)
            code, content = _make_json_result(412, "Unknown content type")

        else:
            # Extract headers
            subject = request.get_header('herald-subject')
            uid = request.get_header('herald-uid')
            reply_to = request.get_header('herald-reply-to')
            timestamp = request.get_header('herald-timestamp')
            sender_uid = request.get_header('herald-sender-uid')
            json_content = to_unicode(request.read_data())
            msg_content = jabsorb.from_jabsorb(json.loads(json_content))

            # Store sender information
            host = request.get_client_address()[0]
            port = int(request.get_header('herald-port', 80))
            extra = {
                'host': host,
                'port': port,
                'path': request.get_header('herald-path'),
                'parent_uid': uid
            }

            try:
                # Check the sender UID port
                # (not perfect, but can avoid spoofing)
                if not self._http_directory.check_access(
                        sender_uid, host, port):
                    # Port doesn't match: invalid UID
                    sender_uid = "<invalid>"
            except ValueError as ex:
                # Unknown peer UID: keep it as is
                pass

            # Let Herald handle the message
            message = herald.beans.MessageReceived(uid, subject, msg_content,
                                                   sender_uid, reply_to,
                                                   ACCESS_ID, timestamp, extra)
            self._core.handle_message(message)

        # Convert content (Python 3)
        if content:
            content = jabsorb.to_jabsorb(content)

        content = to_bytes(content)

        # Send response
        response.send_content(code, content, CONTENT_TYPE_JSON)
Beispiel #26
0
    def do_POST(self, request, response):
        """
        Handles a POST request, i.e. the reception of a message

        :param request: The HTTP request bean
        :param response: The HTTP response handler
        """
        # pylint: disable=C0103
        # Default code and content
        code = 200
        content = ""

        # Extract headers
        """
        content_type = request.get_header('content-type')
        subject = request.get_header('herald-subject')
        uid = request.get_header('herald-uid')
        reply_to = request.get_header('herald-reply-to')
        timestamp = request.get_header('herald-timestamp')
        sender_uid = request.get_header('herald-sender-uid')
        """
        content_type = request.get_header('content-type')
        subject = None
        uid = None
        reply_to = None
        timestamp = None
        sender_uid = None
        
        raw_content = to_unicode(request.read_data())
        
        # Client information
        host = utils.normalize_ip(request.get_client_address()[0])
        
        message = None
        
        if content_type != CONTENT_TYPE_JSON:
            # Raw message
            uid = str(uuid.uuid4())
            subject = herald.SUBJECT_RAW
            #msg_content = raw_content
            msg_content = raw_content
            port = -1
            extra = {'host': host, 'raw': True}     
            
            # construct a new Message bean
            message = herald.beans.MessageReceived(uid, subject, msg_content,
                                               None, None,
                                               ACCESS_ID, None, extra)                  
        else:
            # Herald message
            try:            
                received_msg = utils.from_json(raw_content)                
            except Exception as ex:
                _logger.exception("DoPOST ERROR:: %s", ex)
                
            msg_content = received_msg.content
            
            subject = received_msg.subject
            uid = received_msg.uid
            reply_to = received_msg.reply_to
            timestamp = received_msg.timestamp
            sender_uid = received_msg.sender
            
            if not uid or not subject:
                # Raw message
                uid = str(uuid.uuid4())
                subject = herald.SUBJECT_RAW
                #msg_content = raw_content
                msg_content = raw_content
                port = -1
                extra = {'host': host, 'raw': True}     
                
                # construct a new Message bean
                message = herald.beans.MessageReceived(uid, subject, msg_content,
                                               None, None,
                                               ACCESS_ID, None, extra) 
                
            else:       
                # Store sender information
                try:                 
                    port = int(received_msg.get_header(herald.transports.http.MESSAGE_HEADER_PORT))                    
                except (KeyError, ValueError, TypeError):
                    port = 80
                path = None
                if herald.transports.http.MESSAGE_HEADER_PATH in received_msg.headers:
                    path = received_msg.get_header(herald.transports.http.MESSAGE_HEADER_PATH)                
                extra = {'host': host, 'port': port,
                         'path': path,
                         'parent_uid': uid}
    
                try:
                    # Check the sender UID port
                    # (not perfect, but can avoid spoofing)
                    if not self._http_directory.check_access(
                            sender_uid, host, port):
                        # Port doesn't match: invalid UID
                        sender_uid = "<invalid>"
                except ValueError:
                    # Unknown peer UID: keep it as is
                    pass
            
                # Prepare the bean
                received_msg.add_header(herald.MESSAGE_HEADER_SENDER_UID, sender_uid)
                received_msg.set_access(ACCESS_ID)
                received_msg.set_extra(extra)
                message = received_msg
                           
        # Log before giving message to Herald
        self._probe.store(
            herald.PROBE_CHANNEL_MSG_RECV,
            {"uid": message.uid, "timestamp": time.time(),
             "transport": ACCESS_ID, "subject": message.subject,
             "source": sender_uid, "repliesTo": reply_to or "",
             "transportSource": "[{0}]:{1}".format(host, port)})

        if subject.startswith(peer_contact.SUBJECT_DISCOVERY_PREFIX):
            # Handle discovery message
            self.__contact.herald_message(self._core, message)
        else:
            # All other messages are given to Herald Core
            self._core.handle_message(message)

        # Convert content (Python 3)
        if content:
            content = jabsorb.to_jabsorb(content)

        content = to_bytes(content)

        # Send response
        response.send_content(code, content, CONTENT_TYPE_JSON)
Beispiel #27
0
        def test_echo(self):
            """
            Tests the console shell 'echo' method
            """
            # Get shell PS1 (static method)
            import pelix.shell.core
            ps1 = pelix.shell.core._ShellService.get_ps1()

            # Start the shell process
            process = subprocess.Popen(
                [sys.executable, '-m', 'pelix.shell'],
                stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT)

            # Avoid being blocked...
            timer = threading.Timer(5, process.terminate)
            timer.start()

            # Wait for prompt
            got = ""
            while ps1 not in got:
                char = to_str(process.stdout.read(1))
                if not char:
                    if sys.version_info[0] == 2:
                        self.skipTest("Shell console test doesn't work on "
                                      "Python 2.7 with Travis")
                    else:
                        if process.poll():
                            output = to_str(process.stdout.read())
                        else:
                            output = "<no output>"

                        self.fail("Can't read from stdout (rc={})\n{}"
                                  .format(process.returncode, output))
                else:
                    got += char

            # We should be good
            timer.cancel()

            try:
                # Try echoing
                data = self.random_str()

                # Write command
                process.stdin.write(to_bytes("echo {}\n".format(data)))
                process.stdin.flush()

                # Read result
                last_line = to_str(process.stdout.readline()).rstrip()
                self.assertEqual(last_line, data, "Wrong output")

                # Stop the process
                process.stdin.write(to_bytes("exit\n"))
                process.stdin.flush()

                # Wait for the process to stop (1 second max)
                delta = 0
                start = time.time()
                while delta <= 1:
                    delta = time.time() - start
                    if process.poll() is not None:
                        break
                    time.sleep(.1)
                else:
                    self.fail("Process took too long to stop")
            finally:
                try:
                    # Kill it in any case
                    process.terminate()
                except OSError:
                    # Process was already stopped
                    pass
Beispiel #28
0
    def do_POST(self, request, response):
        """
        Handles a POST request, i.e. the reception of a message

        :param request: The HTTP request bean
        :param response: The HTTP response handler
        """
        # pylint: disable=C0103
        # Default code and content
        code = 200
        content = ""

        # Extract headers
        """
        content_type = request.get_header('content-type')
        subject = request.get_header('herald-subject')
        uid = request.get_header('herald-uid')
        reply_to = request.get_header('herald-reply-to')
        timestamp = request.get_header('herald-timestamp')
        sender_uid = request.get_header('herald-sender-uid')
        """
        content_type = request.get_header('content-type')
        subject = None
        uid = None
        reply_to = None
        timestamp = None
        sender_uid = None

        raw_content = to_unicode(request.read_data())

        # Client information
        host = utils.normalize_ip(request.get_client_address()[0])

        message = None

        if content_type != CONTENT_TYPE_JSON:
            # Raw message
            uid = str(uuid.uuid4())
            subject = herald.SUBJECT_RAW
            #msg_content = raw_content
            msg_content = raw_content
            port = -1
            extra = {'host': host, 'raw': True}

            # construct a new Message bean
            message = herald.beans.MessageReceived(uid, subject, msg_content,
                                                   None, None, ACCESS_ID, None,
                                                   extra)
        else:
            # Herald message
            try:
                received_msg = utils.from_json(raw_content)
            except Exception as ex:
                _logger.exception("DoPOST ERROR:: %s", ex)

            msg_content = received_msg.content

            subject = received_msg.subject
            uid = received_msg.uid
            reply_to = received_msg.reply_to
            timestamp = received_msg.timestamp
            sender_uid = received_msg.sender

            if not uid or not subject:
                # Raw message
                uid = str(uuid.uuid4())
                subject = herald.SUBJECT_RAW
                #msg_content = raw_content
                msg_content = raw_content
                port = -1
                extra = {'host': host, 'raw': True}

                # construct a new Message bean
                message = herald.beans.MessageReceived(uid, subject,
                                                       msg_content, None, None,
                                                       ACCESS_ID, None, extra)

            else:
                # Store sender information
                try:
                    port = int(
                        received_msg.get_header(
                            herald.transports.http.MESSAGE_HEADER_PORT))
                except (KeyError, ValueError, TypeError):
                    port = 80
                path = None
                if herald.transports.http.MESSAGE_HEADER_PATH in received_msg.headers:
                    path = received_msg.get_header(
                        herald.transports.http.MESSAGE_HEADER_PATH)
                extra = {
                    'host': host,
                    'port': port,
                    'path': path,
                    'parent_uid': uid
                }

                try:
                    # Check the sender UID port
                    # (not perfect, but can avoid spoofing)
                    if not self._http_directory.check_access(
                            sender_uid, host, port):
                        # Port doesn't match: invalid UID
                        sender_uid = "<invalid>"
                except ValueError:
                    # Unknown peer UID: keep it as is
                    pass

                # Prepare the bean
                received_msg.add_header(herald.MESSAGE_HEADER_SENDER_UID,
                                        sender_uid)
                received_msg.set_access(ACCESS_ID)
                received_msg.set_extra(extra)
                message = received_msg

        # Log before giving message to Herald
        self._probe.store(
            herald.PROBE_CHANNEL_MSG_RECV, {
                "uid": message.uid,
                "timestamp": time.time(),
                "transport": ACCESS_ID,
                "subject": message.subject,
                "source": sender_uid,
                "repliesTo": reply_to or "",
                "transportSource": "[{0}]:{1}".format(host, port)
            })

        if subject.startswith(peer_contact.SUBJECT_DISCOVERY_PREFIX):
            # Handle discovery message
            self.__contact.herald_message(self._core, message)
        else:
            # All other messages are given to Herald Core
            self._core.handle_message(message)

        # Convert content (Python 3)
        if content:
            content = jabsorb.to_jabsorb(content)

        content = to_bytes(content)

        # Send response
        response.send_content(code, content, CONTENT_TYPE_JSON)