Esempio n. 1
0
    async def test_can_handle_connected_frame_without_heartbeat(
            self, heartbeater_klass_mock):
        frame = Frame("CONNECTED", {}, "{}")

        stomp = StompReader(None, self.loop)
        await stomp._handle_connect(frame)

        heartbeater_klass_mock.assert_not_called()
Esempio n. 2
0
    async def test_can_handle_exception(self, logger_mock):
        frame = Frame('SOMETHING',
                      {'message': 'Invalid error, blah, blah, blah'},
                      'Detail Error: blah, blahh-line-a')

        stomp = StompReader(None, self.loop)
        await stomp._handle_exception(frame)

        logger_mock.warn.assert_called_with('Unhandled frame: SOMETHING')
Esempio n. 3
0
    def test_cannot_ack_an_unsubscribed_frame(self):
        self.stomp._protocol.ack = Mock()
        self.assertEqual(len(self.stomp._subscriptions), 0)

        frame = Frame("MESSAGE", {"subscription": "1"}, "data")

        with self.assertLogs() as cm:
            self.stomp.ack(frame)
            self.assertIn("WARNING:aiostomp:Subscription 1 not found",
                          cm.output)
        self.stomp._protocol.ack.assert_not_called()
Esempio n. 4
0
    def test_cannot_ack_an_unsubscribed_frame(self):
        self.stomp._protocol.ack = Mock()
        self.assertEqual(len(self.stomp._subscriptions), 0)

        frame = Frame('MESSAGE', {'subscription': '1'}, 'data')

        with self.assertLogs() as cm:
            self.stomp.ack(frame)
            self.assertIn('WARNING:aiostomp:Subscription 1 not found',
                          cm.output)
        self.stomp._protocol.ack.assert_not_called()
Esempio n. 5
0
    def process_command(self) -> None:
        body: Optional[bytes] = bytes(self.current_command)
        if body == b"":
            body = None
        frame = Frame(self.action or "", self.headers, body)
        self._frames_ready.append(frame)

        self.processed_headers = False
        self.awaiting_command = True
        self.content_length = -1
        self.current_command.clear()
Esempio n. 6
0
    def process_command(self):
        body = b''.join(self.current_command)
        if body == b'':
            body = None
        frame = Frame(self.action, self.headers, body)
        self._frames_ready.append(frame)

        self.processed_headers = False
        self.awaiting_command = True
        self.content_length = -1
        self.current_command.clear()
Esempio n. 7
0
    def test_can_nack_a_frame(self):
        self.stomp._protocol.subscribe = Mock()
        self.stomp._protocol.nack = Mock()

        self.stomp.subscribe('/queue/test', auto_ack=False)
        self.assertEqual(len(self.stomp._subscriptions), 1)

        frame = Frame('MESSAGE', {'subscription': '1'}, 'data')

        self.stomp.nack(frame)

        self.stomp._protocol.nack.assert_called_with(frame)
Esempio n. 8
0
    def test_can_nack_a_frame(self):
        self.stomp._protocol.subscribe = Mock()
        self.stomp._protocol.nack = Mock()

        self.stomp.subscribe("/queue/test", auto_ack=False)
        self.assertEqual(len(self.stomp._subscriptions), 1)

        frame = Frame("MESSAGE", {"subscription": "1"}, "data")

        self.stomp.nack(frame)

        self.stomp._protocol.nack.assert_called_with(frame)
Esempio n. 9
0
    async def test_can_handle_exception(self):
        frame = Frame(
            "SOMETHING",
            {"message": "Invalid error, blah, blah, blah"},
            "Detail Error: blah, blahh-line-a",
        )

        stomp = StompReader(None, self.loop)
        with self.assertLogs("aiostomp", level="WARNING") as cm:
            await stomp._handle_exception(frame)
            self.assertEqual(cm.output,
                             ["WARNING:aiostomp:Unhandled frame: SOMETHING"])
Esempio n. 10
0
    async def test_can_handle_exception(self):
        frame = Frame(
            'SOMETHING',
            {'message': 'Invalid error, blah, blah, blah'},
            'Detail Error: blah, blahh-line-a',
        )

        stomp = StompReader(None, self.loop)
        with self.assertLogs('aiostomp', level="WARNING") as cm:
            await stomp._handle_exception(frame)
            self.assertEqual(cm.output,
                             ["WARNING:aiostomp:Unhandled frame: SOMETHING"])
Esempio n. 11
0
    async def test_can_handle_message_with_no_subscription(self):
        frame = Frame("MESSAGE", {
            "subscription": "123",
            "message-id": "321"
        }, "blah")

        handler = CoroutineMock()

        frame_handler = Mock()
        frame_handler.get.return_value = None

        stomp = StompReader(frame_handler, self.loop)
        await stomp._handle_message(frame)

        handler.assert_not_called()
