44#nullable enable
55using System . Collections . Generic ;
66using System . Diagnostics ;
7+ using System . Text ;
78
89namespace System . Net . Http . HPack
910{
@@ -96,7 +97,7 @@ public static bool EncodeLiteralHeaderFieldWithoutIndexing(int index, string val
9697 if ( IntegerEncoder . Encode ( index , 4 , destination , out int indexLength ) )
9798 {
9899 Debug . Assert ( indexLength >= 1 ) ;
99- if ( EncodeStringLiteral ( value , destination . Slice ( indexLength ) , out int nameLength ) )
100+ if ( EncodeStringLiteral ( value , valueEncoding : null , destination . Slice ( indexLength ) , out int nameLength ) )
100101 {
101102 bytesWritten = indexLength + nameLength ;
102103 return true ;
@@ -128,7 +129,7 @@ public static bool EncodeLiteralHeaderFieldNeverIndexing(int index, string value
128129 if ( IntegerEncoder . Encode ( index , 4 , destination , out int indexLength ) )
129130 {
130131 Debug . Assert ( indexLength >= 1 ) ;
131- if ( EncodeStringLiteral ( value , destination . Slice ( indexLength ) , out int nameLength ) )
132+ if ( EncodeStringLiteral ( value , valueEncoding : null , destination . Slice ( indexLength ) , out int nameLength ) )
132133 {
133134 bytesWritten = indexLength + nameLength ;
134135 return true ;
@@ -160,7 +161,7 @@ public static bool EncodeLiteralHeaderFieldIndexing(int index, string value, Spa
160161 if ( IntegerEncoder . Encode ( index , 6 , destination , out int indexLength ) )
161162 {
162163 Debug . Assert ( indexLength >= 1 ) ;
163- if ( EncodeStringLiteral ( value , destination . Slice ( indexLength ) , out int nameLength ) )
164+ if ( EncodeStringLiteral ( value , valueEncoding : null , destination . Slice ( indexLength ) , out int nameLength ) )
164165 {
165166 bytesWritten = indexLength + nameLength ;
166167 return true ;
@@ -276,7 +277,7 @@ private static bool EncodeLiteralHeaderNewNameCore(byte mask, string name, strin
276277 {
277278 destination [ 0 ] = mask ;
278279 if ( EncodeLiteralHeaderName ( name , destination . Slice ( 1 ) , out int nameLength ) &&
279- EncodeStringLiteral ( value , destination . Slice ( 1 + nameLength ) , out int valueLength ) )
280+ EncodeStringLiteral ( value , valueEncoding : null , destination . Slice ( 1 + nameLength ) , out int valueLength ) )
280281 {
281282 bytesWritten = 1 + nameLength + valueLength ;
282283 return true ;
@@ -289,6 +290,11 @@ private static bool EncodeLiteralHeaderNewNameCore(byte mask, string name, strin
289290
290291 /// <summary>Encodes a "Literal Header Field without Indexing - New Name".</summary>
291292 public static bool EncodeLiteralHeaderFieldWithoutIndexingNewName ( string name , ReadOnlySpan < string > values , string separator , Span < byte > destination , out int bytesWritten )
293+ {
294+ return EncodeLiteralHeaderFieldWithoutIndexingNewName ( name , values , separator , valueEncoding : null , destination , out bytesWritten ) ;
295+ }
296+
297+ public static bool EncodeLiteralHeaderFieldWithoutIndexingNewName ( string name , ReadOnlySpan < string > values , string separator , Encoding ? valueEncoding , Span < byte > destination , out int bytesWritten )
292298 {
293299 // From https://tools.ietf.org/html/rfc7541#section-6.2.2
294300 // ------------------------------------------------------
@@ -309,7 +315,7 @@ public static bool EncodeLiteralHeaderFieldWithoutIndexingNewName(string name, R
309315 {
310316 destination [ 0 ] = 0 ;
311317 if ( EncodeLiteralHeaderName ( name , destination . Slice ( 1 ) , out int nameLength ) &&
312- EncodeStringLiterals ( values , separator , destination . Slice ( 1 + nameLength ) , out int valueLength ) )
318+ EncodeStringLiterals ( values , separator , valueEncoding , destination . Slice ( 1 + nameLength ) , out int valueLength ) )
313319 {
314320 bytesWritten = 1 + nameLength + valueLength ;
315321 return true ;
@@ -395,27 +401,20 @@ private static bool EncodeLiteralHeaderName(string value, Span<byte> destination
395401 return false ;
396402 }
397403
398- private static bool EncodeStringLiteralValue ( string value , Span < byte > destination , out int bytesWritten )
404+ private static void EncodeValueStringPart ( string value , Span < byte > destination )
399405 {
400- if ( value . Length <= destination . Length )
406+ Debug . Assert ( destination . Length >= value . Length ) ;
407+
408+ for ( int i = 0 ; i < value . Length ; i ++ )
401409 {
402- for ( int i = 0 ; i < value . Length ; i ++ )
410+ char c = value [ i ] ;
411+ if ( ( c & 0xFF80 ) != 0 )
403412 {
404- char c = value [ i ] ;
405- if ( ( c & 0xFF80 ) != 0 )
406- {
407- throw new HttpRequestException ( SR . net_http_request_invalid_char_encoding ) ;
408- }
409-
410- destination [ i ] = ( byte ) c ;
413+ throw new HttpRequestException ( SR . net_http_request_invalid_char_encoding ) ;
411414 }
412415
413- bytesWritten = value . Length ;
414- return true ;
416+ destination [ i ] = ( byte ) c ;
415417 }
416-
417- bytesWritten = 0 ;
418- return false ;
419418 }
420419
421420 public static bool EncodeStringLiteral ( ReadOnlySpan < byte > value , Span < byte > destination , out int bytesWritten )
@@ -453,6 +452,11 @@ public static bool EncodeStringLiteral(ReadOnlySpan<byte> value, Span<byte> dest
453452 }
454453
455454 public static bool EncodeStringLiteral ( string value , Span < byte > destination , out int bytesWritten )
455+ {
456+ return EncodeStringLiteral ( value , valueEncoding : null , destination , out bytesWritten ) ;
457+ }
458+
459+ public static bool EncodeStringLiteral ( string value , Encoding ? valueEncoding , Span < byte > destination , out int bytesWritten )
456460 {
457461 // From https://tools.ietf.org/html/rfc7541#section-5.2
458462 // ------------------------------------------------------
@@ -466,13 +470,28 @@ public static bool EncodeStringLiteral(string value, Span<byte> destination, out
466470 if ( destination . Length != 0 )
467471 {
468472 destination [ 0 ] = 0 ; // TODO: Use Huffman encoding
469- if ( IntegerEncoder . Encode ( value . Length , 7 , destination , out int integerLength ) )
473+
474+ int encodedStringLength = valueEncoding is null || ReferenceEquals ( valueEncoding , Encoding . Latin1 )
475+ ? value . Length
476+ : valueEncoding . GetByteCount ( value ) ;
477+
478+ if ( IntegerEncoder . Encode ( encodedStringLength , 7 , destination , out int integerLength ) )
470479 {
471480 Debug . Assert ( integerLength >= 1 ) ;
472-
473- if ( EncodeStringLiteralValue ( value , destination . Slice ( integerLength ) , out int valueLength ) )
481+ destination = destination . Slice ( integerLength ) ;
482+ if ( encodedStringLength <= destination . Length )
474483 {
475- bytesWritten = integerLength + valueLength ;
484+ if ( valueEncoding is null )
485+ {
486+ EncodeValueStringPart ( value , destination ) ;
487+ }
488+ else
489+ {
490+ int written = valueEncoding . GetBytes ( value , destination ) ;
491+ Debug . Assert ( written == encodedStringLength ) ;
492+ }
493+
494+ bytesWritten = integerLength + encodedStringLength ;
476495 return true ;
477496 }
478497 }
@@ -502,56 +521,87 @@ public static bool EncodeDynamicTableSizeUpdate(int value, Span<byte> destinatio
502521 }
503522
504523 public static bool EncodeStringLiterals ( ReadOnlySpan < string > values , string ? separator , Span < byte > destination , out int bytesWritten )
524+ {
525+ return EncodeStringLiterals ( values , separator , valueEncoding : null , destination , out bytesWritten ) ;
526+ }
527+
528+ public static bool EncodeStringLiterals ( ReadOnlySpan < string > values , string ? separator , Encoding ? valueEncoding , Span < byte > destination , out int bytesWritten )
505529 {
506530 bytesWritten = 0 ;
507531
508532 if ( values . Length == 0 )
509533 {
510- return EncodeStringLiteral ( "" , destination , out bytesWritten ) ;
534+ return EncodeStringLiteral ( "" , valueEncoding : null , destination , out bytesWritten ) ;
511535 }
512536 else if ( values . Length == 1 )
513537 {
514- return EncodeStringLiteral ( values [ 0 ] , destination , out bytesWritten ) ;
538+ return EncodeStringLiteral ( values [ 0 ] , valueEncoding , destination , out bytesWritten ) ;
515539 }
516540
517541 if ( destination . Length != 0 )
518542 {
519- int valueLength = 0 ;
543+ Debug . Assert ( separator != null ) ;
544+ int valueLength ;
520545
521546 // Calculate length of all parts and separators.
522- foreach ( string part in values )
547+ if ( valueEncoding is null || ReferenceEquals ( valueEncoding , Encoding . Latin1 ) )
523548 {
524- valueLength = checked ( ( int ) ( valueLength + part . Length ) ) ;
549+ valueLength = checked ( ( int ) ( values . Length - 1 ) * separator . Length ) ;
550+ foreach ( string part in values )
551+ {
552+ valueLength = checked ( ( int ) ( valueLength + part . Length ) ) ;
553+ }
554+ }
555+ else
556+ {
557+ valueLength = checked ( ( int ) ( values . Length - 1 ) * valueEncoding . GetByteCount ( separator ) ) ;
558+ foreach ( string part in values )
559+ {
560+ valueLength = checked ( ( int ) ( valueLength + valueEncoding . GetByteCount ( part ) ) ) ;
561+ }
525562 }
526-
527- Debug . Assert ( separator != null ) ;
528- valueLength = checked ( ( int ) ( valueLength + ( values . Length - 1 ) * separator . Length ) ) ;
529563
530564 destination [ 0 ] = 0 ;
531565 if ( IntegerEncoder . Encode ( valueLength , 7 , destination , out int integerLength ) )
532566 {
533567 Debug . Assert ( integerLength >= 1 ) ;
534-
535- int encodedLength = 0 ;
536- for ( int j = 0 ; j < values . Length ; j ++ )
568+ destination = destination . Slice ( integerLength ) ;
569+ if ( destination . Length >= valueLength )
537570 {
538- if ( j != 0 && ! EncodeStringLiteralValue ( separator , destination . Slice ( integerLength ) , out encodedLength ) )
571+ if ( valueEncoding is null )
539572 {
540- return false ;
573+ string value = values [ 0 ] ;
574+ EncodeValueStringPart ( value , destination ) ;
575+ destination = destination . Slice ( value . Length ) ;
576+
577+ for ( int i = 1 ; i < values . Length ; i ++ )
578+ {
579+ EncodeValueStringPart ( separator , destination ) ;
580+ destination = destination . Slice ( separator . Length ) ;
581+
582+ value = values [ i ] ;
583+ EncodeValueStringPart ( value , destination ) ;
584+ destination = destination . Slice ( value . Length ) ;
585+ }
541586 }
587+ else
588+ {
589+ int written = valueEncoding . GetBytes ( values [ 0 ] , destination ) ;
590+ destination = destination . Slice ( written ) ;
542591
543- integerLength += encodedLength ;
592+ for ( int i = 1 ; i < values . Length ; i ++ )
593+ {
594+ written = valueEncoding . GetBytes ( separator , destination ) ;
595+ destination = destination . Slice ( written ) ;
544596
545- if ( ! EncodeStringLiteralValue ( values [ j ] , destination . Slice ( integerLength ) , out encodedLength ) )
546- {
547- return false ;
597+ written = valueEncoding . GetBytes ( values [ i ] , destination ) ;
598+ destination = destination . Slice ( written ) ;
599+ }
548600 }
549601
550- integerLength += encodedLength ;
602+ bytesWritten = integerLength + valueLength ;
603+ return true ;
551604 }
552-
553- bytesWritten = integerLength ;
554- return true ;
555605 }
556606 }
557607
0 commit comments