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,
|
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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue