Skip to content

Commit e21fb7e

Browse files
authored
Fix #19991 Made TestHost fail when Response Flush is called with AllowSynchronossIO being dissabled (#20059)
1 parent d0cc04f commit e21fb7e

File tree

5 files changed

+95
-11
lines changed

5 files changed

+95
-11
lines changed

src/Hosting/TestHost/src/ResponseBodyWriterStream.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ public override void SetLength(long value)
4646

4747
public override void Flush()
4848
{
49+
if (!_allowSynchronousIO())
50+
{
51+
throw new InvalidOperationException("Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true.");
52+
}
53+
4954
FlushAsync().GetAwaiter().GetResult();
5055
}
5156

src/Hosting/TestHost/test/ClientHandlerTests.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ public async Task FlushSendsHeaders()
289289
var handler = new ClientHandler(PathString.Empty, new DummyApplication(async context =>
290290
{
291291
context.Response.Headers["TestHeader"] = "TestValue";
292-
context.Response.Body.Flush();
292+
await context.Response.Body.FlushAsync();
293293
await block.Task;
294294
await context.Response.WriteAsync("BodyFinished");
295295
}));
@@ -305,11 +305,11 @@ public async Task FlushSendsHeaders()
305305
public async Task ClientDisposalCloses()
306306
{
307307
var block = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
308-
var handler = new ClientHandler(PathString.Empty, new DummyApplication(context =>
308+
var handler = new ClientHandler(PathString.Empty, new DummyApplication(async context =>
309309
{
310310
context.Response.Headers["TestHeader"] = "TestValue";
311-
context.Response.Body.Flush();
312-
return block.Task;
311+
await context.Response.Body.FlushAsync();
312+
await block.Task;
313313
}));
314314
var httpClient = new HttpClient(handler);
315315
HttpResponseMessage response = await httpClient.GetAsync("https://example.com/",
@@ -327,11 +327,11 @@ public async Task ClientDisposalCloses()
327327
public async Task ClientCancellationAborts()
328328
{
329329
var block = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
330-
var handler = new ClientHandler(PathString.Empty, new DummyApplication(context =>
330+
var handler = new ClientHandler(PathString.Empty, new DummyApplication(async context =>
331331
{
332332
context.Response.Headers["TestHeader"] = "TestValue";
333-
context.Response.Body.Flush();
334-
return block.Task;
333+
await context.Response.Body.FlushAsync();
334+
await block.Task;
335335
}));
336336
var httpClient = new HttpClient(handler);
337337
HttpResponseMessage response = await httpClient.GetAsync("https://example.com/",

src/Hosting/TestHost/test/HttpContextBuilderTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ public async Task FlushSendsHeaders()
176176
app.Run(async c =>
177177
{
178178
c.Response.Headers["TestHeader"] = "TestValue";
179-
c.Response.Body.Flush();
179+
await c.Response.Body.FlushAsync();
180180
await block.Task;
181181
await c.Response.WriteAsync("BodyFinished");
182182
});
@@ -198,7 +198,7 @@ public async Task ClientDisposalCloses()
198198
app.Run(async c =>
199199
{
200200
c.Response.Headers["TestHeader"] = "TestValue";
201-
c.Response.Body.Flush();
201+
await c.Response.Body.FlushAsync();
202202
await block.Task;
203203
await c.Response.WriteAsync("BodyFinished");
204204
});
@@ -247,7 +247,7 @@ public async Task ClientCancellationAbortsReadAsync()
247247
app.Run(async c =>
248248
{
249249
c.Response.Headers["TestHeader"] = "TestValue";
250-
c.Response.Body.Flush();
250+
await c.Response.Body.FlushAsync();
251251
await block.Task;
252252
await c.Response.WriteAsync("BodyFinished");
253253
});

src/Hosting/TestHost/test/ResponseBodyTests.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
5+
using System.Net.Http;
46
using System.Threading.Tasks;
57
using Microsoft.AspNetCore.Builder;
68
using Microsoft.AspNetCore.Hosting;
@@ -46,6 +48,80 @@ public async Task BodyWriter_StartAsyncGetMemoryAdvance_AutoCompleted()
4648
Assert.Equal(length, bytes.Length);
4749
}
4850

51+
[Fact]
52+
public async Task BodyStream_SyncDisabled_WriteThrows()
53+
{
54+
var contentBytes = new byte[] {32};
55+
using var host = await CreateHost(async httpContext =>
56+
{
57+
await httpContext.Response.StartAsync();
58+
httpContext.Response.Body.Write(contentBytes, 0, contentBytes.Length);
59+
await httpContext.Response.CompleteAsync();
60+
});
61+
62+
var client = host.GetTestServer().CreateClient();
63+
var ex = await Assert.ThrowsAsync<InvalidOperationException>(()=> client.GetAsync("/"));
64+
Assert.Contains("Synchronous operations are disallowed.", ex.Message);
65+
}
66+
67+
[Fact]
68+
public async Task BodyStream_SyncEnabled_WriteSucceeds()
69+
{
70+
var contentBytes = new byte[] {32};
71+
using var host = await CreateHost(async httpContext =>
72+
{
73+
await httpContext.Response.StartAsync();
74+
httpContext.Response.Body.Write(contentBytes, 0, contentBytes.Length);
75+
await httpContext.Response.CompleteAsync();
76+
});
77+
78+
host.GetTestServer().AllowSynchronousIO = true;
79+
80+
var client = host.GetTestServer().CreateClient();
81+
var response = await client.GetAsync("/");
82+
var responseBytes = await response.Content.ReadAsByteArrayAsync();
83+
Assert.Equal(contentBytes, responseBytes);
84+
}
85+
86+
[Fact]
87+
public async Task BodyStream_SyncDisabled_FlushThrows()
88+
{
89+
var contentBytes = new byte[] {32};
90+
using var host = await CreateHost(async httpContext =>
91+
{
92+
await httpContext.Response.StartAsync();
93+
await httpContext.Response.Body.WriteAsync(contentBytes, 0, contentBytes.Length);
94+
httpContext.Response.Body.Flush();
95+
await httpContext.Response.CompleteAsync();
96+
});
97+
98+
var client = host.GetTestServer().CreateClient();
99+
var requestException = await Assert.ThrowsAsync<HttpRequestException>(()=> client.GetAsync("/"));
100+
var ex = (InvalidOperationException) requestException?.InnerException?.InnerException;
101+
Assert.NotNull(ex);
102+
Assert.Contains("Synchronous operations are disallowed.", ex.Message);
103+
}
104+
105+
[Fact]
106+
public async Task BodyStream_SyncEnabled_FlushSucceeds()
107+
{
108+
var contentBytes = new byte[] {32};
109+
using var host = await CreateHost(async httpContext =>
110+
{
111+
await httpContext.Response.StartAsync();
112+
await httpContext.Response.Body.WriteAsync(contentBytes, 0, contentBytes.Length);
113+
httpContext.Response.Body.Flush();
114+
await httpContext.Response.CompleteAsync();
115+
});
116+
117+
host.GetTestServer().AllowSynchronousIO = true;
118+
119+
var client = host.GetTestServer().CreateClient();
120+
var response = await client.GetAsync("/");
121+
var responseBytes = await response.Content.ReadAsByteArrayAsync();
122+
Assert.Equal(contentBytes, responseBytes);
123+
}
124+
49125
private Task<IHost> CreateHost(RequestDelegate appDelegate)
50126
{
51127
return new HostBuilder()

src/Middleware/ResponseCompression/test/ResponseCompressionMiddlewareTest.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,10 @@ public async Task FlushHeaders_SendsHeaders_Compresses(string encoding, int expe
598598
});
599599
});
600600

601-
var server = new TestServer(builder);
601+
var server = new TestServer(builder)
602+
{
603+
AllowSynchronousIO = true // needed for synchronous flush
604+
};
602605
var client = server.CreateClient();
603606

604607
var request = new HttpRequestMessage(HttpMethod.Get, "");

0 commit comments

Comments
 (0)