diff --git a/src/MockHttp/IO/RateLimitedStream.cs b/src/MockHttp/IO/RateLimitedStream.cs
index 292ea3fa..4c82de49 100644
--- a/src/MockHttp/IO/RateLimitedStream.cs
+++ b/src/MockHttp/IO/RateLimitedStream.cs
@@ -16,25 +16,28 @@ namespace MockHttp.IO;
///
public class RateLimitedStream : Stream
{
- private const int SampleRate = 100; // How often to take samples (per sec).
- private static readonly TimeSpan ThrottleInterval = TimeSpan.FromMilliseconds(1000D / SampleRate);
internal const int MinBitRate = 128;
+ private const int SampleRate = 100; // How often to take samples (per sec).
private const int MaxBufferSize = 2 << 15; // 128KB
+ private static readonly TimeSpan ThrottleInterval = TimeSpan.FromMilliseconds(1000D / SampleRate);
- private long _totalBytesRead;
-
- private readonly Stopwatch _stopwatch;
private readonly Stream _actualStream;
private readonly int _byteRate;
+ private readonly bool _leaveOpen;
+ private readonly Stopwatch _stopwatch;
+
+ private long _totalBytesRead;
///
/// Initializes a new instance of the .
///
/// The actual stream to wrap.
/// The bit rate to simulate.
+ /// to leave the open on close/disposal.
/// Thrown when is null.
- /// Thrown when the stream is not readable or the bit rate is less than 128.
- public RateLimitedStream(Stream actualStream, int bitRate)
+ /// Thrown when the stream is not readable.
+ /// Thrown when the bit rate is less than 128.
+ public RateLimitedStream(Stream actualStream, int bitRate, bool leaveOpen = false)
{
_actualStream = actualStream ?? throw new ArgumentNullException(nameof(actualStream));
if (!_actualStream.CanRead)
@@ -47,10 +50,30 @@ public RateLimitedStream(Stream actualStream, int bitRate)
throw new ArgumentOutOfRangeException(nameof(bitRate), $"Bit rate must be higher than or equal to {MinBitRate}.");
}
+ _leaveOpen = leaveOpen;
_byteRate = bitRate / 8; // We are computing bytes transferred.
_stopwatch = new Stopwatch();
}
+ ///
+ public override bool CanRead => _actualStream.CanRead;
+
+ ///
+ public override bool CanSeek => _actualStream.CanSeek;
+
+ ///
+ public override bool CanWrite => false;
+
+ ///
+ public override long Length => _actualStream.Length;
+
+ ///
+ public override long Position
+ {
+ get => _actualStream.Position;
+ set => _actualStream.Position = value;
+ }
+
///
public override void Flush()
{
@@ -100,31 +123,15 @@ public override void Write(byte[] buffer, int offset, int count)
throw new NotSupportedException("Modifying stream is not supported.");
}
- ///
- public override bool CanRead => _actualStream.CanRead;
-
- ///
- public override bool CanSeek => _actualStream.CanSeek;
-
- ///
- public override bool CanWrite => false;
-
- ///
- public override long Length => _actualStream.Length;
-
- ///
- public override long Position
- {
- get => _actualStream.Position;
- set => _actualStream.Position = value;
- }
-
///
protected override void Dispose(bool disposing)
{
try
{
- _actualStream.Dispose();
+ if (!_leaveOpen)
+ {
+ _actualStream.Dispose();
+ }
}
finally
{
diff --git a/test/MockHttp.Tests/RateLimitedStreamTests.cs b/test/MockHttp.Tests/RateLimitedStreamTests.cs
index 0d941c67..2b694d8c 100644
--- a/test/MockHttp.Tests/RateLimitedStreamTests.cs
+++ b/test/MockHttp.Tests/RateLimitedStreamTests.cs
@@ -190,6 +190,20 @@ public void When_disposing_it_should_dispose_underlying()
streamStub.Received(1).Close();
}
+ [Fact]
+ public void Keeps_actual_stream_open_if_desired()
+ {
+ using MemoryStream streamStub = Substitute.For();
+ streamStub.CanRead.Returns(true);
+ var sut = new RateLimitedStream(streamStub, 1024, true);
+
+ // Act
+ sut.Dispose();
+
+ // Assert
+ streamStub.DidNotReceive().Close();
+ }
+
public void Dispose()
{
// ReSharper disable ConditionalAccessQualifierIsNonNullableAccordingToAPIContract