Skip to content

Commit 5d914d4

Browse files
Implement HttpRequestStreamReader.ReadToEndAsync (#18232)
1 parent 698ab15 commit 5d914d4

File tree

3 files changed

+113
-1
lines changed

3 files changed

+113
-1
lines changed

src/Http/WebUtilities/ref/Microsoft.AspNetCore.WebUtilities.netcoreapp.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ protected override void Dispose(bool disposing) { }
152152
public override string ReadLine() { throw null; }
153153
[System.Diagnostics.DebuggerStepThroughAttribute]
154154
public override System.Threading.Tasks.Task<string> ReadLineAsync() { throw null; }
155+
[System.Diagnostics.DebuggerStepThroughAttribute]
156+
public override System.Threading.Tasks.Task<string> ReadToEndAsync() { throw null; }
155157
}
156158
public partial class HttpResponseStreamWriter : System.IO.TextWriter
157159
{

src/Http/WebUtilities/src/HttpRequestStreamReader.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ public override async Task<string> ReadLineAsync()
333333
if (_disposed)
334334
{
335335
throw new ObjectDisposedException(nameof(HttpRequestStreamReader));
336-
}
336+
}
337337

338338
StringBuilder sb = null;
339339
var consumeLineFeed = false;
@@ -528,6 +528,20 @@ private async Task<int> ReadIntoBufferAsync()
528528
return _charsRead;
529529
}
530530

531+
public async override Task<string> ReadToEndAsync()
532+
{
533+
StringBuilder sb = new StringBuilder(_charsRead - _charBufferIndex);
534+
do
535+
{
536+
int tmpCharPos = _charBufferIndex;
537+
sb.Append(_charBuffer, tmpCharPos, _charsRead - tmpCharPos);
538+
_charBufferIndex = _charsRead; // We consumed these characters
539+
await ReadIntoBufferAsync().ConfigureAwait(false);
540+
} while (_charsRead > 0);
541+
542+
return sb.ToString();
543+
}
544+
531545
private readonly struct ReadLineStepResult
532546
{
533547
public static readonly ReadLineStepResult Done = new ReadLineStepResult(true, null);

src/Http/WebUtilities/test/HttpRequestStreamReaderTest.cs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Collections.Generic;
88
using System.IO;
99
using System.Text;
10+
using System.Threading;
1011
using System.Threading.Tasks;
1112
using Xunit;
1213

@@ -55,6 +56,22 @@ public static async Task ReadToEndAsync()
5556
Assert.Equal(5000, result.Length);
5657
}
5758

59+
[Fact]
60+
public static async Task ReadToEndAsync_Reads_Asynchronously()
61+
{
62+
// Arrange
63+
var stream = new AsyncOnlyStreamWrapper(GetLargeStream());
64+
var reader = new HttpRequestStreamReader(stream, Encoding.UTF8);
65+
var streamReader = new StreamReader(GetLargeStream());
66+
string expected = await streamReader.ReadToEndAsync();
67+
68+
// Act
69+
var actual = await reader.ReadToEndAsync();
70+
71+
// Assert
72+
Assert.Equal(expected, actual);
73+
}
74+
5875
[Fact]
5976
public static void TestRead()
6077
{
@@ -477,5 +494,84 @@ public static IEnumerable<object[]> ReadLineData()
477494
httpRequestStreamReader.ReadLineAsync()
478495
)};
479496
}
497+
498+
private class AsyncOnlyStreamWrapper : Stream
499+
{
500+
private readonly Stream _inner;
501+
502+
public AsyncOnlyStreamWrapper(Stream inner)
503+
{
504+
_inner = inner;
505+
}
506+
507+
public override bool CanRead => _inner.CanRead;
508+
509+
public override bool CanSeek => _inner.CanSeek;
510+
511+
public override bool CanWrite => _inner.CanWrite;
512+
513+
public override long Length => _inner.Length;
514+
515+
public override long Position
516+
{
517+
get => _inner.Position;
518+
set => _inner.Position = value;
519+
}
520+
521+
public override void Flush()
522+
{
523+
throw SyncOperationForbiddenException();
524+
}
525+
526+
public override Task FlushAsync(CancellationToken cancellationToken)
527+
{
528+
return _inner.FlushAsync(cancellationToken);
529+
}
530+
531+
public override int Read(byte[] buffer, int offset, int count)
532+
{
533+
throw SyncOperationForbiddenException();
534+
}
535+
536+
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
537+
{
538+
return _inner.ReadAsync(buffer, offset, count, cancellationToken);
539+
}
540+
541+
public override long Seek(long offset, SeekOrigin origin)
542+
{
543+
return _inner.Seek(offset, origin);
544+
}
545+
546+
public override void SetLength(long value)
547+
{
548+
_inner.SetLength(value);
549+
}
550+
551+
public override void Write(byte[] buffer, int offset, int count)
552+
{
553+
throw SyncOperationForbiddenException();
554+
}
555+
556+
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
557+
{
558+
return _inner.WriteAsync(buffer, offset, count, cancellationToken);
559+
}
560+
561+
protected override void Dispose(bool disposing)
562+
{
563+
_inner.Dispose();
564+
}
565+
566+
public override ValueTask DisposeAsync()
567+
{
568+
return _inner.DisposeAsync();
569+
}
570+
571+
private Exception SyncOperationForbiddenException()
572+
{
573+
return new InvalidOperationException("The stream cannot be accessed synchronously");
574+
}
575+
}
480576
}
481577
}

0 commit comments

Comments
 (0)