Skip to content
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
1 change: 1 addition & 0 deletions Fluid.Tests/Fluid.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
<ProjectReference Include="..\Fluid.MvcViewEngine\Fluid.MvcViewEngine.csproj" />
<ProjectReference Include="..\MinimalApis.LiquidViews\MinimalApis.LiquidViews.csproj" />
<ProjectReference Include="..\Fluid.SourceGenerator\Fluid.SourceGenerator.csproj" />
<ProjectReference Include="..\Fluid.ViewEngine\Fluid.ViewEngine.csproj" />
<ProjectReference Include="..\Fluid\Fluid.csproj" />
Expand Down
110 changes: 110 additions & 0 deletions Fluid.Tests/PipeWriterFluidOutputTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.Buffers;
using System.IO.Pipelines;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MinimalApis.LiquidViews;
using Xunit;

namespace Fluid.Tests
{
public class PipeWriterFluidOutputTests
{
[Fact]
public async Task WritesUtf8DirectlyToPipe()
{
var pipe = new Pipe();
await using var output = new PipeWriterFluidOutput(pipe.Writer, bufferSize: 16);

output.Write("Hello, ");
output.Write("\u4e16\u754c");
await output.FlushAsync();

Assert.Equal("Hello, \u4e16\u754c", await ReadAsync(pipe.Reader));
}

[Fact]
public async Task PreservesSurrogatePairsAcrossWrites()
{
var pipe = new Pipe();
await using var output = new PipeWriterFluidOutput(pipe.Writer, bufferSize: 16);

output.Write("\ud83d");
output.Write("\ude80");
await output.FlushAsync();

Assert.Equal("\ud83d\ude80", await ReadAsync(pipe.Reader));
}

[Fact]
public async Task EncodesCharactersWrittenThroughBufferWriter()
{
var pipe = new Pipe();
await using var output = new PipeWriterFluidOutput(pipe.Writer, bufferSize: 2);

"Fluid".AsSpan().CopyTo(output.GetSpan(5));
output.Advance(5);
await output.FlushAsync();

Assert.Equal("Fluid", await ReadAsync(pipe.Reader));
}

[Fact]
public async Task FlushesWhenOutputBufferSizeIsReached()
{
var pipe = new Pipe();
await using var output = new PipeWriterFluidOutput(pipe.Writer, bufferSize: 4);

output.Write("content");

Assert.Equal("content", await ReadAsync(pipe.Reader));
}

[Fact]
public async Task BufferGrowthDoesNotIncreaseFlushThreshold()
{
var pipe = new Pipe();
await using var output = new PipeWriterFluidOutput(pipe.Writer, bufferSize: 4);

"data".AsSpan().CopyTo(output.GetSpan(8));
output.Advance(4);

Assert.Equal("data", await ReadAsync(pipe.Reader));
}

[Fact]
public async Task FlushHonorsCancellation()
{
var pipe = new Pipe();
using var cancellation = new CancellationTokenSource();
var output = new PipeWriterFluidOutput(pipe.Writer, bufferSize: 16, cancellation.Token);
output.Write("value");
cancellation.Cancel();

await Assert.ThrowsAsync<OperationCanceledException>(() => output.FlushAsync().AsTask());
await output.DisposeAsync();
}

[Fact]
public async Task DisposeSkipsFlushAfterCancellation()
{
var pipe = new Pipe();
using var cancellation = new CancellationTokenSource();
var output = new PipeWriterFluidOutput(pipe.Writer, bufferSize: 16, cancellation.Token);
output.Write("value");

cancellation.Cancel();

await output.DisposeAsync();
}

private static async Task<string> ReadAsync(PipeReader reader)
{
var result = await reader.ReadAsync();
var value = Encoding.UTF8.GetString(result.Buffer.ToArray());
reader.AdvanceTo(result.Buffer.End);
return value;
}
}
}
11 changes: 3 additions & 8 deletions MinimalApis.LiquidViews/ActionViewResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Fluid.Utils;

namespace MinimalApis.LiquidViews
{
Expand Down Expand Up @@ -58,22 +57,18 @@ public async Task ExecuteAsync(HttpContext httpContext)
httpContext.Response.StatusCode = 200;
httpContext.Response.ContentType = ContentType;

await using var sw = new StreamWriter(httpContext.Response.Body);
var bufferSize = context.Options.OutputBufferSize;
if (bufferSize <= 0)
{
bufferSize = 16 * 1024;
}

await using var output = new TextWriterFluidOutput(
sw,
await using var output = new PipeWriterFluidOutput(
httpContext.Response.BodyWriter,
bufferSize,
httpContext.RequestAborted,
leaveOpen: true,
allowSynchronousIO: false);
httpContext.RequestAborted);
await fluidViewRenderer.RenderViewAsync(output, viewPath, context);
await output.FlushAsync();
await sw.FlushAsync(httpContext.RequestAborted);
}

private static async ValueTask<string> LocatePageFromViewLocationsAsync(
Expand Down
Loading