Skip to content

Commit a983c23

Browse files
committed
Fix default buffering strategy for stream deserialization to prevent discarding unpacking data. #321
For detailed information, see #321 comment.
1 parent 9928225 commit a983c23

File tree

3 files changed

+39
-3
lines changed

3 files changed

+39
-3
lines changed

src/MsgPack/PackerUnpackerStreamOptions.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,19 @@ private static bool ShouldWrapStream( Stream stream )
8282
#else
8383
internal
8484
#endif
85-
static readonly PackerUnpackerStreamOptions SingletonForAsync =
85+
static readonly PackerUnpackerStreamOptions SingletonForAsyncPacking =
86+
// It is OK for serialize to do buffering because we explicitly call FlushAsync for it.
8687
new PackerUnpackerStreamOptions { OwnsStream = true, WithBuffering = true };
8788

89+
#if UNITY && DEBUG
90+
public
91+
#else
92+
internal
93+
#endif
94+
static readonly PackerUnpackerStreamOptions SingletonForAsyncUnpacking =
95+
// Buffering causes data loss in deserialization because buffered bytes will be gone in a tail of Deserialize(Stream) call.
96+
new PackerUnpackerStreamOptions { OwnsStream = true, WithBuffering = false };
97+
8898
#if UNITY && DEBUG
8999
public
90100
#else

src/MsgPack/Serialization/MessagePackSerializer`1.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ public Task PackAsync( Stream stream, T objectTree )
219219
/// <seealso cref="P:Capabilities"/>
220220
public async Task PackAsync( Stream stream, T objectTree, CancellationToken cancellationToken )
221221
{
222-
var packer = Packer.Create( stream, this.PackerCompatibilityOptions, PackerUnpackerStreamOptions.SingletonForAsync );
222+
var packer = Packer.Create( stream, this.PackerCompatibilityOptions, PackerUnpackerStreamOptions.SingletonForAsyncPacking );
223223
try
224224
{
225225
await this.PackToAsync( packer, objectTree, cancellationToken ).ConfigureAwait( false );
@@ -324,7 +324,7 @@ public Task<T> UnpackAsync( Stream stream )
324324
public async Task<T> UnpackAsync( Stream stream, CancellationToken cancellationToken )
325325
{
326326
// Unpacker does not have finalizer, so just avoiding unpacker disposing prevents stream closing.
327-
var unpacker = Unpacker.Create( stream, PackerUnpackerStreamOptions.SingletonForAsync, DefaultUnpackerOptions );
327+
var unpacker = Unpacker.Create( stream, PackerUnpackerStreamOptions.SingletonForAsyncUnpacking, DefaultUnpackerOptions );
328328
if ( !( await unpacker.ReadAsync( cancellationToken ).ConfigureAwait( false ) ) )
329329
{
330330
SerializationExceptions.ThrowUnexpectedEndOfStream( unpacker );

test/MsgPack.UnitTest/Serialization/RegressionTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,5 +547,31 @@ public void TestIssue269()
547547
var forString = Assert.Throws<InvalidOperationException>( () => target.AsString() );
548548
Assert.That( forString.Message, Is.EqualTo( "Do not convert MsgPack.MessagePackExtendedTypeObject MessagePackObject to System.String." ) );
549549
}
550+
551+
#if FEATURE_TAP
552+
[Test]
553+
public async Task TestIssue321_AsyncBuffering()
554+
{
555+
var context = new SerializationContext();
556+
var serializer = context.GetSerializer<string>();
557+
using ( var stream = new MyMemoryStream() ) // could also be a NetworkStream or PipeStream
558+
{
559+
await serializer.PackAsync( stream, "hello" );
560+
TestContext.WriteLine( $"1. Stream now in {stream.Position}" );
561+
await serializer.PackAsync( stream, "world" );
562+
TestContext.WriteLine( $"2. Stream now in {stream.Position}" );
563+
TestContext.WriteLine( Binary.ToHexString( stream.ToArray() ) );
564+
stream.Position = 0;
565+
var result1 = await serializer.UnpackAsync( stream );
566+
TestContext.WriteLine( $"3. Stream now in {stream.Position} -> {result1}" );
567+
var result2 = await serializer.UnpackAsync( stream ); // throws MsgPack.InvalidMessagePackStreamException
568+
TestContext.WriteLine( $"4. Stream now in {stream.Position} -> {result2}" );
569+
}
570+
}
571+
#endif // FEATURE_TAP
572+
573+
public class MyMemoryStream : MemoryStream
574+
{
575+
}
550576
}
551577
}

0 commit comments

Comments
 (0)