185 lines
5.4 KiB
C#
185 lines
5.4 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.IO;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
|
using System.IO.Pipelines;
|
|
|
|
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
|
|
{
|
|
public class AdaptedPipeline : IDuplexPipe
|
|
{
|
|
private const int MinAllocBufferSize = 2048;
|
|
|
|
private readonly IDuplexPipe _transport;
|
|
private readonly IDuplexPipe _application;
|
|
|
|
public AdaptedPipeline(IDuplexPipe transport,
|
|
IDuplexPipe application,
|
|
Pipe inputPipe,
|
|
Pipe outputPipe)
|
|
{
|
|
_transport = transport;
|
|
_application = application;
|
|
Input = inputPipe;
|
|
Output = outputPipe;
|
|
}
|
|
|
|
public Pipe Input { get; }
|
|
|
|
public Pipe Output { get; }
|
|
|
|
PipeReader IDuplexPipe.Input => Input.Reader;
|
|
|
|
PipeWriter IDuplexPipe.Output => Output.Writer;
|
|
|
|
public async Task RunAsync(Stream stream)
|
|
{
|
|
var inputTask = ReadInputAsync(stream);
|
|
var outputTask = WriteOutputAsync(stream);
|
|
|
|
await inputTask;
|
|
await outputTask;
|
|
}
|
|
|
|
private async Task WriteOutputAsync(Stream stream)
|
|
{
|
|
Exception error = null;
|
|
|
|
try
|
|
{
|
|
if (stream == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
var result = await Output.Reader.ReadAsync();
|
|
var buffer = result.Buffer;
|
|
|
|
try
|
|
{
|
|
if (result.IsCancelled)
|
|
{
|
|
// Forward the cancellation to the transport pipe
|
|
_application.Input.CancelPendingRead();
|
|
break;
|
|
}
|
|
|
|
if (buffer.IsEmpty)
|
|
{
|
|
if (result.IsCompleted)
|
|
{
|
|
break;
|
|
}
|
|
await stream.FlushAsync();
|
|
}
|
|
else if (buffer.IsSingleSegment)
|
|
{
|
|
#if NETCOREAPP2_1
|
|
await stream.WriteAsync(buffer.First);
|
|
#else
|
|
var array = buffer.First.GetArray();
|
|
await stream.WriteAsync(array.Array, array.Offset, array.Count);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
foreach (var memory in buffer)
|
|
{
|
|
#if NETCOREAPP2_1
|
|
await stream.WriteAsync(memory);
|
|
#else
|
|
var array = memory.GetArray();
|
|
await stream.WriteAsync(array.Array, array.Offset, array.Count);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Output.Reader.AdvanceTo(buffer.End);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
error = ex;
|
|
}
|
|
finally
|
|
{
|
|
Output.Reader.Complete();
|
|
_transport.Output.Complete();
|
|
}
|
|
}
|
|
|
|
private async Task ReadInputAsync(Stream stream)
|
|
{
|
|
Exception error = null;
|
|
|
|
try
|
|
{
|
|
if (stream == null)
|
|
{
|
|
// REVIEW: Do we need an exception here?
|
|
return;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
|
|
var outputBuffer = Input.Writer.GetMemory(MinAllocBufferSize);
|
|
|
|
try
|
|
{
|
|
#if NETCOREAPP2_1
|
|
var bytesRead = await stream.ReadAsync(outputBuffer);
|
|
#else
|
|
var array = outputBuffer.GetArray();
|
|
var bytesRead = await stream.ReadAsync(array.Array, array.Offset, array.Count);
|
|
#endif
|
|
Input.Writer.Advance(bytesRead);
|
|
|
|
if (bytesRead == 0)
|
|
{
|
|
// FIN
|
|
break;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Input.Writer.Commit();
|
|
}
|
|
|
|
var result = await Input.Writer.FlushAsync();
|
|
|
|
if (result.IsCompleted)
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Don't rethrow the exception. It should be handled by the Pipeline consumer.
|
|
error = ex;
|
|
}
|
|
finally
|
|
{
|
|
Input.Writer.Complete(error);
|
|
// The application could have ended the input pipe so complete
|
|
// the transport pipe as well
|
|
_transport.Input.Complete();
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
}
|
|
}
|