Skip to content

Commit 41c76ce

Browse files
authored
Zero byte reads support for EnableBuffering/FileBufferingReadStream (#41410)
1 parent fd6ea53 commit 41c76ce

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
@@ -317,7 +317,8 @@ public override int Read(Span<byte> buffer)
317317
{
318318
_buffer.Write(buffer.Slice(0, read));
319319
}
320-
else
320+
// Allow zero-byte reads
321+
else if (buffer.Length > 0)
321322
{
322323
_completelyBuffered = true;
323324
}
@@ -392,7 +393,8 @@ public override async ValueTask<int> ReadAsync(Memory<byte> buffer, Cancellation
392393
{
393394
await _buffer.WriteAsync(buffer.Slice(0, read), cancellationToken);
394395
}
395-
else
396+
// Allow zero-byte reads
397+
else if (buffer.Length > 0)
396398
{
397399
_completelyBuffered = true;
398400
}

src/Http/WebUtilities/test/FileBufferingReadStreamTests.cs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,39 @@ public void FileBufferingReadStream_Properties_ExpectedValues()
3535
}
3636
}
3737

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

165198
///////////////////
166199

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

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

0 commit comments

Comments
 (0)