* Disable AllowSynchronousIO by default in all servers
This commit is contained in:
parent
7daa0e0145
commit
93a24b03bb
|
|
@ -21,13 +21,17 @@ namespace Microsoft.AspNetCore.TestHost
|
|||
|
||||
public override bool CanRead => _inner.CanRead;
|
||||
|
||||
public override bool CanSeek => _inner.CanSeek;
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override bool CanWrite => _inner.CanWrite;
|
||||
|
||||
public override long Length => _inner.Length;
|
||||
public override long Length => throw new NotSupportedException("The stream is not seekable.");
|
||||
|
||||
public override long Position { get => _inner.Position; set => _inner.Position = value; }
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotSupportedException("The stream is not seekable.");
|
||||
set => throw new NotSupportedException("The stream is not seekable.");
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
|
|
@ -72,12 +76,12 @@ namespace Microsoft.AspNetCore.TestHost
|
|||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
return _inner.Seek(offset, origin);
|
||||
throw new NotSupportedException("The stream is not seekable.");
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
_inner.SetLength(value);
|
||||
throw new NotSupportedException("The stream is not seekable.");
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
|
|
|
|||
|
|
@ -81,9 +81,9 @@ namespace Microsoft.AspNetCore.TestHost
|
|||
/// Gets or sets a value that controls whether synchronous IO is allowed for the <see cref="HttpContext.Request"/> and <see cref="HttpContext.Response"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Defaults to true.
|
||||
/// Defaults to false.
|
||||
/// </remarks>
|
||||
public bool AllowSynchronousIO { get; set; } = true;
|
||||
public bool AllowSynchronousIO { get; set; } = false;
|
||||
|
||||
private IHttpApplication<Context> Application
|
||||
{
|
||||
|
|
|
|||
|
|
@ -254,6 +254,13 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
|
||||
var dataContractSerializer = GetCachedSerializer(wrappingType);
|
||||
|
||||
// Opt into sync IO support until we can work out an alternative https://github.com/aspnet/AspNetCore/issues/6397
|
||||
var syncIOFeature = context.HttpContext.Features.Get<Http.Features.IHttpBodyControlFeature>();
|
||||
if (syncIOFeature != null)
|
||||
{
|
||||
syncIOFeature.AllowSynchronousIO = true;
|
||||
}
|
||||
|
||||
using (var textWriter = context.WriterFactory(context.HttpContext.Response.Body, writerSettings.Encoding))
|
||||
{
|
||||
using (var xmlWriter = CreateXmlWriter(context, textWriter, writerSettings))
|
||||
|
|
|
|||
|
|
@ -230,6 +230,13 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
|
||||
var xmlSerializer = GetCachedSerializer(wrappingType);
|
||||
|
||||
// Opt into sync IO support until we can work out an alternative https://github.com/aspnet/AspNetCore/issues/6397
|
||||
var syncIOFeature = context.HttpContext.Features.Get<Http.Features.IHttpBodyControlFeature>();
|
||||
if (syncIOFeature != null)
|
||||
{
|
||||
syncIOFeature.AllowSynchronousIO = true;
|
||||
}
|
||||
|
||||
using (var textWriter = context.WriterFactory(context.HttpContext.Response.Body, writerSettings.Encoding))
|
||||
{
|
||||
using (var xmlWriter = CreateXmlWriter(context, textWriter, writerSettings))
|
||||
|
|
|
|||
|
|
@ -105,6 +105,13 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
response.StatusCode = result.StatusCode.Value;
|
||||
}
|
||||
|
||||
// Opt into sync IO support until we can work out an alternative https://github.com/aspnet/AspNetCore/issues/6397
|
||||
var syncIOFeature = context.HttpContext.Features.Get<Http.Features.IHttpBodyControlFeature>();
|
||||
if (syncIOFeature != null)
|
||||
{
|
||||
syncIOFeature.AllowSynchronousIO = true;
|
||||
}
|
||||
|
||||
using (var writer = new HttpResponseStreamWriter(response.Body, resolvedContentTypeEncoding))
|
||||
{
|
||||
var viewContext = new ViewContext(
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
@ -48,6 +50,7 @@ namespace BasicWebSite
|
|||
{
|
||||
private readonly Stream _innerStream;
|
||||
private readonly IHttpMaxRequestBodySizeFeature _maxRequestBodySizeFeature;
|
||||
private long _totalRead;
|
||||
|
||||
public RequestBodySizeCheckingStream(
|
||||
Stream innerStream,
|
||||
|
|
@ -78,12 +81,39 @@ namespace BasicWebSite
|
|||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_maxRequestBodySizeFeature.MaxRequestBodySize != null
|
||||
&& _innerStream.Length > _maxRequestBodySizeFeature.MaxRequestBodySize)
|
||||
&& _innerStream.CanSeek && _innerStream.Length > _maxRequestBodySizeFeature.MaxRequestBodySize)
|
||||
{
|
||||
throw new InvalidOperationException("Request content size is greater than the limit size");
|
||||
}
|
||||
|
||||
return _innerStream.Read(buffer, offset, count);
|
||||
var read = _innerStream.Read(buffer, offset, count);
|
||||
_totalRead += read;
|
||||
|
||||
if (_maxRequestBodySizeFeature.MaxRequestBodySize != null
|
||||
&& _totalRead > _maxRequestBodySizeFeature.MaxRequestBodySize)
|
||||
{
|
||||
throw new InvalidOperationException("Request content size is greater than the limit size");
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_maxRequestBodySizeFeature.MaxRequestBodySize != null
|
||||
&& _innerStream.CanSeek && _innerStream.Length > _maxRequestBodySizeFeature.MaxRequestBodySize)
|
||||
{
|
||||
throw new InvalidOperationException("Request content size is greater than the limit size");
|
||||
}
|
||||
|
||||
var read = await _innerStream.ReadAsync(buffer, offset, count, cancellationToken);
|
||||
_totalRead += read;
|
||||
|
||||
if (_maxRequestBodySizeFeature.MaxRequestBodySize != null
|
||||
&& _totalRead > _maxRequestBodySizeFeature.MaxRequestBodySize)
|
||||
{
|
||||
throw new InvalidOperationException("Request content size is greater than the limit size");
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ namespace FormatterWebSite
|
|||
SupportedEncodings.Add(Encoding.Unicode);
|
||||
}
|
||||
|
||||
public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding effectiveEncoding)
|
||||
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding effectiveEncoding)
|
||||
{
|
||||
var request = context.HttpContext.Request;
|
||||
using (var reader = new StreamReader(request.Body, effectiveEncoding))
|
||||
{
|
||||
var stringContent = reader.ReadToEnd();
|
||||
return InputFormatterResult.SuccessAsync(stringContent);
|
||||
var stringContent = await reader.ReadToEndAsync();
|
||||
return await InputFormatterResult.SuccessAsync(stringContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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;
|
||||
|
|
@ -136,9 +136,9 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that controls whether synchronous IO is allowed for the HttpContext.Request.Body and HttpContext.Response.Body.
|
||||
/// The default is `true`.
|
||||
/// The default is `false`.
|
||||
/// </summary>
|
||||
public bool AllowSynchronousIO { get; set; } = true;
|
||||
public bool AllowSynchronousIO { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that controls how http.sys reacts when rejecting requests due to throttling conditions - like when the request
|
||||
|
|
|
|||
|
|
@ -51,14 +51,13 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
[ConditionalFact]
|
||||
public async Task Https_EchoHelloWorld_Success()
|
||||
{
|
||||
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
||||
using (Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
|
||||
{
|
||||
string input = new StreamReader(httpContext.Request.Body).ReadToEnd();
|
||||
var input = await new StreamReader(httpContext.Request.Body).ReadToEndAsync();
|
||||
Assert.Equal("Hello World", input);
|
||||
byte[] body = Encoding.UTF8.GetBytes("Hello World");
|
||||
var body = Encoding.UTF8.GetBytes("Hello World");
|
||||
httpContext.Response.ContentLength = body.Length;
|
||||
httpContext.Response.Body.Write(body, 0, body.Length);
|
||||
return Task.FromResult(0);
|
||||
await httpContext.Response.Body.WriteAsync(body, 0, body.Length);
|
||||
}))
|
||||
{
|
||||
string response = await SendRequestAsync(address, "Hello World");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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;
|
||||
|
|
@ -17,26 +17,26 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
|
|||
public class RequestBodyTests
|
||||
{
|
||||
[ConditionalFact]
|
||||
public async Task RequestBody_SyncReadEnabledByDefault_ThrowsWhenDisabled()
|
||||
public async Task RequestBody_SyncReadDisabledByDefault_WorksWhenEnabled()
|
||||
{
|
||||
string address;
|
||||
using (var server = Utilities.CreateHttpServer(out address))
|
||||
{
|
||||
Task<string> responseTask = SendRequestAsync(address, "Hello World");
|
||||
|
||||
Assert.True(server.Options.AllowSynchronousIO);
|
||||
Assert.False(server.Options.AllowSynchronousIO);
|
||||
|
||||
var context = await server.AcceptAsync(Utilities.DefaultTimeout).Before(responseTask);
|
||||
byte[] input = new byte[100];
|
||||
Assert.Throws<InvalidOperationException>(() => context.Request.Body.Read(input, 0, input.Length));
|
||||
|
||||
context.AllowSynchronousIO = true;
|
||||
|
||||
Assert.True(context.AllowSynchronousIO);
|
||||
var read = context.Request.Body.Read(input, 0, input.Length);
|
||||
context.Response.ContentLength = read;
|
||||
context.Response.Body.Write(input, 0, read);
|
||||
|
||||
context.AllowSynchronousIO = false;
|
||||
Assert.Throws<InvalidOperationException>(() => context.Request.Body.Read(input, 0, input.Length));
|
||||
|
||||
string response = await responseTask;
|
||||
Assert.Equal("Hello World", response);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
|
|||
public class ResponseBodyTests
|
||||
{
|
||||
[ConditionalFact]
|
||||
public async Task ResponseBody_SyncWriteEnabledByDefault_ThrowsWhenDisabled()
|
||||
public async Task ResponseBody_SyncWriteDisabledByDefault_WorksWhenEnabled()
|
||||
{
|
||||
string address;
|
||||
using (var server = Utilities.CreateHttpServer(out address))
|
||||
|
|
@ -26,19 +26,17 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
|
|||
|
||||
var context = await server.AcceptAsync(Utilities.DefaultTimeout).Before(responseTask);
|
||||
|
||||
Assert.True(context.AllowSynchronousIO);
|
||||
|
||||
context.Response.Body.Flush();
|
||||
context.Response.Body.Write(new byte[10], 0, 10);
|
||||
context.Response.Body.Flush();
|
||||
|
||||
context.AllowSynchronousIO = false;
|
||||
Assert.False(context.AllowSynchronousIO);
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => context.Response.Body.Flush());
|
||||
Assert.Throws<InvalidOperationException>(() => context.Response.Body.Write(new byte[10], 0, 10));
|
||||
Assert.Throws<InvalidOperationException>(() => context.Response.Body.Flush());
|
||||
|
||||
await context.Response.Body.WriteAsync(new byte[10], 0, 10);
|
||||
context.AllowSynchronousIO = true;
|
||||
|
||||
context.Response.Body.Flush();
|
||||
context.Response.Body.Write(new byte[10], 0, 10);
|
||||
context.Response.Body.Flush();
|
||||
context.Dispose();
|
||||
|
||||
var response = await responseTask;
|
||||
|
|
@ -47,7 +45,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
|
|||
IEnumerable<string> ignored;
|
||||
Assert.False(response.Content.Headers.TryGetValues("content-length", out ignored), "Content-Length");
|
||||
Assert.True(response.Headers.TransferEncodingChunked.Value, "Chunked");
|
||||
Assert.Equal(new byte[20], await response.Content.ReadAsByteArrayAsync());
|
||||
Assert.Equal(new byte[10], await response.Content.ReadAsByteArrayAsync());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -477,4 +475,4 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
|||
|
||||
IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator();
|
||||
|
||||
bool IHttpBodyControlFeature.AllowSynchronousIO { get; set; } = true;
|
||||
bool IHttpBodyControlFeature.AllowSynchronousIO { get; set; }
|
||||
|
||||
void IHttpBufferingFeature.DisableRequestBuffering()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ namespace Microsoft.AspNetCore.Builder
|
|||
/// Gets or sets a value that controls whether synchronous IO is allowed for the <see cref="HttpContext.Request"/> and <see cref="HttpContext.Response"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Defaults to true.
|
||||
/// Defaults to false.
|
||||
/// </remarks>
|
||||
public bool AllowSynchronousIO { get; set; } = true;
|
||||
public bool AllowSynchronousIO { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// If true the server should set HttpContext.User. If false the server will only provide an
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// 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.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
|
|
@ -11,44 +10,22 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
|||
{
|
||||
[SkipIfHostableWebCoreNotAvailable]
|
||||
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, "https://github.com/aspnet/IISIntegration/issues/866")]
|
||||
public class HttpBodyControlFeatureTests : StrictTestServerTests
|
||||
public class ConnectionIdFeatureTests : StrictTestServerTests
|
||||
{
|
||||
[ConditionalFact]
|
||||
public async Task ThrowsOnSyncReadOrWrite()
|
||||
public async Task ProvidesConnectionId()
|
||||
{
|
||||
Exception writeException = null;
|
||||
Exception readException = null;
|
||||
using (var testServer = await TestServer.Create(
|
||||
ctx => {
|
||||
var bodyControl = ctx.Features.Get<IHttpBodyControlFeature>();
|
||||
bodyControl.AllowSynchronousIO = false;
|
||||
|
||||
try
|
||||
{
|
||||
ctx.Response.Body.Write(new byte[10]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
writeException = ex;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ctx.Request.Body.Read(new byte[10]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
readException = ex;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}, LoggerFactory))
|
||||
string connectionId = null;
|
||||
using (var testServer = await TestServer.Create(ctx => {
|
||||
var connectionIdFeature = ctx.Features.Get<IHttpConnectionFeature>();
|
||||
connectionId = connectionIdFeature.ConnectionId;
|
||||
return Task.CompletedTask;
|
||||
}, LoggerFactory))
|
||||
{
|
||||
await testServer.HttpClient.GetStringAsync("/");
|
||||
}
|
||||
|
||||
Assert.IsType<InvalidOperationException>(readException);
|
||||
Assert.IsType<InvalidOperationException>(writeException);
|
||||
Assert.NotNull(connectionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
|
|
@ -10,22 +11,44 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
|||
{
|
||||
[SkipIfHostableWebCoreNotAvailable]
|
||||
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, "https://github.com/aspnet/IISIntegration/issues/866")]
|
||||
public class ConnectionIdFeatureTests : StrictTestServerTests
|
||||
public class HttpBodyControlFeatureTests : StrictTestServerTests
|
||||
{
|
||||
[ConditionalFact]
|
||||
public async Task ProvidesConnectionId()
|
||||
public async Task ThrowsOnSyncReadOrWrite()
|
||||
{
|
||||
string connectionId = null;
|
||||
using (var testServer = await TestServer.Create(ctx => {
|
||||
var connectionIdFeature = ctx.Features.Get<IHttpConnectionFeature>();
|
||||
connectionId = connectionIdFeature.ConnectionId;
|
||||
Exception writeException = null;
|
||||
Exception readException = null;
|
||||
using (var testServer = await TestServer.Create(
|
||||
ctx => {
|
||||
var bodyControl = ctx.Features.Get<IHttpBodyControlFeature>();
|
||||
Assert.False(bodyControl.AllowSynchronousIO);
|
||||
|
||||
try
|
||||
{
|
||||
ctx.Response.Body.Write(new byte[10]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
writeException = ex;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ctx.Request.Body.Read(new byte[10]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
readException = ex;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}, LoggerFactory))
|
||||
{
|
||||
await testServer.HttpClient.GetStringAsync("/");
|
||||
}
|
||||
|
||||
Assert.NotNull(connectionId);
|
||||
Assert.IsType<InvalidOperationException>(readException);
|
||||
Assert.IsType<InvalidOperationException>(writeException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,9 +50,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
/// Gets or sets a value that controls whether synchronous IO is allowed for the <see cref="HttpContext.Request"/> and <see cref="HttpContext.Response"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Defaults to true.
|
||||
/// Defaults to false.
|
||||
/// </remarks>
|
||||
public bool AllowSynchronousIO { get; set; } = true;
|
||||
public bool AllowSynchronousIO { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enables the Listen options callback to resolve and use services registered by the application during startup.
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void AllowSynchronousIODefaultsToTrue()
|
||||
public void AllowSynchronousIODefaultsToFalse()
|
||||
{
|
||||
var options = new KestrelServerOptions();
|
||||
|
||||
Assert.True(options.AllowSynchronousIO);
|
||||
Assert.False(options.AllowSynchronousIO);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -65,4 +65,4 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
Assert.False(options.ListenOptions[3].NoDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1077,45 +1077,29 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SynchronousReadsAllowedByDefault()
|
||||
public async Task SynchronousReadsDisallowedByDefault()
|
||||
{
|
||||
var firstRequest = true;
|
||||
|
||||
using (var server = new TestServer(async context =>
|
||||
{
|
||||
var bodyControlFeature = context.Features.Get<IHttpBodyControlFeature>();
|
||||
Assert.True(bodyControlFeature.AllowSynchronousIO);
|
||||
Assert.False(bodyControlFeature.AllowSynchronousIO);
|
||||
|
||||
var buffer = new byte[6];
|
||||
var offset = 0;
|
||||
|
||||
// The request body is 5 bytes long. The 6th byte (buffer[5]) is only used for writing the response body.
|
||||
buffer[5] = (byte)(firstRequest ? '1' : '2');
|
||||
buffer[5] = (byte)'1';
|
||||
|
||||
if (firstRequest)
|
||||
// Synchronous reads throw.
|
||||
var ioEx = Assert.Throws<InvalidOperationException>(() => context.Request.Body.Read(new byte[1], 0, 1));
|
||||
Assert.Equal(CoreStrings.SynchronousReadsDisallowed, ioEx.Message);
|
||||
|
||||
var ioEx2 = Assert.Throws<InvalidOperationException>(() => context.Request.Body.CopyTo(Stream.Null));
|
||||
Assert.Equal(CoreStrings.SynchronousReadsDisallowed, ioEx2.Message);
|
||||
|
||||
while (offset < 5)
|
||||
{
|
||||
while (offset < 5)
|
||||
{
|
||||
offset += context.Request.Body.Read(buffer, offset, 5 - offset);
|
||||
}
|
||||
|
||||
firstRequest = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyControlFeature.AllowSynchronousIO = false;
|
||||
|
||||
// Synchronous reads now throw.
|
||||
var ioEx = Assert.Throws<InvalidOperationException>(() => context.Request.Body.Read(new byte[1], 0, 1));
|
||||
Assert.Equal(CoreStrings.SynchronousReadsDisallowed, ioEx.Message);
|
||||
|
||||
var ioEx2 = Assert.Throws<InvalidOperationException>(() => context.Request.Body.CopyTo(Stream.Null));
|
||||
Assert.Equal(CoreStrings.SynchronousReadsDisallowed, ioEx2.Message);
|
||||
|
||||
while (offset < 5)
|
||||
{
|
||||
offset += await context.Request.Body.ReadAsync(buffer, offset, 5 - offset);
|
||||
}
|
||||
offset += await context.Request.Body.ReadAsync(buffer, offset, 5 - offset);
|
||||
}
|
||||
|
||||
Assert.Equal(0, await context.Request.Body.ReadAsync(new byte[1], 0, 1));
|
||||
|
|
@ -1132,7 +1116,46 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
"Host:",
|
||||
"Content-Length: 5",
|
||||
"",
|
||||
"HelloPOST / HTTP/1.1",
|
||||
"Hello");
|
||||
await connection.Receive(
|
||||
"HTTP/1.1 200 OK",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
"Content-Length: 6",
|
||||
"",
|
||||
"Hello1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SynchronousReadsAllowedByOptIn()
|
||||
{
|
||||
using (var server = new TestServer(async context =>
|
||||
{
|
||||
var bodyControlFeature = context.Features.Get<IHttpBodyControlFeature>();
|
||||
Assert.False(bodyControlFeature.AllowSynchronousIO);
|
||||
|
||||
var buffer = new byte[5];
|
||||
var offset = 0;
|
||||
|
||||
bodyControlFeature.AllowSynchronousIO = true;
|
||||
|
||||
while (offset < 5)
|
||||
{
|
||||
offset += context.Request.Body.Read(buffer, offset, 5 - offset);
|
||||
}
|
||||
|
||||
Assert.Equal(0, await context.Request.Body.ReadAsync(new byte[1], 0, 1));
|
||||
Assert.Equal("Hello", Encoding.ASCII.GetString(buffer, 0, 5));
|
||||
|
||||
context.Response.ContentLength = 5;
|
||||
await context.Response.Body.WriteAsync(buffer, 0, 5);
|
||||
}, new TestServiceContext(LoggerFactory)))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.Send(
|
||||
"POST / HTTP/1.1",
|
||||
"Host:",
|
||||
"Content-Length: 5",
|
||||
"",
|
||||
|
|
@ -1140,13 +1163,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
await connection.Receive(
|
||||
"HTTP/1.1 200 OK",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
"Content-Length: 6",
|
||||
"Content-Length: 5",
|
||||
"",
|
||||
"Hello1HTTP/1.1 200 OK",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
"Content-Length: 6",
|
||||
"",
|
||||
"Hello2");
|
||||
"Hello");
|
||||
}
|
||||
await server.StopAsync();
|
||||
}
|
||||
|
|
@ -1197,6 +1216,48 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SynchronousReadsCanBeAllowedGlobally()
|
||||
{
|
||||
var testContext = new TestServiceContext(LoggerFactory)
|
||||
{
|
||||
ServerOptions = { AllowSynchronousIO = true }
|
||||
};
|
||||
|
||||
using (var server = new TestServer(async context =>
|
||||
{
|
||||
var bodyControlFeature = context.Features.Get<IHttpBodyControlFeature>();
|
||||
Assert.True(bodyControlFeature.AllowSynchronousIO);
|
||||
|
||||
int offset = 0;
|
||||
var buffer = new byte[5];
|
||||
while (offset < 5)
|
||||
{
|
||||
offset += context.Request.Body.Read(buffer, offset, 5 - offset);
|
||||
}
|
||||
|
||||
Assert.Equal(0, await context.Request.Body.ReadAsync(new byte[1], 0, 1));
|
||||
Assert.Equal("Hello", Encoding.ASCII.GetString(buffer, 0, 5));
|
||||
}, testContext))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.Send(
|
||||
"POST / HTTP/1.1",
|
||||
"Host:",
|
||||
"Content-Length: 5",
|
||||
"",
|
||||
"Hello");
|
||||
await connection.Receive(
|
||||
"HTTP/1.1 200 OK",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
"Content-Length: 0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TheoryData<string, string> HostHeaderData => HttpParsingData.HostHeaderData;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3042,34 +3042,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
Assert.Equal(2, callOrder.Pop());
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task SynchronousWritesAllowedByDefault()
|
||||
public async Task SynchronousWritesDisallowedByDefault()
|
||||
{
|
||||
var firstRequest = true;
|
||||
|
||||
using (var server = new TestServer(async context =>
|
||||
{
|
||||
var bodyControlFeature = context.Features.Get<IHttpBodyControlFeature>();
|
||||
Assert.True(bodyControlFeature.AllowSynchronousIO);
|
||||
Assert.False(bodyControlFeature.AllowSynchronousIO);
|
||||
|
||||
context.Response.ContentLength = 6;
|
||||
|
||||
if (firstRequest)
|
||||
{
|
||||
context.Response.Body.Write(Encoding.ASCII.GetBytes("Hello1"), 0, 6);
|
||||
firstRequest = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyControlFeature.AllowSynchronousIO = false;
|
||||
// Synchronous writes now throw.
|
||||
var ioEx = Assert.Throws<InvalidOperationException>(() => context.Response.Body.Write(Encoding.ASCII.GetBytes("What!?"), 0, 6));
|
||||
Assert.Equal(CoreStrings.SynchronousWritesDisallowed, ioEx.Message);
|
||||
await context.Response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello1"), 0, 6);
|
||||
|
||||
// Synchronous writes now throw.
|
||||
var ioEx = Assert.Throws<InvalidOperationException>(() => context.Response.Body.Write(Encoding.ASCII.GetBytes("What!?"), 0, 6));
|
||||
Assert.Equal(CoreStrings.SynchronousWritesDisallowed, ioEx.Message);
|
||||
|
||||
await context.Response.BodyPipe.WriteAsync(new Memory<byte>(Encoding.ASCII.GetBytes("Hello2"), 0, 6));
|
||||
}
|
||||
}, new TestServiceContext(LoggerFactory)))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
|
|
@ -3081,14 +3068,67 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
"Content-Length: 6",
|
||||
"",
|
||||
"Hello1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SynchronousWritesAllowedByOptIn()
|
||||
{
|
||||
using (var server = new TestServer(context =>
|
||||
{
|
||||
var bodyControlFeature = context.Features.Get<IHttpBodyControlFeature>();
|
||||
Assert.False(bodyControlFeature.AllowSynchronousIO);
|
||||
bodyControlFeature.AllowSynchronousIO = true;
|
||||
context.Response.ContentLength = 6;
|
||||
context.Response.Body.Write(Encoding.ASCII.GetBytes("Hello1"), 0, 6);
|
||||
return Task.CompletedTask;
|
||||
}, new TestServiceContext(LoggerFactory)))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.SendEmptyGet();
|
||||
await connection.Receive(
|
||||
"HTTP/1.1 200 OK",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
"Content-Length: 6",
|
||||
"",
|
||||
"Hello2");
|
||||
"Hello1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SynchronousWritesCanBeAllowedGlobally()
|
||||
{
|
||||
var testContext = new TestServiceContext(LoggerFactory)
|
||||
{
|
||||
ServerOptions = { AllowSynchronousIO = true }
|
||||
};
|
||||
|
||||
using (var server = new TestServer(context =>
|
||||
{
|
||||
var bodyControlFeature = context.Features.Get<IHttpBodyControlFeature>();
|
||||
Assert.True(bodyControlFeature.AllowSynchronousIO);
|
||||
|
||||
context.Response.ContentLength = 6;
|
||||
context.Response.Body.Write(Encoding.ASCII.GetBytes("Hello!"), 0, 6);
|
||||
return Task.CompletedTask;
|
||||
}, testContext))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.Send(
|
||||
"GET / HTTP/1.1",
|
||||
"Host:",
|
||||
"",
|
||||
"");
|
||||
await connection.Receive(
|
||||
"HTTP/1.1 200 OK",
|
||||
$"Date: {server.Context.DateHeaderValue}",
|
||||
"Content-Length: 6",
|
||||
"",
|
||||
"Hello!");
|
||||
}
|
||||
await server.StopAsync();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
{
|
||||
await writer.WriteLineAsync("New protocol data");
|
||||
await writer.FlushAsync();
|
||||
await writer.DisposeAsync();
|
||||
}
|
||||
|
||||
upgrade.TrySetResult(true);
|
||||
|
|
@ -79,6 +80,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
Assert.Equal(send, line);
|
||||
await writer.WriteLineAsync(recv);
|
||||
await writer.FlushAsync();
|
||||
await writer.DisposeAsync();
|
||||
}
|
||||
|
||||
upgrade.TrySetResult(true);
|
||||
|
|
|
|||
Loading…
Reference in New Issue