Skip to content

Commit 147a4bf

Browse files
committed
Build the read-ahead mechanism into SftpFileStream
1 parent 2f0ae31 commit 147a4bf

File tree

38 files changed

+767
-4670
lines changed

38 files changed

+767
-4670
lines changed

src/Renci.SshNet/Common/Extensions.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Globalization;
44
#if !NET
55
using System.IO;
6+
using System.Threading.Tasks;
67
#endif
78
using System.Net;
89
using System.Net.Sockets;
@@ -409,6 +410,29 @@ internal static void ReadExactly(this Stream stream, byte[] buffer, int offset,
409410
totalRead += read;
410411
}
411412
}
413+
414+
internal static Task<T> WaitAsync<T>(this Task<T> task, CancellationToken cancellationToken)
415+
{
416+
if (task.IsCompleted || !cancellationToken.CanBeCanceled)
417+
{
418+
return task;
419+
}
420+
421+
return WaitCore();
422+
423+
async Task<T> WaitCore()
424+
{
425+
TaskCompletionSource<T> tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
426+
427+
using var reg = cancellationToken.Register(
428+
() => tcs.TrySetCanceled(cancellationToken),
429+
useSynchronizationContext: false);
430+
431+
var completedTask = await Task.WhenAny(task, tcs.Task).ConfigureAwait(false);
432+
433+
return await completedTask.ConfigureAwait(false);
434+
}
435+
}
412436
#endif
413437
}
414438
}

src/Renci.SshNet/IServiceFactory.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,18 +83,6 @@ internal partial interface IServiceFactory
8383
/// <exception cref="SshConnectionException">No key exchange algorithm is supported by both client and server.</exception>
8484
IKeyExchange CreateKeyExchange(IDictionary<string, Func<IKeyExchange>> clientAlgorithms, string[] serverAlgorithms);
8585

86-
/// <summary>
87-
/// Creates an <see cref="ISftpFileReader"/> for the specified file and with the specified
88-
/// buffer size.
89-
/// </summary>
90-
/// <param name="fileName">The file to read.</param>
91-
/// <param name="sftpSession">The SFTP session to use.</param>
92-
/// <param name="bufferSize">The size of buffer.</param>
93-
/// <returns>
94-
/// An <see cref="ISftpFileReader"/>.
95-
/// </returns>
96-
ISftpFileReader CreateSftpFileReader(string fileName, ISftpSession sftpSession, uint bufferSize);
97-
9886
/// <summary>
9987
/// Creates a new <see cref="ISftpResponseFactory"/> instance.
10088
/// </summary>

src/Renci.SshNet/ServiceFactory.cs

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
using System.Net.Sockets;
55
using System.Text;
66

7-
using Microsoft.Extensions.Logging;
8-
97
using Renci.SshNet.Common;
108
using Renci.SshNet.Connection;
119
using Renci.SshNet.Messages.Transport;
@@ -118,51 +116,6 @@ public INetConfSession CreateNetConfSession(ISession session, int operationTimeo
118116
return new NetConfSession(session, operationTimeout);
119117
}
120118

121-
/// <summary>
122-
/// Creates an <see cref="ISftpFileReader"/> for the specified file and with the specified
123-
/// buffer size.
124-
/// </summary>
125-
/// <param name="fileName">The file to read.</param>
126-
/// <param name="sftpSession">The SFTP session to use.</param>
127-
/// <param name="bufferSize">The size of buffer.</param>
128-
/// <returns>
129-
/// An <see cref="ISftpFileReader"/>.
130-
/// </returns>
131-
public ISftpFileReader CreateSftpFileReader(string fileName, ISftpSession sftpSession, uint bufferSize)
132-
{
133-
const int defaultMaxPendingReads = 10;
134-
135-
// Issue #292: Avoid overlapping SSH_FXP_OPEN and SSH_FXP_LSTAT requests for the same file as this
136-
// causes a performance degradation on Sun SSH
137-
var openAsyncResult = sftpSession.BeginOpen(fileName, Flags.Read, callback: null, state: null);
138-
var handle = sftpSession.EndOpen(openAsyncResult);
139-
140-
var statAsyncResult = sftpSession.BeginLStat(fileName, callback: null, state: null);
141-
142-
long? fileSize;
143-
int maxPendingReads;
144-
145-
var chunkSize = sftpSession.CalculateOptimalReadLength(bufferSize);
146-
147-
// fallback to a default maximum of pending reads when remote server does not allow us to obtain
148-
// the attributes of the file
149-
try
150-
{
151-
var fileAttributes = sftpSession.EndLStat(statAsyncResult);
152-
fileSize = fileAttributes.Size;
153-
maxPendingReads = Math.Min(100, (int)Math.Ceiling((double)fileAttributes.Size / chunkSize) + 1);
154-
}
155-
catch (SshException ex)
156-
{
157-
fileSize = null;
158-
maxPendingReads = defaultMaxPendingReads;
159-
160-
sftpSession.SessionLoggerFactory.CreateLogger<ServiceFactory>().LogInformation(ex, "Failed to obtain size of file. Allowing maximum {MaxPendingReads} pending reads", maxPendingReads);
161-
}
162-
163-
return sftpSession.CreateFileReader(handle, sftpSession, chunkSize, maxPendingReads, fileSize);
164-
}
165-
166119
/// <summary>
167120
/// Creates a new <see cref="ISftpResponseFactory"/> instance.
168121
/// </summary>

src/Renci.SshNet/Sftp/ISftpFileReader.cs

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/Renci.SshNet/Sftp/ISftpSession.cs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ internal interface ISftpSession : ISubsystemSession
7171
/// <returns>
7272
/// The file attributes.
7373
/// </returns>
74-
SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError);
74+
SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError = false);
7575

7676
/// <summary>
7777
/// Asynchronously performs a <c>SSH_FXP_FSTAT</c> request.
@@ -522,19 +522,5 @@ void RequestWrite(byte[] handle,
522522
/// Currently, we do not take the remote window size into account.
523523
/// </remarks>
524524
uint CalculateOptimalWriteLength(uint bufferSize, byte[] handle);
525-
526-
/// <summary>
527-
/// Creates an <see cref="ISftpFileReader"/> for reading the content of the file represented by a given <paramref name="handle"/>.
528-
/// </summary>
529-
/// <param name="handle">The handle of the file to read.</param>
530-
/// <param name="sftpSession">The SFTP session.</param>
531-
/// <param name="chunkSize">The maximum number of bytes to read with each chunk.</param>
532-
/// <param name="maxPendingReads">The maximum number of pending reads.</param>
533-
/// <param name="fileSize">The size of the file or <see langword="null"/> when the size could not be determined.</param>
534-
/// <returns>
535-
/// An <see cref="ISftpFileReader"/> for reading the content of the file represented by the
536-
/// specified <paramref name="handle"/>.
537-
/// </returns>
538-
ISftpFileReader CreateFileReader(byte[] handle, ISftpSession sftpSession, uint chunkSize, int maxPendingReads, long? fileSize);
539525
}
540526
}

0 commit comments

Comments
 (0)