2020-05-17 15:50:13 +01:00
|
|
|
const std = @import("std");
|
|
|
|
|
2022-12-27 06:47:57 +00:00
|
|
|
const Header = @This();
|
2020-05-17 15:50:13 +01:00
|
|
|
|
2022-12-27 06:47:57 +00:00
|
|
|
content_length: usize,
|
2020-05-17 15:50:13 +01:00
|
|
|
|
2022-12-27 06:47:57 +00:00
|
|
|
/// null implies "application/vscode-jsonrpc; charset=utf-8"
|
|
|
|
content_type: ?[]const u8 = null,
|
|
|
|
|
|
|
|
pub fn deinit(self: @This(), allocator: std.mem.Allocator) void {
|
|
|
|
if (self.content_type) |ct| allocator.free(ct);
|
|
|
|
}
|
2020-05-17 15:50:13 +01:00
|
|
|
|
2022-12-27 06:47:57 +00:00
|
|
|
// Caller owns returned memory.
|
2022-12-31 06:45:45 +00:00
|
|
|
pub fn parse(allocator: std.mem.Allocator, include_carriage_return: bool, reader: anytype) !Header {
|
2022-12-27 06:47:57 +00:00
|
|
|
var r = Header{
|
2020-05-17 15:50:13 +01:00
|
|
|
.content_length = undefined,
|
|
|
|
.content_type = null,
|
|
|
|
};
|
|
|
|
errdefer r.deinit(allocator);
|
|
|
|
|
|
|
|
var has_content_length = false;
|
|
|
|
while (true) {
|
2022-12-27 06:47:57 +00:00
|
|
|
const header = try reader.readUntilDelimiterAlloc(allocator, '\n', 0x100);
|
2020-05-17 15:50:13 +01:00
|
|
|
defer allocator.free(header);
|
2022-12-31 06:45:45 +00:00
|
|
|
if (include_carriage_return) {
|
|
|
|
if (header.len == 0 or header[header.len - 1] != '\r') return error.MissingCarriageReturn;
|
|
|
|
if (header.len == 1) break;
|
|
|
|
} else {
|
|
|
|
if (header.len == 0) break;
|
|
|
|
}
|
2020-05-17 15:50:13 +01:00
|
|
|
|
2021-10-01 02:47:19 +01:00
|
|
|
const header_name = header[0 .. std.mem.indexOf(u8, header, ": ") orelse return error.MissingColon];
|
2022-12-31 06:45:45 +00:00
|
|
|
const header_value = header[header_name.len + 2 .. header.len - @boolToInt(include_carriage_return)];
|
2021-10-01 02:47:19 +01:00
|
|
|
if (std.mem.eql(u8, header_name, "Content-Length")) {
|
2020-05-17 15:50:13 +01:00
|
|
|
if (header_value.len == 0) return error.MissingHeaderValue;
|
|
|
|
r.content_length = std.fmt.parseInt(usize, header_value, 10) catch return error.InvalidContentLength;
|
|
|
|
has_content_length = true;
|
2021-10-01 02:47:19 +01:00
|
|
|
} else if (std.mem.eql(u8, header_name, "Content-Type")) {
|
2021-11-30 16:50:02 +00:00
|
|
|
r.content_type = try allocator.dupe(u8, header_value);
|
2020-05-17 15:50:13 +01:00
|
|
|
} else {
|
|
|
|
return error.UnknownHeader;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!has_content_length) return error.MissingContentLength;
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
2022-12-27 06:47:57 +00:00
|
|
|
|
2022-12-31 06:45:45 +00:00
|
|
|
pub fn write(header: Header, include_carriage_return: bool, writer: anytype) @TypeOf(writer).Error!void {
|
|
|
|
const seperator: []const u8 = if (include_carriage_return) "\r\n" else "\n";
|
2023-01-02 19:59:01 +00:00
|
|
|
try writer.print("Content-Length: {}{s}", .{ header.content_length, seperator });
|
2022-12-27 06:47:57 +00:00
|
|
|
if (header.content_type) |content_type| {
|
2023-01-02 19:59:01 +00:00
|
|
|
try writer.print("Content-Type: {s}{s}", .{ content_type, seperator });
|
2022-12-27 06:47:57 +00:00
|
|
|
}
|
2022-12-31 06:45:45 +00:00
|
|
|
try writer.writeAll(seperator);
|
2022-12-27 06:47:57 +00:00
|
|
|
}
|