Skip to content

Commit f041d49

Browse files
authored
Zero byte reads support for FileBufferingReadStream #41287 (#41309)
1 parent cf513d3 commit f041d49

File tree

2 files changed

+111
-2
lines changed

2 files changed

+111
-2
lines changed

src/Http/WebUtilities/src/FileBufferingReadStream.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,8 @@ public override int Read(Span<byte> buffer)
313313
{
314314
_buffer.Write(buffer.Slice(0, read));
315315
}
316-
else
316+
// Allow zero-byte reads
317+
else if (buffer.Length > 0)
317318
{
318319
_completelyBuffered = true;
319320
}
@@ -388,7 +389,8 @@ public override async ValueTask<int> ReadAsync(Memory<byte> buffer, Cancellation
388389
{
389390
await _buffer.WriteAsync(buffer.Slice(0, read), cancellationToken);
390391
}
391-
else
392+
// Allow zero-byte reads
393+
else if (buffer.Length > 0)
392394
{
393395
_completelyBuffered = true;
394396
}

src/Http/WebUtilities/test/FileBufferingReadStreamTests.cs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,39 @@ public void FileBufferingReadStream_Properties_ExpectedValues()
3636
Assert.True(inner.CanSeek);
3737
}
3838

39+
[Fact]
40+
public void FileBufferingReadStream_Sync0ByteReadUnderThreshold_DoesntCreateFile()
41+
{
42+
var inner = MakeStream(1024);
43+
using (var stream = new FileBufferingReadStream(inner, 1024 * 2, null, Directory.GetCurrentDirectory()))
44+
{
45+
var bytes = new byte[1000];
46+
var read0 = stream.Read(bytes, 0, 0);
47+
Assert.Equal(0, read0);
48+
Assert.Equal(read0, stream.Length);
49+
Assert.Equal(read0, stream.Position);
50+
Assert.True(stream.InMemory);
51+
Assert.Null(stream.TempFileName);
52+
53+
var read1 = stream.Read(bytes, 0, bytes.Length);
54+
Assert.Equal(bytes.Length, read1);
55+
Assert.Equal(read0 + read1, stream.Length);
56+
Assert.Equal(read0 + read1, stream.Position);
57+
Assert.True(stream.InMemory);
58+
Assert.Null(stream.TempFileName);
59+
60+
var read2 = stream.Read(bytes, 0, bytes.Length);
61+
Assert.Equal(inner.Length - read0 - read1, read2);
62+
Assert.Equal(read0 + read1 + read2, stream.Length);
63+
Assert.Equal(read0 + read1 + read2, stream.Position);
64+
Assert.True(stream.InMemory);
65+
Assert.Null(stream.TempFileName);
66+
67+
var read3 = stream.Read(bytes, 0, bytes.Length);
68+
Assert.Equal(0, read3);
69+
}
70+
}
71+
3972
[Fact]
4073
public void FileBufferingReadStream_SyncReadUnderThreshold_DoesntCreateFile()
4174
{
@@ -165,6 +198,39 @@ public void FileBufferingReadStream_SyncReadWithOnDiskLimit_EnforcesLimit()
165198

166199
///////////////////
167200

201+
[Fact]
202+
public async Task FileBufferingReadStream_Async0ByteReadUnderThreshold_DoesntCreateFile()
203+
{
204+
var inner = MakeStream(1024);
205+
using (var stream = new FileBufferingReadStream(inner, 1024 * 2, null, Directory.GetCurrentDirectory()))
206+
{
207+
var bytes = new byte[1000];
208+
var read0 = await stream.ReadAsync(bytes, 0, 0);
209+
Assert.Equal(0, read0);
210+
Assert.Equal(read0, stream.Length);
211+
Assert.Equal(read0, stream.Position);
212+
Assert.True(stream.InMemory);
213+
Assert.Null(stream.TempFileName);
214+
215+
var read1 = await stream.ReadAsync(bytes, 0, bytes.Length);
216+
Assert.Equal(bytes.Length, read1);
217+
Assert.Equal(read0 + read1, stream.Length);
218+
Assert.Equal(read0 + read1, stream.Position);
219+
Assert.True(stream.InMemory);
220+
Assert.Null(stream.TempFileName);
221+
222+
var read2 = await stream.ReadAsync(bytes, 0, bytes.Length);
223+
Assert.Equal(inner.Length - read0 - read1, read2);
224+
Assert.Equal(read0 + read1 + read2, stream.Length);
225+
Assert.Equal(read0 + read1 + read2, stream.Position);
226+
Assert.True(stream.InMemory);
227+
Assert.Null(stream.TempFileName);
228+
229+
var read3 = await stream.ReadAsync(bytes, 0, bytes.Length);
230+
Assert.Equal(0, read3);
231+
}
232+
}
233+
168234
[Fact]
169235
public async Task FileBufferingReadStream_AsyncReadUnderThreshold_DoesntCreateFile()
170236
{
@@ -237,6 +303,47 @@ public async Task FileBufferingReadStream_AsyncReadOverThreshold_CreatesFile()
237303
Assert.False(File.Exists(tempFileName));
238304
}
239305

306+
[Fact]
307+
public async Task FileBufferingReadStream_Async0ByteReadAfterBuffering_ReadsFromFile()
308+
{
309+
var inner = MakeStream(1024 * 2);
310+
string tempFileName;
311+
using (var stream = new FileBufferingReadStream(inner, 1024, null, GetCurrentDirectory()))
312+
{
313+
await stream.DrainAsync(default);
314+
stream.Position = 0;
315+
Assert.Equal(inner.Length, stream.Length);
316+
Assert.Equal(0, stream.Position);
317+
Assert.False(stream.InMemory);
318+
Assert.NotNull(stream.TempFileName);
319+
tempFileName = stream.TempFileName!;
320+
Assert.True(File.Exists(tempFileName));
321+
322+
var bytes = new byte[1000];
323+
var read0 = await stream.ReadAsync(bytes, 0, 0);
324+
Assert.Equal(0, read0);
325+
Assert.Equal(read0, stream.Position);
326+
327+
var read1 = await stream.ReadAsync(bytes, 0, bytes.Length);
328+
Assert.Equal(bytes.Length, read1);
329+
Assert.Equal(read0 + read1, stream.Position);
330+
331+
var read2 = await stream.ReadAsync(bytes, 0, bytes.Length);
332+
Assert.Equal(bytes.Length, read2);
333+
Assert.Equal(read0 + read1 + read2, stream.Position);
334+
335+
var read3 = await stream.ReadAsync(bytes, 0, bytes.Length);
336+
Assert.Equal(inner.Length - read0 - read1 - read2, read3);
337+
Assert.Equal(read0 + read1 + read2 + read3, stream.Length);
338+
Assert.Equal(read0 + read1 + read2 + read3, stream.Position);
339+
340+
var read4 = await stream.ReadAsync(bytes, 0, bytes.Length);
341+
Assert.Equal(0, read4);
342+
}
343+
344+
Assert.False(File.Exists(tempFileName));
345+
}
346+
240347
[Fact]
241348
public async Task FileBufferingReadStream_AsyncReadWithInMemoryLimit_EnforcesLimit()
242349
{

0 commit comments

Comments
 (0)