PipeWriter and length prefixed messages

370 Views Asked by At

I'm trying to write binary message using PipeWriter that is prefixed with byte length. Byte length is unknown to me until whole message is finished. Can this be done at all with PipeWriter?

Basically I'm trying to implement those three methods:

void StartMessage(PipeWriter writer)
{
    // leave 4 empty bytes for length written at later time
    this.header_ = writer.GetMemory(4);
    
    writer.Advance(4);
}

void Write(PipeWriter writer, ReadOnlySpan<byte> span)
{
    var buffer = writer.GetSpan(span.Length);

    span.CopyTo(buffer);

    writer.Advance(span.Length);

    this.totalLength_ += span.Length;
}

ValueTask EndMessage(PipeWriter writer)
{
    // should prepend this.totalLength_ to the message
    BinaryPrimities.WriteUInt32BingEndian(this.header_.Span, this.totalLength_);

    // signal end of message
    return writer.FlushAsync();
}

This is giving me expected results, but documentation of PipeWrite.Advance states:

You must request a new buffer after calling System.IO.Pipelines.PipeWriter.Advance(System.Int32) to continue writing more data; you cannot write to a previously acquired buffer.

Reading such message can be easily done using PipeReader and AdvanceTo(consumed,examined) method, where we only consume input when whole length of message is available, but I've been unable to come up with PipeWriter usage that is correct according to documentation.

1

There are 1 best solutions below

0
On

The pattern you are showing will lead to an use after free. And you will overwrite your output buffer on random parts.

You need to not send the message until all the bytes is present, it mean that you need all the message being in memory before being able to send it.

You can easily achieve this by writing to a MemoryStream then pass the underlying buffer to the PipeWriter later.

Also, mind that PipeWriter.GetMemory can, and will return more bytes than what you asked for.