#366 Add flag to disable synchronous IO

This commit is contained in:
Chris R 2017-06-27 11:48:48 -07:00
parent ac4157c627
commit 13b867a90e
20 changed files with 161 additions and 44 deletions

View File

@ -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

View File

@ -131,6 +131,12 @@ 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`.
/// </summary>
public bool AllowSynchronousIO { get; set; } = true;
internal void Apply(UrlGroup urlGroup, RequestQueue requestQueue)
{
_urlGroup = urlGroup;

View File

@ -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;
}

View File

@ -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<Stream> UpgradeAsync()
{
if (!IsUpgradableRequest)

View File

@ -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)

View File

@ -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<byte>(buffer, offset, count);
CheckDisposed();
CheckWriteCount(count);

View File

@ -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;

View File

@ -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);

View File

@ -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<InvalidOperationException>(() => context.Response.Headers["Upgrade"] = "WebSocket"); // Win8.1 blocks anything but WebSocket
await Assert.ThrowsAsync<InvalidOperationException>(async () => await context.UpgradeAsync());

View File

@ -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<string> 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<InvalidOperationException>(() => 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<string> 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<string> responseTask = SendRequestAsync(address, "Hello World");
server.Options.AllowSynchronousIO = true;
var context = await server.AcceptAsync(Utilities.DefaultTimeout);
byte[] input = new byte[100];
Assert.Throws<ArgumentNullException>("buffer", () => context.Request.Body.Read(null, 0, 1));
@ -106,6 +134,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
{
Task<string> 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);

View File

@ -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<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.Dispose();
var response = await responseTask;
Assert.Equal(200, (int)response.StatusCode);
Assert.Equal(new Version(1, 1), response.Version);
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());
}
}
[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())
{

View File

@ -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;

View File

@ -390,6 +390,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
{
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
server.Options.AllowSynchronousIO = true;
var context = await server.AcceptAsync(Utilities.DefaultTimeout);
var responseHeaders = context.Response.Headers;

View File

@ -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");

View File

@ -123,7 +123,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
Assert.True(feature.IsReadOnly);
Assert.Null(feature.MaxRequestBodySize);
Assert.Throws<InvalidOperationException>(() => 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");
}

View File

@ -21,6 +21,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
string address;
using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 11, httpContext =>
{
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>();
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<IHttpBodyControlFeature>().AllowSynchronousIO = true;
var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>();
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<IHttpBodyControlFeature>().AllowSynchronousIO = true;
var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>();
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<IHttpBodyControlFeature>().AllowSynchronousIO = true;
var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>();
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<IHttpBodyControlFeature>().AllowSynchronousIO = true;
var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>();
Assert.NotNull(feature);
Assert.False(feature.IsReadOnly);

View File

@ -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<IHttpBodyControlFeature>().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<IHttpBodyControlFeature>().AllowSynchronousIO = true;
byte[] input = new byte[100];
Assert.Throws<ArgumentNullException>("buffer", () => httpContext.Request.Body.Read(null, 0, 1));
Assert.Throws<ArgumentOutOfRangeException>("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<IHttpBodyControlFeature>().AllowSynchronousIO = true;
int read = httpContext.Request.Body.Read(input, 0, input.Length);
Assert.Equal(5, read);
content.Block.Release();

View File

@ -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<IHttpBodyControlFeature>().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<IHttpBodyControlFeature>().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<IHttpBodyControlFeature>().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<AggregateException>(() => 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<InvalidOperationException>(() => httpContext.Response.Body.Write(new byte[6], 0, 6));
await httpContext.Response.Body.WriteAsync(new byte[5], 0, 5);
await Assert.ThrowsAsync<InvalidOperationException>(() =>
httpContext.Response.Body.WriteAsync(new byte[6], 0, 6));
completed = true;
return Task.FromResult(0);
}))
{
await Assert.ThrowsAsync<HttpRequestException>(() => SendRequestAsync(address));
@ -161,6 +164,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
{
try
{
httpContext.Features.Get<IHttpBodyControlFeature>().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<IHttpBodyControlFeature>().AllowSynchronousIO = true;
httpContext.Response.OnStarting(state =>
{
onStartingCalled = true;

View File

@ -130,8 +130,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
var responseInfo = httpContext.Features.Get<IHttpResponseFeature>();
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<IHttpBodyControlFeature>().AllowSynchronousIO = true;
var responseInfo = httpContext.Features.Get<IHttpResponseFeature>();
var responseHeaders = responseInfo.Headers;
responseHeaders.Add("Custom1", new string[] { "value1a", "value1b" });

View File

@ -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");