@@ -1564,13 +1564,17 @@ const Reader = struct {
15641564
15651565 header_done : bool ,
15661566
1567+ // Whether or not the current header has to be skipped [because it's too long].
1568+ skip_current_header : bool ,
1569+
15671570 fn init (state : * State , keepalive : * bool ) Reader {
15681571 return .{
15691572 .pos = 0 ,
15701573 .response = .{},
15711574 .body_reader = null ,
15721575 .header_done = false ,
15731576 .keepalive = keepalive ,
1577+ .skip_current_header = false ,
15741578 .header_buf = state .header_buf ,
15751579 .arena = state .arena .allocator (),
15761580 };
@@ -1610,6 +1614,17 @@ const Reader = struct {
16101614 var done = false ;
16111615 var unprocessed = data ;
16121616
1617+ if (self .skip_current_header ) {
1618+ const index = std .mem .indexOfScalarPos (u8 , data , 0 , '\n ' ) orelse {
1619+ // discard all of this data, since it belongs to a header we
1620+ // want to skip
1621+ return .{ .done = false , .data = null , .unprocessed = null };
1622+ };
1623+ self .pos = 0 ;
1624+ self .skip_current_header = false ;
1625+ unprocessed = data [index + 1 .. ];
1626+ }
1627+
16131628 // Data from a previous call to process that we weren't able to parse
16141629 const pos = self .pos ;
16151630 const header_buf = self .header_buf ;
@@ -1624,21 +1639,26 @@ const Reader = struct {
16241639 // data doesn't represent a complete header line. We need more data
16251640 const end = pos + data .len ;
16261641 if (end > header_buf .len ) {
1627- return error .HeaderTooLarge ;
1642+ self .prepareToSkipLongHeader ();
1643+ } else {
1644+ self .pos = end ;
1645+ @memcpy (self .header_buf [pos .. end ], data );
16281646 }
1629- self .pos = end ;
1630- @memcpy (self .header_buf [pos .. end ], data );
16311647 return .{ .done = false , .data = null , .unprocessed = null };
16321648 }) + 1 ;
16331649
16341650 const end = pos + line_end ;
16351651 if (end > header_buf .len ) {
1636- return error .HeaderTooLarge ;
1652+ unprocessed = &.{};
1653+ self .prepareToSkipLongHeader ();
1654+ // we can disable this immediately, since we've essentially
1655+ // finished skipping it this point.
1656+ self .skip_current_header = false ;
1657+ } else {
1658+ @memcpy (header_buf [pos .. end ], data [0.. line_end ]);
1659+ done , unprocessed = try self .parseHeader (header_buf [0.. end ]);
16371660 }
16381661
1639- @memcpy (header_buf [pos .. end ], data [0.. line_end ]);
1640- done , unprocessed = try self .parseHeader (header_buf [0.. end ]);
1641-
16421662 // we gave parseHeader exactly 1 header line, there should be no leftovers
16431663 std .debug .assert (unprocessed .len == 0 );
16441664
@@ -1661,10 +1681,11 @@ const Reader = struct {
16611681 const p = self .pos ; // don't use pos, self.pos might have been altered
16621682 const end = p + unprocessed .len ;
16631683 if (end > header_buf .len ) {
1664- return error .HeaderTooLarge ;
1684+ self .prepareToSkipLongHeader ();
1685+ } else {
1686+ @memcpy (header_buf [p .. end ], unprocessed );
1687+ self .pos = end ;
16651688 }
1666- @memcpy (header_buf [p .. end ], unprocessed );
1667- self .pos = end ;
16681689 return .{ .done = false , .data = null , .unprocessed = null };
16691690 }
16701691 }
@@ -1724,6 +1745,13 @@ const Reader = struct {
17241745 return .{ .done = false , .data = null , .unprocessed = null };
17251746 }
17261747
1748+ fn prepareToSkipLongHeader (self : * Reader ) void {
1749+ self .skip_current_header = true ;
1750+ const buf = self .header_buf ;
1751+ const pos = std .mem .indexOfScalar (u8 , buf , ':' ) orelse @min (buf .len , 20 );
1752+ log .warn (.http_client , "skipping long header" , .{ .name = buf [0.. pos ] });
1753+ }
1754+
17271755 // returns true when done
17281756 // returns any remaining unprocessed data
17291757 // When done == true, the remaining data must belong to the body
@@ -1770,6 +1798,15 @@ const Reader = struct {
17701798 };
17711799 const name_end = pos + sep ;
17721800
1801+ if (value_end - pos > MAX_HEADER_LINE_LEN ) {
1802+ // at this point, we could return this header, but then it would
1803+ // be inconsistent with long headers that are split up and need
1804+ // to be buffered.
1805+ log .warn (.http_client , "skipping long header" , .{ .name = data [pos .. name_end ] });
1806+ pos = value_end + 1 ;
1807+ continue ;
1808+ }
1809+
17731810 const value_start = name_end + 1 ;
17741811
17751812 if (value_end == value_start or data [value_end - 1 ] != '\r ' ) {
@@ -2481,9 +2518,11 @@ test "HttpClient Reader: fuzz" {
24812518 }
24822519
24832520 {
2484- // header too big
2485- const data = "HTTP/1.1 200 OK\r \n " ++ ("a" ** 1500 );
2486- try testing .expectError (error .HeaderTooLarge , testReader (& state , & res , data ));
2521+ // skips large headers
2522+ const data = "HTTP/1.1 200 OK\r \n a: b\r \n " ++ ("a" ** 5000 ) ++ ": wow\r \n x:zz\r \n \r \n " ;
2523+ try testReader (& state , & res , data );
2524+ try testing .expectEqual (200 , res .status );
2525+ try res .assertHeaders (&.{ "a" , "b" , "x" , "zz" });
24872526 }
24882527 }
24892528}
0 commit comments