Percent encode/decode header values

This commit is contained in:
jaina heartles 2022-11-21 00:54:03 -08:00
parent e1c0d2942c
commit 3544222065
2 changed files with 47 additions and 9 deletions

View file

@ -102,16 +102,44 @@ fn parseHeaders(allocator: std.mem.Allocator, reader: anytype) !Fields {
error.StreamTooLong => return error.HeaderLineTooLong, error.StreamTooLong => return error.HeaderLineTooLong,
else => return err, else => return err,
}; };
const line = std.mem.trimRight(u8, full_line, "\r"); const line = if (full_line.len != 0 and full_line[full_line.len - 1] == '\r')
full_line[0 .. full_line.len - 1]
else
full_line;
if (line.len == 0) break; if (line.len == 0) break;
const name = std.mem.sliceTo(line, ':'); const name = std.mem.sliceTo(line, ':');
if (!isTokenValid(name)) return error.BadRequest; if (!isTokenValid(name)) return error.BadRequest;
if (name.len == line.len) return error.BadRequest; if (name.len == line.len) return error.BadRequest;
const value = std.mem.trim(u8, line[name.len + 1 ..], " \t"); const encoded_value = line[name.len + 1 ..];
const decoded_value = blk: {
var ii: usize = 0;
var io: usize = 0;
while (ii < encoded_value.len) : ({
ii += 1;
io += 1;
}) {
switch (encoded_value[ii]) {
'\r', '\n', 0 => return error.BadRequest,
else => {},
}
try headers.append(name, value); if (encoded_value[ii] == '%') {
if (encoded_value.len < ii + 2) return error.BadRequest;
const ch_buf = [2]u8{ encoded_value[ii + 1], encoded_value[ii + 2] };
encoded_value[io] = try std.fmt.parseInt(u8, &ch_buf, 16);
ii += 2;
} else {
encoded_value[io] = encoded_value[ii];
}
}
break :blk encoded_value[0..io];
};
const val = std.mem.trim(u8, decoded_value, " \t");
try headers.append(name, val);
} }
return headers; return headers;

View file

@ -39,21 +39,31 @@ fn writeStatusLine(writer: anytype, status: Status) !void {
fn writeFields(writer: anytype, headers: *const Fields) !void { fn writeFields(writer: anytype, headers: *const Fields) !void {
var iter = headers.iterator(); var iter = headers.iterator();
while (iter.next()) |header| { while (iter.next()) |header| {
for (header.value_ptr.*) |ch| {
if (ch == '\r' or ch == '\n') @panic("newlines not yet supported in headers");
}
if (std.ascii.eqlIgnoreCase("Set-Cookie", header.key_ptr.*)) continue; if (std.ascii.eqlIgnoreCase("Set-Cookie", header.key_ptr.*)) continue;
try writer.print("{s}: {s}\r\n", .{ header.key_ptr.*, header.value_ptr.* }); try writer.print("{s}: {s}\r\n", .{ header.key_ptr.*, percentEncode(header.value_ptr.*) });
} }
var cookie_iter = headers.getList("Set-Cookie"); var cookie_iter = headers.getList("Set-Cookie");
while (cookie_iter.next()) |cookie| { while (cookie_iter.next()) |cookie| {
try writer.print("Set-Cookie: {s}\r\n", .{cookie}); try writer.print("Set-Cookie: {s}\r\n", .{percentEncode(cookie)});
} }
} }
const PercentEncode = struct {
str: []const u8,
pub fn format(v: PercentEncode, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
for (v.str) |ch| switch (ch) {
' ', '\t', 0x21...0x7e, 0x80...0xff => try writer.writeByte(ch),
else => try std.fmt.format(writer, "%{x:0>2}", .{ch}),
};
}
};
fn percentEncode(str: []const u8) PercentEncode {
return PercentEncode{ .str = str };
}
fn writeChunk(writer: anytype, contents: []const u8) @TypeOf(writer).Error!void { fn writeChunk(writer: anytype, contents: []const u8) @TypeOf(writer).Error!void {
try writer.print("{x}\r\n", .{contents.len}); try writer.print("{x}\r\n", .{contents.len});
try writer.writeAll(contents); try writer.writeAll(contents);