@@ -29,6 +29,17 @@ public partial class Frame : FrameContext, IFrameControl
29
29
private static readonly ArraySegment < byte > _emptyData = new ArraySegment < byte > ( new byte [ 0 ] ) ;
30
30
private static readonly byte [ ] _hex = Encoding . ASCII . GetBytes ( "0123456789abcdef" ) ;
31
31
32
+ private static readonly byte [ ] _bytesConnectionClose = Encoding . ASCII . GetBytes ( "\r \n Connection: close" ) ;
33
+ private static readonly byte [ ] _bytesConnectionKeepAlive = Encoding . ASCII . GetBytes ( "\r \n Connection: keep-alive" ) ;
34
+ private static readonly byte [ ] _bytesTransferEncodingChunked = Encoding . ASCII . GetBytes ( "\r \n Transfer-Encoding: chunked" ) ;
35
+ private static readonly byte [ ] _bytesHttpVersion1_0 = Encoding . ASCII . GetBytes ( "HTTP/1.0 " ) ;
36
+ private static readonly byte [ ] _bytesHttpVersion1_1 = Encoding . ASCII . GetBytes ( "HTTP/1.1 " ) ;
37
+ private static readonly byte [ ] _bytesContentLengthZero = Encoding . ASCII . GetBytes ( "\r \n Content-Length: 0" ) ;
38
+ private static readonly byte [ ] _bytesSpace = Encoding . ASCII . GetBytes ( " " ) ;
39
+ private static readonly byte [ ] _bytesServer = Encoding . ASCII . GetBytes ( "\r \n Server: Kestrel" ) ;
40
+ private static readonly byte [ ] _bytesDate = Encoding . ASCII . GetBytes ( "Date: " ) ;
41
+ private static readonly byte [ ] _bytesEndHeaders = Encoding . ASCII . GetBytes ( "\r \n \r \n " ) ;
42
+
32
43
private readonly object _onStartingSync = new Object ( ) ;
33
44
private readonly object _onCompletedSync = new Object ( ) ;
34
45
private readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders ( ) ;
@@ -53,6 +64,8 @@ public partial class Frame : FrameContext, IFrameControl
53
64
private bool _autoChunk ;
54
65
private Exception _applicationException ;
55
66
67
+ private HttpVersionType _httpVersion ;
68
+
56
69
private readonly IPEndPoint _localEndPoint ;
57
70
private readonly IPEndPoint _remoteEndPoint ;
58
71
private readonly Action < IFeatureCollection > _prepareRequest ;
@@ -81,7 +94,37 @@ public Frame(ConnectionContext context,
81
94
public string RequestUri { get ; set ; }
82
95
public string Path { get ; set ; }
83
96
public string QueryString { get ; set ; }
84
- public string HttpVersion { get ; set ; }
97
+ public string HttpVersion
98
+ {
99
+ get
100
+ {
101
+ if ( _httpVersion == HttpVersionType . Http1_1 )
102
+ {
103
+ return "HTTP/1.1" ;
104
+ }
105
+ if ( _httpVersion == HttpVersionType . Http1_0 )
106
+ {
107
+ return "HTTP/1.0" ;
108
+ }
109
+ return "" ;
110
+ }
111
+ set
112
+ {
113
+ if ( value == "HTTP/1.1" )
114
+ {
115
+ _httpVersion = HttpVersionType . Http1_1 ;
116
+ }
117
+ else if ( value == "HTTP/1.0" )
118
+ {
119
+ _httpVersion = HttpVersionType . Http1_0 ;
120
+ }
121
+ else
122
+ {
123
+ _httpVersion = HttpVersionType . Unknown ;
124
+ }
125
+ }
126
+ }
127
+
85
128
public IHeaderDictionary RequestHeaders { get ; set ; }
86
129
public Stream RequestBody { get ; set ; }
87
130
@@ -118,7 +161,7 @@ public void Reset()
118
161
RequestUri = null ;
119
162
Path = null ;
120
163
QueryString = null ;
121
- HttpVersion = null ;
164
+ _httpVersion = HttpVersionType . Unknown ;
122
165
RequestHeaders = _requestHeaders ;
123
166
RequestBody = null ;
124
167
StatusCode = 200 ;
@@ -151,8 +194,12 @@ public void Reset()
151
194
public void ResetResponseHeaders ( )
152
195
{
153
196
_responseHeaders . Reset ( ) ;
154
- _responseHeaders . HeaderServer = "Kestrel" ;
155
- _responseHeaders . HeaderDate = DateHeaderValueManager . GetDateHeaderValue ( ) ;
197
+ _responseHeaders . SetRawDate (
198
+ DateHeaderValueManager . GetDateHeaderValue ( ) ,
199
+ DateHeaderValueManager . GetDateHeaderValueBytes ( ) ) ;
200
+ _responseHeaders . SetRawServer (
201
+ "Kestrel" ,
202
+ _bytesServer ) ;
156
203
}
157
204
158
205
/// <summary>
@@ -502,7 +549,7 @@ public void ProduceContinue()
502
549
if ( _responseStarted ) return ;
503
550
504
551
StringValues expect ;
505
- if ( HttpVersion . Equals ( "HTTP/1.1" ) &&
552
+ if ( _httpVersion == HttpVersionType . Http1_1 &&
506
553
RequestHeaders . TryGetValue ( "Expect" , out expect ) &&
507
554
( expect . FirstOrDefault ( ) ?? "" ) . Equals ( "100-continue" , StringComparison . OrdinalIgnoreCase ) )
508
555
{
@@ -526,19 +573,14 @@ public async Task ProduceStartAndFireOnStarting(bool immediate = true)
526
573
await ProduceStart ( immediate , appCompleted : false ) ;
527
574
}
528
575
529
- private async Task ProduceStart ( bool immediate , bool appCompleted )
576
+ private Task ProduceStart ( bool immediate , bool appCompleted )
530
577
{
531
- if ( _responseStarted ) return ;
578
+ if ( _responseStarted ) return TaskUtilities . CompletedTask ;
532
579
_responseStarted = true ;
533
580
534
- var status = ReasonPhrases . ToStatus ( StatusCode , ReasonPhrase ) ;
535
-
536
- var responseHeader = CreateResponseHeader ( status , appCompleted ) ;
581
+ var statusBytes = ReasonPhrases . ToStatusBytes ( StatusCode , ReasonPhrase ) ;
537
582
538
- using ( responseHeader . Item2 )
539
- {
540
- await SocketOutput . WriteAsync ( responseHeader . Item1 , immediate : immediate ) ;
541
- }
583
+ return CreateResponseHeader ( statusBytes , appCompleted , immediate ) ;
542
584
}
543
585
544
586
private async Task ProduceEnd ( )
@@ -557,7 +599,7 @@ private async Task ProduceEnd()
557
599
ReasonPhrase = null ;
558
600
559
601
ResetResponseHeaders ( ) ;
560
- _responseHeaders . HeaderContentLength = "0" ;
602
+ _responseHeaders . SetRawContentLength ( "0" , _bytesContentLengthZero ) ;
561
603
}
562
604
}
563
605
@@ -576,58 +618,26 @@ private async Task ProduceEnd()
576
618
}
577
619
}
578
620
579
- private Tuple < ArraySegment < byte > , IDisposable > CreateResponseHeader (
580
- string status ,
581
- bool appCompleted )
621
+ private Task CreateResponseHeader (
622
+ byte [ ] statusBytes ,
623
+ bool appCompleted ,
624
+ bool immediate )
582
625
{
583
- var writer = new MemoryPoolTextWriter ( Memory ) ;
584
- writer . Write ( HttpVersion ) ;
585
- writer . Write ( ' ' ) ;
586
- writer . Write ( status ) ;
587
- writer . Write ( '\r ' ) ;
588
- writer . Write ( '\n ' ) ;
589
-
590
- var hasConnection = false ;
591
- var hasTransferEncoding = false ;
592
- var hasContentLength = false ;
593
-
594
- foreach ( var header in _responseHeaders )
626
+ var memoryBlock = Memory2 . Lease ( ) ;
627
+ var begin = memoryBlock . GetIterator ( ) ;
628
+ var end = begin ;
629
+ if ( _keepAlive )
595
630
{
596
- var isConnection = false ;
597
- if ( ! hasConnection &&
598
- string . Equals ( header . Key , "Connection" , StringComparison . OrdinalIgnoreCase ) )
599
- {
600
- hasConnection = isConnection = true ;
601
- }
602
- else if ( ! hasTransferEncoding &&
603
- string . Equals ( header . Key , "Transfer-Encoding" , StringComparison . OrdinalIgnoreCase ) )
631
+ foreach ( var connectionValue in _responseHeaders . HeaderConnection )
604
632
{
605
- hasTransferEncoding = true ;
606
- }
607
- else if ( ! hasContentLength &&
608
- string . Equals ( header . Key , "Content-Length" , StringComparison . OrdinalIgnoreCase ) )
609
- {
610
- hasContentLength = true ;
611
- }
612
-
613
- foreach ( var value in header . Value )
614
- {
615
- writer . Write ( header . Key ) ;
616
- writer . Write ( ':' ) ;
617
- writer . Write ( ' ' ) ;
618
- writer . Write ( value ) ;
619
- writer . Write ( '\r ' ) ;
620
- writer . Write ( '\n ' ) ;
621
-
622
- if ( isConnection && value . IndexOf ( "close" , StringComparison . OrdinalIgnoreCase ) != - 1 )
633
+ if ( connectionValue . IndexOf ( "close" , StringComparison . OrdinalIgnoreCase ) != - 1 )
623
634
{
624
635
_keepAlive = false ;
625
636
}
626
637
}
627
-
628
638
}
629
639
630
- if ( _keepAlive && ! hasTransferEncoding && ! hasContentLength )
640
+ if ( _keepAlive && ! _responseHeaders . HasTransferEncoding && ! _responseHeaders . HasContentLength )
631
641
{
632
642
if ( appCompleted )
633
643
{
@@ -637,15 +647,15 @@ private Tuple<ArraySegment<byte>, IDisposable> CreateResponseHeader(
637
647
{
638
648
// Since the app has completed and we are only now generating
639
649
// the headers we can safely set the Content-Length to 0.
640
- writer . Write ( "Content-Length: 0 \r \n " ) ;
650
+ _responseHeaders . SetRawContentLength ( "0" , _bytesContentLengthZero ) ;
641
651
}
642
652
}
643
653
else
644
654
{
645
- if ( HttpVersion == "HTTP/1.1" )
655
+ if ( _httpVersion == HttpVersionType . Http1_1 )
646
656
{
647
657
_autoChunk = true ;
648
- writer . Write ( "Transfer-Encoding: chunked\r \n " ) ;
658
+ _responseHeaders . SetRawTransferEncoding ( " chunked" , _bytesTransferEncodingChunked ) ;
649
659
}
650
660
else
651
661
{
@@ -654,21 +664,58 @@ private Tuple<ArraySegment<byte>, IDisposable> CreateResponseHeader(
654
664
}
655
665
}
656
666
657
- if ( _keepAlive == false && hasConnection == false && HttpVersion == "HTTP/1.1" )
667
+ if ( _keepAlive == false && _responseHeaders . HasConnection == false && _httpVersion == HttpVersionType . Http1_1 )
658
668
{
659
- writer . Write ( "Connection: close\r \n \r \n " ) ;
669
+ _responseHeaders . SetRawConnection ( " close" , _bytesConnectionClose ) ;
660
670
}
661
- else if ( _keepAlive && hasConnection == false && HttpVersion == "HTTP/1.0" )
671
+ else if ( _keepAlive && _responseHeaders . HasConnection == false && _httpVersion == HttpVersionType . Http1_0 )
662
672
{
663
- writer . Write ( "Connection: keep-alive\r \n \r \n " ) ;
673
+ _responseHeaders . SetRawConnection ( "keep-alive" , _bytesConnectionKeepAlive ) ;
674
+ }
675
+
676
+ end . CopyFrom ( _httpVersion == HttpVersionType . Http1_1 ? _bytesHttpVersion1_1 : _bytesHttpVersion1_0 ) ;
677
+ end . CopyFrom ( statusBytes ) ;
678
+ _responseHeaders . CopyTo ( ref end ) ;
679
+ end . CopyFrom ( _bytesEndHeaders , 0 , _bytesEndHeaders . Length ) ;
680
+
681
+ // TODO: change this to SocketOutput.ProduceStart/ProduceComplete once that change is made
682
+ var scan = begin . Block ;
683
+ while ( scan . Next != null )
684
+ {
685
+ if ( scan . Start != scan . End )
686
+ {
687
+ SocketOutput . WriteAsync (
688
+ new ArraySegment < byte > ( scan . Array , scan . Start , scan . End - scan . Start ) ,
689
+ false ) ;
690
+ }
691
+ var next = scan . Next ;
692
+ Memory2 . Return ( scan ) ;
693
+ scan = next ;
694
+ }
695
+ var writeTask = SocketOutput . WriteAsync (
696
+ new ArraySegment < byte > ( scan . Array , scan . Start , scan . End - scan . Start ) ,
697
+ immediate ) ;
698
+
699
+ if ( writeTask . IsCompleted )
700
+ {
701
+ Memory2 . Return ( scan ) ;
702
+ return TaskUtilities . CompletedTask ;
664
703
}
665
704
else
666
705
{
667
- writer . Write ( '\r ' ) ;
668
- writer . Write ( '\n ' ) ;
706
+ return writeTask . ContinueWith (
707
+ ( t , o ) =>
708
+ {
709
+ var mb = ( MemoryPoolBlock2 ) o ;
710
+ mb . Pool . Return ( mb ) ;
711
+
712
+ if ( t . IsFaulted )
713
+ {
714
+ throw t . Exception ;
715
+ }
716
+ } ,
717
+ scan ) ;
669
718
}
670
- writer . Flush ( ) ;
671
- return new Tuple < ArraySegment < byte > , IDisposable > ( writer . Buffer , writer ) ;
672
719
}
673
720
674
721
private bool TakeStartLine ( SocketInput input )
@@ -877,5 +924,12 @@ private void ReportApplicationError(Exception ex)
877
924
_applicationException = ex ;
878
925
Log . ApplicationError ( ex ) ;
879
926
}
927
+
928
+ private enum HttpVersionType
929
+ {
930
+ Unknown = - 1 ,
931
+ Http1_0 = 0 ,
932
+ Http1_1 = 1
933
+ }
880
934
}
881
935
}
0 commit comments