@@ -28,6 +28,13 @@ public partial class Frame : FrameContext, IFrameControl
28
28
private static readonly ArraySegment < byte > _emptyData = new ArraySegment < byte > ( new byte [ 0 ] ) ;
29
29
private static readonly byte [ ] _hex = Encoding . ASCII . GetBytes ( "0123456789abcdef" ) ;
30
30
31
+ private static readonly byte [ ] _bytesEndLine = Encoding . ASCII . GetBytes ( "\r \n " ) ;
32
+ private static readonly byte [ ] _bytesConnectionClose = Encoding . ASCII . GetBytes ( "Connection: close\r \n \r \n " ) ;
33
+ private static readonly byte [ ] _bytesConnectionKeepAlive = Encoding . ASCII . GetBytes ( "Connection: keep-alive\r \n \r \n " ) ;
34
+ private static readonly byte [ ] _bytesTransferEncodingChunked = Encoding . ASCII . GetBytes ( "Transfer-Encoding: chunked\r \n " ) ;
35
+ private static readonly byte [ ] _bytesContentLengthZero = Encoding . ASCII . GetBytes ( "Content-Length: 0\r \n " ) ;
36
+ private static readonly byte [ ] _bytesSpace = Encoding . ASCII . GetBytes ( " " ) ;
37
+
31
38
private readonly object _onStartingSync = new Object ( ) ;
32
39
private readonly object _onCompletedSync = new Object ( ) ;
33
40
private readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders ( ) ;
@@ -471,19 +478,14 @@ public async Task ProduceStartAndFireOnStarting(bool immediate = true)
471
478
await ProduceStart ( immediate , appCompleted : false ) ;
472
479
}
473
480
474
- private async Task ProduceStart ( bool immediate , bool appCompleted )
481
+ private Task ProduceStart ( bool immediate , bool appCompleted )
475
482
{
476
- if ( _responseStarted ) return ;
483
+ if ( _responseStarted ) return TaskUtilities . CompletedTask ;
477
484
_responseStarted = true ;
478
485
479
- var status = ReasonPhrases . ToStatus ( StatusCode , ReasonPhrase ) ;
486
+ var statusBytes = ReasonPhrases . ToStatusBytes ( StatusCode , ReasonPhrase ) ;
480
487
481
- var responseHeader = CreateResponseHeader ( status , appCompleted ) ;
482
-
483
- using ( responseHeader . Item2 )
484
- {
485
- await SocketOutput . WriteAsync ( responseHeader . Item1 , immediate : immediate ) ;
486
- }
488
+ return CreateResponseHeader ( statusBytes , appCompleted , immediate ) ;
487
489
}
488
490
489
491
private async Task ProduceEnd ( )
@@ -521,99 +523,128 @@ private async Task ProduceEnd()
521
523
}
522
524
}
523
525
524
- private Tuple < ArraySegment < byte > , IDisposable > CreateResponseHeader (
525
- string status ,
526
- bool appCompleted )
526
+ private static void OutputAsciiBlock ( string data , MemoryPoolBlock2 memoryBlock , ISocketOutput output )
527
527
{
528
- var writer = new MemoryPoolTextWriter ( Memory ) ;
529
- writer . Write ( HttpVersion ) ;
530
- writer . Write ( ' ' ) ;
531
- writer . Write ( status ) ;
532
- writer . Write ( '\r ' ) ;
533
- writer . Write ( '\n ' ) ;
534
-
535
- var hasConnection = false ;
536
- var hasTransferEncoding = false ;
537
- var hasContentLength = false ;
528
+ var end = memoryBlock . Start + memoryBlock . Data . Count ;
538
529
539
- foreach ( var header in _responseHeaders )
530
+ foreach ( var chr in data )
540
531
{
541
- var isConnection = false ;
542
- if ( ! hasConnection &&
543
- string . Equals ( header . Key , "Connection" , StringComparison . OrdinalIgnoreCase ) )
544
- {
545
- hasConnection = isConnection = true ;
546
- }
547
- else if ( ! hasTransferEncoding &&
548
- string . Equals ( header . Key , "Transfer-Encoding" , StringComparison . OrdinalIgnoreCase ) )
549
- {
550
- hasTransferEncoding = true ;
551
- }
552
- else if ( ! hasContentLength &&
553
- string . Equals ( header . Key , "Content-Length" , StringComparison . OrdinalIgnoreCase ) )
532
+ memoryBlock . Array [ memoryBlock . End ] = ( byte ) chr ;
533
+
534
+ memoryBlock . End ++ ;
535
+
536
+ if ( memoryBlock . End == end )
554
537
{
555
- hasContentLength = true ;
538
+ output . Write ( memoryBlock . Data , immediate : false ) ;
539
+ memoryBlock . End = memoryBlock . Start ;
556
540
}
541
+ }
542
+ }
557
543
558
- foreach ( var value in header . Value )
544
+ private static void OutputAsciiBlock ( byte [ ] data , MemoryPoolBlock2 memoryBlock , ISocketOutput output )
545
+ {
546
+ var offset = 0 ;
547
+ var remaining = data . Length ;
548
+ var end = memoryBlock . Start + memoryBlock . Data . Count ;
549
+
550
+ while ( remaining > 0 )
551
+ {
552
+ var blockRemaining = end - memoryBlock . End ;
553
+ var copyAmount = blockRemaining >= remaining ? remaining : blockRemaining ;
554
+ Buffer . BlockCopy ( data , offset , memoryBlock . Array , memoryBlock . End , copyAmount ) ;
555
+
556
+ memoryBlock . End += copyAmount ;
557
+ remaining -= copyAmount ;
558
+ offset += copyAmount ;
559
+
560
+ if ( memoryBlock . End == end )
559
561
{
560
- writer . Write ( header . Key ) ;
561
- writer . Write ( ':' ) ;
562
- writer . Write ( ' ' ) ;
563
- writer . Write ( value ) ;
564
- writer . Write ( '\r ' ) ;
565
- writer . Write ( '\n ' ) ;
566
-
567
- if ( isConnection && value . IndexOf ( "close" , StringComparison . OrdinalIgnoreCase ) != - 1 )
568
- {
569
- _keepAlive = false ;
570
- }
562
+ output . Write ( memoryBlock . Data , immediate : false ) ;
563
+ memoryBlock . End = memoryBlock . Start ;
571
564
}
572
-
573
565
}
566
+ }
574
567
575
- if ( _keepAlive && ! hasTransferEncoding && ! hasContentLength )
568
+ private Task CreateResponseHeader (
569
+ byte [ ] statusBytes ,
570
+ bool appCompleted ,
571
+ bool immediate )
572
+ {
573
+ var memoryBlock = Memory2 . Lease ( ) ;
574
+ try
576
575
{
577
- if ( appCompleted )
576
+ var blockRemaining = memoryBlock . Data . Count ;
577
+
578
+ OutputAsciiBlock ( HttpVersion , memoryBlock , SocketOutput ) ;
579
+ OutputAsciiBlock ( _bytesSpace , memoryBlock , SocketOutput ) ;
580
+ OutputAsciiBlock ( statusBytes , memoryBlock , SocketOutput ) ;
581
+
582
+ foreach ( var header in _responseHeaders . AsOutputEnumerable ( ) )
578
583
{
579
- // Don't set the Content-Length or Transfer-Encoding headers
580
- // automatically for HEAD requests or 101, 204, 205, 304 responses.
581
- if ( Method != "HEAD" && StatusCanHaveBody ( StatusCode ) )
584
+ foreach ( var value in header . Value )
582
585
{
583
- // Since the app has completed and we are only now generating
584
- // the headers we can safely set the Content-Length to 0.
585
- writer . Write ( "Content-Length: 0\r \n " ) ;
586
+ OutputAsciiBlock ( header . Key , memoryBlock , SocketOutput ) ;
587
+ OutputAsciiBlock ( value , memoryBlock , SocketOutput ) ;
588
+ OutputAsciiBlock ( _bytesEndLine , memoryBlock , SocketOutput ) ;
589
+
590
+ if ( _responseHeaders . HasConnection && value . IndexOf ( "close" , StringComparison . OrdinalIgnoreCase ) != - 1 )
591
+ {
592
+ _keepAlive = false ;
593
+ }
586
594
}
595
+
587
596
}
588
- else
597
+
598
+ if ( _keepAlive && ! _responseHeaders . HasTransferEncoding && ! _responseHeaders . HasContentLength )
589
599
{
590
- if ( HttpVersion == "HTTP/1.1" )
600
+ if ( appCompleted )
591
601
{
592
- _autoChunk = true ;
593
- writer . Write ( "Transfer-Encoding: chunked\r \n " ) ;
602
+ // Don't set the Content-Length or Transfer-Encoding headers
603
+ // automatically for HEAD requests or 101, 204, 205, 304 responses.
604
+ if ( Method != "HEAD" && StatusCanHaveBody ( StatusCode ) )
605
+ {
606
+ // Since the app has completed and we are only now generating
607
+ // the headers we can safely set the Content-Length to 0.
608
+ OutputAsciiBlock ( _bytesContentLengthZero , memoryBlock , SocketOutput ) ;
609
+ }
594
610
}
595
611
else
596
612
{
597
- _keepAlive = false ;
613
+ if ( HttpVersion == "HTTP/1.1" )
614
+ {
615
+ _autoChunk = true ;
616
+ OutputAsciiBlock ( _bytesTransferEncodingChunked , memoryBlock , SocketOutput ) ;
617
+ }
618
+ else
619
+ {
620
+ _keepAlive = false ;
621
+ }
598
622
}
599
623
}
600
- }
601
624
602
- if ( _keepAlive == false && hasConnection == false && HttpVersion == "HTTP/1.1" )
603
- {
604
- writer . Write ( "Connection: close\r \n \r \n " ) ;
605
- }
606
- else if ( _keepAlive && hasConnection == false && HttpVersion == "HTTP/1.0" )
607
- {
608
- writer . Write ( "Connection: keep-alive\r \n \r \n " ) ;
625
+ if ( _keepAlive == false && _responseHeaders . HasConnection == false && HttpVersion == "HTTP/1.1" )
626
+ {
627
+ OutputAsciiBlock ( _bytesConnectionClose , memoryBlock , SocketOutput ) ;
628
+ }
629
+ else if ( _keepAlive && _responseHeaders . HasConnection == false && HttpVersion == "HTTP/1.0" )
630
+ {
631
+ OutputAsciiBlock ( _bytesConnectionKeepAlive , memoryBlock , SocketOutput ) ;
632
+ }
633
+ else
634
+ {
635
+ OutputAsciiBlock ( _bytesEndLine , memoryBlock , SocketOutput ) ;
636
+ }
637
+
638
+ return SocketOutput . WriteAsync (
639
+ ( memoryBlock . Start == memoryBlock . End ) ?
640
+ default ( ArraySegment < byte > ) :
641
+ new ArraySegment < byte > ( memoryBlock . Array , memoryBlock . Start , memoryBlock . End - memoryBlock . Start ) ,
642
+ immediate ) ;
609
643
}
610
- else
644
+ finally
611
645
{
612
- writer . Write ( '\r ' ) ;
613
- writer . Write ( '\n ' ) ;
646
+ Memory2 . Return ( memoryBlock ) ;
614
647
}
615
- writer . Flush ( ) ;
616
- return new Tuple < ArraySegment < byte > , IDisposable > ( writer . Buffer , writer ) ;
617
648
}
618
649
619
650
private bool TakeStartLine ( SocketInput input )
0 commit comments