aspnetcore/src/Microsoft.AspNetCore.Server.../Server/IISHttpContext.ReadWrite.cs

172 lines
5.3 KiB
C#

// 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;
using System.Buffers;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal partial class IISHttpContext
{
/// <summary>
/// Reads data from the Input pipe to the user.
/// </summary>
/// <param name="memory"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
internal async Task<int> ReadAsync(Memory<byte> memory, CancellationToken cancellationToken)
{
if (!_hasRequestReadingStarted)
{
InitializeRequestIO();
}
while (true)
{
var result = await _bodyInputPipe.Reader.ReadAsync();
var readableBuffer = result.Buffer;
try
{
if (!readableBuffer.IsEmpty)
{
var actual = Math.Min(readableBuffer.Length, memory.Length);
readableBuffer = readableBuffer.Slice(0, actual);
readableBuffer.CopyTo(memory.Span);
return (int)actual;
}
else if (result.IsCompleted)
{
return 0;
}
}
finally
{
_bodyInputPipe.Reader.AdvanceTo(readableBuffer.End, readableBuffer.End);
}
}
}
/// <summary>
/// Writes data to the output pipe.
/// </summary>
/// <param name="memory"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
internal Task WriteAsync(ReadOnlyMemory<byte> memory, CancellationToken cancellationToken = default(CancellationToken))
{
async Task WriteFirstAsync()
{
await InitializeResponse(flushHeaders: false);
await _bodyOutput.WriteAsync(memory, cancellationToken);
}
return !HasResponseStarted ? WriteFirstAsync() : _bodyOutput.WriteAsync(memory, cancellationToken);
}
/// <summary>
/// Flushes the data in the output pipe
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
internal Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken))
{
async Task FlushFirstAsync()
{
await InitializeResponse(flushHeaders: true);
await _bodyOutput.FlushAsync(cancellationToken);
}
return !HasResponseStarted ? FlushFirstAsync() : _bodyOutput.FlushAsync(cancellationToken);
}
private async Task ReadBody()
{
try
{
while (true)
{
var memory = _bodyInputPipe.Writer.GetMemory();
var read = await AsyncIO.ReadAsync(memory);
// End of body
if (read == 0)
{
break;
}
// Read was not canceled because of incoming write or IO stopping
if (read != -1)
{
_bodyInputPipe.Writer.Advance(read);
}
var result = await _bodyInputPipe.Writer.FlushAsync();
if (result.IsCompleted || result.IsCanceled)
{
break;
}
}
}
catch (Exception ex)
{
_bodyInputPipe.Writer.Complete(ex);
}
finally
{
_bodyInputPipe.Writer.Complete();
}
}
private async Task WriteBody(bool flush = false)
{
try
{
while (true)
{
var result = await _bodyOutput.Reader.ReadAsync();
var buffer = result.Buffer;
try
{
if (!buffer.IsEmpty)
{
await AsyncIO.WriteAsync(buffer);
}
// if request is done no need to flush, http.sys would do it for us
if (result.IsCompleted)
{
break;
}
flush = flush | result.IsCanceled;
if (flush)
{
await AsyncIO.FlushAsync();
flush = false;
}
}
finally
{
_bodyOutput.Reader.AdvanceTo(buffer.End);
}
}
}
catch (Exception ex)
{
_bodyOutput.Reader.Complete(ex);
}
finally
{
_bodyOutput.Reader.Complete();
}
}
}
}