@@ -122,6 +122,40 @@ public async Task BodyStream_SyncEnabled_FlushSucceeds()
122
122
Assert . Equal ( contentBytes , responseBytes ) ;
123
123
}
124
124
125
+ [ Fact ]
126
+ public async Task BodyStream_ZeroByteRead_Success ( )
127
+ {
128
+ var emptyReadStarted = new TaskCompletionSource ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
129
+
130
+ var contentBytes = new byte [ ] { 32 } ;
131
+ using var host = await CreateHost ( async httpContext =>
132
+ {
133
+ await httpContext . Response . Body . WriteAsync ( contentBytes ) ;
134
+ await emptyReadStarted . Task ;
135
+ await httpContext . Response . Body . WriteAsync ( contentBytes ) ;
136
+ } ) ;
137
+
138
+ var client = host . GetTestServer ( ) . CreateClient ( ) ;
139
+ var response = await client . GetAsync ( "/" , HttpCompletionOption . ResponseHeadersRead ) ;
140
+ var stream = await response . Content . ReadAsStreamAsync ( ) ;
141
+ var bytes = new byte [ 5 ] ;
142
+ var read = await stream . ReadAsync ( bytes ) ;
143
+ Assert . Equal ( 1 , read ) ;
144
+ Assert . Equal ( contentBytes [ 0 ] , bytes [ 0 ] ) ;
145
+
146
+ // This will chain to the Memory overload, but that does less restrictive validation on the input.
147
+ // https://github.com/dotnet/aspnetcore/issues/41692#issuecomment-1248714684
148
+ var zeroByteRead = stream . ReadAsync ( Array . Empty < byte > ( ) , 0 , 0 ) ;
149
+ Assert . False ( zeroByteRead . IsCompleted ) ;
150
+ emptyReadStarted . SetResult ( ) ;
151
+ read = await zeroByteRead ;
152
+ Assert . Equal ( 0 , read ) ;
153
+
154
+ read = await stream . ReadAsync ( bytes ) ;
155
+ Assert . Equal ( 1 , read ) ;
156
+ Assert . Equal ( contentBytes [ 0 ] , bytes [ 0 ] ) ;
157
+ }
158
+
125
159
private Task < IHost > CreateHost ( RequestDelegate appDelegate )
126
160
{
127
161
return new HostBuilder ( )
0 commit comments