#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, IHttpAuthenticationFeature,
IHttpUpgradeFeature, IHttpUpgradeFeature,
IHttpRequestIdentifierFeature, IHttpRequestIdentifierFeature,
IHttpMaxRequestBodySizeFeature IHttpMaxRequestBodySizeFeature,
IHttpBodyControlFeature
{ {
private RequestContext _requestContext; private RequestContext _requestContext;
private IFeatureCollection _features; 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; bool IHttpMaxRequestBodySizeFeature.IsReadOnly => Request.HasRequestBodyStarted;
long? IHttpMaxRequestBodySizeFeature.MaxRequestBodySize 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) internal void Apply(UrlGroup urlGroup, RequestQueue requestQueue)
{ {
_urlGroup = urlGroup; _urlGroup = urlGroup;

View File

@ -23,6 +23,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
// private byte[] _referredTokenBindingId; // private byte[] _referredTokenBindingId;
private BoundaryType _contentBoundaryType; private BoundaryType _contentBoundaryType;
private long? _contentLength; private long? _contentLength;
private RequestStream _nativeStream; private RequestStream _nativeStream;
@ -149,10 +150,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
{ {
if (_nativeStream == null && HasEntityBody) if (_nativeStream == null && HasEntityBody)
{ {
_nativeStream = new RequestStream(RequestContext) _nativeStream = new RequestStream(RequestContext);
{
MaxSize = RequestContext.Server.Options.MaxRequestBodySize
};
} }
return _nativeStream; return _nativeStream;
} }

View File

@ -30,6 +30,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
_memoryBlob = memoryBlob; _memoryBlob = memoryBlob;
Request = new Request(this, _memoryBlob); Request = new Request(this, _memoryBlob);
Response = new Response(this); Response = new Response(this);
AllowSynchronousIO = server.Options.AllowSynchronousIO;
} }
internal HttpSysListener Server { get; } internal HttpSysListener Server { get; }
@ -88,6 +89,8 @@ namespace Microsoft.AspNetCore.Server.HttpSys
public bool IsUpgradableRequest => Request.IsUpgradable; public bool IsUpgradableRequest => Request.IsUpgradable;
internal bool AllowSynchronousIO { get; set; }
public Task<Stream> UpgradeAsync() public Task<Stream> UpgradeAsync()
{ {
if (!IsUpgradableRequest) if (!IsUpgradableRequest)

View File

@ -25,6 +25,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
internal RequestStream(RequestContext httpContext) internal RequestStream(RequestContext httpContext)
{ {
_requestContext = httpContext; _requestContext = httpContext;
_maxSize = _requestContext.Server.Options.MaxRequestBodySize;
} }
internal RequestContext RequestContext 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) 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); ValidateReadBuffer(buffer, offset, size);
CheckSizeLimit(); CheckSizeLimit();
if (_closed) if (_closed)

View File

@ -91,10 +91,16 @@ namespace Microsoft.AspNetCore.Server.HttpSys
// Send headers // Send headers
public override void Flush() public override void Flush()
{ {
if (!RequestContext.AllowSynchronousIO)
{
throw new InvalidOperationException("Synchronous IO APIs are disabled, see AllowSynchronousIO.");
}
if (_disposed) if (_disposed)
{ {
return; return;
} }
FlushInternal(endOfRequest: false); FlushInternal(endOfRequest: false);
} }
@ -449,9 +455,15 @@ namespace Microsoft.AspNetCore.Server.HttpSys
public override void Write(byte[] buffer, int offset, int count) 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. // Validates for null and bounds. Allows count == 0.
// TODO: Verbose log parameters // TODO: Verbose log parameters
var data = new ArraySegment<byte>(buffer, offset, count); var data = new ArraySegment<byte>(buffer, offset, count);
CheckDisposed(); CheckDisposed();
CheckWriteCount(count); CheckWriteCount(count);

View File

@ -27,6 +27,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
{ typeof(IHttpRequestIdentifierFeature), _identityFunc }, { typeof(IHttpRequestIdentifierFeature), _identityFunc },
{ typeof(RequestContext), ctx => ctx.RequestContext }, { typeof(RequestContext), ctx => ctx.RequestContext },
{ typeof(IHttpMaxRequestBodySizeFeature), _identityFunc }, { typeof(IHttpMaxRequestBodySizeFeature), _identityFunc },
{ typeof(IHttpBodyControlFeature), _identityFunc },
}; };
private readonly FeatureContext _featureContext; private readonly FeatureContext _featureContext;

