mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
Skip large header lines that don't fit into the header buffer.
https://github.com/lightpanda-io/browser/issues/672
This commit is contained in:
@@ -1564,6 +1564,9 @@ const Reader = struct {
|
|||||||
|
|
||||||
header_done: bool,
|
header_done: bool,
|
||||||
|
|
||||||
|
// Whether or not the current header has to be skipped [because it's too long].
|
||||||
|
skip_current_header: bool,
|
||||||
|
|
||||||
fn init(state: *State, keepalive: *bool) Reader {
|
fn init(state: *State, keepalive: *bool) Reader {
|
||||||
return .{
|
return .{
|
||||||
.pos = 0,
|
.pos = 0,
|
||||||
@@ -1571,6 +1574,7 @@ const Reader = struct {
|
|||||||
.body_reader = null,
|
.body_reader = null,
|
||||||
.header_done = false,
|
.header_done = false,
|
||||||
.keepalive = keepalive,
|
.keepalive = keepalive,
|
||||||
|
.skip_current_header = false,
|
||||||
.header_buf = state.header_buf,
|
.header_buf = state.header_buf,
|
||||||
.arena = state.arena.allocator(),
|
.arena = state.arena.allocator(),
|
||||||
};
|
};
|
||||||
@@ -1610,6 +1614,17 @@ const Reader = struct {
|
|||||||
var done = false;
|
var done = false;
|
||||||
var unprocessed = data;
|
var unprocessed = data;
|
||||||
|
|
||||||
|
if (self.skip_current_header) {
|
||||||
|
const index = std.mem.indexOfScalarPos(u8, data, 0, '\n') orelse {
|
||||||
|
// discard all of this data, since it belongs to a header we
|
||||||
|
// want to skip
|
||||||
|
return .{ .done = false, .data = null, .unprocessed = null };
|
||||||
|
};
|
||||||
|
self.pos = 0;
|
||||||
|
self.skip_current_header = false;
|
||||||
|
unprocessed = data[index + 1 ..];
|
||||||
|
}
|
||||||
|
|
||||||
// Data from a previous call to process that we weren't able to parse
|
// Data from a previous call to process that we weren't able to parse
|
||||||
const pos = self.pos;
|
const pos = self.pos;
|
||||||
const header_buf = self.header_buf;
|
const header_buf = self.header_buf;
|
||||||
@@ -1624,21 +1639,26 @@ const Reader = struct {
|
|||||||
// data doesn't represent a complete header line. We need more data
|
// data doesn't represent a complete header line. We need more data
|
||||||
const end = pos + data.len;
|
const end = pos + data.len;
|
||||||
if (end > header_buf.len) {
|
if (end > header_buf.len) {
|
||||||
return error.HeaderTooLarge;
|
self.prepareToSkipLongHeader();
|
||||||
|
} else {
|
||||||
|
self.pos = end;
|
||||||
|
@memcpy(self.header_buf[pos..end], data);
|
||||||
}
|
}
|
||||||
self.pos = end;
|
|
||||||
@memcpy(self.header_buf[pos..end], data);
|
|
||||||
return .{ .done = false, .data = null, .unprocessed = null };
|
return .{ .done = false, .data = null, .unprocessed = null };
|
||||||
}) + 1;
|
}) + 1;
|
||||||
|
|
||||||
const end = pos + line_end;
|
const end = pos + line_end;
|
||||||
if (end > header_buf.len) {
|
if (end > header_buf.len) {
|
||||||
return error.HeaderTooLarge;
|
unprocessed = &.{};
|
||||||
|
self.prepareToSkipLongHeader();
|
||||||
|
// we can disable this immediately, since we've essentially
|
||||||
|
// finished skipping it this point.
|
||||||
|
self.skip_current_header = false;
|
||||||
|
} else {
|
||||||
|
@memcpy(header_buf[pos..end], data[0..line_end]);
|
||||||
|
done, unprocessed = try self.parseHeader(header_buf[0..end]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@memcpy(header_buf[pos..end], data[0..line_end]);
|
|
||||||
done, unprocessed = try self.parseHeader(header_buf[0..end]);
|
|
||||||
|
|
||||||
// we gave parseHeader exactly 1 header line, there should be no leftovers
|
// we gave parseHeader exactly 1 header line, there should be no leftovers
|
||||||
std.debug.assert(unprocessed.len == 0);
|
std.debug.assert(unprocessed.len == 0);
|
||||||
|
|
||||||
@@ -1661,10 +1681,11 @@ const Reader = struct {
|
|||||||
const p = self.pos; // don't use pos, self.pos might have been altered
|
const p = self.pos; // don't use pos, self.pos might have been altered
|
||||||
const end = p + unprocessed.len;
|
const end = p + unprocessed.len;
|
||||||
if (end > header_buf.len) {
|
if (end > header_buf.len) {
|
||||||
return error.HeaderTooLarge;
|
self.prepareToSkipLongHeader();
|
||||||
|
} else {
|
||||||
|
@memcpy(header_buf[p..end], unprocessed);
|
||||||
|
self.pos = end;
|
||||||
}
|
}
|
||||||
@memcpy(header_buf[p..end], unprocessed);
|
|
||||||
self.pos = end;
|
|
||||||
return .{ .done = false, .data = null, .unprocessed = null };
|
return .{ .done = false, .data = null, .unprocessed = null };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1724,6 +1745,13 @@ const Reader = struct {
|
|||||||
return .{ .done = false, .data = null, .unprocessed = null };
|
return .{ .done = false, .data = null, .unprocessed = null };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prepareToSkipLongHeader(self: *Reader) void {
|
||||||
|
self.skip_current_header = true;
|
||||||
|
const buf = self.header_buf;
|
||||||
|
const pos = std.mem.indexOfScalar(u8, buf, ':') orelse @min(buf.len, 20);
|
||||||
|
log.warn(.http_client, "skipping long header", .{ .name = buf[0..pos] });
|
||||||
|
}
|
||||||
|
|
||||||
// returns true when done
|
// returns true when done
|
||||||
// returns any remaining unprocessed data
|
// returns any remaining unprocessed data
|
||||||
// When done == true, the remaining data must belong to the body
|
// When done == true, the remaining data must belong to the body
|
||||||
@@ -1770,6 +1798,15 @@ const Reader = struct {
|
|||||||
};
|
};
|
||||||
const name_end = pos + sep;
|
const name_end = pos + sep;
|
||||||
|
|
||||||
|
if (value_end - pos > MAX_HEADER_LINE_LEN) {
|
||||||
|
// at this point, we could return this header, but then it would
|
||||||
|
// be inconsistent with long headers that are split up and need
|
||||||
|
// to be buffered.
|
||||||
|
log.warn(.http_client, "skipping long header", .{ .name = data[pos..name_end] });
|
||||||
|
pos = value_end + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const value_start = name_end + 1;
|
const value_start = name_end + 1;
|
||||||
|
|
||||||
if (value_end == value_start or data[value_end - 1] != '\r') {
|
if (value_end == value_start or data[value_end - 1] != '\r') {
|
||||||
@@ -2481,9 +2518,11 @@ test "HttpClient Reader: fuzz" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// header too big
|
// skips large headers
|
||||||
const data = "HTTP/1.1 200 OK\r\n" ++ ("a" ** 1500);
|
const data = "HTTP/1.1 200 OK\r\na: b\r\n" ++ ("a" ** 5000) ++ ": wow\r\nx:zz\r\n\r\n";
|
||||||
try testing.expectError(error.HeaderTooLarge, testReader(&state, &res, data));
|
try testReader(&state, &res, data);
|
||||||
|
try testing.expectEqual(200, res.status);
|
||||||
|
try res.assertHeaders(&.{ "a", "b", "x", "zz" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user