Skip to content

Implement HttpRequestStreamReader.ReadToEndAsync #18232

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ protected override void Dispose(bool disposing) { }
public override string ReadLine() { throw null; }
[System.Diagnostics.DebuggerStepThroughAttribute]
public override System.Threading.Tasks.Task<string> ReadLineAsync() { throw null; }
[System.Diagnostics.DebuggerStepThroughAttribute]
public override System.Threading.Tasks.Task<string> ReadToEndAsync() { throw null; }
}
public partial class HttpResponseStreamWriter : System.IO.TextWriter
{
Expand Down
16 changes: 15 additions & 1 deletion src/Http/WebUtilities/src/HttpRequestStreamReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ public override async Task<string> ReadLineAsync()
if (_disposed)
{
throw new ObjectDisposedException(nameof(HttpRequestStreamReader));
}
}

StringBuilder sb = null;
var consumeLineFeed = false;
Expand Down Expand Up @@ -528,6 +528,20 @@ private async Task<int> ReadIntoBufferAsync()
return _charsRead;
}

public async override Task<string> ReadToEndAsync()
{
StringBuilder sb = new StringBuilder(_charsRead - _charBufferIndex);
do
{
int tmpCharPos = _charBufferIndex;
sb.Append(_charBuffer, tmpCharPos, _charsRead - tmpCharPos);
_charBufferIndex = _charsRead; // We consumed these characters
await ReadIntoBufferAsync().ConfigureAwait(false);
} while (_charsRead > 0);

return sb.ToString();
}

private readonly struct ReadLineStepResult
{
public static readonly ReadLineStepResult Done = new ReadLineStepResult(true, null);
Expand Down
96 changes: 96 additions & 0 deletions src/Http/WebUtilities/test/HttpRequestStreamReaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

Expand Down Expand Up @@ -55,6 +56,22 @@ public static async Task ReadToEndAsync()
Assert.Equal(5000, result.Length);
}

[Fact]
public static async Task ReadToEndAsync_Reads_Asynchronously()
{
// Arrange
var stream = new AsyncOnlyStreamWrapper(GetLargeStream());
var reader = new HttpRequestStreamReader(stream, Encoding.UTF8);
var streamReader = new StreamReader(GetLargeStream());
string expected = await streamReader.ReadToEndAsync();

// Act
var actual = await reader.ReadToEndAsync();

// Assert
Assert.Equal(expected, actual);
}

[Fact]
public static void TestRead()
{
Expand Down Expand Up @@ -477,5 +494,84 @@ public static IEnumerable<object[]> ReadLineData()
httpRequestStreamReader.ReadLineAsync()
)};
}

private class AsyncOnlyStreamWrapper : Stream
{
private readonly Stream _inner;

public AsyncOnlyStreamWrapper(Stream inner)
{
_inner = inner;
}

public override bool CanRead => _inner.CanRead;

public override bool CanSeek => _inner.CanSeek;

public override bool CanWrite => _inner.CanWrite;

public override long Length => _inner.Length;

public override long Position
{
get => _inner.Position;
set => _inner.Position = value;
}

public override void Flush()
{
throw SyncOperationForbiddenException();
}

public override Task FlushAsync(CancellationToken cancellationToken)
{
return _inner.FlushAsync(cancellationToken);
}

public override int Read(byte[] buffer, int offset, int count)
{
throw SyncOperationForbiddenException();
}

public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return _inner.ReadAsync(buffer, offset, count, cancellationToken);
}

public override long Seek(long offset, SeekOrigin origin)
{
return _inner.Seek(offset, origin);
}

public override void SetLength(long value)
{
_inner.SetLength(value);
}

public override void Write(byte[] buffer, int offset, int count)
{
throw SyncOperationForbiddenException();
}

public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return _inner.WriteAsync(buffer, offset, count, cancellationToken);
}

protected override void Dispose(bool disposing)
{
_inner.Dispose();
}

public override ValueTask DisposeAsync()
{
return _inner.DisposeAsync();
}

private Exception SyncOperationForbiddenException()
{
return new InvalidOperationException("The stream cannot be accessed synchronously");
}
}
}
}