Esempio n. 12
0
    async def test_can_handle_message_with_no_subscription(self):
        frame = Frame('MESSAGE', {
            'subscription': '123',
            'message-id': '321'
        }, 'blah')

        handler = CoroutineMock()

        frame_handler = Mock()
        frame_handler.get.return_value = None

        stomp = StompReader(frame_handler, self.loop)
        await stomp._handle_message(frame)

        handler.assert_not_called()
Esempio n. 13
0
    def test_cannot_ack_an_auto_ack_frame(self):
        self.stomp._protocol.subscribe = Mock()
        self.stomp._protocol.ack = Mock()

        self.stomp.subscribe("/queue/test", auto_ack=True)
        self.assertEqual(len(self.stomp._subscriptions), 1)

        frame = Frame("MESSAGE", {"subscription": "1"}, "data")

        with self.assertLogs() as cm:
            self.stomp.ack(frame)
            self.assertIn(
                "WARNING:aiostomp:Auto ack/nack is enabled. Ignoring call.",
                cm.output)

        self.stomp._protocol.ack.assert_not_called()
Esempio n. 14
0
    async def test_can_handle_message(self):
        frame = Frame("MESSAGE", {
            "subscription": "123",
            "message-id": "321"
        }, "blah")

        handler = CoroutineMock()
        subscription = Subscription("123", 1, "auto", {}, handler)

        frame_handler = Mock()
        frame_handler.get.return_value = subscription

        stomp = StompReader(frame_handler, self.loop)
        await stomp._handle_message(frame)

        handler.assert_called_with(frame, frame.body)
Esempio n. 15
0
    def test_cannot_ack_an_auto_ack_frame(self):
        self.stomp._protocol.subscribe = Mock()
        self.stomp._protocol.ack = Mock()

        self.stomp.subscribe('/queue/test', auto_ack=True)
        self.assertEqual(len(self.stomp._subscriptions), 1)

        frame = Frame('MESSAGE', {'subscription': '1'}, 'data')

        with self.assertLogs() as cm:
            self.stomp.ack(frame)
            self.assertIn(
                'WARNING:aiostomp:Auto ack/nack is enabled. Ignoring call.',
                cm.output)

        self.stomp._protocol.ack.assert_not_called()
Esempio n. 16
0
    async def test_can_handle_message(self):
        frame = Frame('MESSAGE', {
            'subscription': '123',
            'message-id': '321'
        }, 'blah')

        handler = CoroutineMock()
        subscription = Subscription('123', 1, 'auto', {}, handler)

        frame_handler = Mock()
        frame_handler.get.return_value = subscription

        stomp = StompReader(frame_handler, self.loop)
        await stomp._handle_message(frame)

        handler.assert_called_with(frame, frame.body)
Esempio n. 17
0
    async def test_can_handle_connected_frame_with_heartbeat_disabled(
            self, heartbeater_klass_mock):
        frame = Frame("CONNECTED", {"heart-beat": "1000,1000"}, "{}")

        heartbeater_mock = heartbeater_klass_mock.return_value
        heartbeater_mock.start = CoroutineMock()

        stomp = StompReader(None,
                            self.loop,
                            heartbeat={
                                "enabled": False,
                                "cx": 0,
                                "cy": 0
                            })
        stomp._transport = Mock()
        await stomp._handle_connect(frame)

        heartbeater_klass_mock.assert_not_called
Esempio n. 18
0
    def _process_frame(self, data):
        command, remaining = data.split(b'\n', 1)
        command = self._decode(command)

        raw_headers, remaining = remaining.split(b'\n\n', 1)
        raw_headers = self._decode(raw_headers)
        headers = dict([l.split(':', 1) for l in raw_headers.split('\n')])

        body = None

        # Only SEND, MESSAGE and ERROR frames can have body
        if remaining and command in ('SEND', 'MESSAGE', 'ERROR'):
            if 'content-length' in headers:
                body = remaining[:int(headers['content-length'])]
            else:
                body = remaining

        self._frames_ready.append(Frame(command, headers=headers, body=body))
Esempio n. 19
0
    async def test_can_handle_connected_frame_with_heartbeat_disabled(
            self, heartbeater_klass_mock):
        frame = Frame('CONNECTED', {'heart-beat': '1000,1000'}, '{}')

        heartbeater_mock = heartbeater_klass_mock.return_value
        heartbeater_mock.start = CoroutineMock()

        stomp = StompReader(None,
                            self.loop,
                            heartbeat={
                                'enabled': False,
                                'cx': 0,
                                'cy': 0
                            })
        stomp._transport = Mock()
        await stomp._handle_connect(frame)

        heartbeater_klass_mock.assert_not_called
