SFTP CreateDirectoryAsync is not idempotent
还没有人认领这个 Issue。
评估
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 新手友好度
- 38/100
- Issue 类型
- 功能
- 描述清晰度
- 基本清楚
- 活跃度
- 冷清
- 技术栈
- csharp
- 领域
- networking
调研方向
从 SftpClient.CreateDirectoryAsync 及其现有的 XML 文档开始,然后将其行为与 ExistsAsync 以及目录已存在时报告的 SFTP 响应进行比较。确定所请求的幂等行为或更具体的异常是否符合协议;完成的标准是调用方能够将已存在的目录与其他失败区分开来。
由索引模型根据 Issue 内容生成。
描述
My specifications:
OS: Ubuntu Desktop and Windows 11
Framework: .NET 9
Package version: 2025.1.0
This is my current code:
using SftpClient client = new SftpClient(host, port, username, password);
await client.ConnectAsync(cancellationToken);
try
{
await client.CreateDirectoryAsync(someDir, cancellationToken);
}
catch (SftpException ex)
{
// Only some logging code here.
}
When this directory already exists, all I get as an exception message is "failure".
Renci.SshNet.Common.SftpException: failure
at Renci.SshNet.SubsystemSession.<WaitOnHandleAsync> g__DoWaitAsync | 38_0[T](TaskCompletionSource`1 tcs, Int32 millisecondsTimeout, CancellationToken cancellationToken)
at Renci.SshNet.SftpClient.CreateDirectoryAsync(String path, CancellationToken cancellationToken)
Problems:
- I can't know for sure if the exception is about the directory already existing, or whether something else is wrong. Type SftpException with message "failure" is just not descriptive enough.
- CreateDirectoryAsync is not idempotent. Throwing an exception seems very counter-intuitive for a method like this.
- The SftpException is not described in the method documentation:
/// <summary>
/// Asynchronously requests to create a remote directory specified by path.
/// </summary>
/// <param name="path">Directory path to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
/// <returns>A <see cref="Task"/> that represents the asynchronous create directory operation.</returns>
/// <exception cref="ArgumentException"><paramref name="path"/> is <see langword="null"/> or contains only whitespace characters.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
/// <exception cref="SftpPermissionDeniedException">Permission to create the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
/// <exception cref="SshException">A SSH error where <see cref="Exception.Message"/> is the message from the remote host.</exception>
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
If I would want to do this properly, I need quite a lot of code. This is the extension method I wrote:
internal static async ValueTask EnsureDirectoryExists(this SftpClient client, string directory, CancellationToken cancellationToken)
{
bool directoryExists = await client.ExistsAsync(directory, cancellationToken);
if (directoryExists) return;
try
{
await client.CreateDirectoryAsync(directory, cancellationToken);
}
catch (SftpException ex)
{
try
{
bool directoryAlreadyExisted= await client.ExistsAsync(directory, cancellationToken);
if (directoryAlreadyExisted)
{
// The directory already existed, so we can ignore the exception.
return;
}
else
{
// The directory does not exist.
throw;
}
}
catch (SftpException)
{
// Failed checking if the directory exists.
// Throwing the original exception as that illustrates what really is the problem here.
throw ex;
}
}
}
There are 2 ways this could be fixed:
- Add an EnsureDirectoryExists method. I'm sure this can be done much closer to the actual SFTP protocol than the extension method I created.
- Add an SftpDirectoryAlreadyExistsException that inherits from SftpException.
I'm very curious, is there a reason why something like EnsureDirectoryExists was never added to SSH.NET?
- 主要语言
- C#
- 星标
- 4.4k
- 派生
- 993
- 平均合并
- 9 天 21 小时
- 30 天内合并 PR
- 1
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
sshnet/SSH.NET 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 62/100
-
难度 3/5 1-2 天 新手友好度 68/100
-
难度 4/5 3-5 天 新手友好度 48/100
-
难度 3/5 1-2 天 新手友好度 67/100
-
难度 4/5 3-5 天 新手友好度 55/100
相似的 Issue
-
untriaged
难度 1/5 1 小时以内 新手友好度 88/100
dotnet/dotnet-api-docs#13095 ·
-
area-deployment area-integrations triage:bot-seen
难度 2/5 半天 新手友好度 86/100
-
type/automation type/tech-debt
难度 2/5 1-3 小时 新手友好度 84/100
-
bug
难度 1/5 1 小时以内 新手友好度 90/100
newrelic/newrelic-dotnet-agent#3850 · 1 条评论 ·
-
难度 2/5 1-3 小时 新手友好度 88/100
LuckyPennySoftware/AutoMapper#4660 ·