Merge pull request #699 from aspnet/release/2.1

This commit is contained in:
Justin Kotalik 2018-03-19 17:10:44 -07:00 committed by GitHub
commit be08942b57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 30 deletions

View File

@ -27,7 +27,6 @@
<attribute name="disableStartUpErrorPage" type="bool" defaultValue="false" />
<attribute name="hostingModel" type="string" />
<element name="recycleOnFileChange">
<collection addElement="file" clearElement="clear">
<attribute name="path" type="string" required="true" validationType="nonEmptyString" expanded="true"/>
</collection>
@ -38,5 +37,11 @@
<attribute name="value" type="string" required="true"/>
</collection>
</element>
<element name="handlerSettings">
<collection addElement="handlerSetting" clearElement="clear" >
<attribute name="name" type="string" required="true" validationType="nonEmptyString"/>
<attribute name="value" type="string" required="true"/>
</collection>
</element>
</sectionSchema>
</configSchema>

View File

@ -12,6 +12,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal partial class IISHttpContext
{
private const int HttpDataChunkStackLimit = 128; // 16 bytes per HTTP_DATA_CHUNK
/// <summary>
/// Reads data from the Input pipe to the user.
/// </summary>
@ -212,6 +214,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
var hr = 0;
var nChunks = 0;
// Count the number of chunks in memory.
if (buffer.IsSingleSegment)
{
nChunks = 1;
@ -224,8 +227,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
}
}
if (buffer.IsSingleSegment)
if (nChunks == 1)
{
// If there is only a single chunk, use fixed to get a pointer to the buffer
var pDataChunks = stackalloc HttpApiTypes.HTTP_DATA_CHUNK[1];
fixed (byte* pBuffer = &MemoryMarshal.GetReference(buffer.First.Span))
@ -238,35 +242,20 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
hr = NativeMethods.http_write_response_bytes(_pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected);
}
}
else if (nChunks < HttpDataChunkStackLimit)
{
// To avoid stackoverflows, we will only stackalloc if the write size is less than the StackChunkLimit
// The stack size is IIS is by default 128/256 KB, so we are generous with this threshold.
var pDataChunks = stackalloc HttpApiTypes.HTTP_DATA_CHUNK[nChunks];
hr = WriteSequenceToIIS(nChunks, buffer, pDataChunks, out fCompletionExpected);
}
else
{
// REVIEW: Do we need to guard against this getting too big? It seems unlikely that we'd have more than say 10 chunks in real life
var pDataChunks = stackalloc HttpApiTypes.HTTP_DATA_CHUNK[nChunks];
var currentChunk = 0;
// REVIEW: We don't really need this list since the memory is already pinned with the default pool,
// but shouldn't assume the pool implementation right now. Unfortunately, this causes a heap allocation...
var handles = new MemoryHandle[nChunks];
foreach (var b in buffer)
// Otherwise allocate the chunks on the heap.
var chunks = new HttpApiTypes.HTTP_DATA_CHUNK[nChunks];
fixed (HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks = chunks)
{
ref var handle = ref handles[currentChunk];
ref var chunk = ref pDataChunks[currentChunk];
handle = b.Retain(true);
chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
chunk.fromMemory.BufferLength = (uint)b.Length;
chunk.fromMemory.pBuffer = (IntPtr)handle.Pointer;
currentChunk++;
}
hr = NativeMethods.http_write_response_bytes(_pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected);
// Free the handles
foreach (var handle in handles)
{
handle.Dispose();
hr = WriteSequenceToIIS(nChunks, buffer, pDataChunks, out fCompletionExpected);
}
}
@ -277,6 +266,39 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
return _operation;
}
private unsafe int WriteSequenceToIIS(int nChunks, ReadOnlySequence<byte> buffer, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, out bool fCompletionExpected)
{
var currentChunk = 0;
var hr = 0;
// REVIEW: We don't really need this list since the memory is already pinned with the default pool,
// but shouldn't assume the pool implementation right now. Unfortunately, this causes a heap allocation...
var handles = new MemoryHandle[nChunks];
foreach (var b in buffer)
{
ref var handle = ref handles[currentChunk];
ref var chunk = ref pDataChunks[currentChunk];
handle = b.Retain(true);
chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
chunk.fromMemory.BufferLength = (uint)b.Length;
chunk.fromMemory.pBuffer = (IntPtr)handle.Pointer;
currentChunk++;
}
hr = NativeMethods.http_write_response_bytes(_pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected);
// Free the handles
foreach (var handle in handles)
{
handle.Dispose();
}
return hr;
}
private unsafe IISAwaitable FlushToIISAsync()
{
// Calls flush

View File

@ -18,11 +18,19 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
}
[ConditionalTheory]
[InlineData(65000)]
[InlineData(1000000)]
[InlineData(10000000)]
public async Task LargeResponseBodyTestCheckAllResponseBodyBytesWritten(int query)
[InlineData(100000000)]
public async Task LargeResponseBodyTest_CheckAllResponseBodyBytesWritten(int query)
{
Assert.Equal(new string('a', query), await _fixture.Client.GetStringAsync($"/LargeResponseBody?length={query}"));
}
[ConditionalFact]
public async Task LargeResponseBodyFromFile_CheckAllResponseBodyBytesWritten()
{
Assert.Equal(200000000, (await _fixture.Client.GetStringAsync($"/LargeResponseFile")).Length);
}
}
}

View File

@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
Assert.Equal(body, responseText);
}
[ConditionalFact]
[ConditionalFact(Skip = "See https://github.com/aspnet/IISIntegration/issues/687")]
public async Task ReadAndWriteEchoTwice()
{
var requestBody = new string('a', 10000);

View File

@ -49,6 +49,7 @@ namespace IISTestSite
app.Map("/TestInvalidReadOperations", TestInvalidReadOperations);
app.Map("/TestInvalidWriteOperations", TestInvalidWriteOperations);
app.Map("/TestReadOffsetWorks", TestReadOffsetWorks);
app.Map("/LargeResponseFile", LargeResponseFile);
}
private void ServerVariable(IApplicationBuilder app)
@ -628,5 +629,26 @@ namespace IISTestSite
await context.Response.WriteAsync(success ? "Success" : "Failure");
});
}
private void LargeResponseFile(IApplicationBuilder app)
{
app.Run(async ctx =>
{
var tempFile = Path.GetTempFileName();
File.WriteAllText(tempFile, new string('a', 200000000));
await ctx.Response.SendFileAsync(tempFile, 0, null);
// Try to delete the file from the temp directory. If it fails, don't report an error
// to the application. File should eventually be cleaned up from the temp directory
// by OS.
try
{
File.Delete(tempFile);
}
catch (Exception)
{
}
});
}
}
}