76 lines
2.6 KiB
Zig
76 lines
2.6 KiB
Zig
const std = @import("std");
|
|
|
|
pub const Transport = struct {
|
|
in: *std.Io.Reader,
|
|
out_file: std.Io.File,
|
|
io: std.Io,
|
|
allocator: std.mem.Allocator,
|
|
|
|
pub fn init(allocator: std.mem.Allocator, io: std.Io, in: *std.Io.Reader, out_file: std.Io.File) Transport {
|
|
return .{
|
|
.in = in,
|
|
.out_file = out_file,
|
|
.io = io,
|
|
.allocator = allocator,
|
|
};
|
|
}
|
|
|
|
/// Read one LSP message: parse Content-Length header, read body.
|
|
pub fn readMessage(self: *Transport) ![]const u8 {
|
|
var content_length: ?usize = null;
|
|
|
|
// Parse headers (terminated by \r\n\r\n)
|
|
while (true) {
|
|
const line = try self.readLine();
|
|
if (line.len == 0) break; // empty line = end of headers
|
|
|
|
if (std.mem.startsWith(u8, line, "Content-Length: ")) {
|
|
content_length = std.fmt.parseInt(usize, line["Content-Length: ".len..], 10) catch
|
|
return error.InvalidContentLength;
|
|
}
|
|
}
|
|
|
|
const len = content_length orelse return error.MissingContentLength;
|
|
|
|
const body = try self.allocator.alloc(u8, len);
|
|
try self.in.readSliceAll(body);
|
|
|
|
return body;
|
|
}
|
|
|
|
/// Write one LSP message: Content-Length header + body.
|
|
pub fn writeMessage(self: *Transport, body: []const u8) !void {
|
|
var buf: [32]u8 = undefined;
|
|
const len_str = std.fmt.bufPrint(&buf, "{d}", .{body.len}) catch unreachable;
|
|
|
|
self.out_file.writeStreamingAll(self.io, "Content-Length: ") catch return error.WriteFailed;
|
|
self.out_file.writeStreamingAll(self.io, len_str) catch return error.WriteFailed;
|
|
self.out_file.writeStreamingAll(self.io, "\r\n\r\n") catch return error.WriteFailed;
|
|
self.out_file.writeStreamingAll(self.io, body) catch return error.WriteFailed;
|
|
}
|
|
|
|
/// Read a single line terminated by \r\n. Returns content without \r\n.
|
|
fn readLine(self: *Transport) ![]const u8 {
|
|
var buf = std.ArrayList(u8).empty;
|
|
while (true) {
|
|
const byte = self.in.takeByte() catch |err| switch (err) {
|
|
error.EndOfStream => {
|
|
if (buf.items.len == 0) return error.EndOfStream;
|
|
return buf.items;
|
|
},
|
|
else => return error.ReadFailed,
|
|
};
|
|
|
|
if (byte == '\n') {
|
|
const line = buf.items;
|
|
if (line.len > 0 and line[line.len - 1] == '\r') {
|
|
return line[0 .. line.len - 1];
|
|
}
|
|
return line;
|
|
}
|
|
|
|
try buf.append(self.allocator, byte);
|
|
}
|
|
}
|
|
};
|