Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
5 changes: 5 additions & 0 deletions src/Hosting/TestHost/src/ResponseBodyWriterStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public override void SetLength(long value)

public override void Flush()
{
if (!_allowSynchronousIO())
{
throw new InvalidOperationException("Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true.");
}

FlushAsync().GetAwaiter().GetResult();
}

Expand Down
14 changes: 7 additions & 7 deletions src/Hosting/TestHost/test/ClientHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ public async Task FlushSendsHeaders()
var handler = new ClientHandler(PathString.Empty, new DummyApplication(async context =>
{
context.Response.Headers["TestHeader"] = "TestValue";
context.Response.Body.Flush();
await context.Response.Body.FlushAsync();
await block.Task;
await context.Response.WriteAsync("BodyFinished");
}));
Expand All @@ -305,11 +305,11 @@ public async Task FlushSendsHeaders()
public async Task ClientDisposalCloses()
{
var block = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
var handler = new ClientHandler(PathString.Empty, new DummyApplication(context =>
var handler = new ClientHandler(PathString.Empty, new DummyApplication(async context =>
{
context.Response.Headers["TestHeader"] = "TestValue";
context.Response.Body.Flush();
return block.Task;
await context.Response.Body.FlushAsync();
await block.Task;
}));
var httpClient = new HttpClient(handler);
HttpResponseMessage response = await httpClient.GetAsync("https://example.com/",
Expand All @@ -327,11 +327,11 @@ public async Task ClientDisposalCloses()
public async Task ClientCancellationAborts()
{
var block = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
var handler = new ClientHandler(PathString.Empty, new DummyApplication(context =>
var handler = new ClientHandler(PathString.Empty, new DummyApplication(async context =>
{
context.Response.Headers["TestHeader"] = "TestValue";
context.Response.Body.Flush();
return block.Task;
await context.Response.Body.FlushAsync();
await block.Task;
}));
var httpClient = new HttpClient(handler);
HttpResponseMessage response = await httpClient.GetAsync("https://example.com/",
Expand Down
6 changes: 3 additions & 3 deletions src/Hosting/TestHost/test/HttpContextBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ public async Task FlushSendsHeaders()
app.Run(async c =>
{
c.Response.Headers["TestHeader"] = "TestValue";
c.Response.Body.Flush();
await c.Response.Body.FlushAsync();
await block.Task;
await c.Response.WriteAsync("BodyFinished");
});
Expand All @@ -198,7 +198,7 @@ public async Task ClientDisposalCloses()
app.Run(async c =>
{
c.Response.Headers["TestHeader"] = "TestValue";
c.Response.Body.Flush();
await c.Response.Body.FlushAsync();
await block.Task;
await c.Response.WriteAsync("BodyFinished");
});
Expand Down Expand Up @@ -247,7 +247,7 @@ public async Task ClientCancellationAbortsReadAsync()
app.Run(async c =>
{
c.Response.Headers["TestHeader"] = "TestValue";
c.Response.Body.Flush();
await c.Response.Body.FlushAsync();
await block.Task;
await c.Response.WriteAsync("BodyFinished");
});
Expand Down
76 changes: 76 additions & 0 deletions src/Hosting/TestHost/test/ResponseBodyTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
Expand Down Expand Up @@ -46,6 +48,80 @@ public async Task BodyWriter_StartAsyncGetMemoryAdvance_AutoCompleted()
Assert.Equal(length, bytes.Length);
}

[Fact]
public async Task BodyStream_SyncDisabled_WriteThrows()
{
var contentBytes = new byte[] {32};
using var host = await CreateHost(async httpContext =>
{
await httpContext.Response.StartAsync();
httpContext.Response.Body.Write(contentBytes, 0, contentBytes.Length);
await httpContext.Response.CompleteAsync();
});

var client = host.GetTestServer().CreateClient();
var ex = await Assert.ThrowsAsync<InvalidOperationException>(()=> client.GetAsync("/"));
Assert.Contains("Synchronous operations are disallowed.", ex.Message);
}

[Fact]
public async Task BodyStream_SyncEnabled_WriteSucceeds()
{
var contentBytes = new byte[] {32};
using var host = await CreateHost(async httpContext =>
{
await httpContext.Response.StartAsync();
httpContext.Response.Body.Write(contentBytes, 0, contentBytes.Length);
await httpContext.Response.CompleteAsync();
});

host.GetTestServer().AllowSynchronousIO = true;

var client = host.GetTestServer().CreateClient();
var response = await client.GetAsync("/");
var responseBytes = await response.Content.ReadAsByteArrayAsync();
Assert.Equal(contentBytes, responseBytes);
}

[Fact]
public async Task BodyStream_SyncDisabled_FlushThrows()
{
var contentBytes = new byte[] {32};
using var host = await CreateHost(async httpContext =>
{
await httpContext.Response.StartAsync();
await httpContext.Response.Body.WriteAsync(contentBytes, 0, contentBytes.Length);
httpContext.Response.Body.Flush();
await httpContext.Response.CompleteAsync();
});

var client = host.GetTestServer().CreateClient();
var requestException = await Assert.ThrowsAsync<HttpRequestException>(()=> client.GetAsync("/"));
var ex = (InvalidOperationException) requestException?.InnerException?.InnerException;
Assert.NotNull(ex);
Assert.Contains("Synchronous operations are disallowed.", ex.Message);
}

[Fact]
public async Task BodyStream_SyncEnabled_FlushSucceeds()
{
var contentBytes = new byte[] {32};
using var host = await CreateHost(async httpContext =>
{
await httpContext.Response.StartAsync();
await httpContext.Response.Body.WriteAsync(contentBytes, 0, contentBytes.Length);
httpContext.Response.Body.Flush();
await httpContext.Response.CompleteAsync();
});

host.GetTestServer().AllowSynchronousIO = true;

var client = host.GetTestServer().CreateClient();
var response = await client.GetAsync("/");
var responseBytes = await response.Content.ReadAsByteArrayAsync();
Assert.Equal(contentBytes, responseBytes);
}

private Task<IHost> CreateHost(RequestDelegate appDelegate)
{
return new HostBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ public async Task FlushHeaders_SendsHeaders_Compresses(string encoding, int expe
{
context.Response.Headers[HeaderNames.ContentMD5] = "MD5";
context.Response.ContentType = TextPlain;
context.Response.Body.Flush();
await context.Response.Body.FlushAsync();
await responseReceived.Task.TimeoutAfter(TimeSpan.FromSeconds(3));
await context.Response.WriteAsync(new string('a', 100));
});
Expand Down