Forge
csharpcf25d736
1using System.Buffers;
2using System.Text;
3using System.Text.Json;
4
5namespace CascadeIDE.Services.Lsp;
6
7/// <summary>Чтение/запись LSP по stdio (заголовок Content-Length + тело JSON UTF-8).</summary>
8internal static class LspStdioFraming
9{
10 public static async Task<JsonDocument?> ReadMessageAsync(Stream input, CancellationToken ct)
11 {
12 int? length = null;
13 while (true)
14 {
15 var line = await ReadHeaderLineAsync(input, ct).ConfigureAwait(false);
16 if (line is null)
17 return null;
18 if (line.Length == 0)
19 break;
20 const string prefix = "Content-Length:";
21 if (line.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
22 {
23 var n = line.AsSpan(prefix.Length).Trim();
24 if (int.TryParse(n, out var len) && len >= 0)
25 length = len;
26 }
27 }
28
29 if (length is null or <= 0)
30 return null;
31
32 var rent = ArrayPool<byte>.Shared.Rent(length.Value);
33 try
34 {
35 var mem = rent.AsMemory(0, length.Value);
36 var read = 0;
37 while (read < length.Value)
38 {
39 var n = await input.ReadAsync(mem[read..], ct).ConfigureAwait(false);
40 if (n == 0)
41 return null;
42 read += n;
43 }
44
45 var copy = new byte[length.Value];
46 rent.AsSpan(0, length.Value).CopyTo(copy);
47 return JsonDocument.Parse(copy);
48 }
49 finally
50 {
51 ArrayPool<byte>.Shared.Return(rent);
52 }
53 }
54
55 private static async Task<string?> ReadHeaderLineAsync(Stream input, CancellationToken ct)
56 {
57 var buf = new List<byte>(64);
58 while (true)
59 {
60 var b = new byte[1];
61 var n = await input.ReadAsync(b, ct).ConfigureAwait(false);
62 if (n == 0)
63 return buf.Count == 0 ? null : Encoding.ASCII.GetString(buf.ToArray());
64 if (b[0] == (byte)'\n')
65 {
66 var s = Encoding.ASCII.GetString(buf.ToArray());
67 if (s.Length > 0 && s[^1] == '\r')
68 s = s[..^1];
69 return s;
70 }
71
72 buf.Add(b[0]);
73 if (buf.Count > 65536)
74 return null;
75 }
76 }
77
78 public static async Task WriteMessageAsync(Stream output, ReadOnlyMemory<byte> utf8Body, CancellationToken ct)
79 {
80 var header = Encoding.ASCII.GetBytes($"Content-Length: {utf8Body.Length}\r\n\r\n");
81 await output.WriteAsync(header, ct).ConfigureAwait(false);
82 await output.WriteAsync(utf8Body, ct).ConfigureAwait(false);
83 await output.FlushAsync(ct).ConfigureAwait(false);
84 }
85
86 public static Task WriteJsonAsync(Stream output, JsonElement payload, CancellationToken ct)
87 {
88 var bytes = JsonSerializer.SerializeToUtf8Bytes(payload);
89 return WriteMessageAsync(output, bytes, ct);
90 }
91}
92
View only · write via MCP/CIDE