3
3
4
4
using System ;
5
5
using System . Buffers ;
6
+ using System . Diagnostics ;
6
7
using System . IO ;
7
8
using System . IO . Pipelines ;
9
+ using System . Threading ;
8
10
using System . Threading . Tasks ;
9
11
using Microsoft . AspNetCore . Connections ;
10
12
using Microsoft . AspNetCore . Http ;
@@ -27,6 +29,8 @@ protected Http1MessageBody(Http1Connection context)
27
29
28
30
private async Task PumpAsync ( )
29
31
{
32
+ Debug . Assert ( ! RequestUpgrade , "Upgraded connections should never use this code path!" ) ;
33
+
30
34
Exception error = null ;
31
35
32
36
try
@@ -81,14 +85,6 @@ private async Task PumpAsync()
81
85
// closing the connection without a response as expected.
82
86
_context . OnInputOrOutputCompleted ( ) ;
83
87
84
- // Treat any FIN from an upgraded request as expected.
85
- // It's up to higher-level consumer (i.e. WebSocket middleware) to determine
86
- // if the end is actually expected based on higher-level framing.
87
- if ( RequestUpgrade )
88
- {
89
- break ;
90
- }
91
-
92
88
BadHttpRequestException . Throw ( RequestRejectionReason . UnexpectedEndOfRequestContent ) ;
93
89
}
94
90
}
@@ -291,6 +287,10 @@ public static MessageBody For(
291
287
return keepAlive ? MessageBody . ZeroContentLengthKeepAlive : MessageBody . ZeroContentLengthClose ;
292
288
}
293
289
290
+ /// <summary>
291
+ /// The upgrade stream uses the raw connection stream instead of going through the RequestBodyPipe. This
292
+ /// removes the redundant copy from the transport pipe to the body pipe.
293
+ /// </summary>
294
294
private class ForUpgrade : Http1MessageBody
295
295
{
296
296
public ForUpgrade ( Http1Connection context )
@@ -299,14 +299,87 @@ public ForUpgrade(Http1Connection context)
299
299
RequestUpgrade = true ;
300
300
}
301
301
302
+ // This returns IsEmpty so we can avoid draining the body (since it's basically an endless stream)
302
303
public override bool IsEmpty => true ;
303
304
304
- protected override bool Read ( ReadOnlySequence < byte > readableBuffer , PipeWriter writableBuffer , out SequencePosition consumed , out SequencePosition examined )
305
+ public override async Task CopyToAsync ( Stream destination , CancellationToken cancellationToken = default )
305
306
{
306
- Copy ( readableBuffer , writableBuffer ) ;
307
- consumed = readableBuffer . End ;
308
- examined = readableBuffer . End ;
309
- return false ;
307
+ while ( true )
308
+ {
309
+ var result = await _context . Input . ReadAsync ( cancellationToken ) ;
310
+ var readableBuffer = result . Buffer ;
311
+ var readableBufferLength = readableBuffer . Length ;
312
+
313
+ try
314
+ {
315
+ if ( ! readableBuffer . IsEmpty )
316
+ {
317
+ foreach ( var memory in readableBuffer )
318
+ {
319
+ // REVIEW: This *could* be slower if 2 things are true
320
+ // - The WriteAsync(ReadOnlyMemory<byte>) isn't overridden on the destination
321
+ // - We change the Kestrel Memory Pool to not use pinned arrays but instead use native memory
322
+ await destination . WriteAsync ( memory , cancellationToken ) ;
323
+ }
324
+ }
325
+
326
+ if ( result . IsCompleted )
327
+ {
328
+ return ;
329
+ }
330
+ }
331
+ finally
332
+ {
333
+ _context . Input . AdvanceTo ( readableBuffer . End ) ;
334
+ }
335
+ }
336
+ }
337
+
338
+ public override async ValueTask < int > ReadAsync ( Memory < byte > buffer , CancellationToken cancellationToken = default )
339
+ {
340
+ while ( true )
341
+ {
342
+ var result = await _context . Input . ReadAsync ( cancellationToken ) ;
343
+ var readableBuffer = result . Buffer ;
344
+ var readableBufferLength = readableBuffer . Length ;
345
+
346
+ var consumed = readableBuffer . End ;
347
+ var actual = 0 ;
348
+
349
+ try
350
+ {
351
+ if ( readableBufferLength != 0 )
352
+ {
353
+ // buffer.Length is int
354
+ actual = ( int ) Math . Min ( readableBufferLength , buffer . Length ) ;
355
+
356
+ var slice = actual == readableBufferLength ? readableBuffer : readableBuffer . Slice ( 0 , actual ) ;
357
+ consumed = slice . End ;
358
+ slice . CopyTo ( buffer . Span ) ;
359
+
360
+ return actual ;
361
+ }
362
+
363
+ if ( result . IsCompleted )
364
+ {
365
+ return 0 ;
366
+ }
367
+ }
368
+ finally
369
+ {
370
+ _context . Input . AdvanceTo ( consumed ) ;
371
+ }
372
+ }
373
+ }
374
+
375
+ public override Task ConsumeAsync ( )
376
+ {
377
+ return Task . CompletedTask ;
378
+ }
379
+
380
+ public override Task StopAsync ( )
381
+ {
382
+ return Task . CompletedTask ;
310
383
}
311
384
}
312
385
0 commit comments