-
-
Notifications
You must be signed in to change notification settings - Fork 968
Remove unnecessary SftpFileStream unit tests and dedup Open{Async} #1680
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| using System; | ||
| using System.Diagnostics; | ||
| using System.Diagnostics.CodeAnalysis; | ||
| using System.Globalization; | ||
| using System.IO; | ||
|
|
@@ -191,7 +192,7 @@ public TimeSpan Timeout | |
| } | ||
| } | ||
|
|
||
| private SftpFileStream(ISftpSession session, string path, FileAccess access, int bufferSize, byte[] handle, long position) | ||
| private SftpFileStream(ISftpSession session, string path, FileAccess access, int readBufferSize, int writeBufferSize, byte[] handle, long position) | ||
| { | ||
| Timeout = TimeSpan.FromSeconds(30); | ||
| Name = path; | ||
|
|
@@ -202,25 +203,24 @@ private SftpFileStream(ISftpSession session, string path, FileAccess access, int | |
| _canWrite = (access & FileAccess.Write) == FileAccess.Write; | ||
|
|
||
| _handle = handle; | ||
| _readBufferSize = readBufferSize; | ||
| _writeBufferSize = writeBufferSize; | ||
| _position = position; | ||
| } | ||
|
|
||
| /* | ||
| * Instead of using the specified buffer size as is, we use it to calculate a buffer size | ||
| * that ensures we always receive or send the max. number of bytes in a single SSH_FXP_READ | ||
| * or SSH_FXP_WRITE message. | ||
| */ | ||
|
|
||
| _readBufferSize = (int)session.CalculateOptimalReadLength((uint)bufferSize); | ||
| _writeBufferSize = (int)session.CalculateOptimalWriteLength((uint)bufferSize, _handle); | ||
| internal static SftpFileStream Open(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize) | ||
| { | ||
| return Open(session, path, mode, access, bufferSize, isAsync: false, CancellationToken.None).GetAwaiter().GetResult(); | ||
| } | ||
|
|
||
| _position = position; | ||
| internal static Task<SftpFileStream> OpenAsync(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize, CancellationToken cancellationToken) | ||
| { | ||
| return Open(session, path, mode, access, bufferSize, isAsync: true, cancellationToken); | ||
| } | ||
|
|
||
| internal SftpFileStream(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize) | ||
| private static async Task<SftpFileStream> Open(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize, bool isAsync, CancellationToken cancellationToken) | ||
| { | ||
| if (session is null) | ||
| { | ||
| throw new SshConnectionException("Client not connected."); | ||
| } | ||
| Debug.Assert(isAsync || cancellationToken == default); | ||
|
|
||
| ThrowHelper.ThrowIfNull(path); | ||
|
|
||
|
|
@@ -229,14 +229,10 @@ internal SftpFileStream(ISftpSession session, string path, FileMode mode, FileAc | |
| throw new ArgumentOutOfRangeException(nameof(bufferSize), "Cannot be less than or equal to zero."); | ||
| } | ||
|
|
||
| Timeout = TimeSpan.FromSeconds(30); | ||
| Name = path; | ||
|
|
||
| // Initialize the object state. | ||
| _session = session; | ||
| _canRead = (access & FileAccess.Read) == FileAccess.Read; | ||
| _canSeek = true; | ||
| _canWrite = (access & FileAccess.Write) == FileAccess.Write; | ||
| if (session is null) | ||
| { | ||
| throw new SshConnectionException("Client not connected."); | ||
| } | ||
|
|
||
| var flags = Flags.None; | ||
|
|
||
|
|
@@ -284,16 +280,7 @@ internal SftpFileStream(ISftpSession session, string path, FileMode mode, FileAc | |
| flags |= Flags.Append | Flags.CreateNewOrOpen; | ||
| break; | ||
| case FileMode.Create: | ||
| _handle = _session.RequestOpen(path, flags | Flags.Truncate, nullOnError: true); | ||
| if (_handle is null) | ||
| { | ||
| flags |= Flags.CreateNew; | ||
| } | ||
| else | ||
| { | ||
| flags |= Flags.Truncate; | ||
| } | ||
|
|
||
| flags |= Flags.CreateNewOrOpen | Flags.Truncate; | ||
| break; | ||
| case FileMode.CreateNew: | ||
| flags |= Flags.CreateNew; | ||
|
|
@@ -310,127 +297,44 @@ internal SftpFileStream(ISftpSession session, string path, FileMode mode, FileAc | |
| throw new ArgumentOutOfRangeException(nameof(mode)); | ||
| } | ||
|
|
||
| _handle ??= _session.RequestOpen(path, flags); | ||
| byte[] handle; | ||
|
|
||
| if (isAsync) | ||
| { | ||
| handle = await session.RequestOpenAsync(path, flags, cancellationToken).ConfigureAwait(false); | ||
| } | ||
| else | ||
| { | ||
| handle = session.RequestOpen(path, flags); | ||
| } | ||
|
|
||
| /* | ||
| * Instead of using the specified buffer size as is, we use it to calculate a buffer size | ||
| * that ensures we always receive or send the max. number of bytes in a single SSH_FXP_READ | ||
| * or SSH_FXP_WRITE message. | ||
| */ | ||
|
|
||
| _readBufferSize = (int)session.CalculateOptimalReadLength((uint)bufferSize); | ||
| _writeBufferSize = (int)session.CalculateOptimalWriteLength((uint)bufferSize, _handle); | ||
| var readBufferSize = (int)session.CalculateOptimalReadLength((uint)bufferSize); | ||
| var writeBufferSize = (int)session.CalculateOptimalWriteLength((uint)bufferSize, handle); | ||
|
|
||
| long position = 0; | ||
| if (mode == FileMode.Append) | ||
| { | ||
| var attributes = _session.RequestFStat(_handle, nullOnError: false); | ||
| _position = attributes.Size; | ||
| } | ||
| } | ||
|
|
||
| internal static async Task<SftpFileStream> OpenAsync(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize, CancellationToken cancellationToken) | ||
| { | ||
| if (session is null) | ||
| { | ||
| throw new SshConnectionException("Client not connected."); | ||
| } | ||
|
|
||
| ThrowHelper.ThrowIfNull(path); | ||
|
|
||
| if (bufferSize <= 0) | ||
| { | ||
| throw new ArgumentOutOfRangeException(nameof(bufferSize), "Cannot be less than or equal to zero."); | ||
| } | ||
|
|
||
| var flags = Flags.None; | ||
|
|
||
| switch (access) | ||
| { | ||
| case FileAccess.Read: | ||
| flags |= Flags.Read; | ||
| break; | ||
| case FileAccess.Write: | ||
| flags |= Flags.Write; | ||
| break; | ||
| case FileAccess.ReadWrite: | ||
| flags |= Flags.Read; | ||
| flags |= Flags.Write; | ||
| break; | ||
| default: | ||
| throw new ArgumentOutOfRangeException(nameof(access)); | ||
| } | ||
|
|
||
| if ((access & FileAccess.Read) == FileAccess.Read && mode == FileMode.Append) | ||
| { | ||
| throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, | ||
| "{0} mode can be requested only when combined with write-only access.", | ||
| mode.ToString("G")), | ||
| nameof(mode)); | ||
| } | ||
| SftpFileAttributes attributes; | ||
|
|
||
| if ((access & FileAccess.Write) != FileAccess.Write) | ||
| { | ||
| if (mode is FileMode.Create or FileMode.CreateNew or FileMode.Truncate or FileMode.Append) | ||
| if (isAsync) | ||
| { | ||
| throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, | ||
| "Combining {0}: {1} with {2}: {3} is invalid.", | ||
| nameof(FileMode), | ||
| mode, | ||
| nameof(FileAccess), | ||
| access), | ||
| nameof(mode)); | ||
| attributes = await session.RequestFStatAsync(handle, cancellationToken).ConfigureAwait(false); | ||
| } | ||
| } | ||
|
|
||
| switch (mode) | ||
| { | ||
| case FileMode.Append: | ||
| flags |= Flags.Append | Flags.CreateNewOrOpen; | ||
| break; | ||
| case FileMode.Create: | ||
| flags |= Flags.CreateNewOrOpen | Flags.Truncate; | ||
| break; | ||
| case FileMode.CreateNew: | ||
| flags |= Flags.CreateNew; | ||
| break; | ||
| case FileMode.Open: | ||
| break; | ||
| case FileMode.OpenOrCreate: | ||
| flags |= Flags.CreateNewOrOpen; | ||
| break; | ||
| case FileMode.Truncate: | ||
| flags |= Flags.Truncate; | ||
| break; | ||
| default: | ||
| throw new ArgumentOutOfRangeException(nameof(mode)); | ||
| } | ||
|
|
||
| var handle = await session.RequestOpenAsync(path, flags, cancellationToken).ConfigureAwait(false); | ||
|
|
||
| long position = 0; | ||
| if (mode == FileMode.Append) | ||
| { | ||
| try | ||
| else | ||
| { | ||
| var attributes = await session.RequestFStatAsync(handle, cancellationToken).ConfigureAwait(false); | ||
| position = attributes.Size; | ||
| attributes = session.RequestFStat(handle, nullOnError: false); | ||
| } | ||
| catch | ||
| { | ||
| try | ||
| { | ||
| await session.RequestCloseAsync(handle, cancellationToken).ConfigureAwait(false); | ||
| } | ||
| catch | ||
| { | ||
| // The original exception is presumably more informative, so we just ignore this one. | ||
| } | ||
|
Comment on lines
-413
to
-427
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the only other library change aside from my comment above is that we use the Open (not OpenAsync) version of the FileMode.Append handling, which doesn't try RequestClose on FStat failure. Seems sensible to call it, but given the sync method is probably used much more and there are seemingly no known issues (with this part), I forwent the RequestClose |
||
|
|
||
| throw; | ||
| } | ||
| position = attributes.Size; | ||
| } | ||
|
|
||
| return new SftpFileStream(session, path, access, bufferSize, handle, position); | ||
| return new SftpFileStream(session, path, access, readBufferSize, writeBufferSize, handle, position); | ||
| } | ||
|
|
||
| /// <summary> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is at best ineffectual and at worst a bug that we send
SSH_FXF_TRUNCwithoutSSH_FXF_CREAT(what we have asFlags.CreateNewOrOpen). Per the draft SFTPv3 spec (https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02#section-6.3),SSH_FXF_TRUNCmust be accompanied bySSH_FXF_CREAT. OpenAsync had the logic correct, so we use that one. I am wondering whether it has something to do with #1215