From 13b867a90eefb17094d7d02c935d82408e58f5ee Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 27 Jun 2017 11:48:48 -0700 Subject: [PATCH] #366 Add flag to disable synchronous IO --- .../FeatureContext.cs | 9 +++- .../HttpSysOptions.cs | 6 +++ .../RequestProcessing/Request.cs | 6 +-- .../RequestProcessing/RequestContext.cs | 3 ++ .../RequestProcessing/RequestStream.cs | 6 +++ .../RequestProcessing/ResponseBody.cs | 12 ++++++ .../StandardFeatureCollection.cs | 1 + .../Listener/HttpsTests.cs | 7 ++- .../Listener/OpaqueUpgradeTests.cs | 2 +- .../Listener/RequestBodyTests.cs | 29 +++++++++++++ .../Listener/ResponseBodyTests.cs | 43 +++++++++++++++++++ .../Listener/ResponseCachingTests.cs | 17 ++++---- .../Listener/ResponseHeaderTests.cs | 1 + .../Listener/ServerTests.cs | 23 +++++----- .../OpaqueUpgradeTests.cs | 4 +- .../RequestBodyLimitTests.cs | 5 +++ .../RequestBodyTests.cs | 4 ++ .../ResponseBodyTests.cs | 17 +++++--- .../ResponseHeaderTests.cs | 4 +- .../ServerTests.cs | 6 +-- 20 files changed, 161 insertions(+), 44 deletions(-) diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/FeatureContext.cs b/src/Microsoft.AspNetCore.Server.HttpSys/FeatureContext.cs index b2e41d46aa..9acef96deb 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/FeatureContext.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/FeatureContext.cs @@ -29,7 +29,8 @@ namespace Microsoft.AspNetCore.Server.HttpSys IHttpAuthenticationFeature, IHttpUpgradeFeature, IHttpRequestIdentifierFeature, - IHttpMaxRequestBodySizeFeature + IHttpMaxRequestBodySizeFeature, + IHttpBodyControlFeature { private RequestContext _requestContext; private IFeatureCollection _features; @@ -464,6 +465,12 @@ namespace Microsoft.AspNetCore.Server.HttpSys } } + bool IHttpBodyControlFeature.AllowSynchronousIO + { + get => _requestContext.AllowSynchronousIO; + set => _requestContext.AllowSynchronousIO = value; + } + bool IHttpMaxRequestBodySizeFeature.IsReadOnly => Request.HasRequestBodyStarted; long? IHttpMaxRequestBodySizeFeature.MaxRequestBodySize diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/HttpSysOptions.cs b/src/Microsoft.AspNetCore.Server.HttpSys/HttpSysOptions.cs index 720eee3739..cf34e6e368 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/HttpSysOptions.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/HttpSysOptions.cs @@ -131,6 +131,12 @@ namespace Microsoft.AspNetCore.Server.HttpSys } } + /// + /// 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`. + /// + public bool AllowSynchronousIO { get; set; } = true; + internal void Apply(UrlGroup urlGroup, RequestQueue requestQueue) { _urlGroup = urlGroup; diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/Request.cs b/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/Request.cs index 0147b84f03..7e4ef322d6 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/Request.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/Request.cs @@ -23,6 +23,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys // private byte[] _referredTokenBindingId; private BoundaryType _contentBoundaryType; + private long? _contentLength; private RequestStream _nativeStream; @@ -149,10 +150,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys { if (_nativeStream == null && HasEntityBody) { - _nativeStream = new RequestStream(RequestContext) - { - MaxSize = RequestContext.Server.Options.MaxRequestBodySize - }; + _nativeStream = new RequestStream(RequestContext); } return _nativeStream; } diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/RequestContext.cs b/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/RequestContext.cs index 22b3ca6b13..a57064fb55 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/RequestContext.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/RequestContext.cs @@ -30,6 +30,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys _memoryBlob = memoryBlob; Request = new Request(this, _memoryBlob); Response = new Response(this); + AllowSynchronousIO = server.Options.AllowSynchronousIO; } internal HttpSysListener Server { get; } @@ -88,6 +89,8 @@ namespace Microsoft.AspNetCore.Server.HttpSys public bool IsUpgradableRequest => Request.IsUpgradable; + internal bool AllowSynchronousIO { get; set; } + public Task UpgradeAsync() { if (!IsUpgradableRequest) diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/RequestStream.cs b/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/RequestStream.cs index ce7642dacf..baef4c5a9f 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/RequestStream.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/RequestStream.cs @@ -25,6 +25,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys internal RequestStream(RequestContext httpContext) { _requestContext = httpContext; + _maxSize = _requestContext.Server.Options.MaxRequestBodySize; } internal RequestContext RequestContext @@ -111,6 +112,11 @@ namespace Microsoft.AspNetCore.Server.HttpSys public override unsafe int Read([In, Out] byte[] buffer, int offset, int size) { + if (!RequestContext.AllowSynchronousIO) + { + throw new InvalidOperationException("Synchronous IO APIs are disabled, see AllowSynchronousIO."); + } + ValidateReadBuffer(buffer, offset, size); CheckSizeLimit(); if (_closed) diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/ResponseBody.cs b/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/ResponseBody.cs index 506bba99a7..35eb89c6e7 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/ResponseBody.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/ResponseBody.cs @@ -91,10 +91,16 @@ namespace Microsoft.AspNetCore.Server.HttpSys // Send headers public override void Flush() { + if (!RequestContext.AllowSynchronousIO) + { + throw new InvalidOperationException("Synchronous IO APIs are disabled, see AllowSynchronousIO."); + } + if (_disposed) { return; } + FlushInternal(endOfRequest: false); } @@ -449,9 +455,15 @@ namespace Microsoft.AspNetCore.Server.HttpSys public override void Write(byte[] buffer, int offset, int count) { + if (!RequestContext.AllowSynchronousIO) + { + throw new InvalidOperationException("Synchronous IO APIs are disabled, see AllowSynchronousIO."); + } + // Validates for null and bounds. Allows count == 0. // TODO: Verbose log parameters var data = new ArraySegment(buffer, offset, count); + CheckDisposed(); CheckWriteCount(count); diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/StandardFeatureCollection.cs b/src/Microsoft.AspNetCore.Server.HttpSys/StandardFeatureCollection.cs index d0fa5789dc..0830de0fec 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/StandardFeatureCollection.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/StandardFeatureCollection.cs @@ -27,6 +27,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys { typeof(IHttpRequestIdentifierFeature), _identityFunc }, { typeof(RequestContext), ctx => ctx.RequestContext }, { typeof(IHttpMaxRequestBodySizeFeature), _identityFunc }, + { typeof(IHttpBodyControlFeature), _identityFunc }, }; private readonly FeatureContext _featureContext; diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/HttpsTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/HttpsTests.cs index 4291f00e6e..a4c16c785c 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/HttpsTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/HttpsTests.cs @@ -60,10 +60,9 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener string input = new StreamReader(context.Request.Body).ReadToEnd(); Assert.Equal("Hello World", input); context.Response.ContentLength = 11; - using (var writer = new StreamWriter(context.Response.Body)) - { - writer.Write("Hello World"); - } + var writer = new StreamWriter(context.Response.Body); + await writer.WriteAsync("Hello World"); + await writer.FlushAsync(); string response = await responseTask; Assert.Equal("Hello World", response); diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/OpaqueUpgradeTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/OpaqueUpgradeTests.cs index 26cc299f09..d749fdb285 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/OpaqueUpgradeTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/OpaqueUpgradeTests.cs @@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener var context = await server.AcceptAsync(Utilities.DefaultTimeout); byte[] body = Encoding.UTF8.GetBytes("Hello World"); - context.Response.Body.Write(body, 0, body.Length); + await context.Response.Body.WriteAsync(body, 0, body.Length); Assert.Throws(() => context.Response.Headers["Upgrade"] = "WebSocket"); // Win8.1 blocks anything but WebSocket await Assert.ThrowsAsync(async () => await context.UpgradeAsync()); diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/RequestBodyTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/RequestBodyTests.cs index b34cd35ed6..9ea7f4805f 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/RequestBodyTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/RequestBodyTests.cs @@ -16,6 +16,32 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener { public class RequestBodyTests { + [ConditionalFact] + public async Task RequestBody_SyncReadEnabledByDefault_ThrowsWhenDisabled() + { + string address; + using (var server = Utilities.CreateHttpServer(out address)) + { + Task responseTask = SendRequestAsync(address, "Hello World"); + + Assert.True(server.Options.AllowSynchronousIO); + + var context = await server.AcceptAsync(Utilities.DefaultTimeout); + byte[] input = new byte[100]; + + 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(() => context.Request.Body.Read(input, 0, input.Length)); + + string response = await responseTask; + Assert.Equal("Hello World", response); + } + } + [ConditionalFact] public async Task RequestBody_ReadSync_Success() { @@ -24,6 +50,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener { Task responseTask = SendRequestAsync(address, "Hello World"); + server.Options.AllowSynchronousIO = true; var context = await server.AcceptAsync(Utilities.DefaultTimeout); byte[] input = new byte[100]; int read = context.Request.Body.Read(input, 0, input.Length); @@ -81,6 +108,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener { Task responseTask = SendRequestAsync(address, "Hello World"); + server.Options.AllowSynchronousIO = true; var context = await server.AcceptAsync(Utilities.DefaultTimeout); byte[] input = new byte[100]; Assert.Throws("buffer", () => context.Request.Body.Read(null, 0, 1)); @@ -106,6 +134,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener { Task responseTask = SendRequestAsync(address, content); + server.Options.AllowSynchronousIO = true; var context = await server.AcceptAsync(Utilities.DefaultTimeout); byte[] input = new byte[10]; int read = context.Request.Body.Read(input, 0, input.Length); diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ResponseBodyTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ResponseBodyTests.cs index c3e681b848..c41ef371a7 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ResponseBodyTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ResponseBodyTests.cs @@ -16,6 +16,41 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener { public class ResponseBodyTests { + [ConditionalFact] + public async Task ResponseBody_SyncWriteEnabledByDefault_ThrowsWhenDisabled() + { + string address; + using (var server = Utilities.CreateHttpServer(out address)) + { + var responseTask = SendRequestAsync(address); + + var context = await server.AcceptAsync(Utilities.DefaultTimeout); + + 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.Throws(() => context.Response.Body.Flush()); + Assert.Throws(() => context.Response.Body.Write(new byte[10], 0, 10)); + Assert.Throws(() => context.Response.Body.Flush()); + + await context.Response.Body.WriteAsync(new byte[10], 0, 10); + context.Dispose(); + + var response = await responseTask; + Assert.Equal(200, (int)response.StatusCode); + Assert.Equal(new Version(1, 1), response.Version); + IEnumerable 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()); + } + } + [ConditionalFact] public async Task ResponseBody_WriteNoHeaders_DefaultsToChunked() { @@ -24,6 +59,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener { var responseTask = SendRequestAsync(address); + server.Options.AllowSynchronousIO = true; var context = await server.AcceptAsync(Utilities.DefaultTimeout); context.Response.Body.Write(new byte[10], 0, 10); await context.Response.Body.WriteAsync(new byte[10], 0, 10); @@ -45,6 +81,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener string address; using (var server = Utilities.CreateHttpServer(out address)) { + server.Options.AllowSynchronousIO = true; var responseTask = SendRequestAsync(address); var context = await server.AcceptAsync(Utilities.DefaultTimeout); @@ -95,6 +132,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener { var responseTask = SendRequestAsync(address); + server.Options.AllowSynchronousIO = true; var context = await server.AcceptAsync(Utilities.DefaultTimeout); context.Response.Headers["Content-lenGth"] = " 30 "; var stream = context.Response.Body; @@ -181,6 +219,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener { var responseTask = SendRequestAsync(address); + server.Options.AllowSynchronousIO = true; var context = await server.AcceptAsync(Utilities.DefaultTimeout); context.Response.Headers["Content-lenGth"] = " 10 "; context.Response.Body.Write(new byte[10], 0, 10); @@ -206,6 +245,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener { var responseTask = SendRequestAsync(address); + server.Options.AllowSynchronousIO = true; var context = await server.AcceptAsync(Utilities.DefaultTimeout); context.Response.Body.Write(new byte[10], 0, 0); Assert.True(context.Response.HasStarted); @@ -380,6 +420,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener using (var server = Utilities.CreateHttpServer(out address)) { server.Options.ThrowWriteExceptions = true; + server.Options.AllowSynchronousIO = true; var cts = new CancellationTokenSource(); var responseTask = SendRequestAsync(address, cts.Token); @@ -444,6 +485,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener var cts = new CancellationTokenSource(); var responseTask = SendRequestAsync(address, cts.Token); + server.Options.AllowSynchronousIO = true; var context = await server.AcceptAsync(Utilities.DefaultTimeout); // First write sends headers cts.Cancel(); @@ -555,6 +597,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener string address; using (var server = Utilities.CreateHttpServer(out address)) { + server.Options.AllowSynchronousIO = true; RequestContext context; using (var client = new HttpClient()) { diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ResponseCachingTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ResponseCachingTests.cs index 1505fd1dba..1ed657515e 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ResponseCachingTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ResponseCachingTests.cs @@ -324,7 +324,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener context.Response.Headers["content-type"] = "some/thing"; // Http.sys requires a content-type to cache context.Response.ContentLength = 10; context.Response.CacheTtl = TimeSpan.FromSeconds(10); - context.Response.Body.Write(new byte[10], 0, 10); + await context.Response.Body.WriteAsync(new byte[10], 0, 10); // Http.Sys will add this for us Assert.Null(context.Response.ContentLength); context.Dispose(); @@ -381,6 +381,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener { var responseTask = SendRequestAsync(address); + server.Options.AllowSynchronousIO = true; var context = await server.AcceptAsync(Utilities.DefaultTimeout); context.Response.Headers["x-request-count"] = "1"; context.Response.Headers["content-type"] = "some/thing"; // Http.sys requires a content-type to cache @@ -418,8 +419,8 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener context.Response.Headers["x-request-count"] = "1"; context.Response.Headers["content-type"] = "some/thing"; // Http.sys requires a content-type to cache context.Response.CacheTtl = TimeSpan.FromSeconds(10); - context.Response.Body.Write(new byte[10], 0, 10); - context.Response.Body.Flush(); + await context.Response.Body.WriteAsync(new byte[10], 0, 10); + await context.Response.Body.FlushAsync(); context.Dispose(); var response = await responseTask; @@ -453,7 +454,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener context.Response.Headers["content-type"] = "some/thing"; // Http.sys requires a content-type to cache context.Response.ContentLength = 10; context.Response.CacheTtl = TimeSpan.FromSeconds(10); - context.Response.Body.Write(new byte[10], 0, 10); + await context.Response.Body.WriteAsync(new byte[10], 0, 10); // Http.Sys will add this for us Assert.Null(context.Response.ContentLength); context.Dispose(); @@ -999,7 +1000,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener context.Response.Headers["content-range"] = "bytes 0-10/100"; context.Response.ContentLength = 11; context.Response.CacheTtl = TimeSpan.FromSeconds(10); - context.Response.Body.Write(new byte[100], 0, 11); + await context.Response.Body.WriteAsync(new byte[100], 0, 11); context.Dispose(); var response = await responseTask; @@ -1016,7 +1017,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener context.Response.Headers["content-range"] = "bytes 0-10/100"; context.Response.ContentLength = 11; context.Response.CacheTtl = TimeSpan.FromSeconds(10); - context.Response.Body.Write(new byte[100], 0, 11); + await context.Response.Body.WriteAsync(new byte[100], 0, 11); context.Dispose(); response = await responseTask; @@ -1041,7 +1042,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener context.Response.Headers["content-type"] = "some/thing"; // Http.sys requires a content-type to cache context.Response.ContentLength = 100; context.Response.CacheTtl = TimeSpan.FromSeconds(10); - context.Response.Body.Write(new byte[100], 0, 100); + await context.Response.Body.WriteAsync(new byte[100], 0, 100); context.Dispose(); var response = await responseTask; @@ -1071,7 +1072,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener context.Response.Headers["content-type"] = "some/thing"; // Http.sys requires a content-type to cache context.Response.ContentLength = 100; context.Response.CacheTtl = TimeSpan.FromSeconds(10); - context.Response.Body.Write(new byte[100], 0, 100); + await context.Response.Body.WriteAsync(new byte[100], 0, 100); context.Dispose(); var response = await responseTask; diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ResponseHeaderTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ResponseHeaderTests.cs index 982024730e..66ecbd7e19 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ResponseHeaderTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ResponseHeaderTests.cs @@ -390,6 +390,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener { Task responseTask = SendRequestAsync(address); + server.Options.AllowSynchronousIO = true; var context = await server.AcceptAsync(Utilities.DefaultTimeout); var responseHeaders = context.Response.Headers; diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ServerTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ServerTests.cs index cbd7000614..e12488f13e 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ServerTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/ServerTests.cs @@ -42,10 +42,9 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener var context = await server.AcceptAsync(Utilities.DefaultTimeout); context.Response.ContentLength = 11; - using (var writer = new StreamWriter(context.Response.Body)) - { - writer.Write("Hello World"); - } + var writer = new StreamWriter(context.Response.Body); + await writer.WriteAsync("Hello World"); + await writer.FlushAsync(); string response = await responseTask; Assert.Equal("Hello World", response); @@ -61,13 +60,12 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener var responseTask = SendRequestAsync(address, "Hello World"); var context = await server.AcceptAsync(Utilities.DefaultTimeout); - string input = new StreamReader(context.Request.Body).ReadToEnd(); + var input = await new StreamReader(context.Request.Body).ReadToEndAsync(); Assert.Equal("Hello World", input); context.Response.ContentLength = 11; - using (var writer = new StreamWriter(context.Response.Body)) - { - writer.Write("Hello World"); - } + var writer = new StreamWriter(context.Response.Body); + await writer.WriteAsync("Hello World"); + await writer.FlushAsync(); var response = await responseTask; Assert.Equal("Hello World", response); @@ -218,10 +216,9 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener context.Response.Headers["Connection"] = "close"; context.Response.ContentLength = 11; - using (var writer = new StreamWriter(context.Response.Body)) - { - writer.Write("Hello World"); - } + var writer = new StreamWriter(context.Response.Body); + await writer.WriteAsync("Hello World"); + await writer.FlushAsync(); Assert.True(canceled.WaitOne(interval), "Disconnected"); Assert.True(ct.IsCancellationRequested, "IsCancellationRequested"); diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/OpaqueUpgradeTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/OpaqueUpgradeTests.cs index 48421ddcaa..a37daceb04 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/OpaqueUpgradeTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/OpaqueUpgradeTests.cs @@ -123,7 +123,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys Assert.True(feature.IsReadOnly); Assert.Null(feature.MaxRequestBodySize); Assert.Throws(() => feature.MaxRequestBodySize = 12); - Assert.Equal(15, stream.Read(new byte[15], 0, 15)); + Assert.Equal(15, await stream.ReadAsync(new byte[15], 0, 15)); upgraded = true; waitHandle.Set(); })) @@ -131,7 +131,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys using (Stream stream = await SendOpaqueRequestAsync("GET", address)) { stream.Write(new byte[15], 0, 15); - Assert.True(waitHandle.WaitOne(TimeSpan.FromSeconds(1)), "Timed out"); + Assert.True(waitHandle.WaitOne(TimeSpan.FromSeconds(10)), "Timed out"); Assert.True(upgraded.HasValue, "Upgraded not set"); Assert.True(upgraded.Value, "Upgrade failed"); } diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestBodyLimitTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestBodyLimitTests.cs index ed835f345f..ebc0fa9f98 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestBodyLimitTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestBodyLimitTests.cs @@ -21,6 +21,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys string address; using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 11, httpContext => { + httpContext.Features.Get().AllowSynchronousIO = true; var feature = httpContext.Features.Get(); Assert.NotNull(feature); Assert.False(feature.IsReadOnly); @@ -86,6 +87,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys string address; using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 11, httpContext => { + httpContext.Features.Get().AllowSynchronousIO = true; var feature = httpContext.Features.Get(); Assert.NotNull(feature); Assert.False(feature.IsReadOnly); @@ -151,6 +153,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys string address; using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 10, httpContext => { + httpContext.Features.Get().AllowSynchronousIO = true; var feature = httpContext.Features.Get(); Assert.NotNull(feature); Assert.False(feature.IsReadOnly); @@ -220,6 +223,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys string address; using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 10, httpContext => { + httpContext.Features.Get().AllowSynchronousIO = true; var feature = httpContext.Features.Get(); Assert.NotNull(feature); Assert.False(feature.IsReadOnly); @@ -290,6 +294,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys string address; using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 10, httpContext => { + httpContext.Features.Get().AllowSynchronousIO = true; var feature = httpContext.Features.Get(); Assert.NotNull(feature); Assert.False(feature.IsReadOnly); diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestBodyTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestBodyTests.cs index 23c7c56be1..1b87920103 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestBodyTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestBodyTests.cs @@ -9,6 +9,7 @@ using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Testing.xunit; using Xunit; @@ -23,6 +24,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys using (Utilities.CreateHttpServer(out address, httpContext => { byte[] input = new byte[100]; + httpContext.Features.Get().AllowSynchronousIO = true; int read = httpContext.Request.Body.Read(input, 0, input.Length); httpContext.Response.ContentLength = read; httpContext.Response.Body.Write(input, 0, read); @@ -75,6 +77,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys string address; using (Utilities.CreateHttpServer(out address, httpContext => { + httpContext.Features.Get().AllowSynchronousIO = true; byte[] input = new byte[100]; Assert.Throws("buffer", () => httpContext.Request.Body.Read(null, 0, 1)); Assert.Throws("offset", () => httpContext.Request.Body.Read(input, -1, 1)); @@ -99,6 +102,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys using (Utilities.CreateHttpServer(out address, httpContext => { byte[] input = new byte[10]; + httpContext.Features.Get().AllowSynchronousIO = true; int read = httpContext.Request.Body.Read(input, 0, input.Length); Assert.Equal(5, read); content.Block.Release(); diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ResponseBodyTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ResponseBodyTests.cs index 18dee99410..4c3089edea 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ResponseBodyTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ResponseBodyTests.cs @@ -9,6 +9,7 @@ using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Testing.xunit; using Xunit; @@ -22,6 +23,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys string address; using (Utilities.CreateHttpServer(out address, httpContext => { + httpContext.Features.Get().AllowSynchronousIO = true; httpContext.Response.Body.Write(new byte[10], 0, 10); return httpContext.Response.Body.WriteAsync(new byte[10], 0, 10); })) @@ -42,6 +44,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys string address; using (Utilities.CreateHttpServer(out address, async httpContext => { + httpContext.Features.Get().AllowSynchronousIO = true; httpContext.Response.Body.Write(new byte[10], 0, 10); await httpContext.Response.Body.WriteAsync(new byte[10], 0, 10); await httpContext.Response.Body.FlushAsync(); @@ -85,6 +88,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys string address; using (Utilities.CreateHttpServer(out address, async httpContext => { + httpContext.Features.Get().AllowSynchronousIO = true; httpContext.Response.Headers["Content-lenGth"] = " 30 "; Stream stream = httpContext.Response.Body; stream.EndWrite(stream.BeginWrite(new byte[10], 0, 10, null, null)); @@ -124,8 +128,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys using (Utilities.CreateHttpServer(out address, httpContext => { httpContext.Response.Headers["Content-lenGth"] = " 20 "; - httpContext.Response.Body.Write(new byte[5], 0, 5); - return Task.FromResult(0); + return httpContext.Response.Body.WriteAsync(new byte[5], 0, 5); })) { Assert.Throws(() => SendRequestAsync(address).Result); @@ -137,13 +140,13 @@ namespace Microsoft.AspNetCore.Server.HttpSys { var completed = false; string address; - using (Utilities.CreateHttpServer(out address, httpContext => + using (Utilities.CreateHttpServer(out address, async httpContext => { httpContext.Response.Headers["Content-lenGth"] = " 10 "; - httpContext.Response.Body.Write(new byte[5], 0, 5); - Assert.Throws(() => httpContext.Response.Body.Write(new byte[6], 0, 6)); + await httpContext.Response.Body.WriteAsync(new byte[5], 0, 5); + await Assert.ThrowsAsync(() => + httpContext.Response.Body.WriteAsync(new byte[6], 0, 6)); completed = true; - return Task.FromResult(0); })) { await Assert.ThrowsAsync(() => SendRequestAsync(address)); @@ -161,6 +164,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys { try { + httpContext.Features.Get().AllowSynchronousIO = true; httpContext.Response.Headers["Content-lenGth"] = " 10 "; httpContext.Response.Body.Write(new byte[10], 0, 10); httpContext.Response.Body.Write(new byte[9], 0, 9); @@ -197,6 +201,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys string address; using (Utilities.CreateHttpServer(out address, httpContext => { + httpContext.Features.Get().AllowSynchronousIO = true; httpContext.Response.OnStarting(state => { onStartingCalled = true; diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ResponseHeaderTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ResponseHeaderTests.cs index 96b112ae59..0b169e906e 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ResponseHeaderTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ResponseHeaderTests.cs @@ -130,8 +130,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys var responseInfo = httpContext.Features.Get(); var responseHeaders = responseInfo.Headers; responseHeaders["Connection"] = new string[] { "Close" }; - httpContext.Response.Body.Flush(); // Http.Sys adds the Content-Length: header for us if we don't flush - return Task.FromResult(0); + return httpContext.Response.Body.FlushAsync(); // Http.Sys adds the Content-Length: header for us if we don't flush })) { HttpResponseMessage response = await SendRequestAsync(address); @@ -204,6 +203,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys string address; using (Utilities.CreateHttpServer(out address, httpContext => { + httpContext.Features.Get().AllowSynchronousIO = true; var responseInfo = httpContext.Features.Get(); var responseHeaders = responseInfo.Headers; responseHeaders.Add("Custom1", new string[] { "value1a", "value1b" }); diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ServerTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ServerTests.cs index 4d8129d7f4..01224b2bc6 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ServerTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ServerTests.cs @@ -53,12 +53,12 @@ namespace Microsoft.AspNetCore.Server.HttpSys public async Task Server_EchoHelloWorld_Success() { string address; - using (Utilities.CreateHttpServer(out address, httpContext => + using (Utilities.CreateHttpServer(out 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); httpContext.Response.ContentLength = 11; - return httpContext.Response.WriteAsync("Hello World"); + await httpContext.Response.WriteAsync("Hello World"); })) { string response = await SendRequestAsync(address, "Hello World");