[System.Net.Http] Allow re-sending StreamContent. Fixes #52448
authorMarek Safar <marek.safar@gmail.com>
Mon, 13 Feb 2017 15:05:54 +0000 (16:05 +0100)
committerMarek Safar <marek.safar@gmail.com>
Mon, 13 Feb 2017 21:38:15 +0000 (22:38 +0100)
mcs/class/System.Net.Http/System.Net.Http/StreamContent.cs
mcs/class/System.Net.Http/Test/System.Net.Http/StreamContentTest.cs

index a249aad6f5b44cfd60172918b207a05e6338df27..b8c27858a26f1f9d5049f74444811b1c9d5424f2 100644 (file)
@@ -37,6 +37,8 @@ namespace System.Net.Http
                readonly Stream content;
                readonly int bufferSize;
                readonly CancellationToken cancellationToken;
+               readonly long startPosition;
+               bool contentCopied;
 
                public StreamContent (Stream content)
                        : this (content, 16 * 1024)
@@ -53,6 +55,10 @@ namespace System.Net.Http
 
                        this.content = content;
                        this.bufferSize = bufferSize;
+
+                       if (content.CanSeek) {
+                               startPosition = content.Position;
+                       }
                }
 
                //
@@ -83,6 +89,16 @@ namespace System.Net.Http
 
                protected internal override Task SerializeToStreamAsync (Stream stream, TransportContext context)
                {
+                       if (contentCopied) {
+                               if (!content.CanSeek) {
+                                       throw new InvalidOperationException ("The stream was already consumed. It cannot be read again.");
+                               }
+
+                               content.Seek (startPosition, SeekOrigin.Begin);
+                       } else {
+                               contentCopied = true;
+                       }
+
                        return content.CopyToAsync (stream, bufferSize, cancellationToken);
                }
 
@@ -92,7 +108,7 @@ namespace System.Net.Http
                                length = 0;
                                return false;
                        }
-                       length = content.Length;
+                       length = content.Length - startPosition;
                        return true;
                }
        }
index e56094741263210f94b21d56a8bb6093ffb410ff..a7c9875186ce6fd5a144e8f56291139d203a3270 100644 (file)
@@ -149,8 +149,8 @@ namespace MonoTests.System.Net.Http
                        /*
                        sc = new StreamContent (new ExceptionStream ());
                        try {
-                           sc.CopyToAsync (m).Wait ();
-                           Assert.Fail ("#2");
+                               sc.CopyToAsync (m).Wait ();
+                               Assert.Fail ("#2");
                        } catch (AggregateException) {
                        }
                        */ 
@@ -190,6 +190,32 @@ namespace MonoTests.System.Net.Http
                        Assert.IsTrue (hit, "#10");
                }
 
+               [Test]
+               public void CopyToAsync_Twice ()
+               {
+                       var ms = new MemoryStream();
+                       ms.WriteByte(4);
+                       ms.WriteByte(12);
+                       ms.WriteByte(7);
+                       ms.Seek(1, SeekOrigin.Begin);
+
+                       var sc = new StreamContent(ms);
+
+                       var dest = new MemoryStream();
+                       var task = sc.CopyToAsync(dest);
+                       Assert.True(task.Wait(3000), "#0");
+                       Assert.AreEqual(2, dest.Length, "#1");
+                       dest.Seek(0, SeekOrigin.Begin);
+                       Assert.AreEqual(12, dest.ReadByte(), "#2");
+
+                       dest = new MemoryStream();
+                       task = sc.CopyToAsync(dest);
+                       Assert.True(task.Wait(3000), "#10");
+                       Assert.AreEqual(2, dest.Length, "#11");
+                       dest.Seek(0, SeekOrigin.Begin);
+                       Assert.AreEqual(12, dest.ReadByte(), "#12");
+               }
+
                [Test]
                public void CopyToAsync_ClosedInput ()
                {