fix: message streaming

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-06-29 15:42:29 -04:00
parent 5b27535723
commit 826940087d
4 changed files with 36 additions and 18 deletions
+15 -4
View File
@@ -56,16 +56,27 @@ final class ProtocolReader
}
if (str_starts_with($raw, '* ')) {
$parts = preg_split('/\s+/', substr($raw, 2), 2) ?: [];
$label = strtoupper($parts[0] ?? '');
// Keep the payload as a slice of the raw response. FETCH payloads can
// contain large message literals, so eagerly splitting here would keep
// a second full copy alive while the message is parsed.
$labelEnd = 2;
while (isset($raw[$labelEnd]) && !ctype_space($raw[$labelEnd])) {
$labelEnd++;
}
$label = strtoupper(substr($raw, 2, $labelEnd - 2));
$payloadOffset = $labelEnd;
while (isset($raw[$payloadOffset]) && ctype_space($raw[$payloadOffset])) {
$payloadOffset++;
}
$this->logger?->debug('IMAP untagged response received: {raw}', [
'label' => $label,
'raw' => $raw,
]);
return new UntaggedResponse(
$label,
$parts[1] ?? '',
'',
$raw,
$payloadOffset,
);
}
@@ -181,4 +192,4 @@ final class ProtocolReader
return $raw;
}
}
}
@@ -10,6 +10,7 @@ final class UntaggedResponse implements ResponseInterface
private readonly string $label,
private readonly string $payload,
private readonly string $raw,
private readonly ?int $payloadOffset = null,
) {}
public function label(): string
@@ -19,6 +20,12 @@ final class UntaggedResponse implements ResponseInterface
public function payload(): string
{
// ProtocolReader records an offset for potentially large responses and
// only materializes the payload for command parsers that actually need it.
if ($this->payloadOffset !== null) {
return substr($this->raw, $this->payloadOffset);
}
return $this->payload;
}
@@ -27,7 +34,7 @@ final class UntaggedResponse implements ResponseInterface
*/
public function payloadTokens(): array
{
$payload = trim($this->payload);
$payload = trim($this->payload());
if ($payload === '') {
return [];
@@ -40,4 +47,4 @@ final class UntaggedResponse implements ResponseInterface
{
return $this->raw;
}
}
}