3
3
4
4
using System ;
5
5
using System . Buffers ;
6
+ using System . Collections . Generic ;
6
7
using System . IO ;
7
8
using System . Threading ;
8
9
using System . Threading . Tasks ;
9
10
using Microsoft . AspNetCore . Connections ;
10
11
using Microsoft . AspNetCore . Http . Features ;
12
+ using Microsoft . AspNetCore . Server . Kestrel . Core . Internal . Infrastructure ;
11
13
12
14
namespace Microsoft . AspNetCore . Server . Kestrel . Core . Internal . Http
13
15
{
14
16
internal sealed class HttpRequestStream : Stream
15
17
{
16
18
private readonly HttpRequestPipeReader _pipeReader ;
17
19
private readonly IHttpBodyControlFeature _bodyControl ;
20
+ private AsyncEnumerableReader _asyncReader ;
18
21
19
22
public HttpRequestStream ( IHttpBodyControlFeature bodyControl , HttpRequestPipeReader pipeReader )
20
23
{
@@ -44,12 +47,26 @@ public override int WriteTimeout
44
47
45
48
public override ValueTask < int > ReadAsync ( Memory < byte > destination , CancellationToken cancellationToken = default )
46
49
{
47
- return ReadAsyncWrapper ( destination , cancellationToken ) ;
50
+ try
51
+ {
52
+ return ReadAsyncInternal ( destination , cancellationToken ) ;
53
+ }
54
+ catch ( ConnectionAbortedException ex )
55
+ {
56
+ throw new TaskCanceledException ( "The request was aborted" , ex ) ;
57
+ }
48
58
}
49
59
50
60
public override Task < int > ReadAsync ( byte [ ] buffer , int offset , int count , CancellationToken cancellationToken )
51
61
{
52
- return ReadAsyncWrapper ( new Memory < byte > ( buffer , offset , count ) , cancellationToken ) . AsTask ( ) ;
62
+ try
63
+ {
64
+ return ReadAsyncInternal ( new Memory < byte > ( buffer , offset , count ) , cancellationToken ) . AsTask ( ) ;
65
+ }
66
+ catch ( ConnectionAbortedException ex )
67
+ {
68
+ throw new TaskCanceledException ( "The request was aborted" , ex ) ;
69
+ }
53
70
}
54
71
55
72
public override int Read ( byte [ ] buffer , int offset , int count )
@@ -127,23 +144,78 @@ private Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationTo
127
144
return tcs . Task ;
128
145
}
129
146
130
- private ValueTask < int > ReadAsyncWrapper ( Memory < byte > destination , CancellationToken cancellationToken )
147
+ private ValueTask < int > ReadAsyncInternal ( Memory < byte > buffer , CancellationToken cancellationToken )
131
148
{
149
+ if ( _asyncReader ? . InProgress ?? false )
150
+ {
151
+ // Throw if there are overlapping reads; throwing unwrapped as it suggests last read was not awaited
152
+ // so we surface it directly rather than wrapped in a Task (as this one will likely also not be awaited).
153
+ throw new InvalidOperationException ( "Concurrent reads are not supported; await the " + nameof ( ValueTask < int > ) + " before starting next read." ) ;
154
+ }
155
+
132
156
try
133
157
{
134
- return ReadAsyncInternal ( destination , cancellationToken ) ;
158
+ while ( true )
159
+ {
160
+ if ( ! _pipeReader . TryRead ( out var result ) )
161
+ {
162
+ break ;
163
+ }
164
+
165
+ if ( result . IsCanceled )
166
+ {
167
+ throw new OperationCanceledException ( "The read was canceled" ) ;
168
+ }
169
+
170
+ var readableBuffer = result . Buffer ;
171
+ var readableBufferLength = readableBuffer . Length ;
172
+
173
+ var consumed = readableBuffer . End ;
174
+ var actual = 0 ;
175
+ try
176
+ {
177
+ if ( readableBufferLength != 0 )
178
+ {
179
+ actual = ( int ) Math . Min ( readableBufferLength , buffer . Length ) ;
180
+
181
+ var slice = actual == readableBufferLength ? readableBuffer : readableBuffer . Slice ( 0 , actual ) ;
182
+ consumed = slice . End ;
183
+ slice . CopyTo ( buffer . Span ) ;
184
+
185
+ return new ValueTask < int > ( actual ) ;
186
+ }
187
+
188
+ if ( result . IsCompleted )
189
+ {
190
+ return new ValueTask < int > ( 0 ) ;
191
+ }
192
+ }
193
+ finally
194
+ {
195
+ _pipeReader . AdvanceTo ( consumed ) ;
196
+ }
197
+ }
135
198
}
136
- catch ( ConnectionAbortedException ex )
199
+ catch ( Exception ex )
137
200
{
138
- throw new TaskCanceledException ( "The request was aborted" , ex ) ;
201
+ return new ValueTask < int > ( Task . FromException < int > ( ex ) ) ;
202
+ }
203
+
204
+ var asyncReader = _asyncReader ;
205
+ if ( asyncReader is null )
206
+ {
207
+ _asyncReader = asyncReader = new AsyncEnumerableReader ( ) ;
208
+ asyncReader . Initialize ( ReadAsyncAwaited ( asyncReader ) ) ;
139
209
}
210
+
211
+ return asyncReader . ReadAsync ( buffer , cancellationToken ) ;
140
212
}
141
213
142
- private async ValueTask < int > ReadAsyncInternal ( Memory < byte > buffer , CancellationToken cancellationToken )
214
+ private async IAsyncEnumerable < int > ReadAsyncAwaited ( AsyncEnumerableReader reader )
143
215
{
144
216
while ( true )
145
217
{
146
- var result = await _pipeReader . ReadAsync ( cancellationToken ) ;
218
+ var result = await _pipeReader . ReadAsync ( reader . CancellationToken ) ;
147
219
148
220
if ( result . IsCanceled )
149
221
{
@@ -154,30 +226,40 @@ private async ValueTask<int> ReadAsyncInternal(Memory<byte> buffer, Cancellation
154
226
var readableBufferLength = readableBuffer . Length ;
155
227
156
228
var consumed = readableBuffer . End ;
229
+ var advanced = false ;
157
230
try
158
231
{
159
232
if ( readableBufferLength != 0 )
160
233
{
161
- var actual = ( int ) Math . Min ( readableBufferLength , buffer . Length ) ;
234
+ var actual = ( int ) Math . Min ( readableBufferLength , reader . Buffer . Length ) ;
162
235
163
236
var slice = actual == readableBufferLength ? readableBuffer : readableBuffer . Slice ( 0 , actual ) ;
164
237
consumed = slice . End ;
165
- slice . CopyTo ( buffer . Span ) ;
238
+ slice . CopyTo ( reader . Buffer . Span ) ;
166
239
167
- return actual ;
240
+ // Finally blocks in enumerators aren't excuted prior to the yield return,
241
+ // so we advance here
242
+ advanced = true ;
243
+ _pipeReader . AdvanceTo ( consumed ) ;
244
+ yield return actual ;
168
245
}
169
-
170
- if ( result . IsCompleted )
246
+ else if ( result . IsCompleted )
171
247
{
172
- return 0 ;
248
+ // Finally blocks in enumerators aren't excuted prior to the yield return,
249
+ // so we advance here
250
+ advanced = true ;
251
+ _pipeReader . AdvanceTo ( consumed ) ;
252
+ yield return 0 ;
173
253
}
174
254
}
175
255
finally
176
256
{
177
- _pipeReader . AdvanceTo ( consumed ) ;
257
+ if ( ! advanced )
258
+ {
259
+ _pipeReader . AdvanceTo ( consumed ) ;
260
+ }
178
261
}
179
262
}
180
-
181
263
}
182
264
183
265
/// <inheritdoc />
0 commit comments