Public Access
Terminal: deliver large pastes whole, keep keystrokes in order, and keep one session's failure off the shared socket
- A message larger than the 64 KiB receive buffer was skipped and its tail dispatched as a frame of its own. Messages are now reassembled up to 16 MiB; one beyond that is dropped whole. - Input frames are handed to the pump without waiting, and ShellStream takes no lock around its write buffer, so a snippet's text and its carriage return could interleave or lose bytes. Writes are serialised per pump in arrival order. - A resize reaching a shell SSH.NET had already disposed threw out of the receive loop and closed the socket every tab shares. Each frame's failure now stays with its session.
This commit is contained in:
1 parent
6846f67c20
commit
2ff9dff560
4 files changed
+205
-11
No files matched your search
@@ -78,6 +78,14 @@ internal sealed class FakeShellSession : ISshShellSession
|
||||
/// <summary>The last size the pump forwarded, or null if it forwarded none.</summary>
|
||||
public TerminalSize? LastResize { get; private set; }
|
||||
|
||||
/// <summary>How long the first write takes, so a test can see whether a second one overtakes it.</summary>
|
||||
public TimeSpan FirstWriteDelay { get; init; }
|
||||
|
||||
/// <summary>Whether a resize throws, as SSH.NET's does once it has disposed the shell's stream.</summary>
|
||||
public bool ResizeThrows { get; init; }
|
||||
|
||||
private int writes;
|
||||
|
||||
/// <summary>How many resizes were forwarded, so a dropped one is observable.</summary>
|
||||
public int ResizeCount { get; private set; }
|
||||
|
||||
@@ -119,19 +127,27 @@ internal sealed class FakeShellSession : ISshShellSession
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask WriteAsync(ReadOnlyMemory<byte> data, CancellationToken cancellationToken)
|
||||
public async ValueTask WriteAsync(ReadOnlyMemory<byte> data, CancellationToken cancellationToken)
|
||||
{
|
||||
if (Interlocked.Increment(ref writes) == 1 && FirstWriteDelay > TimeSpan.Zero)
|
||||
{
|
||||
await Task.Delay(FirstWriteDelay, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
lock (gate)
|
||||
{
|
||||
written.AddRange(data.ToArray());
|
||||
}
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Resize(TerminalSize size)
|
||||
{
|
||||
if (ResizeThrows)
|
||||
{
|
||||
throw new ObjectDisposedException("ShellStream");
|
||||
}
|
||||
|
||||
// Mirrors the real session: an unusable size is dropped rather than forwarded.
|
||||
if (!size.IsUsable)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user