8
8
using Microsoft . AspNetCore . Http ;
9
9
using Microsoft . AspNetCore . Server . Kestrel . Core . Internal . Http ;
10
10
using Microsoft . Extensions . Primitives ;
11
+ using Microsoft . Net . Http . Headers ;
11
12
using Xunit ;
12
13
using static CodeGenerator . KnownHeaders ;
13
14
@@ -307,11 +308,10 @@ public void AppendThrowsWhenHeaderNameContainsNonASCIICharacters()
307
308
var headers = new HttpRequestHeaders ( ) ;
308
309
const string key = "\u0014 1\u00F3 d\0 17c" ;
309
310
310
- var encoding = Encoding . GetEncoding ( "iso-8859-1" ) ;
311
311
#pragma warning disable CS0618 // Type or member is obsolete
312
312
var exception = Assert . Throws < BadHttpRequestException > (
313
313
#pragma warning restore CS0618 // Type or member is obsolete
314
- ( ) => headers . Append ( encoding . GetBytes ( key ) , Encoding . ASCII . GetBytes ( "value" ) ) ) ;
314
+ ( ) => headers . Append ( Encoding . Latin1 . GetBytes ( key ) , Encoding . ASCII . GetBytes ( "value" ) ) ) ;
315
315
Assert . Equal ( StatusCodes . Status400BadRequest , exception . StatusCode ) ;
316
316
}
317
317
@@ -473,7 +473,7 @@ public void ValueReuseLatin1NotConfusedForUtf16AndStillRejected(bool reuseValue,
473
473
Assert . Throws < InvalidOperationException > ( ( ) =>
474
474
{
475
475
var headerName = Encoding . ASCII . GetBytes ( header . Name ) . AsSpan ( ) ;
476
- var nextSpan = Encoding . GetEncoding ( "iso-8859-1" ) . GetBytes ( headerValueUtf16Latin1CrossOver ) . AsSpan ( ) ;
476
+ var nextSpan = Encoding . Latin1 . GetBytes ( headerValueUtf16Latin1CrossOver ) . AsSpan ( ) ;
477
477
478
478
Assert . False ( nextSpan . SequenceEqual ( Encoding . ASCII . GetBytes ( headerValueUtf16Latin1CrossOver ) ) ) ;
479
479
@@ -517,19 +517,24 @@ public void Latin1ValuesAcceptedInLatin1ModeButNotReused(bool reuseValue, KnownH
517
517
headerValueUtf16Latin1CrossOver = new string ( headerValue . AsSpan ( ) . Slice ( 0 , i + 1 ) ) ;
518
518
}
519
519
520
- headers . Reset ( ) ;
521
-
522
520
var headerName = Encoding . ASCII . GetBytes ( header . Name ) . AsSpan ( ) ;
523
- var latinValueSpan = Encoding . GetEncoding ( "iso-8859-1" ) . GetBytes ( headerValueUtf16Latin1CrossOver ) . AsSpan ( ) ;
521
+ var latinValueSpan = Encoding . Latin1 . GetBytes ( headerValueUtf16Latin1CrossOver ) . AsSpan ( ) ;
524
522
525
523
Assert . False ( latinValueSpan . SequenceEqual ( Encoding . ASCII . GetBytes ( headerValueUtf16Latin1CrossOver ) ) ) ;
526
524
525
+ headers . Reset ( ) ;
526
+ headers . Append ( headerName , latinValueSpan ) ;
527
+ headers . OnHeadersComplete ( ) ;
528
+ var parsedHeaderValue1 = ( ( IHeaderDictionary ) headers ) [ header . Name ] . ToString ( ) ;
529
+
530
+ headers . Reset ( ) ;
527
531
headers . Append ( headerName , latinValueSpan ) ;
528
532
headers . OnHeadersComplete ( ) ;
529
- var parsedHeaderValue = ( ( IHeaderDictionary ) headers ) [ header . Name ] . ToString ( ) ;
533
+ var parsedHeaderValue2 = ( ( IHeaderDictionary ) headers ) [ header . Name ] . ToString ( ) ;
530
534
531
- Assert . Equal ( headerValueUtf16Latin1CrossOver , parsedHeaderValue ) ;
532
- Assert . NotSame ( headerValueUtf16Latin1CrossOver , parsedHeaderValue ) ;
535
+ Assert . Equal ( headerValueUtf16Latin1CrossOver , parsedHeaderValue1 ) ;
536
+ Assert . Equal ( parsedHeaderValue1 , parsedHeaderValue2 ) ;
537
+ Assert . NotSame ( parsedHeaderValue1 , parsedHeaderValue2 ) ;
533
538
}
534
539
535
540
// Reset back to Ascii
@@ -541,11 +546,12 @@ public void Latin1ValuesAcceptedInLatin1ModeButNotReused(bool reuseValue, KnownH
541
546
[ MemberData ( nameof ( KnownRequestHeaders ) ) ]
542
547
public void NullCharactersRejectedInUTF8AndLatin1Mode ( bool useLatin1 , KnownHeader header )
543
548
{
544
- var selector = useLatin1 ?
545
- KestrelServerOptions . DefaultLatin1RequestHeaderEncodingSelector :
546
- KestrelServerOptions . DefaultRequestHeaderEncodingSelector ;
549
+ var kso = new KestrelServerOptions
550
+ {
551
+ Latin1RequestHeaders = useLatin1 ,
552
+ } ;
547
553
548
- var headers = new HttpRequestHeaders ( encodingSelector : selector ) ;
554
+ var headers = new HttpRequestHeaders ( encodingSelector : kso . GetRequestHeaderEncodingSelector ( ) ) ;
549
555
550
556
var valueArray = new char [ 127 ] ; // 64 + 32 + 16 + 8 + 4 + 2 + 1
551
557
for ( var i = 0 ; i < valueArray . Length ; i ++ )
@@ -573,6 +579,53 @@ public void NullCharactersRejectedInUTF8AndLatin1Mode(bool useLatin1, KnownHeade
573
579
}
574
580
}
575
581
582
+ [ Fact ]
583
+ public void CanSpecifyEncodingBasedOnHeaderName ( )
584
+ {
585
+ const string headerValue = "Hello \u03a0 " ;
586
+ var acceptNameBytes = Encoding . ASCII . GetBytes ( HeaderNames . Accept ) ;
587
+ var cookieNameBytes = Encoding . ASCII . GetBytes ( HeaderNames . Cookie ) ;
588
+ var headerValueBytes = Encoding . UTF8 . GetBytes ( headerValue ) ;
589
+
590
+ var headers = new HttpRequestHeaders ( encodingSelector : headerName =>
591
+ {
592
+ // For known headers, the HeaderNames value is passed in.
593
+ if ( ReferenceEquals ( headerName , HeaderNames . Accept ) )
594
+ {
595
+ return Encoding . GetEncoding ( "ASCII" , EncoderFallback . ExceptionFallback , DecoderFallback . ExceptionFallback ) ;
596
+ }
597
+
598
+ return Encoding . UTF8 ;
599
+ } ) ;
600
+
601
+ Assert . Throws < InvalidOperationException > ( ( ) => headers . Append ( acceptNameBytes , headerValueBytes ) ) ;
602
+ headers . Append ( cookieNameBytes , headerValueBytes ) ;
603
+ headers . OnHeadersComplete ( ) ;
604
+
605
+ var parsedAcceptHeaderValue = ( ( IHeaderDictionary ) headers ) [ HeaderNames . Accept ] . ToString ( ) ;
606
+ var parsedCookieHeaderValue = ( ( IHeaderDictionary ) headers ) [ HeaderNames . Cookie ] . ToString ( ) ;
607
+
608
+ Assert . Empty ( parsedAcceptHeaderValue ) ;
609
+ Assert . Equal ( headerValue , parsedCookieHeaderValue ) ;
610
+ }
611
+
612
+ [ Fact ]
613
+ public void CanSpecifyEncodingForContentLength ( )
614
+ {
615
+ var contentLengthNameBytes = Encoding . ASCII . GetBytes ( HeaderNames . ContentLength ) ;
616
+ // Always 32 bits per code point, so not a superset of ASCII
617
+ var contentLengthValueBytes = Encoding . UTF32 . GetBytes ( "1337" ) ;
618
+
619
+ var headers = new HttpRequestHeaders ( encodingSelector : _ => Encoding . UTF32 ) ;
620
+ headers . Append ( contentLengthNameBytes , contentLengthValueBytes ) ;
621
+ headers . OnHeadersComplete ( ) ;
622
+
623
+ Assert . Equal ( 1337 , headers . ContentLength ) ;
624
+
625
+ Assert . Throws < InvalidOperationException > ( ( ) =>
626
+ new HttpRequestHeaders ( ) . Append ( contentLengthNameBytes , contentLengthValueBytes ) ) ;
627
+ }
628
+
576
629
[ Fact ]
577
630
public void ValueReuseNeverWhenUnknownHeader ( )
578
631
{
0 commit comments