Esempio n. 20
0
    async def test_can_handle_error_frame(self, logger_mock):
        frame = Frame('ERROR', {'message': 'Invalid error, blah, blah, blah'},
                      'Detail Error: blah, blahh-line-a')

        frame_handler = Mock()
        frame_handler._on_error = CoroutineMock()

        stomp = StompReader(frame_handler, self.loop)

        await stomp._handle_error(frame)

        frame_handler._on_error.assert_called_once()
        self.assertTrue(
            isinstance(frame_handler._on_error.call_args[0][0], StompError))

        logger_mock.error.assert_called_with(
            'Received error: Invalid error, blah, blah, blah')
        logger_mock.debug.assert_called_with(
            'Error details: Detail Error: blah, blahh-line-a')
Esempio n. 21
0
    async def test_can_handle_message_can_nack(self, send_frame_mock):
        frame = Frame('MESSAGE', {
            'subscription': '123',
            'message-id': '321'
        }, 'blah')

        handler = CoroutineMock()
        handler.return_value = False
        subscription = Subscription('123', 1, 'client-individual', {}, handler)

        frame_handler = Mock()
        frame_handler.get.return_value = subscription

        stomp = StompReader(frame_handler, self.loop)
        await stomp._handle_message(frame)

        handler.assert_called_with(frame, frame.body)
        send_frame_mock.assert_called_with('NACK', {
            'subscription': '123',
            'message-id': '321'
        })
Esempio n. 22
0
    async def test_can_handle_message_can_nack(self, send_frame_mock):
        frame = Frame("MESSAGE", {
            "subscription": "123",
            "message-id": "321"
        }, "blah")

        handler = CoroutineMock()
        handler.return_value = False
        subscription = Subscription("123", 1, "client-individual", {}, handler)

        frame_handler = Mock()
        frame_handler.get.return_value = subscription

        stomp = StompReader(frame_handler, self.loop)
        await stomp._handle_message(frame)

        handler.assert_called_with(frame, frame.body)
        send_frame_mock.assert_called_with("NACK", {
            "subscription": "123",
            "message-id": "321"
        })
Esempio n. 23
0
    def _feed_data(self, data):

        if data is None:
            return None

        if not self._pending_parts and data.startswith(self.HEART_BEAT):
            self._frames_ready.append(Frame('HEARTBEAT', headers={}, body=''))
            data = data[1:]

            if data:
                return data

        before_eof, sep, after_eof = data.partition(self.EOF)

        if before_eof:
            self._pending_parts.append(before_eof)

        if sep:
            frame_data = b''.join(self._pending_parts)
            self._pending_parts = []
            self._process_frame(frame_data)

        if after_eof:
            return after_eof
Esempio n. 24
0
    def _feed_data(self, data):

        if data.startswith(self.HEART_BEAT) and not self._pending_parts:
            self._frames_ready.append(Frame('HEARTBEAT', headers={}, body=''))
            data = data[1:]

            if not data:
                return

        self._pending_parts.append(data)

        extra_data, partial_data = self._parse_data(b''.join(
            self._pending_parts))

        if partial_data:
            self._pending_parts = [partial_data]
        else:
            self._pending_parts = []

        # Check if the frame is incomplete or has
        # additional data
        if extra_data:
            self._pending_parts = []
            return extra_data
Esempio n. 25
0
    def feed_data(self, inp: bytes) -> None:
        read_size = len(inp)
        data: Deque[int] = deque(inp)
        i = 0

        while i < read_size:
            i += 1
            b = bytes([data.popleft()])

            if (not self.processed_headers and self.previous_byte == self.EOF
                    and b == self.EOF):
                continue

            if not self.processed_headers:
                if self.awaiting_command and b == b"\n":
                    self._frames_ready.append(
                        Frame("HEARTBEAT", headers={}, body=None))
                    continue
                else:
                    self.awaiting_command = False

                self.current_command.append(b[0])
                if b == b"\n" and (self.previous_byte == b"\n"
                                   or ends_with_crlf(self.current_command)):

                    try:
                        self.action = self._parse_action(self.current_command)
                        self.headers = self._parse_headers(
                            self.current_command)
                        logger.debug("Parsed action %s", self.action)

                        if (self.action in ("SEND", "MESSAGE", "ERROR")
                                and "content-length" in self.headers):
                            self.content_length = int(
                                self.headers["content-length"])
                        else:
                            self.content_length = -1
                    except Exception:
                        self.current_command.clear()
                        return

                    self.processed_headers = True
                    self.current_command.clear()
            else:
                if self.content_length == -1:
                    if b == self.EOF:
                        self.process_command()
                    else:
                        self.current_command.append(b[0])

                        if len(self.current_command) > self.MAX_DATA_LENGTH:
                            # error
                            return
                else:
                    if self.read_length == self.content_length:
                        self.process_command()
                        self.read_length = 0
                    else:
                        self.read_length += 1
                        self.current_command.append(b[0])

            self.previous_byte = b
