| File: Core\IO\AsyncWriteOperationBase.cs | Web Access |
| Project: src\aspnetcore\src\Servers\IIS\IIS\src\Microsoft.AspNetCore.Server.IIS.csproj (Microsoft.AspNetCore.Server.IIS) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.Diagnostics; using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.IIS.Core.IO; internal abstract class AsyncWriteOperationBase : AsyncIOOperation { private const int HttpDataChunkStackLimit = 128; // 16 bytes per HTTP_DATA_CHUNK private NativeSafeHandle? _requestHandler; private ReadOnlySequence<byte> _buffer; private MemoryHandle[]? _handles; public void Initialize(NativeSafeHandle requestHandler, ReadOnlySequence<byte> buffer) { _requestHandler = requestHandler; _buffer = buffer; } protected override unsafe bool InvokeOperation(out int hr, out int bytes) { Debug.Assert(_requestHandler != null, "Must initialize first."); if (_buffer.Length > int.MaxValue) { throw new InvalidOperationException($"Writes larger then {int.MaxValue} are not supported."); } bool completionExpected; var chunkCount = GetChunkCount(); var bufferLength = (int)_buffer.Length; if (chunkCount < 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 chunks = stackalloc HTTP_DATA_CHUNK[chunkCount]; hr = WriteSequence(_requestHandler, chunkCount, _buffer, chunks, out completionExpected); } else { // Otherwise allocate the chunks on the heap. var chunks = new HTTP_DATA_CHUNK[chunkCount]; fixed (HTTP_DATA_CHUNK* pDataChunks = chunks) { hr = WriteSequence(_requestHandler, chunkCount, _buffer, pDataChunks, out completionExpected); } } bytes = bufferLength; return !completionExpected; } public override void FreeOperationResources(int hr, int bytes) { if (_handles != null) { // Free the handles foreach (var handle in _handles) { handle.Dispose(); } } } protected override void ResetOperation() { base.ResetOperation(); _requestHandler = default; _buffer = default; _handles.AsSpan().Clear(); } private int GetChunkCount() { if (_buffer.IsSingleSegment) { return 1; } var count = 0; foreach (var _ in _buffer) { count++; } return count; } private unsafe int WriteSequence(NativeSafeHandle requestHandler, int nChunks, ReadOnlySequence<byte> buffer, HTTP_DATA_CHUNK* pDataChunks, out bool fCompletionExpected) { var currentChunk = 0; if (_handles == null || _handles.Length < nChunks) { _handles = new MemoryHandle[nChunks]; } foreach (var readOnlyMemory in buffer) { ref var handle = ref _handles[currentChunk]; ref var chunk = ref pDataChunks[currentChunk]; handle = readOnlyMemory.Pin(); chunk.DataChunkType = HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; chunk.Anonymous.FromMemory.BufferLength = (uint)readOnlyMemory.Length; chunk.Anonymous.FromMemory.pBuffer = handle.Pointer; currentChunk++; } return WriteChunks(requestHandler, nChunks, pDataChunks, out fCompletionExpected); } protected abstract unsafe int WriteChunks(NativeSafeHandle requestHandler, int chunkCount, HTTP_DATA_CHUNK* dataChunks, out bool completionExpected); }