@@ -46,8 +46,13 @@ public class HPackDecoderTests
4646
4747 private const string _headerNameString = "new-header" ;
4848
49+ // On purpose longer than 4096 (DefaultStringOctetsSize from HPackDecoder) to trigger https://github.com/dotnet/runtime/issues/78516
50+ private static readonly string _literalHeaderNameString = string . Concat ( Enumerable . Range ( 0 , 4100 ) . Select ( c => ( char ) ( 'a' + ( c % 26 ) ) ) ) ;
51+
4952 private static readonly byte [ ] _headerNameBytes = Encoding . ASCII . GetBytes ( _headerNameString ) ;
5053
54+ private static readonly byte [ ] _literalHeaderNameBytes = Encoding . ASCII . GetBytes ( _literalHeaderNameString ) ;
55+
5156 // n e w - h e a d e r *
5257 // 10101000 10111110 00010110 10011100 10100011 10010000 10110110 01111111
5358 private static readonly byte [ ] _headerNameHuffmanBytes = new byte [ ] { 0xa8 , 0xbe , 0x16 , 0x9c , 0xa3 , 0x90 , 0xb6 , 0x7f } ;
@@ -64,6 +69,12 @@ public class HPackDecoderTests
6469 . Concat ( _headerNameBytes )
6570 . ToArray ( ) ;
6671
72+ // size = 4096 ==> 0x7f, 0x81, 0x1f (7+) prefixed integer
73+ // size = 4100 ==> 0x7f, 0x85, 0x1f (7+) prefixed integer
74+ private static readonly byte [ ] _literalHeaderName = new byte [ ] { 0x7f , 0x85 , 0x1f } // 4100
75+ . Concat ( _literalHeaderNameBytes )
76+ . ToArray ( ) ;
77+
6778 private static readonly byte [ ] _headerNameHuffman = new byte [ ] { ( byte ) ( 0x80 | _headerNameHuffmanBytes . Length ) }
6879 . Concat ( _headerNameHuffmanBytes )
6980 . ToArray ( ) ;
@@ -392,6 +403,101 @@ public void DecodesLiteralHeaderFieldNeverIndexed_IndexedName_OutOfRange_Error()
392403 Assert . Empty ( _handler . DecodedHeaders ) ;
393404 }
394405
406+ [ Fact ]
407+ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_SingleBuffer ( )
408+ {
409+ byte [ ] encoded = _literalHeaderFieldWithoutIndexingNewName
410+ . Concat ( _literalHeaderName )
411+ . Concat ( _headerValue )
412+ . ToArray ( ) ;
413+
414+ _decoder . Decode ( encoded , endHeaders : true , handler : _handler ) ;
415+
416+ Assert . Single ( _handler . DecodedHeaders ) ;
417+ Assert . True ( _handler . DecodedHeaders . ContainsKey ( _literalHeaderNameString ) ) ;
418+ Assert . Equal ( _headerValueString , _handler . DecodedHeaders [ _literalHeaderNameString ] ) ;
419+ }
420+
421+ [ Fact ]
422+ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_NameLengthBrokenIntoSeparateBuffers ( )
423+ {
424+ byte [ ] encoded = _literalHeaderFieldWithoutIndexingNewName
425+ . Concat ( _literalHeaderName )
426+ . Concat ( _headerValue )
427+ . ToArray ( ) ;
428+
429+ _decoder . Decode ( encoded [ ..1 ] , endHeaders : false , handler : _handler ) ;
430+ _decoder . Decode ( encoded [ 1 ..] , endHeaders : true , handler : _handler ) ;
431+
432+ Assert . Single ( _handler . DecodedHeaders ) ;
433+ Assert . True ( _handler . DecodedHeaders . ContainsKey ( _literalHeaderNameString ) ) ;
434+ Assert . Equal ( _headerValueString , _handler . DecodedHeaders [ _literalHeaderNameString ] ) ;
435+ }
436+
437+ [ Fact ]
438+ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_NameBrokenIntoSeparateBuffers ( )
439+ {
440+ byte [ ] encoded = _literalHeaderFieldWithoutIndexingNewName
441+ . Concat ( _literalHeaderName )
442+ . Concat ( _headerValue )
443+ . ToArray ( ) ;
444+
445+ _decoder . Decode ( encoded [ ..( _literalHeaderNameString . Length / 2 ) ] , endHeaders : false , handler : _handler ) ;
446+ _decoder . Decode ( encoded [ ( _literalHeaderNameString . Length / 2 ) ..] , endHeaders : true , handler : _handler ) ;
447+
448+ Assert . Single ( _handler . DecodedHeaders ) ;
449+ Assert . True ( _handler . DecodedHeaders . ContainsKey ( _literalHeaderNameString ) ) ;
450+ Assert . Equal ( _headerValueString , _handler . DecodedHeaders [ _literalHeaderNameString ] ) ;
451+ }
452+
453+ [ Fact ]
454+ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_NameAndValueBrokenIntoSeparateBuffers ( )
455+ {
456+ byte [ ] encoded = _literalHeaderFieldWithoutIndexingNewName
457+ . Concat ( _literalHeaderName )
458+ . Concat ( _headerValue )
459+ . ToArray ( ) ;
460+
461+ _decoder . Decode ( encoded [ ..^ _headerValue . Length ] , endHeaders : false , handler : _handler ) ;
462+ _decoder . Decode ( encoded [ ^ _headerValue . Length ..] , endHeaders : true , handler : _handler ) ;
463+
464+ Assert . Single ( _handler . DecodedHeaders ) ;
465+ Assert . True ( _handler . DecodedHeaders . ContainsKey ( _literalHeaderNameString ) ) ;
466+ Assert . Equal ( _headerValueString , _handler . DecodedHeaders [ _literalHeaderNameString ] ) ;
467+ }
468+
469+ [ Fact ]
470+ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_ValueLengthBrokenIntoSeparateBuffers ( )
471+ {
472+ byte [ ] encoded = _literalHeaderFieldWithoutIndexingNewName
473+ . Concat ( _literalHeaderName )
474+ . Concat ( _headerValue )
475+ . ToArray ( ) ;
476+
477+ _decoder . Decode ( encoded [ ..^ ( _headerValue . Length - 1 ) ] , endHeaders : false , handler : _handler ) ;
478+ _decoder . Decode ( encoded [ ^ ( _headerValue . Length - 1 ) ..] , endHeaders : true , handler : _handler ) ;
479+
480+ Assert . Single ( _handler . DecodedHeaders ) ;
481+ Assert . True ( _handler . DecodedHeaders . ContainsKey ( _literalHeaderNameString ) ) ;
482+ Assert . Equal ( _headerValueString , _handler . DecodedHeaders [ _literalHeaderNameString ] ) ;
483+ }
484+
485+ [ Fact ]
486+ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_ValueBrokenIntoSeparateBuffers ( )
487+ {
488+ byte [ ] encoded = _literalHeaderFieldWithoutIndexingNewName
489+ . Concat ( _literalHeaderName )
490+ . Concat ( _headerValue )
491+ . ToArray ( ) ;
492+
493+ _decoder . Decode ( encoded [ ..^ ( _headerValueString . Length / 2 ) ] , endHeaders : false , handler : _handler ) ;
494+ _decoder . Decode ( encoded [ ^ ( _headerValueString . Length / 2 ) ..] , endHeaders : true , handler : _handler ) ;
495+
496+ Assert . Single ( _handler . DecodedHeaders ) ;
497+ Assert . True ( _handler . DecodedHeaders . ContainsKey ( _literalHeaderNameString ) ) ;
498+ Assert . Equal ( _headerValueString , _handler . DecodedHeaders [ _literalHeaderNameString ] ) ;
499+ }
500+
395501 [ Fact ]
396502 public void DecodesDynamicTableSizeUpdate ( )
397503 {
@@ -500,10 +606,8 @@ public void DecodesStringLength_ExceedsLimit_Throws()
500606 string string8191 = new string ( 'a' , MaxHeaderFieldSize - 1 ) ;
501607 string string8193 = new string ( 'a' , MaxHeaderFieldSize + 1 ) ;
502608 string string8194 = new string ( 'a' , MaxHeaderFieldSize + 2 ) ;
503-
504609 var bytes = new byte [ 3 ] ;
505610 var success = IntegerEncoder . Encode ( 8194 , 7 , bytes , out var written ) ;
506-
507611 byte [ ] encoded = _literalHeaderFieldWithoutIndexingNewName
508612 . Concat ( new byte [ ] { 0x7f , 0x80 , 0x3f } ) // 8191 encoded with 7-bit prefix, no Huffman encoding
509613 . Concat ( Encoding . ASCII . GetBytes ( string8191 ) )
@@ -520,14 +624,12 @@ public void DecodesStringLength_ExceedsLimit_Throws()
520624 . Concat ( new byte [ ] { 0x7f , 0x83 , 0x3f } ) // 8194 encoded with 7-bit prefix, no Huffman encoding
521625 . Concat ( Encoding . ASCII . GetBytes ( string8194 ) )
522626 . ToArray ( ) ;
523-
524627 var ex = Assert . Throws < HPackDecodingException > ( ( ) => decoder . Decode ( encoded , endHeaders : true , handler : _handler ) ) ;
525628 Assert . Equal ( SR . Format ( SR . net_http_headers_exceeded_length , MaxHeaderFieldSize + 1 ) , ex . Message ) ;
526629 Assert . Equal ( string8191 , _handler . DecodedHeaders [ string8191 ] ) ;
527630 Assert . Equal ( string8193 , _handler . DecodedHeaders [ string8193 ] ) ;
528631 Assert . False ( _handler . DecodedHeaders . ContainsKey ( string8194 ) ) ;
529632 }
530-
531633 [ Fact ]
532634 public void DecodesStringLength_IndividualBytes ( )
533635 {
0 commit comments