View File

@ -60,10 +60,9 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
string input = new StreamReader(context.Request.Body).ReadToEnd(); string input = new StreamReader(context.Request.Body).ReadToEnd();
Assert.Equal("Hello World", input); Assert.Equal("Hello World", input);
context.Response.ContentLength = 11; context.Response.ContentLength = 11;
using (var writer = new StreamWriter(context.Response.Body)) var writer = new StreamWriter(context.Response.Body);
{ await writer.WriteAsync("Hello World");
writer.Write("Hello World"); await writer.FlushAsync();
}
string response = await responseTask; string response = await responseTask;
Assert.Equal("Hello World", response); Assert.Equal("Hello World", response);

View File

@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
var context = await server.AcceptAsync(Utilities.DefaultTimeout); var context = await server.AcceptAsync(Utilities.DefaultTimeout);
byte[] body = Encoding.UTF8.GetBytes("Hello World"); 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 Assert.Throws<InvalidOperationException>(() => context.Response.Headers["Upgrade"] = "WebSocket"); // Win8.1 blocks anything but WebSocket
await Assert.ThrowsAsync<InvalidOperationException>(async () => await context.UpgradeAsync()); await Assert.ThrowsAsync<InvalidOperationException>(async () => await context.UpgradeAsync());

View File

@ -16,6 +16,32 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
{ {
public class RequestBodyTests 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] [ConditionalFact]
public async Task RequestBody_ReadSync_Success() public async Task RequestBody_ReadSync_Success()
{ {
@ -24,6 +50,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
{ {
Task<string> responseTask = SendRequestAsync(address, "Hello World"); Task<string> responseTask = SendRequestAsync(address, "Hello World");
server.Options.AllowSynchronousIO = true;
var context = await server.AcceptAsync(Utilities.DefaultTimeout); var context = await server.AcceptAsync(Utilities.DefaultTimeout);
byte[] input = new byte[100]; byte[] input = new byte[100];
int read = context.Request.Body.Read(input, 0, input.Length); 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"); Task<string> responseTask = SendRequestAsync(address, "Hello World");
server.Options.AllowSynchronousIO = true;
var context = await server.AcceptAsync(Utilities.DefaultTimeout); var context = await server.AcceptAsync(Utilities.DefaultTimeout);
byte[] input = new byte[100]; byte[] input = new byte[100];
Assert.Throws<ArgumentNullException>("buffer", () => context.Request.Body.Read(null, 0, 1)); 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); Task<string> responseTask = SendRequestAsync(address, content);
server.Options.AllowSynchronousIO = true;
var context = await server.AcceptAsync(Utilities.DefaultTimeout); var context = await server.AcceptAsync(Utilities.DefaultTimeout);
byte[] input = new byte[10]; byte[] input = new byte[10];
int read = context.Request.Body.Read(input, 0, input.Length); 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 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] [ConditionalFact]
public async Task ResponseBody_WriteNoHeaders_DefaultsToChunked() public async Task ResponseBody_WriteNoHeaders_DefaultsToChunked()
{ {
@ -24,6 +59,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
{ {
var responseTask = SendRequestAsync(address); var responseTask = SendRequestAsync(address);
server.Options.AllowSynchronousIO = true;
var context = await server.AcceptAsync(Utilities.DefaultTimeout); var context = await server.AcceptAsync(Utilities.DefaultTimeout);
context.Response.Body.Write(new byte[10], 0, 10); context.Response.Body.Write(new byte[10], 0, 10);
await context.Response.Body.WriteAsync(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; string address;
using (var server = Utilities.CreateHttpServer(out address)) using (var server = Utilities.CreateHttpServer(out address))
{ {
server.Options.AllowSynchronousIO = true;
var responseTask = SendRequestAsync(address); var responseTask = SendRequestAsync(address);
var context = await server.AcceptAsync(Utilities.DefaultTimeout); var context = await server.AcceptAsync(Utilities.DefaultTimeout);
@ -95,6 +132,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
{ {
var responseTask = SendRequestAsync(address); var responseTask = SendRequestAsync(address);
server.Options.AllowSynchronousIO = true;
var context = await server.AcceptAsync(Utilities.DefaultTimeout); var context = await server.AcceptAsync(Utilities.DefaultTimeout);
context.Response.Headers["Content-lenGth"] = " 30 "; context.Response.Headers["Content-lenGth"] = " 30 ";
var stream = context.Response.Body; var stream = context.Response.Body;
@ -181,6 +219,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
{ {
var responseTask = SendRequestAsync(address); var responseTask = SendRequestAsync(address);
server.Options.AllowSynchronousIO = true;
var context = await server.AcceptAsync(Utilities.DefaultTimeout); var context = await server.AcceptAsync(Utilities.DefaultTimeout);
context.Response.Headers["Content-lenGth"] = " 10 "; context.Response.Headers["Content-lenGth"] = " 10 ";
context.Response.Body.Write(new byte[10], 0, 10); context.Response.Body.Write(new byte[10], 0, 10);
@ -206,6 +245,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
{ {
var responseTask = SendRequestAsync(address); var responseTask = SendRequestAsync(address);
server.Options.AllowSynchronousIO = true;
var context = await server.AcceptAsync(Utilities.DefaultTimeout); var context = await server.AcceptAsync(Utilities.DefaultTimeout);
context.Response.Body.Write(new byte[10], 0, 0); context.Response.Body.Write(new byte[10], 0, 0);
Assert.True(context.Response.HasStarted); Assert.True(context.Response.HasStarted);
@ -380,6 +420,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
using (var server = Utilities.CreateHttpServer(out address)) using (var server = Utilities.CreateHttpServer(out address))
{ {
server.Options.ThrowWriteExceptions = true; server.Options.ThrowWriteExceptions = true;
server.Options.AllowSynchronousIO = true;
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
var responseTask = SendRequestAsync(address, cts.Token); var responseTask = SendRequestAsync(address, cts.Token);
@ -444,6 +485,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
var responseTask = SendRequestAsync(address, cts.Token); var responseTask = SendRequestAsync(address, cts.Token);
server.Options.AllowSynchronousIO = true;
var context = await server.AcceptAsync(Utilities.DefaultTimeout); var context = await server.AcceptAsync(Utilities.DefaultTimeout);
// First write sends headers // First write sends headers
cts.Cancel(); cts.Cancel();
@ -555,6 +597,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
string address; string address;
using (var server = Utilities.CreateHttpServer(out address)) using (var server = Utilities.CreateHttpServer(out address))
{ {
server.Options.AllowSynchronousIO = true;
RequestContext context; RequestContext context;
using (var client = new HttpClient()) 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.Headers["content-type"] = "some/thing"; // Http.sys requires a content-type to cache
context.Response.ContentLength = 10; context.Response.ContentLength = 10;
context.Response.CacheTtl = TimeSpan.FromSeconds(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 // Http.Sys will add this for us
Assert.Null(context.Response.ContentLength); Assert.Null(context.Response.ContentLength);
context.Dispose(); context.Dispose();
@ -381,6 +381,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
{ {
var responseTask = SendRequestAsync(address); var responseTask = SendRequestAsync(address);
server.Options.AllowSynchronousIO = true;
var context = await server.AcceptAsync(Utilities.DefaultTimeout); var context = await server.AcceptAsync(Utilities.DefaultTimeout);
context.Response.Headers["x-request-count"] = "1"; context.Response.Headers["x-request-count"] = "1";
context.Response.Headers["content-type"] = "some/thing"; // Http.sys requires a content-type to cache 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["x-request-count"] = "1";
context.Response.Headers["content-type"] = "some/thing"; // Http.sys requires a content-type to cache context.Response.Headers["content-type"] = "some/thing"; // Http.sys requires a content-type to cache
context.Response.CacheTtl = TimeSpan.FromSeconds(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);
context.Response.Body.Flush(); await context.Response.Body.FlushAsync();
context.Dispose(); context.Dispose();
var response = await responseTask; 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.Headers["content-type"] = "some/thing"; // Http.sys requires a content-type to cache
context.Response.ContentLength = 10; context.Response.ContentLength = 10;
context.Response.CacheTtl = TimeSpan.FromSeconds(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 // Http.Sys will add this for us
Assert.Null(context.Response.ContentLength); Assert.Null(context.Response.ContentLength);
context.Dispose(); context.Dispose();
@ -999,7 +1000,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
context.Response.Headers["content-range"] = "bytes 0-10/100"; context.Response.Headers["content-range"] = "bytes 0-10/100";
context.Response.ContentLength = 11; context.Response.ContentLength = 11;
context.Response.CacheTtl = TimeSpan.FromSeconds(10); 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(); context.Dispose();
var response = await responseTask; 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.Headers["content-range"] = "bytes 0-10/100";
context.Response.ContentLength = 11; context.Response.ContentLength = 11;
context.Response.CacheTtl = TimeSpan.FromSeconds(10); 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(); context.Dispose();
response = await responseTask; 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.Headers["content-type"] = "some/thing"; // Http.sys requires a content-type to cache
context.Response.ContentLength = 100; context.Response.ContentLength = 100;
context.Response.CacheTtl = TimeSpan.FromSeconds(10); 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(); context.Dispose();
var response = await responseTask; 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.Headers["content-type"] = "some/thing"; // Http.sys requires a content-type to cache
context.Response.ContentLength = 100; context.Response.ContentLength = 100;
context.Response.CacheTtl = TimeSpan.FromSeconds(10); 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(); context.Dispose();
var response = await responseTask; var response = await responseTask;

View File

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

View File

@ -42,10 +42,9 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
var context = await server.AcceptAsync(Utilities.DefaultTimeout); var context = await server.AcceptAsync(Utilities.DefaultTimeout);
context.Response.ContentLength = 11; context.Response.ContentLength = 11;
using (var writer = new StreamWriter(context.Response.Body)) var writer = new StreamWriter(context.Response.Body);
{ await writer.WriteAsync("Hello World");
writer.Write("Hello World"); await writer.FlushAsync();
}
string response = await responseTask; string response = await responseTask;
Assert.Equal("Hello World", response); Assert.Equal("Hello World", response);
@ -61,13 +60,12 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
var responseTask = SendRequestAsync(address, "Hello World"); var responseTask = SendRequestAsync(address, "Hello World");
var context = await server.AcceptAsync(Utilities.DefaultTimeout); 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); Assert.Equal("Hello World", input);
context.Response.ContentLength = 11; context.Response.ContentLength = 11;
using (var writer = new StreamWriter(context.Response.Body)) var writer = new StreamWriter(context.Response.Body);
{ await writer.WriteAsync("Hello World");
writer.Write("Hello World"); await writer.FlushAsync();
}
var response = await responseTask; var response = await responseTask;
Assert.Equal("Hello World", response); Assert.Equal("Hello World", response);
@ -218,10 +216,9 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
context.Response.Headers["Connection"] = "close"; context.Response.Headers["Connection"] = "close";
context.Response.ContentLength = 11; context.Response.ContentLength = 11;
using (var writer = new StreamWriter(context.Response.Body)) var writer = new StreamWriter(context.Response.Body);
{ await writer.WriteAsync("Hello World");
writer.Write("Hello World"); await writer.FlushAsync();
}
Assert.True(canceled.WaitOne(interval), "Disconnected"); Assert.True(canceled.WaitOne(interval), "Disconnected");
Assert.True(ct.IsCancellationRequested, "IsCancellationRequested"); Assert.True(ct.IsCancellationRequested, "IsCancellationRequested");

View File

@ -123,7 +123,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
Assert.True(feature.IsReadOnly); Assert.True(feature.IsReadOnly);
Assert.Null(feature.MaxRequestBodySize); Assert.Null(feature.MaxRequestBodySize);
Assert.Throws<InvalidOperationException>(() => feature.MaxRequestBodySize = 12); 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; upgraded = true;
waitHandle.Set(); waitHandle.Set();
})) }))
@ -131,7 +131,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
using (Stream stream = await SendOpaqueRequestAsync("GET", address)) using (Stream stream = await SendOpaqueRequestAsync("GET", address))
{ {
stream.Write(new byte[15], 0, 15); 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.HasValue, "Upgraded not set");
Assert.True(upgraded.Value, "Upgrade failed"); Assert.True(upgraded.Value, "Upgrade failed");
} }

View File

@ -21,6 +21,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
string address; string address;
using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 11, httpContext => using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 11, httpContext =>
{ {
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>(); var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>();
Assert.NotNull(feature); Assert.NotNull(feature);
Assert.False(feature.IsReadOnly); Assert.False(feature.IsReadOnly);
@ -86,6 +87,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
string address; string address;
using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 11, httpContext => using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 11, httpContext =>
{ {
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>(); var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>();
Assert.NotNull(feature); Assert.NotNull(feature);
Assert.False(feature.IsReadOnly); Assert.False(feature.IsReadOnly);
@ -151,6 +153,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
string address; string address;
using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 10, httpContext => using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 10, httpContext =>
{ {
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>(); var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>();
Assert.NotNull(feature); Assert.NotNull(feature);
Assert.False(feature.IsReadOnly); Assert.False(feature.IsReadOnly);
@ -220,6 +223,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
string address; string address;
using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 10, httpContext => using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 10, httpContext =>
{ {
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>(); var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>();
Assert.NotNull(feature); Assert.NotNull(feature);
Assert.False(feature.IsReadOnly); Assert.False(feature.IsReadOnly);
@ -290,6 +294,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
string address; string address;
using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 10, httpContext => using (Utilities.CreateHttpServer(out address, options => options.MaxRequestBodySize = 10, httpContext =>
{ {
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>(); var feature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>();
Assert.NotNull(feature); Assert.NotNull(feature);
Assert.False(feature.IsReadOnly); Assert.False(feature.IsReadOnly);

View File

@ -9,6 +9,7 @@ using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Xunit; using Xunit;
@ -23,6 +24,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
using (Utilities.CreateHttpServer(out address, httpContext => using (Utilities.CreateHttpServer(out address, httpContext =>
{ {
byte[] input = new byte[100]; byte[] input = new byte[100];
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
int read = httpContext.Request.Body.Read(input, 0, input.Length); int read = httpContext.Request.Body.Read(input, 0, input.Length);
httpContext.Response.ContentLength = read; httpContext.Response.ContentLength = read;
httpContext.Response.Body.Write(input, 0, read); httpContext.Response.Body.Write(input, 0, read);
@ -75,6 +77,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
string address; string address;
using (Utilities.CreateHttpServer(out address, httpContext => using (Utilities.CreateHttpServer(out address, httpContext =>
{ {
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
byte[] input = new byte[100]; byte[] input = new byte[100];
Assert.Throws<ArgumentNullException>("buffer", () => httpContext.Request.Body.Read(null, 0, 1)); Assert.Throws<ArgumentNullException>("buffer", () => httpContext.Request.Body.Read(null, 0, 1));
Assert.Throws<ArgumentOutOfRangeException>("offset", () => httpContext.Request.Body.Read(input, -1, 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 => using (Utilities.CreateHttpServer(out address, httpContext =>
{ {
byte[] input = new byte[10]; byte[] input = new byte[10];
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
int read = httpContext.Request.Body.Read(input, 0, input.Length); int read = httpContext.Request.Body.Read(input, 0, input.Length);
Assert.Equal(5, read); Assert.Equal(5, read);
content.Block.Release(); content.Block.Release();

View File

@ -9,6 +9,7 @@ using System.Net.Http;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Xunit; using Xunit;
@ -22,6 +23,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
string address; string address;
using (Utilities.CreateHttpServer(out address, httpContext => using (Utilities.CreateHttpServer(out address, httpContext =>
{ {
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
httpContext.Response.Body.Write(new byte[10], 0, 10); httpContext.Response.Body.Write(new byte[10], 0, 10);
return httpContext.Response.Body.WriteAsync(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; string address;
using (Utilities.CreateHttpServer(out address, async httpContext => using (Utilities.CreateHttpServer(out address, async httpContext =>
{ {
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
httpContext.Response.Body.Write(new byte[10], 0, 10); httpContext.Response.Body.Write(new byte[10], 0, 10);
await httpContext.Response.Body.WriteAsync(new byte[10], 0, 10); await httpContext.Response.Body.WriteAsync(new byte[10], 0, 10);
await httpContext.Response.Body.FlushAsync(); await httpContext.Response.Body.FlushAsync();
@ -85,6 +88,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
string address; string address;
using (Utilities.CreateHttpServer(out address, async httpContext => using (Utilities.CreateHttpServer(out address, async httpContext =>
{ {
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
httpContext.Response.Headers["Content-lenGth"] = " 30 "; httpContext.Response.Headers["Content-lenGth"] = " 30 ";
Stream stream = httpContext.Response.Body; Stream stream = httpContext.Response.Body;
stream.EndWrite(stream.BeginWrite(new byte[10], 0, 10, null, null)); 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 => using (Utilities.CreateHttpServer(out address, httpContext =>
{ {
httpContext.Response.Headers["Content-lenGth"] = " 20 "; httpContext.Response.Headers["Content-lenGth"] = " 20 ";
httpContext.Response.Body.Write(new byte[5], 0, 5); return httpContext.Response.Body.WriteAsync(new byte[5], 0, 5);
return Task.FromResult(0);
})) }))
{ {
Assert.Throws<AggregateException>(() => SendRequestAsync(address).Result); Assert.Throws<AggregateException>(() => SendRequestAsync(address).Result);
@ -137,13 +140,13 @@ namespace Microsoft.AspNetCore.Server.HttpSys
{ {
var completed = false; var completed = false;
string address; string address;
using (Utilities.CreateHttpServer(out address, httpContext => using (Utilities.CreateHttpServer(out address, async httpContext =>
{ {
httpContext.Response.Headers["Content-lenGth"] = " 10 "; httpContext.Response.Headers["Content-lenGth"] = " 10 ";
httpContext.Response.Body.Write(new byte[5], 0, 5); await httpContext.Response.Body.WriteAsync(new byte[5], 0, 5);
Assert.Throws<InvalidOperationException>(() => httpContext.Response.Body.Write(new byte[6], 0, 6)); await Assert.ThrowsAsync<InvalidOperationException>(() =>
httpContext.Response.Body.WriteAsync(new byte[6], 0, 6));
completed = true; completed = true;
return Task.FromResult(0);
})) }))
{ {
await Assert.ThrowsAsync<HttpRequestException>(() => SendRequestAsync(address)); await Assert.ThrowsAsync<HttpRequestException>(() => SendRequestAsync(address));
@ -161,6 +164,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
{ {
try try
{ {
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
httpContext.Response.Headers["Content-lenGth"] = " 10 "; httpContext.Response.Headers["Content-lenGth"] = " 10 ";
httpContext.Response.Body.Write(new byte[10], 0, 10); httpContext.Response.Body.Write(new byte[10], 0, 10);
httpContext.Response.Body.Write(new byte[9], 0, 9); httpContext.Response.Body.Write(new byte[9], 0, 9);
@ -197,6 +201,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
string address; string address;
using (Utilities.CreateHttpServer(out address, httpContext => using (Utilities.CreateHttpServer(out address, httpContext =>
{ {
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
httpContext.Response.OnStarting(state => httpContext.Response.OnStarting(state =>
{ {
onStartingCalled = true; onStartingCalled = true;

View File

@ -130,8 +130,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
var responseInfo = httpContext.Features.Get<IHttpResponseFeature>(); var responseInfo = httpContext.Features.Get<IHttpResponseFeature>();
var responseHeaders = responseInfo.Headers; var responseHeaders = responseInfo.Headers;
responseHeaders["Connection"] = new string[] { "Close" }; responseHeaders["Connection"] = new string[] { "Close" };
httpContext.Response.Body.Flush(); // Http.Sys adds the Content-Length: header for us if we don't flush return httpContext.Response.Body.FlushAsync(); // Http.Sys adds the Content-Length: header for us if we don't flush
return Task.FromResult(0);
})) }))
{ {
HttpResponseMessage response = await SendRequestAsync(address); HttpResponseMessage response = await SendRequestAsync(address);
@ -204,6 +203,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
string address; string address;
using (Utilities.CreateHttpServer(out address, httpContext => using (Utilities.CreateHttpServer(out address, httpContext =>
{ {
httpContext.Features.Get<IHttpBodyControlFeature>().AllowSynchronousIO = true;
var responseInfo = httpContext.Features.Get<IHttpResponseFeature>(); var responseInfo = httpContext.Features.Get<IHttpResponseFeature>();
var responseHeaders = responseInfo.Headers; var responseHeaders = responseInfo.Headers;
responseHeaders.Add("Custom1", new string[] { "value1a", "value1b" }); responseHeaders.Add("Custom1", new string[] { "value1a", "value1b" });

View File

@ -53,12 +53,12 @@ namespace Microsoft.AspNetCore.Server.HttpSys
public async Task Server_EchoHelloWorld_Success() public async Task Server_EchoHelloWorld_Success()
{ {
string address; 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); Assert.Equal("Hello World", input);
httpContext.Response.ContentLength = 11; httpContext.Response.ContentLength = 11;
return httpContext.Response.WriteAsync("Hello World"); await httpContext.Response.WriteAsync("Hello World");
})) }))
{ {
string response = await SendRequestAsync(address, "Hello World"); string response = await SendRequestAsync(address, "Hello World");