Skip to content
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

Fix corrupted streaming SSR output #50380

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ba6f214
Update sample to make issue easier to observe
SteveSandersonMS Aug 25, 2023
4d4fb58
Make ViewBufferTextWriter fail clearly if corrupt output will occur
SteveSandersonMS Aug 25, 2023
a62e540
Tiny cleanup
SteveSandersonMS Aug 25, 2023
5c91126
Add investigation notes as comments
SteveSandersonMS Aug 25, 2023
149be39
Temporary experiment with an alternative using MemoryStream
SteveSandersonMS Aug 28, 2023
7c9c9a7
Better implementation with paged pool
SteveSandersonMS Aug 28, 2023
4f6eaef
Cleaner implementation
SteveSandersonMS Aug 28, 2023
e3b1167
Optimizations for writing string or int
SteveSandersonMS Aug 28, 2023
fdd58c4
Split up code files
SteveSandersonMS Aug 28, 2023
000b02c
Add TextChunkTest
SteveSandersonMS Aug 28, 2023
6213043
Add TextChunkPageTest
SteveSandersonMS Aug 28, 2023
20c6156
Add TextChunkPagePoolTest
SteveSandersonMS Aug 28, 2023
e3831c6
Add TextChunkListBuilderTest
SteveSandersonMS Aug 28, 2023
00a1022
Fix accidentally-changed PageSize
SteveSandersonMS Aug 28, 2023
3d64f31
Clean up disposal
SteveSandersonMS Aug 28, 2023
db0c296
Remove all the pooling. Perf is the same.
SteveSandersonMS Aug 28, 2023
0d55544
Tidy and update tests
SteveSandersonMS Aug 28, 2023
2b22977
Add BufferedTextWriterTest
SteveSandersonMS Aug 28, 2023
52262d5
Tidy
SteveSandersonMS Aug 28, 2023
65e537d
Remove temporary comments
SteveSandersonMS Aug 29, 2023
2346a57
Revert sample changes
SteveSandersonMS Aug 29, 2023
4ff9b3f
Another revert
SteveSandersonMS Aug 29, 2023
f248a6a
Add E2E test
SteveSandersonMS Aug 29, 2023
c5c956b
Fix char array segments
SteveSandersonMS Aug 29, 2023
9e4ee1a
More performant fix
SteveSandersonMS Aug 29, 2023
e4ca2e7
Fix build
SteveSandersonMS Aug 29, 2023
d457dcb
Make a test more meaningful
SteveSandersonMS Aug 29, 2023
ba685c4
E2E test fix for Linux
SteveSandersonMS Aug 29, 2023
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
Prev Previous commit
Next Next commit
Split up code files
  • Loading branch information
SteveSandersonMS committed Aug 28, 2023
commit fdd58c4ab5c4c448f266850312827501846b48f4
278 changes: 0 additions & 278 deletions src/Components/Endpoints/src/Rendering/BufferedTextWriter.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;
using System.Text;

namespace Microsoft.AspNetCore.Components.Endpoints.Rendering;

internal class BufferedTextWriter : TextWriter
{
private const int PageSize = 64;
private readonly TextWriter _underlying;
private TextChunkListBuilder _currentOutput;
private TextChunkListBuilder? _previousOutput;
private Task _currentFlushAsyncTask = Task.CompletedTask;

public BufferedTextWriter(TextWriter underlying)
{
_underlying = underlying;
_currentOutput = new(ArrayPool<TextChunk>.Shared, PageSize);
}

public override Encoding Encoding => Encoding.UTF8;

public override void Write(char value)
=> _currentOutput.Add(new TextChunk(value));

public override void Write(char[] buffer, int index, int count)
=> _currentOutput.Add(new TextChunk(new ArraySegment<char>(buffer, index, count)));

public override void Write(string? value)
{
if (value is not null)
{
_currentOutput.Add(new TextChunk(value));
}
}

public override void Write(int value)
=> _currentOutput.Add(new TextChunk(value));

public override void Flush()
=> throw new NotSupportedException();

public override Task FlushAsync()
{
_currentFlushAsyncTask = FlushAsyncCore(_currentFlushAsyncTask);
return _currentFlushAsyncTask;
}

private async Task FlushAsyncCore(Task priorTask)
{
// Must always wait for prior flushes to complete first, since they are
// using _previousOutput and nothing else is allowed to do so
if (!priorTask.IsCompletedSuccessfully)
{
await priorTask;
}

// Swap buffers
var outputToFlush = _currentOutput;
_currentOutput = _previousOutput ?? new(ArrayPool<TextChunk>.Shared, PageSize);
_previousOutput = outputToFlush;

await outputToFlush.WriteToAsync(_underlying);
outputToFlush.Clear();
await _underlying.FlushAsync();
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
_currentOutput.Dispose();
_previousOutput?.Dispose();
}

base.Dispose(disposing);
}
}
Loading