Use correct config for response buffer limit (#1516)

This commit is contained in:
Stephen Halter 2017-03-20 15:43:55 -07:00 committed by GitHub
parent 39819d6708
commit 8923b0da70
7 changed files with 127 additions and 78 deletions

View File

@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
private long GetOutputResponseBufferSize()
{
var bufferSize = ServiceContext.ServerOptions.Limits.MaxRequestBufferSize;
var bufferSize = ServiceContext.ServerOptions.Limits.MaxResponseBufferSize;
if (bufferSize == 0)
{
// 0 = no buffering so we need to configure the pipe so the the writer waits on the reader directly

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
[Setup]
public void Setup()
{
var connectionContext = new MockConnection(new KestrelServerOptions());
var connectionContext = new MockConnection();
connectionContext.ListenerContext.ServiceContext.HttpParserFactory = frame => NullParser.Instance;
_frame = new Frame<object>(application: null, context: connectionContext);

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
[Setup]
public void Setup()
{
var connectionContext = new MockConnection(new KestrelServerOptions());
var connectionContext = new MockConnection();
connectionContext.ListenerContext.ServiceContext.HttpParserFactory = frame => new KestrelHttpParser(frame.ConnectionContext.ListenerContext.ServiceContext.Log);
Frame = new Frame<object>(application: null, context: connectionContext);

View File

@ -167,7 +167,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
[Setup]
public void Setup()
{
var connectionContext = new MockConnection(new KestrelServerOptions());
var connectionContext = new MockConnection();
connectionContext.ListenerContext.ServiceContext.HttpParserFactory = f => new KestrelHttpParser(f.ConnectionContext.ListenerContext.ServiceContext.Log);
var frame = new Frame<object>(application: null, context: connectionContext);
frame.Reset();

View File

@ -0,0 +1,72 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO.Pipelines;
using Microsoft.AspNetCore.Server.Kestrel.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
using Microsoft.AspNetCore.Testing;
using Xunit;
namespace Microsoft.AspNetCore.Server.KestrelTests
{
public class ListenerContextTests
{
[Theory]
[InlineData(10, 10, 10)]
[InlineData(0, 1, 1)]
[InlineData(null, 0, 0)]
public void LibuvOutputPipeOptionsConfiguredCorrectly(long? maxResponseBufferSize, long expectedMaximumSizeLow, long expectedMaximumSizeHigh)
{
var serviceContext = new TestServiceContext();
serviceContext.ServerOptions.Limits.MaxResponseBufferSize = maxResponseBufferSize;
serviceContext.ThreadPool = new LoggingThreadPool(null);
var listenerContext = new ListenerContext(serviceContext)
{
Thread = new KestrelThread(new KestrelEngine(null, serviceContext))
};
Assert.Equal(expectedMaximumSizeLow, listenerContext.LibuvOutputPipeOptions.MaximumSizeLow);
Assert.Equal(expectedMaximumSizeHigh, listenerContext.LibuvOutputPipeOptions.MaximumSizeHigh);
Assert.Same(listenerContext.Thread, listenerContext.LibuvOutputPipeOptions.ReaderScheduler);
Assert.Same(serviceContext.ThreadPool, listenerContext.LibuvOutputPipeOptions.WriterScheduler);
}
[Theory]
[InlineData(10, 10, 10)]
[InlineData(null, 0, 0)]
public void LibuvInputPipeOptionsConfiguredCorrectly(long? maxRequestBufferSize, long expectedMaximumSizeLow, long expectedMaximumSizeHigh)
{
var serviceContext = new TestServiceContext();
serviceContext.ServerOptions.Limits.MaxRequestBufferSize = maxRequestBufferSize;
serviceContext.ThreadPool = new LoggingThreadPool(null);
var listenerContext = new ListenerContext(serviceContext)
{
Thread = new KestrelThread(new KestrelEngine(null, serviceContext))
};
Assert.Equal(expectedMaximumSizeLow, listenerContext.LibuvInputPipeOptions.MaximumSizeLow);
Assert.Equal(expectedMaximumSizeHigh, listenerContext.LibuvInputPipeOptions.MaximumSizeHigh);
Assert.Same(serviceContext.ThreadPool, listenerContext.LibuvInputPipeOptions.ReaderScheduler);
Assert.Same(listenerContext.Thread, listenerContext.LibuvInputPipeOptions.WriterScheduler);
}
[Theory]
[InlineData(10, 10, 10)]
[InlineData(null, 0, 0)]
public void AdaptedPipeOptionsConfiguredCorrectly(long? maxRequestBufferSize, long expectedMaximumSizeLow, long expectedMaximumSizeHigh)
{
var serviceContext = new TestServiceContext();
serviceContext.ServerOptions.Limits.MaxRequestBufferSize = maxRequestBufferSize;
var listenerContext = new ListenerContext(serviceContext);
Assert.Equal(expectedMaximumSizeLow, listenerContext.AdaptedPipeOptions.MaximumSizeLow);
Assert.Equal(expectedMaximumSizeHigh, listenerContext.AdaptedPipeOptions.MaximumSizeHigh);
Assert.Same(InlineScheduler.Default, listenerContext.AdaptedPipeOptions.ReaderScheduler);
Assert.Same(InlineScheduler.Default, listenerContext.AdaptedPipeOptions.WriterScheduler);
}
}
}

View File

@ -9,7 +9,6 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel;
using Microsoft.AspNetCore.Server.Kestrel.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.KestrelTests.TestHelpers;
using Microsoft.AspNetCore.Testing;
using Xunit;
@ -18,43 +17,19 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{
public class SocketOutputTests
{
public static TheoryData<KestrelServerOptions> MaxResponseBufferSizeData => new TheoryData<KestrelServerOptions>
public static TheoryData<long?> MaxResponseBufferSizeData => new TheoryData<long?>
{
new KestrelServerOptions(),
new KestrelServerOptions
{
Limits = { MaxResponseBufferSize = 0 }
},
new KestrelServerOptions
{
Limits = { MaxResponseBufferSize = 1024 }
},
new KestrelServerOptions
{
Limits = { MaxResponseBufferSize = 1024 * 1024 }
},
new KestrelServerOptions
{
Limits = { MaxResponseBufferSize = null }
},
new KestrelServerOptions().Limits.MaxResponseBufferSize, 0, 1024, 1024 * 1024, null
};
public static TheoryData<KestrelServerOptions> PositiveMaxResponseBufferSizeData => new TheoryData<KestrelServerOptions>
public static TheoryData<int> PositiveMaxResponseBufferSizeData => new TheoryData<int>
{
new KestrelServerOptions(),
new KestrelServerOptions
{
Limits = { MaxResponseBufferSize = 1024 }
},
new KestrelServerOptions
{
Limits = { MaxResponseBufferSize = (1024 * 1024) + 1 }
}
(int)new KestrelServerOptions().Limits.MaxResponseBufferSize, 1024, (1024 * 1024) + 1
};
[Theory]
[MemberData(nameof(MaxResponseBufferSizeData))]
public async Task CanWrite1MB(KestrelServerOptions options)
public async Task CanWrite1MB(long? maxResponseBufferSize)
{
// This test was added because when initially implementing write-behind buffering in
// SocketOutput, the write callback would never be invoked for writes larger than
@ -71,15 +46,17 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
var trace = new KestrelTrace(new TestKestrelTrace());
var connection = new MockConnection(options);
// ListenerContext will set MaximumSizeHigh/Low to zero when MaxResponseBufferSize is null.
// This is verified in ListenerContextTests.LibuvOutputPipeOptionsConfiguredCorrectly.
var pipeOptions = new PipeOptions
{
ReaderScheduler = kestrelThread,
MaximumSizeHigh = options.Limits.MaxResponseBufferSize ?? 0,
MaximumSizeLow = options.Limits.MaxResponseBufferSize ?? 0,
MaximumSizeHigh = maxResponseBufferSize ?? 0,
MaximumSizeLow = maxResponseBufferSize ?? 0,
};
var pipe = factory.Create(pipeOptions);
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, connection, "0", trace);
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, new MockConnection(), "0", trace);
// At least one run of this test should have a MaxResponseBufferSize < 1 MB.
var bufferSize = 1024 * 1024;
@ -120,15 +97,17 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
var trace = new KestrelTrace(new TestKestrelTrace());
var options = new KestrelServerOptions { Limits = { MaxResponseBufferSize = null } };
// ListenerContext will set MaximumSizeHigh/Low to zero when MaxResponseBufferSize is null.
// This is verified in ListenerContextTests.LibuvOutputPipeOptionsConfiguredCorrectly.
var pipeOptions = new PipeOptions
{
ReaderScheduler = kestrelThread,
MaximumSizeHigh = options.Limits.MaxResponseBufferSize ?? 0,
MaximumSizeLow = options.Limits.MaxResponseBufferSize ?? 0,
MaximumSizeHigh = 0,
MaximumSizeLow = 0,
};
var pipe = factory.Create(pipeOptions);
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, new MockConnection(options), "0", trace);
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, new MockConnection(), "0", trace);
// Don't want to allocate anything too huge for perf. This is at least larger than the default buffer.
var bufferSize = 1024 * 1024;
@ -177,7 +156,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
var trace = new KestrelTrace(new TestKestrelTrace());
var options = new KestrelServerOptions { Limits = { MaxResponseBufferSize = 0 } };
// ListenerContext will set MaximumSizeHigh/Low to 1 when MaxResponseBufferSize is zero.
// This is verified in ListenerContextTests.LibuvOutputPipeOptionsConfiguredCorrectly.
var pipeOptions = new PipeOptions
{
ReaderScheduler = kestrelThread,
@ -185,7 +166,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
MaximumSizeLow = 1,
};
var pipe = factory.Create(pipeOptions);
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, new MockConnection(options), "0", trace);
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, new MockConnection(), "0", trace);
var bufferSize = 1;
var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
@ -221,9 +202,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory]
[MemberData(nameof(PositiveMaxResponseBufferSizeData))]
public async Task WritesDontCompleteImmediatelyWhenTooManyBytesAreAlreadyBuffered(KestrelServerOptions options)
public async Task WritesDontCompleteImmediatelyWhenTooManyBytesAreAlreadyBuffered(int maxResponseBufferSize)
{
var maxBytesPreCompleted = (int)options.Limits.MaxResponseBufferSize.Value;
var completeQueue = new ConcurrentQueue<Action<int>>();
// Arrange
@ -245,17 +225,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
var trace = new KestrelTrace(new TestKestrelTrace());
var mockConnection = new MockConnection(options);
var pipeOptions = new PipeOptions
{
ReaderScheduler = kestrelThread,
MaximumSizeHigh = options.Limits.MaxResponseBufferSize ?? 0,
MaximumSizeLow = options.Limits.MaxResponseBufferSize ?? 0,
MaximumSizeHigh = maxResponseBufferSize,
MaximumSizeLow = maxResponseBufferSize,
};
var pipe = factory.Create(pipeOptions);
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, mockConnection, "0", trace);
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, new MockConnection(), "0", trace);
var bufferSize = maxBytesPreCompleted - 1;
var bufferSize = maxResponseBufferSize - 1;
var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
// Act
@ -295,9 +274,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory]
[MemberData(nameof(PositiveMaxResponseBufferSizeData))]
public async Task WritesDontCompleteImmediatelyWhenTooManyBytesIncludingNonImmediateAreAlreadyBuffered(KestrelServerOptions options)
public async Task WritesDontCompleteImmediatelyWhenTooManyBytesIncludingNonImmediateAreAlreadyBuffered(int maxResponseBufferSize)
{
var maxBytesPreCompleted = (int)options.Limits.MaxResponseBufferSize.Value;
var completeQueue = new ConcurrentQueue<Action<int>>();
// Arrange
@ -319,17 +297,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
var trace = new KestrelTrace(new TestKestrelTrace());
var mockConnection = new MockConnection(options);
var pipeOptions = new PipeOptions
{
ReaderScheduler = kestrelThread,
MaximumSizeHigh = options.Limits.MaxResponseBufferSize ?? 0,
MaximumSizeLow = options.Limits.MaxResponseBufferSize ?? 0,
MaximumSizeHigh = maxResponseBufferSize,
MaximumSizeLow = maxResponseBufferSize,
};
var pipe = factory.Create(pipeOptions);
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, mockConnection, "0", trace);
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, new MockConnection(), "0", trace);
var bufferSize = maxBytesPreCompleted / 2;
var bufferSize = maxResponseBufferSize / 2;
var data = new byte[bufferSize];
var halfWriteBehindBuffer = new ArraySegment<byte>(data, 0, bufferSize);
@ -374,9 +351,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory]
[MemberData(nameof(PositiveMaxResponseBufferSizeData))]
public async Task FailedWriteCompletesOrCancelsAllPendingTasks(KestrelServerOptions options)
public async Task FailedWriteCompletesOrCancelsAllPendingTasks(int maxResponseBufferSize)
{
var maxBytesPreCompleted = (int)options.Limits.MaxResponseBufferSize.Value;
var completeQueue = new ConcurrentQueue<Action<int>>();
// Arrange
@ -399,19 +375,19 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
var trace = new KestrelTrace(new TestKestrelTrace());
using (var mockConnection = new MockConnection(options))
using (var mockConnection = new MockConnection())
{
var pipeOptions = new PipeOptions
{
ReaderScheduler = kestrelThread,
MaximumSizeHigh = options.Limits.MaxResponseBufferSize ?? 0,
MaximumSizeLow = options.Limits.MaxResponseBufferSize ?? 0,
MaximumSizeHigh = maxResponseBufferSize,
MaximumSizeLow = maxResponseBufferSize,
};
var pipe = factory.Create(pipeOptions);
var abortedSource = mockConnection.RequestAbortedSource;
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, mockConnection, "0", trace);
var bufferSize = maxBytesPreCompleted - 1;
var bufferSize = maxResponseBufferSize - 1;
var data = new byte[bufferSize];
var fullBuffer = new ArraySegment<byte>(data, 0, bufferSize);
@ -467,9 +443,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory]
[MemberData(nameof(PositiveMaxResponseBufferSizeData))]
public async Task WritesDontGetCompletedTooQuickly(KestrelServerOptions options)
public async Task WritesDontGetCompletedTooQuickly(int maxResponseBufferSize)
{
var maxBytesPreCompleted = (int)options.Limits.MaxResponseBufferSize.Value;
var completeQueue = new ConcurrentQueue<Action<int>>();
// Arrange
@ -491,17 +466,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
var trace = new KestrelTrace(new TestKestrelTrace());
var mockConnection = new MockConnection(options);
var pipeOptions = new PipeOptions
{
ReaderScheduler = kestrelThread,
MaximumSizeHigh = options.Limits.MaxResponseBufferSize ?? 0,
MaximumSizeLow = options.Limits.MaxResponseBufferSize ?? 0,
MaximumSizeHigh = maxResponseBufferSize,
MaximumSizeLow = maxResponseBufferSize,
};
var pipe = factory.Create(pipeOptions);
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, mockConnection, "0", trace);
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, new MockConnection(), "0", trace);
var bufferSize = maxBytesPreCompleted - 1;
var bufferSize = maxResponseBufferSize - 1;
var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
// Act (Pre-complete the maximum number of bytes in preparation for the rest of the test)
@ -538,7 +512,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory]
[MemberData(nameof(MaxResponseBufferSizeData))]
public async Task WritesAreAggregated(KestrelServerOptions options)
public async Task WritesAreAggregated(long? maxResponseBufferSize)
{
var writeCalled = false;
var writeCount = 0;
@ -563,14 +537,17 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
var trace = new KestrelTrace(new TestKestrelTrace());
// ListenerContext will set MaximumSizeHigh/Low to zero when MaxResponseBufferSize is null.
// This is verified in ListenerContextTests.LibuvOutputPipeOptionsConfiguredCorrectly.
var pipeOptions = new PipeOptions
{
ReaderScheduler = kestrelThread,
MaximumSizeHigh = options.Limits.MaxResponseBufferSize ?? 0,
MaximumSizeLow = options.Limits.MaxResponseBufferSize ?? 0,
MaximumSizeHigh = maxResponseBufferSize ?? 0,
MaximumSizeLow = maxResponseBufferSize ?? 0,
};
var pipe = factory.Create(pipeOptions);
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, new MockConnection(new KestrelServerOptions()), "0", trace);
var socketOutput = new SocketOutput(pipe, kestrelThread, socket, new MockConnection(), "0", trace);
mockLibuv.KestrelThreadBlocker.Reset();
@ -616,7 +593,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
var trace = new KestrelTrace(new TestKestrelTrace());
var connection = new MockConnection(new KestrelServerOptions());
var connection = new MockConnection();
var pipeOptions = new PipeOptions
{
ReaderScheduler = kestrelThread,

View File

@ -16,11 +16,11 @@ namespace Microsoft.AspNetCore.Testing
{
private readonly TaskCompletionSource<object> _socketClosedTcs = new TaskCompletionSource<object>();
public MockConnection(KestrelServerOptions options)
public MockConnection()
{
ConnectionControl = this;
RequestAbortedSource = new CancellationTokenSource();
ListenerContext = new ListenerContext(new ServiceContext {ServerOptions = options})
ListenerContext = new ListenerContext(new ServiceContext())
{
ListenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 5000))
};