Skip to content

Binary message are not properly handled #140

@giyokun

Description

@giyokun

Hello,

Thank you for the work on stompman.

I was trying to pull content from a server that returns binary content in the body.

However, I found out the parser was cutting off the stream mid-string and was not respecting the content-length header.
I implemented a really dirty workaround to get around the problem but I think the whole parsing would need to be redone to parse the headers as they come and to use the content-length for body processing. Unfortunately, I don't really know how one would go about doing this change correctly and cleanly...

--
@DataClass(kw_only=True, slots=True)
class FrameParser:
_lines: deque[bytearray] = field(default_factory=deque, init=False)
_current_line: bytearray = field(default_factory=bytearray, init=False)
_previous_byte: bytes = field(default=b"", init=False)
_headers_processed: bool = field(default=False, init=False)
_binary: bool = field(default=False, init=False)

def parse_frames_from_chunk(self, chunk: bytes) -> Iterator[AnyClientFrame | AnyServerFrame]:
    for byte in iter_bytes(chunk):
        if byte == NULL and not self._binary:
            if self._headers_processed:
                self._lines.append(self._current_line)
                yield parse_lines_into_frame(self._lines)
            self._reset()

        elif not self._headers_processed and byte == NEWLINE:
            if self._current_line or self._lines:
                if self._previous_byte == CARRIAGE:
                    self._current_line.pop()
                self._headers_processed = not self._current_line  # extra empty line after headers
                if self._headers_processed:
                    #  check if content-length
                    for l in self._lines:
                        header = parse_header(l)
                        if header and header[0] == "content-length":
                            self._binary = True
                            break
                if not self._lines and bytes(self._current_line) not in COMMANDS_TO_FRAMES:
                    self._reset()
                else:
                    self._lines.append(self._current_line)
                    self._current_line = bytearray()
            else:
                yield HeartbeatFrame()

        else:
            self._current_line += byte

        self._previous_byte = byte

    if self._previous_byte == NULL and len(self._current_line):
        self._lines.append(self._current_line)
        yield parse_lines_into_frame(self._lines)
        self._reset()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions