Percent encode/decode header values
This commit is contained in:
parent
e1c0d2942c
commit
3544222065
2 changed files with 47 additions and 9 deletions
|
@ -102,16 +102,44 @@ fn parseHeaders(allocator: std.mem.Allocator, reader: anytype) !Fields {
|
|||
error.StreamTooLong => return error.HeaderLineTooLong,
|
||||
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;
|
||||
|
||||
const name = std.mem.sliceTo(line, ':');
|
||||
if (!isTokenValid(name)) 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;
|
||||
|
|
|
@ -39,21 +39,31 @@ fn writeStatusLine(writer: anytype, status: Status) !void {
|
|||
fn writeFields(writer: anytype, headers: *const Fields) !void {
|
||||
var iter = headers.iterator();
|
||||
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;
|
||||
|
||||
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");
|
||||
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 {
|
||||
try writer.print("{x}\r\n", .{contents.len});
|
||||
try writer.writeAll(contents);
|
||||
|
|
Loading…
Reference in a new issue