219 lines
6.5 KiB
C#
219 lines
6.5 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;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Microsoft.AspNetCore.Buffering
|
|
{
|
|
internal class BufferingWriteStream : Stream
|
|
{
|
|
private readonly Stream _innerStream;
|
|
private readonly MemoryStream _buffer = new MemoryStream();
|
|
private bool _isBuffering = true;
|
|
|
|
public BufferingWriteStream(Stream innerStream)
|
|
{
|
|
_innerStream = innerStream;
|
|
}
|
|
|
|
public override bool CanRead
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
public override bool CanSeek
|
|
{
|
|
get { return _isBuffering; }
|
|
}
|
|
|
|
public override bool CanWrite
|
|
{
|
|
get { return _innerStream.CanWrite; }
|
|
}
|
|
|
|
public override long Length
|
|
{
|
|
get
|
|
{
|
|
if (_isBuffering)
|
|
{
|
|
return _buffer.Length;
|
|
}
|
|
// May throw
|
|
return _innerStream.Length;
|
|
}
|
|
}
|
|
|
|
// Clear/Reset the buffer by setting Position, Seek, or SetLength to 0. Random access is not supported.
|
|
public override long Position
|
|
{
|
|
get
|
|
{
|
|
if (_isBuffering)
|
|
{
|
|
return _buffer.Position;
|
|
}
|
|
// May throw
|
|
return _innerStream.Position;
|
|
}
|
|
set
|
|
{
|
|
if (_isBuffering)
|
|
{
|
|
if (value != 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(value), value, nameof(Position) + " can only be set to 0.");
|
|
}
|
|
_buffer.Position = value;
|
|
_buffer.SetLength(value);
|
|
}
|
|
else
|
|
{
|
|
// May throw
|
|
_innerStream.Position = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear/Reset the buffer by setting Position, Seek, or SetLength to 0. Random access is not supported.
|
|
public override void SetLength(long value)
|
|
{
|
|
if (_isBuffering)
|
|
{
|
|
if (value != 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(value), value, nameof(Length) + " can only be set to 0.");
|
|
}
|
|
_buffer.Position = value;
|
|
_buffer.SetLength(value);
|
|
}
|
|
else
|
|
{
|
|
// May throw
|
|
_innerStream.SetLength(value);
|
|
}
|
|
}
|
|
|
|
// Clear/Reset the buffer by setting Position, Seek, or SetLength to 0. Random access is not supported.
|
|
public override long Seek(long offset, SeekOrigin origin)
|
|
{
|
|
if (_isBuffering)
|
|
{
|
|
if (origin != SeekOrigin.Begin)
|
|
{
|
|
throw new ArgumentException(nameof(origin), nameof(Seek) + " can only be set to " + nameof(SeekOrigin.Begin) + ".");
|
|
}
|
|
if (offset != 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(offset), offset, nameof(Seek) + " can only be set to 0.");
|
|
}
|
|
_buffer.SetLength(offset);
|
|
return _buffer.Seek(offset, origin);
|
|
}
|
|
// Try the inner stream instead, but this will usually fail.
|
|
return _innerStream.Seek(offset, origin);
|
|
}
|
|
|
|
internal void DisableBuffering()
|
|
{
|
|
_isBuffering = false;
|
|
if (_buffer.Length > 0)
|
|
{
|
|
Flush();
|
|
}
|
|
}
|
|
|
|
internal Task DisableBufferingAsync(CancellationToken cancellationToken)
|
|
{
|
|
_isBuffering = false;
|
|
if (_buffer.Length > 0)
|
|
{
|
|
return FlushAsync(cancellationToken);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public override void Write(byte[] buffer, int offset, int count)
|
|
{
|
|
if (_isBuffering)
|
|
{
|
|
_buffer.Write(buffer, offset, count);
|
|
}
|
|
else
|
|
{
|
|
_innerStream.Write(buffer, offset, count);
|
|
}
|
|
}
|
|
|
|
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
|
{
|
|
if (_isBuffering)
|
|
{
|
|
return _buffer.WriteAsync(buffer, offset, count, cancellationToken);
|
|
}
|
|
else
|
|
{
|
|
return _innerStream.WriteAsync(buffer, offset, count, cancellationToken);
|
|
}
|
|
}
|
|
|
|
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
|
{
|
|
if (_isBuffering)
|
|
{
|
|
return _buffer.BeginWrite(buffer, offset, count, callback, state);
|
|
}
|
|
else
|
|
{
|
|
return _innerStream.BeginWrite(buffer, offset, count, callback, state);
|
|
}
|
|
}
|
|
|
|
public override void EndWrite(IAsyncResult asyncResult)
|
|
{
|
|
if (_isBuffering)
|
|
{
|
|
_buffer.EndWrite(asyncResult);
|
|
}
|
|
else
|
|
{
|
|
_innerStream.EndWrite(asyncResult);
|
|
}
|
|
}
|
|
|
|
public override void Flush()
|
|
{
|
|
_isBuffering = false;
|
|
if (_buffer.Length > 0)
|
|
{
|
|
_buffer.Seek(0, SeekOrigin.Begin);
|
|
_buffer.CopyTo(_innerStream);
|
|
_buffer.Seek(0, SeekOrigin.Begin);
|
|
_buffer.SetLength(0);
|
|
}
|
|
_innerStream.Flush();
|
|
}
|
|
|
|
public override async Task FlushAsync(CancellationToken cancellationToken)
|
|
{
|
|
_isBuffering = false;
|
|
if (_buffer.Length > 0)
|
|
{
|
|
_buffer.Seek(0, SeekOrigin.Begin);
|
|
await _buffer.CopyToAsync(_innerStream, 1024 * 16, cancellationToken);
|
|
_buffer.Seek(0, SeekOrigin.Begin);
|
|
_buffer.SetLength(0);
|
|
}
|
|
await _innerStream.FlushAsync(cancellationToken);
|
|
}
|
|
|
|
public override int Read(byte[] buffer, int offset, int count)
|
|
{
|
|
throw new NotSupportedException("This Stream only supports Write operations.");
|
|
}
|
|
}
|
|
}
|