Esempio n. 26
0
    def _parse_data(self, data):

        if not self._intermediate_frame:
            command, data = data.split(b'\n', 1)
            command = self._decode(command)
            self._intermediate_frame = {'command': command}

        if 'headers' not in self._intermediate_frame:

            headers_body = data.split(b'\n\n', 1)

            if len(headers_body) < 2:
                return None, data

            raw_headers, data = headers_body
            raw_headers = self._decode(raw_headers)
            headers = dict([l.split(':', 1) for l in raw_headers.split('\n')])

            self._intermediate_frame['headers'] = headers

        # After parsing the headers if any, there must be EOF to signal
        # end of frame, failing which return all data
        if not data:
            return None, None

        if self._intermediate_frame['command'] not in\
                ('SEND', 'MESSAGE', 'ERROR'):
            self._intermediate_frame['body'] = None
            self._frames_ready.append(Frame(**self._intermediate_frame))

            self._intermediate_frame = None

            # For commands not allowed to have body, discard all data
            # between headers and the end of frame
            _, extra = data.split(self.EOF, 1)
            return extra, None

        # Only SEND, MESSAGE and ERROR frames can have body

        headers = self._intermediate_frame['headers']
        if 'content-length' in headers:
            content_length = int(headers['content-length'])

            if 'body' not in self._intermediate_frame:
                self._intermediate_frame['body'] = b''

            existing_length = len(self._intermediate_frame['body'])

            missing_length = content_length - existing_length
            # Wait till the entire body is received
            if len(data) <= missing_length:
                self._intermediate_frame['body'] += data
                return None, None

            self._intermediate_frame['body'] += data[:missing_length]
            self._frames_ready.append(Frame(**self._intermediate_frame))

            self._intermediate_frame = None
            # Split at the end of the frame which is at the end of the body
            # and return the rest for further processing
            _, extra = data[missing_length:].split(self.EOF, 1)
            return extra, None

        else:

            if 'body' not in self._intermediate_frame:
                self._intermediate_frame['body'] = b''

            # Try to find the end of the frame
            body, sep, extra = data.partition(self.EOF)

            # If end of frame is not found, return the entire data
            # as partial data to be processed when the rest of the frame
            # arrives
            if not sep:
                self._intermediate_frame['body'] += data
                return None, None

            self._intermediate_frame['body'] += body

            self._frames_ready.append(Frame(**self._intermediate_frame))
            self._intermediate_frame = None
            return extra, None
Esempio n. 27
0
    def feed_data(self, data):
        read_size = len(data)
        data = deque(data)
        i = 0

        while i < read_size:
            i += 1
            b = bytes([data.popleft()])

            if (not self.processed_headers and self.previous_byte == self.EOF
                    and b == self.EOF):
                continue

            if not self.processed_headers:
                if self.awaiting_command and b == b'\n':
                    self._frames_ready.append(
                        Frame('HEARTBEAT', headers={}, body=''))
                    continue
                else:
                    self.awaiting_command = False

                self.current_command.append(b)
                if b == b'\n' and (self.previous_byte == b'\n' or
                                   self.ends_with_crlf(self.current_command)):

                    try:
                        self.action = self._parse_action(self.current_command)
                        self.headers = self._parse_headers(
                            self.current_command)

                        if (self.action in ('SEND', 'MESSAGE', 'ERROR')
                                and 'content-length' in self.headers):
                            self.content_length = int(
                                self.headers['content-length'])
                        else:
                            self.content_length = -1
                    except Exception:
                        self.current_command.clear()
                        return

                    self.processed_headers = True
                    self.current_command.clear()
            else:
                if self.content_length == -1:
                    if b == self.EOF:
                        self.process_command()
                    else:
                        self.current_command.append(b)

                        if len(self.current_command) > self.MAX_DATA_LENGTH:
                            # error
                            return
                else:
                    if self.read_length == self.content_length:
                        self.process_command()
                        self.read_length = 0
                    else:
                        self.read_length += 1
                        self.current_command.append(b)

            self.previous_byte = b