const std = @import("std"); const RequestHeader = struct { content_length: usize, /// null implies "application/vscode-jsonrpc; charset=utf-8" content_type: ?[]const u8, pub fn deinit(self: @This(), allocator: *std.mem.Allocator) void { if (self.content_type) |ct| allocator.free(ct); } }; pub fn readRequestHeader(allocator: *std.mem.Allocator, instream: anytype) !RequestHeader { var r = RequestHeader{ .content_length = undefined, .content_type = null, }; errdefer r.deinit(allocator); var has_content_length = false; while (true) { const header = try instream.readUntilDelimiterAlloc(allocator, '\n', 0x100); defer allocator.free(header); if (header.len == 0 or header[header.len - 1] != '\r') return error.MissingCarriageReturn; if (header.len == 1) break; const header_name = header[0 .. std.mem.indexOf(u8, header, ": ") orelse return error.MissingColon]; const header_value = header[header_name.len + 2 .. header.len - 1]; if (std.mem.eql(u8, header_name, "Content-Length")) { 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; } else if (std.mem.eql(u8, header_name, "Content-Type")) { r.content_type = try allocator.dupe(u8, header_value); } else { return error.UnknownHeader; } } if (!has_content_length) return error.MissingContentLength; return r; }